162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 462306a36Sopenharmony_ci * Driver EMU10K1X chips 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Parts of this code were adapted from audigyls.c driver which is 762306a36Sopenharmony_ci * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * BUGS: 1062306a36Sopenharmony_ci * -- 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * TODO: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Chips (SB0200 model): 1562306a36Sopenharmony_ci * - EMU10K1X-DBQ 1662306a36Sopenharmony_ci * - STAC 9708T 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/pci.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <sound/core.h> 2562306a36Sopenharmony_ci#include <sound/initval.h> 2662306a36Sopenharmony_ci#include <sound/pcm.h> 2762306a36Sopenharmony_ci#include <sound/ac97_codec.h> 2862306a36Sopenharmony_ci#include <sound/info.h> 2962306a36Sopenharmony_ci#include <sound/rawmidi.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciMODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>"); 3262306a36Sopenharmony_ciMODULE_DESCRIPTION("EMU10K1X"); 3362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci// module parameters (see "Module Parameters") 3662306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 3762306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 3862306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard."); 4262306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 4362306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the EMU10K1X soundcard."); 4462306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 4562306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard."); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci// some definitions were borrowed from emu10k1 driver as they seem to be the same 4962306a36Sopenharmony_ci/************************************************************************************************/ 5062306a36Sopenharmony_ci/* PCI function 0 registers, address = <val> + PCIBASE0 */ 5162306a36Sopenharmony_ci/************************************************************************************************/ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define PTR 0x00 /* Indexed register set pointer register */ 5462306a36Sopenharmony_ci /* NOTE: The CHANNELNUM and ADDRESS words can */ 5562306a36Sopenharmony_ci /* be modified independently of each other. */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define DATA 0x04 /* Indexed register set data register */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define IPR 0x08 /* Global interrupt pending register */ 6062306a36Sopenharmony_ci /* Clear pending interrupts by writing a 1 to */ 6162306a36Sopenharmony_ci /* the relevant bits and zero to the other bits */ 6262306a36Sopenharmony_ci#define IPR_MIDITRANSBUFEMPTY 0x00000001 /* MIDI UART transmit buffer empty */ 6362306a36Sopenharmony_ci#define IPR_MIDIRECVBUFEMPTY 0x00000002 /* MIDI UART receive buffer empty */ 6462306a36Sopenharmony_ci#define IPR_CH_0_LOOP 0x00000800 /* Channel 0 loop */ 6562306a36Sopenharmony_ci#define IPR_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */ 6662306a36Sopenharmony_ci#define IPR_CAP_0_LOOP 0x00080000 /* Channel capture loop */ 6762306a36Sopenharmony_ci#define IPR_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define INTE 0x0c /* Interrupt enable register */ 7062306a36Sopenharmony_ci#define INTE_MIDITXENABLE 0x00000001 /* Enable MIDI transmit-buffer-empty interrupts */ 7162306a36Sopenharmony_ci#define INTE_MIDIRXENABLE 0x00000002 /* Enable MIDI receive-buffer-empty interrupts */ 7262306a36Sopenharmony_ci#define INTE_CH_0_LOOP 0x00000800 /* Channel 0 loop */ 7362306a36Sopenharmony_ci#define INTE_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */ 7462306a36Sopenharmony_ci#define INTE_CAP_0_LOOP 0x00080000 /* Channel capture loop */ 7562306a36Sopenharmony_ci#define INTE_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define HCFG 0x14 /* Hardware config register */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ 8062306a36Sopenharmony_ci /* NOTE: This should generally never be used. */ 8162306a36Sopenharmony_ci#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ 8262306a36Sopenharmony_ci /* Should be set to 1 when the EMU10K1 is */ 8362306a36Sopenharmony_ci /* completely initialized. */ 8462306a36Sopenharmony_ci#define GPIO 0x18 /* Defaults: 00001080-Analog, 00001000-SPDIF. */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/********************************************************************************************************/ 9262306a36Sopenharmony_ci/* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers */ 9362306a36Sopenharmony_ci/********************************************************************************************************/ 9462306a36Sopenharmony_ci#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */ 9562306a36Sopenharmony_ci /* One list entry: 4 bytes for DMA address, 9662306a36Sopenharmony_ci * 4 bytes for period_size << 16. 9762306a36Sopenharmony_ci * One list entry is 8 bytes long. 9862306a36Sopenharmony_ci * One list entry for each period in the buffer. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */ 10162306a36Sopenharmony_ci#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */ 10262306a36Sopenharmony_ci#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */ 10362306a36Sopenharmony_ci#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size */ 10462306a36Sopenharmony_ci#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Sample currently in DAC */ 10562306a36Sopenharmony_ci#define PLAYBACK_UNKNOWN1 0x07 10662306a36Sopenharmony_ci#define PLAYBACK_UNKNOWN2 0x08 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Only one capture channel supported */ 10962306a36Sopenharmony_ci#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */ 11062306a36Sopenharmony_ci#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */ 11162306a36Sopenharmony_ci#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */ 11262306a36Sopenharmony_ci#define CAPTURE_UNKNOWN 0x13 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* From 0x20 - 0x3f, last samples played on each channel */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define TRIGGER_CHANNEL 0x40 /* Trigger channel playback */ 11762306a36Sopenharmony_ci#define TRIGGER_CHANNEL_0 0x00000001 /* Trigger channel 0 */ 11862306a36Sopenharmony_ci#define TRIGGER_CHANNEL_1 0x00000002 /* Trigger channel 1 */ 11962306a36Sopenharmony_ci#define TRIGGER_CHANNEL_2 0x00000004 /* Trigger channel 2 */ 12062306a36Sopenharmony_ci#define TRIGGER_CAPTURE 0x00000100 /* Trigger capture channel */ 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define ROUTING 0x41 /* Setup sound routing ? */ 12362306a36Sopenharmony_ci#define ROUTING_FRONT_LEFT 0x00000001 12462306a36Sopenharmony_ci#define ROUTING_FRONT_RIGHT 0x00000002 12562306a36Sopenharmony_ci#define ROUTING_REAR_LEFT 0x00000004 12662306a36Sopenharmony_ci#define ROUTING_REAR_RIGHT 0x00000008 12762306a36Sopenharmony_ci#define ROUTING_CENTER_LFE 0x00010000 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define SPCS0 0x42 /* SPDIF output Channel Status 0 register */ 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define SPCS1 0x43 /* SPDIF output Channel Status 1 register */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci#define SPCS2 0x44 /* SPDIF output Channel Status 2 register */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ 13662306a36Sopenharmony_ci#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ 13762306a36Sopenharmony_ci#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ 13862306a36Sopenharmony_ci#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ 13962306a36Sopenharmony_ci#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ 14062306a36Sopenharmony_ci#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ 14162306a36Sopenharmony_ci#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ 14262306a36Sopenharmony_ci#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ 14362306a36Sopenharmony_ci#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ 14462306a36Sopenharmony_ci#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ 14562306a36Sopenharmony_ci#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ 14662306a36Sopenharmony_ci#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ 14762306a36Sopenharmony_ci#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ 14862306a36Sopenharmony_ci#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ 14962306a36Sopenharmony_ci#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ 15062306a36Sopenharmony_ci#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ 15162306a36Sopenharmony_ci#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ 15262306a36Sopenharmony_ci#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ 15362306a36Sopenharmony_ci#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ 15462306a36Sopenharmony_ci#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ 15562306a36Sopenharmony_ci#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ 15662306a36Sopenharmony_ci#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ 15762306a36Sopenharmony_ci#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define SPDIF_SELECT 0x45 /* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF */ 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* This is the MPU port on the card */ 16262306a36Sopenharmony_ci#define MUDATA 0x47 16362306a36Sopenharmony_ci#define MUCMD 0x48 16462306a36Sopenharmony_ci#define MUSTAT MUCMD 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* From 0x50 - 0x5f, last samples captured */ 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * The hardware has 3 channels for playback and 1 for capture. 17062306a36Sopenharmony_ci * - channel 0 is the front channel 17162306a36Sopenharmony_ci * - channel 1 is the rear channel 17262306a36Sopenharmony_ci * - channel 2 is the center/lfe channel 17362306a36Sopenharmony_ci * Volume is controlled by the AC97 for the front and rear channels by 17462306a36Sopenharmony_ci * the PCM Playback Volume, Sigmatel Surround Playback Volume and 17562306a36Sopenharmony_ci * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects 17662306a36Sopenharmony_ci * the front/rear channel mixing in the REAR OUT jack. When using the 17762306a36Sopenharmony_ci * 4-Speaker Stereo, both front and rear channels will be mixed in the 17862306a36Sopenharmony_ci * REAR OUT. 17962306a36Sopenharmony_ci * The center/lfe channel has no volume control and cannot be muted during 18062306a36Sopenharmony_ci * playback. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistruct emu10k1x_voice { 18462306a36Sopenharmony_ci struct emu10k1x *emu; 18562306a36Sopenharmony_ci int number; 18662306a36Sopenharmony_ci int use; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci struct emu10k1x_pcm *epcm; 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistruct emu10k1x_pcm { 19262306a36Sopenharmony_ci struct emu10k1x *emu; 19362306a36Sopenharmony_ci struct snd_pcm_substream *substream; 19462306a36Sopenharmony_ci struct emu10k1x_voice *voice; 19562306a36Sopenharmony_ci unsigned short running; 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistruct emu10k1x_midi { 19962306a36Sopenharmony_ci struct emu10k1x *emu; 20062306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 20162306a36Sopenharmony_ci struct snd_rawmidi_substream *substream_input; 20262306a36Sopenharmony_ci struct snd_rawmidi_substream *substream_output; 20362306a36Sopenharmony_ci unsigned int midi_mode; 20462306a36Sopenharmony_ci spinlock_t input_lock; 20562306a36Sopenharmony_ci spinlock_t output_lock; 20662306a36Sopenharmony_ci spinlock_t open_lock; 20762306a36Sopenharmony_ci int tx_enable, rx_enable; 20862306a36Sopenharmony_ci int port; 20962306a36Sopenharmony_ci int ipr_tx, ipr_rx; 21062306a36Sopenharmony_ci void (*interrupt)(struct emu10k1x *emu, unsigned int status); 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci// definition of the chip-specific record 21462306a36Sopenharmony_cistruct emu10k1x { 21562306a36Sopenharmony_ci struct snd_card *card; 21662306a36Sopenharmony_ci struct pci_dev *pci; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci unsigned long port; 21962306a36Sopenharmony_ci int irq; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci unsigned char revision; /* chip revision */ 22262306a36Sopenharmony_ci unsigned int serial; /* serial number */ 22362306a36Sopenharmony_ci unsigned short model; /* subsystem id */ 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci spinlock_t emu_lock; 22662306a36Sopenharmony_ci spinlock_t voice_lock; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci struct snd_ac97 *ac97; 22962306a36Sopenharmony_ci struct snd_pcm *pcm; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci struct emu10k1x_voice voices[3]; 23262306a36Sopenharmony_ci struct emu10k1x_voice capture_voice; 23362306a36Sopenharmony_ci u32 spdif_bits[3]; // SPDIF out setup 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci struct snd_dma_buffer *dma_buffer; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci struct emu10k1x_midi midi; 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci/* hardware definition */ 24162306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1x_playback_hw = { 24262306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 24362306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 24462306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 24562306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 24662306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 24762306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 24862306a36Sopenharmony_ci .rate_min = 48000, 24962306a36Sopenharmony_ci .rate_max = 48000, 25062306a36Sopenharmony_ci .channels_min = 2, 25162306a36Sopenharmony_ci .channels_max = 2, 25262306a36Sopenharmony_ci .buffer_bytes_max = (32*1024), 25362306a36Sopenharmony_ci .period_bytes_min = 64, 25462306a36Sopenharmony_ci .period_bytes_max = (16*1024), 25562306a36Sopenharmony_ci .periods_min = 2, 25662306a36Sopenharmony_ci .periods_max = 8, 25762306a36Sopenharmony_ci .fifo_size = 0, 25862306a36Sopenharmony_ci}; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1x_capture_hw = { 26162306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 26262306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 26362306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 26462306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 26562306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 26662306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 26762306a36Sopenharmony_ci .rate_min = 48000, 26862306a36Sopenharmony_ci .rate_max = 48000, 26962306a36Sopenharmony_ci .channels_min = 2, 27062306a36Sopenharmony_ci .channels_max = 2, 27162306a36Sopenharmony_ci .buffer_bytes_max = (32*1024), 27262306a36Sopenharmony_ci .period_bytes_min = 64, 27362306a36Sopenharmony_ci .period_bytes_max = (16*1024), 27462306a36Sopenharmony_ci .periods_min = 2, 27562306a36Sopenharmony_ci .periods_max = 2, 27662306a36Sopenharmony_ci .fifo_size = 0, 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic unsigned int snd_emu10k1x_ptr_read(struct emu10k1x * emu, 28062306a36Sopenharmony_ci unsigned int reg, 28162306a36Sopenharmony_ci unsigned int chn) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci unsigned long flags; 28462306a36Sopenharmony_ci unsigned int regptr, val; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci regptr = (reg << 16) | chn; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 28962306a36Sopenharmony_ci outl(regptr, emu->port + PTR); 29062306a36Sopenharmony_ci val = inl(emu->port + DATA); 29162306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 29262306a36Sopenharmony_ci return val; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void snd_emu10k1x_ptr_write(struct emu10k1x *emu, 29662306a36Sopenharmony_ci unsigned int reg, 29762306a36Sopenharmony_ci unsigned int chn, 29862306a36Sopenharmony_ci unsigned int data) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci unsigned int regptr; 30162306a36Sopenharmony_ci unsigned long flags; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci regptr = (reg << 16) | chn; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 30662306a36Sopenharmony_ci outl(regptr, emu->port + PTR); 30762306a36Sopenharmony_ci outl(data, emu->port + DATA); 30862306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci unsigned long flags; 31462306a36Sopenharmony_ci unsigned int intr_enable; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 31762306a36Sopenharmony_ci intr_enable = inl(emu->port + INTE) | intrenb; 31862306a36Sopenharmony_ci outl(intr_enable, emu->port + INTE); 31962306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci unsigned long flags; 32562306a36Sopenharmony_ci unsigned int intr_enable; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 32862306a36Sopenharmony_ci intr_enable = inl(emu->port + INTE) & ~intrenb; 32962306a36Sopenharmony_ci outl(intr_enable, emu->port + INTE); 33062306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic void snd_emu10k1x_gpio_write(struct emu10k1x *emu, unsigned int value) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci unsigned long flags; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 33862306a36Sopenharmony_ci outl(value, emu->port + GPIO); 33962306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void snd_emu10k1x_pcm_free_substream(struct snd_pcm_runtime *runtime) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci kfree(runtime->private_data); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void snd_emu10k1x_pcm_interrupt(struct emu10k1x *emu, struct emu10k1x_voice *voice) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct emu10k1x_pcm *epcm; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci epcm = voice->epcm; 35262306a36Sopenharmony_ci if (!epcm) 35362306a36Sopenharmony_ci return; 35462306a36Sopenharmony_ci if (epcm->substream == NULL) 35562306a36Sopenharmony_ci return; 35662306a36Sopenharmony_ci#if 0 35762306a36Sopenharmony_ci dev_info(emu->card->dev, 35862306a36Sopenharmony_ci "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", 35962306a36Sopenharmony_ci epcm->substream->ops->pointer(epcm->substream), 36062306a36Sopenharmony_ci snd_pcm_lib_period_bytes(epcm->substream), 36162306a36Sopenharmony_ci snd_pcm_lib_buffer_bytes(epcm->substream)); 36262306a36Sopenharmony_ci#endif 36362306a36Sopenharmony_ci snd_pcm_period_elapsed(epcm->substream); 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* open callback */ 36762306a36Sopenharmony_cistatic int snd_emu10k1x_playback_open(struct snd_pcm_substream *substream) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct emu10k1x *chip = snd_pcm_substream_chip(substream); 37062306a36Sopenharmony_ci struct emu10k1x_pcm *epcm; 37162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 37262306a36Sopenharmony_ci int err; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 37562306a36Sopenharmony_ci if (err < 0) 37662306a36Sopenharmony_ci return err; 37762306a36Sopenharmony_ci err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); 37862306a36Sopenharmony_ci if (err < 0) 37962306a36Sopenharmony_ci return err; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 38262306a36Sopenharmony_ci if (epcm == NULL) 38362306a36Sopenharmony_ci return -ENOMEM; 38462306a36Sopenharmony_ci epcm->emu = chip; 38562306a36Sopenharmony_ci epcm->substream = substream; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci runtime->private_data = epcm; 38862306a36Sopenharmony_ci runtime->private_free = snd_emu10k1x_pcm_free_substream; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci runtime->hw = snd_emu10k1x_playback_hw; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* close callback */ 39662306a36Sopenharmony_cistatic int snd_emu10k1x_playback_close(struct snd_pcm_substream *substream) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci return 0; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci/* hw_params callback */ 40262306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_params(struct snd_pcm_substream *substream, 40362306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 40662306a36Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (! epcm->voice) { 40962306a36Sopenharmony_ci epcm->voice = &epcm->emu->voices[substream->pcm->device]; 41062306a36Sopenharmony_ci epcm->voice->use = 1; 41162306a36Sopenharmony_ci epcm->voice->epcm = epcm; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci return 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/* hw_free callback */ 41862306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_free(struct snd_pcm_substream *substream) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 42162306a36Sopenharmony_ci struct emu10k1x_pcm *epcm; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (runtime->private_data == NULL) 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci epcm = runtime->private_data; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (epcm->voice) { 42962306a36Sopenharmony_ci epcm->voice->use = 0; 43062306a36Sopenharmony_ci epcm->voice->epcm = NULL; 43162306a36Sopenharmony_ci epcm->voice = NULL; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return 0; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/* prepare callback */ 43862306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 44162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 44262306a36Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 44362306a36Sopenharmony_ci int voice = epcm->voice->number; 44462306a36Sopenharmony_ci u32 *table_base = (u32 *)(emu->dma_buffer->area+1024*voice); 44562306a36Sopenharmony_ci u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size); 44662306a36Sopenharmony_ci int i; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci for(i = 0; i < runtime->periods; i++) { 44962306a36Sopenharmony_ci *table_base++=runtime->dma_addr+(i*period_size_bytes); 45062306a36Sopenharmony_ci *table_base++=period_size_bytes<<16; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer->addr+1024*voice); 45462306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19); 45562306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0); 45662306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0); 45762306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN1, voice, 0); 45862306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN2, voice, 0); 45962306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_DMA_ADDR, voice, runtime->dma_addr); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_PERIOD_SIZE, voice, frames_to_bytes(runtime, runtime->period_size)<<16); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* trigger callback */ 46762306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_trigger(struct snd_pcm_substream *substream, 46862306a36Sopenharmony_ci int cmd) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 47162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 47262306a36Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 47362306a36Sopenharmony_ci int channel = epcm->voice->number; 47462306a36Sopenharmony_ci int result = 0; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* 47762306a36Sopenharmony_ci dev_dbg(emu->card->dev, 47862306a36Sopenharmony_ci "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", 47962306a36Sopenharmony_ci (int)emu, cmd, (int)substream->ops->pointer(substream)); 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci switch (cmd) { 48362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 48462306a36Sopenharmony_ci if(runtime->periods == 2) 48562306a36Sopenharmony_ci snd_emu10k1x_intr_enable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel); 48662306a36Sopenharmony_ci else 48762306a36Sopenharmony_ci snd_emu10k1x_intr_enable(emu, INTE_CH_0_LOOP << channel); 48862306a36Sopenharmony_ci epcm->running = 1; 48962306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|(TRIGGER_CHANNEL_0<<channel)); 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 49262306a36Sopenharmony_ci epcm->running = 0; 49362306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel); 49462306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CHANNEL_0<<channel)); 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci default: 49762306a36Sopenharmony_ci result = -EINVAL; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci return result; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci/* pointer callback */ 50462306a36Sopenharmony_cistatic snd_pcm_uframes_t 50562306a36Sopenharmony_cisnd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 50862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 50962306a36Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 51062306a36Sopenharmony_ci int channel = epcm->voice->number; 51162306a36Sopenharmony_ci snd_pcm_uframes_t ptr = 0, ptr1 = 0, ptr2= 0,ptr3 = 0,ptr4 = 0; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!epcm->running) 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci ptr3 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel); 51762306a36Sopenharmony_ci ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel); 51862306a36Sopenharmony_ci ptr4 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if(ptr4 == 0 && ptr1 == frames_to_bytes(runtime, runtime->buffer_size)) 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (ptr3 != ptr4) 52462306a36Sopenharmony_ci ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel); 52562306a36Sopenharmony_ci ptr2 = bytes_to_frames(runtime, ptr1); 52662306a36Sopenharmony_ci ptr2 += (ptr4 >> 3) * runtime->period_size; 52762306a36Sopenharmony_ci ptr = ptr2; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (ptr >= runtime->buffer_size) 53062306a36Sopenharmony_ci ptr -= runtime->buffer_size; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return ptr; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* operators */ 53662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1x_playback_ops = { 53762306a36Sopenharmony_ci .open = snd_emu10k1x_playback_open, 53862306a36Sopenharmony_ci .close = snd_emu10k1x_playback_close, 53962306a36Sopenharmony_ci .hw_params = snd_emu10k1x_pcm_hw_params, 54062306a36Sopenharmony_ci .hw_free = snd_emu10k1x_pcm_hw_free, 54162306a36Sopenharmony_ci .prepare = snd_emu10k1x_pcm_prepare, 54262306a36Sopenharmony_ci .trigger = snd_emu10k1x_pcm_trigger, 54362306a36Sopenharmony_ci .pointer = snd_emu10k1x_pcm_pointer, 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* open_capture callback */ 54762306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_open_capture(struct snd_pcm_substream *substream) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct emu10k1x *chip = snd_pcm_substream_chip(substream); 55062306a36Sopenharmony_ci struct emu10k1x_pcm *epcm; 55162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 55262306a36Sopenharmony_ci int err; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 55562306a36Sopenharmony_ci if (err < 0) 55662306a36Sopenharmony_ci return err; 55762306a36Sopenharmony_ci err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); 55862306a36Sopenharmony_ci if (err < 0) 55962306a36Sopenharmony_ci return err; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 56262306a36Sopenharmony_ci if (epcm == NULL) 56362306a36Sopenharmony_ci return -ENOMEM; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci epcm->emu = chip; 56662306a36Sopenharmony_ci epcm->substream = substream; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci runtime->private_data = epcm; 56962306a36Sopenharmony_ci runtime->private_free = snd_emu10k1x_pcm_free_substream; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci runtime->hw = snd_emu10k1x_capture_hw; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* close callback */ 57762306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_close_capture(struct snd_pcm_substream *substream) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci return 0; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* hw_params callback */ 58362306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_params_capture(struct snd_pcm_substream *substream, 58462306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 58762306a36Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (! epcm->voice) { 59062306a36Sopenharmony_ci if (epcm->emu->capture_voice.use) 59162306a36Sopenharmony_ci return -EBUSY; 59262306a36Sopenharmony_ci epcm->voice = &epcm->emu->capture_voice; 59362306a36Sopenharmony_ci epcm->voice->epcm = epcm; 59462306a36Sopenharmony_ci epcm->voice->use = 1; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* hw_free callback */ 60162306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_free_capture(struct snd_pcm_substream *substream) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci struct emu10k1x_pcm *epcm; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (runtime->private_data == NULL) 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci epcm = runtime->private_data; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (epcm->voice) { 61262306a36Sopenharmony_ci epcm->voice->use = 0; 61362306a36Sopenharmony_ci epcm->voice->epcm = NULL; 61462306a36Sopenharmony_ci epcm->voice = NULL; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci return 0; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* prepare capture callback */ 62162306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_prepare_capture(struct snd_pcm_substream *substream) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 62462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_DMA_ADDR, 0, runtime->dma_addr); 62762306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_BUFFER_SIZE, 0, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes 62862306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_POINTER, 0, 0); 62962306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_UNKNOWN, 0, 0); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/* trigger_capture callback */ 63562306a36Sopenharmony_cistatic int snd_emu10k1x_pcm_trigger_capture(struct snd_pcm_substream *substream, 63662306a36Sopenharmony_ci int cmd) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 63962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 64062306a36Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 64162306a36Sopenharmony_ci int result = 0; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci switch (cmd) { 64462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 64562306a36Sopenharmony_ci snd_emu10k1x_intr_enable(emu, INTE_CAP_0_LOOP | 64662306a36Sopenharmony_ci INTE_CAP_0_HALF_LOOP); 64762306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|TRIGGER_CAPTURE); 64862306a36Sopenharmony_ci epcm->running = 1; 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 65162306a36Sopenharmony_ci epcm->running = 0; 65262306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, INTE_CAP_0_LOOP | 65362306a36Sopenharmony_ci INTE_CAP_0_HALF_LOOP); 65462306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CAPTURE)); 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci default: 65762306a36Sopenharmony_ci result = -EINVAL; 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci return result; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/* pointer_capture callback */ 66462306a36Sopenharmony_cistatic snd_pcm_uframes_t 66562306a36Sopenharmony_cisnd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 66862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 66962306a36Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 67062306a36Sopenharmony_ci snd_pcm_uframes_t ptr; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (!epcm->running) 67362306a36Sopenharmony_ci return 0; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, CAPTURE_POINTER, 0)); 67662306a36Sopenharmony_ci if (ptr >= runtime->buffer_size) 67762306a36Sopenharmony_ci ptr -= runtime->buffer_size; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return ptr; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1x_capture_ops = { 68362306a36Sopenharmony_ci .open = snd_emu10k1x_pcm_open_capture, 68462306a36Sopenharmony_ci .close = snd_emu10k1x_pcm_close_capture, 68562306a36Sopenharmony_ci .hw_params = snd_emu10k1x_pcm_hw_params_capture, 68662306a36Sopenharmony_ci .hw_free = snd_emu10k1x_pcm_hw_free_capture, 68762306a36Sopenharmony_ci .prepare = snd_emu10k1x_pcm_prepare_capture, 68862306a36Sopenharmony_ci .trigger = snd_emu10k1x_pcm_trigger_capture, 68962306a36Sopenharmony_ci .pointer = snd_emu10k1x_pcm_pointer_capture, 69062306a36Sopenharmony_ci}; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic unsigned short snd_emu10k1x_ac97_read(struct snd_ac97 *ac97, 69362306a36Sopenharmony_ci unsigned short reg) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct emu10k1x *emu = ac97->private_data; 69662306a36Sopenharmony_ci unsigned long flags; 69762306a36Sopenharmony_ci unsigned short val; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 70062306a36Sopenharmony_ci outb(reg, emu->port + AC97ADDRESS); 70162306a36Sopenharmony_ci val = inw(emu->port + AC97DATA); 70262306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 70362306a36Sopenharmony_ci return val; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic void snd_emu10k1x_ac97_write(struct snd_ac97 *ac97, 70762306a36Sopenharmony_ci unsigned short reg, unsigned short val) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct emu10k1x *emu = ac97->private_data; 71062306a36Sopenharmony_ci unsigned long flags; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 71362306a36Sopenharmony_ci outb(reg, emu->port + AC97ADDRESS); 71462306a36Sopenharmony_ci outw(val, emu->port + AC97DATA); 71562306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int snd_emu10k1x_ac97(struct emu10k1x *chip) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct snd_ac97_bus *pbus; 72162306a36Sopenharmony_ci struct snd_ac97_template ac97; 72262306a36Sopenharmony_ci int err; 72362306a36Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 72462306a36Sopenharmony_ci .write = snd_emu10k1x_ac97_write, 72562306a36Sopenharmony_ci .read = snd_emu10k1x_ac97_read, 72662306a36Sopenharmony_ci }; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus); 72962306a36Sopenharmony_ci if (err < 0) 73062306a36Sopenharmony_ci return err; 73162306a36Sopenharmony_ci pbus->no_vra = 1; /* we don't need VRA */ 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 73462306a36Sopenharmony_ci ac97.private_data = chip; 73562306a36Sopenharmony_ci ac97.scaps = AC97_SCAP_NO_SPDIF; 73662306a36Sopenharmony_ci return snd_ac97_mixer(pbus, &ac97, &chip->ac97); 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic void snd_emu10k1x_free(struct snd_card *card) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci struct emu10k1x *chip = card->private_data; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0); 74462306a36Sopenharmony_ci // disable interrupts 74562306a36Sopenharmony_ci outl(0, chip->port + INTE); 74662306a36Sopenharmony_ci // disable audio 74762306a36Sopenharmony_ci outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci unsigned int status; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci struct emu10k1x *chip = dev_id; 75562306a36Sopenharmony_ci struct emu10k1x_voice *pvoice = chip->voices; 75662306a36Sopenharmony_ci int i; 75762306a36Sopenharmony_ci int mask; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci status = inl(chip->port + IPR); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (! status) 76262306a36Sopenharmony_ci return IRQ_NONE; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci // capture interrupt 76562306a36Sopenharmony_ci if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) { 76662306a36Sopenharmony_ci struct emu10k1x_voice *cap_voice = &chip->capture_voice; 76762306a36Sopenharmony_ci if (cap_voice->use) 76862306a36Sopenharmony_ci snd_emu10k1x_pcm_interrupt(chip, cap_voice); 76962306a36Sopenharmony_ci else 77062306a36Sopenharmony_ci snd_emu10k1x_intr_disable(chip, 77162306a36Sopenharmony_ci INTE_CAP_0_LOOP | 77262306a36Sopenharmony_ci INTE_CAP_0_HALF_LOOP); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci mask = IPR_CH_0_LOOP|IPR_CH_0_HALF_LOOP; 77662306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 77762306a36Sopenharmony_ci if (status & mask) { 77862306a36Sopenharmony_ci if (pvoice->use) 77962306a36Sopenharmony_ci snd_emu10k1x_pcm_interrupt(chip, pvoice); 78062306a36Sopenharmony_ci else 78162306a36Sopenharmony_ci snd_emu10k1x_intr_disable(chip, mask); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci pvoice++; 78462306a36Sopenharmony_ci mask <<= 1; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 78862306a36Sopenharmony_ci if (chip->midi.interrupt) 78962306a36Sopenharmony_ci chip->midi.interrupt(chip, status); 79062306a36Sopenharmony_ci else 79162306a36Sopenharmony_ci snd_emu10k1x_intr_disable(chip, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci // acknowledge the interrupt if necessary 79562306a36Sopenharmony_ci outl(status, chip->port + IPR); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* dev_dbg(chip->card->dev, "interrupt %08x\n", status); */ 79862306a36Sopenharmony_ci return IRQ_HANDLED; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic const struct snd_pcm_chmap_elem surround_map[] = { 80262306a36Sopenharmony_ci { .channels = 2, 80362306a36Sopenharmony_ci .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 80462306a36Sopenharmony_ci { } 80562306a36Sopenharmony_ci}; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cistatic const struct snd_pcm_chmap_elem clfe_map[] = { 80862306a36Sopenharmony_ci { .channels = 2, 80962306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, 81062306a36Sopenharmony_ci { } 81162306a36Sopenharmony_ci}; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic int snd_emu10k1x_pcm(struct emu10k1x *emu, int device) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct snd_pcm *pcm; 81662306a36Sopenharmony_ci const struct snd_pcm_chmap_elem *map = NULL; 81762306a36Sopenharmony_ci int err; 81862306a36Sopenharmony_ci int capture = 0; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (device == 0) 82162306a36Sopenharmony_ci capture = 1; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm); 82462306a36Sopenharmony_ci if (err < 0) 82562306a36Sopenharmony_ci return err; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci pcm->private_data = emu; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci switch(device) { 83062306a36Sopenharmony_ci case 0: 83162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops); 83262306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops); 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci case 1: 83562306a36Sopenharmony_ci case 2: 83662306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops); 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci pcm->info_flags = 0; 84162306a36Sopenharmony_ci switch(device) { 84262306a36Sopenharmony_ci case 0: 84362306a36Sopenharmony_ci strcpy(pcm->name, "EMU10K1X Front"); 84462306a36Sopenharmony_ci map = snd_pcm_std_chmaps; 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci case 1: 84762306a36Sopenharmony_ci strcpy(pcm->name, "EMU10K1X Rear"); 84862306a36Sopenharmony_ci map = surround_map; 84962306a36Sopenharmony_ci break; 85062306a36Sopenharmony_ci case 2: 85162306a36Sopenharmony_ci strcpy(pcm->name, "EMU10K1X Center/LFE"); 85262306a36Sopenharmony_ci map = clfe_map; 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci emu->pcm = pcm; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 85862306a36Sopenharmony_ci &emu->pci->dev, 32*1024, 32*1024); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2, 86162306a36Sopenharmony_ci 1 << 2, NULL); 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int snd_emu10k1x_create(struct snd_card *card, 86562306a36Sopenharmony_ci struct pci_dev *pci) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct emu10k1x *chip = card->private_data; 86862306a36Sopenharmony_ci int err; 86962306a36Sopenharmony_ci int ch; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci err = pcim_enable_device(pci); 87262306a36Sopenharmony_ci if (err < 0) 87362306a36Sopenharmony_ci return err; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28)) < 0) { 87662306a36Sopenharmony_ci dev_err(card->dev, "error to set 28bit mask DMA\n"); 87762306a36Sopenharmony_ci return -ENXIO; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci chip->card = card; 88162306a36Sopenharmony_ci chip->pci = pci; 88262306a36Sopenharmony_ci chip->irq = -1; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci spin_lock_init(&chip->emu_lock); 88562306a36Sopenharmony_ci spin_lock_init(&chip->voice_lock); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci err = pci_request_regions(pci, "EMU10K1X"); 88862306a36Sopenharmony_ci if (err < 0) 88962306a36Sopenharmony_ci return err; 89062306a36Sopenharmony_ci chip->port = pci_resource_start(pci, 0); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (devm_request_irq(&pci->dev, pci->irq, snd_emu10k1x_interrupt, 89362306a36Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip)) { 89462306a36Sopenharmony_ci dev_err(card->dev, "cannot grab irq %d\n", pci->irq); 89562306a36Sopenharmony_ci return -EBUSY; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci chip->irq = pci->irq; 89862306a36Sopenharmony_ci card->sync_irq = chip->irq; 89962306a36Sopenharmony_ci card->private_free = snd_emu10k1x_free; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci chip->dma_buffer = snd_devm_alloc_pages(&pci->dev, SNDRV_DMA_TYPE_DEV, 90262306a36Sopenharmony_ci 4 * 1024); 90362306a36Sopenharmony_ci if (!chip->dma_buffer) 90462306a36Sopenharmony_ci return -ENOMEM; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci pci_set_master(pci); 90762306a36Sopenharmony_ci /* read revision & serial */ 90862306a36Sopenharmony_ci chip->revision = pci->revision; 90962306a36Sopenharmony_ci pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial); 91062306a36Sopenharmony_ci pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); 91162306a36Sopenharmony_ci dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model, 91262306a36Sopenharmony_ci chip->revision, chip->serial); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci outl(0, chip->port + INTE); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci for(ch = 0; ch < 3; ch++) { 91762306a36Sopenharmony_ci chip->voices[ch].emu = chip; 91862306a36Sopenharmony_ci chip->voices[ch].number = ch; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci /* 92262306a36Sopenharmony_ci * Init to 0x02109204 : 92362306a36Sopenharmony_ci * Clock accuracy = 0 (1000ppm) 92462306a36Sopenharmony_ci * Sample Rate = 2 (48kHz) 92562306a36Sopenharmony_ci * Audio Channel = 1 (Left of 2) 92662306a36Sopenharmony_ci * Source Number = 0 (Unspecified) 92762306a36Sopenharmony_ci * Generation Status = 1 (Original for Cat Code 12) 92862306a36Sopenharmony_ci * Cat Code = 12 (Digital Signal Mixer) 92962306a36Sopenharmony_ci * Mode = 0 (Mode 0) 93062306a36Sopenharmony_ci * Emphasis = 0 (None) 93162306a36Sopenharmony_ci * CP = 1 (Copyright unasserted) 93262306a36Sopenharmony_ci * AN = 0 (Audio data) 93362306a36Sopenharmony_ci * P = 0 (Consumer) 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPCS0, 0, 93662306a36Sopenharmony_ci chip->spdif_bits[0] = 93762306a36Sopenharmony_ci SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 93862306a36Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | 93962306a36Sopenharmony_ci SPCS_GENERATIONSTATUS | 0x00001200 | 94062306a36Sopenharmony_ci 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); 94162306a36Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPCS1, 0, 94262306a36Sopenharmony_ci chip->spdif_bits[1] = 94362306a36Sopenharmony_ci SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 94462306a36Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | 94562306a36Sopenharmony_ci SPCS_GENERATIONSTATUS | 0x00001200 | 94662306a36Sopenharmony_ci 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); 94762306a36Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPCS2, 0, 94862306a36Sopenharmony_ci chip->spdif_bits[2] = 94962306a36Sopenharmony_ci SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 95062306a36Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | 95162306a36Sopenharmony_ci SPCS_GENERATIONSTATUS | 0x00001200 | 95262306a36Sopenharmony_ci 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPDIF_SELECT, 0, 0x700); // disable SPDIF 95562306a36Sopenharmony_ci snd_emu10k1x_ptr_write(chip, ROUTING, 0, 0x1003F); // routing 95662306a36Sopenharmony_ci snd_emu10k1x_gpio_write(chip, 0x1080); // analog mode 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci return 0; 96162306a36Sopenharmony_ci} 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_cistatic void snd_emu10k1x_proc_reg_read(struct snd_info_entry *entry, 96462306a36Sopenharmony_ci struct snd_info_buffer *buffer) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct emu10k1x *emu = entry->private_data; 96762306a36Sopenharmony_ci unsigned long value,value1,value2; 96862306a36Sopenharmony_ci unsigned long flags; 96962306a36Sopenharmony_ci int i; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci snd_iprintf(buffer, "Registers:\n\n"); 97262306a36Sopenharmony_ci for(i = 0; i < 0x20; i+=4) { 97362306a36Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 97462306a36Sopenharmony_ci value = inl(emu->port + i); 97562306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 97662306a36Sopenharmony_ci snd_iprintf(buffer, "Register %02X: %08lX\n", i, value); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci snd_iprintf(buffer, "\nRegisters\n\n"); 97962306a36Sopenharmony_ci for(i = 0; i <= 0x48; i++) { 98062306a36Sopenharmony_ci value = snd_emu10k1x_ptr_read(emu, i, 0); 98162306a36Sopenharmony_ci if(i < 0x10 || (i >= 0x20 && i < 0x40)) { 98262306a36Sopenharmony_ci value1 = snd_emu10k1x_ptr_read(emu, i, 1); 98362306a36Sopenharmony_ci value2 = snd_emu10k1x_ptr_read(emu, i, 2); 98462306a36Sopenharmony_ci snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2); 98562306a36Sopenharmony_ci } else { 98662306a36Sopenharmony_ci snd_iprintf(buffer, "%02X: %08lX\n", i, value); 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, 99262306a36Sopenharmony_ci struct snd_info_buffer *buffer) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci struct emu10k1x *emu = entry->private_data; 99562306a36Sopenharmony_ci char line[64]; 99662306a36Sopenharmony_ci unsigned int reg, channel_id , val; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci while (!snd_info_get_line(buffer, line, sizeof(line))) { 99962306a36Sopenharmony_ci if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) 100062306a36Sopenharmony_ci continue; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (reg < 0x49 && channel_id <= 2) 100362306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, reg, channel_id, val); 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic int snd_emu10k1x_proc_init(struct emu10k1x *emu) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci snd_card_rw_proc_new(emu->card, "emu10k1x_regs", emu, 101062306a36Sopenharmony_ci snd_emu10k1x_proc_reg_read, 101162306a36Sopenharmony_ci snd_emu10k1x_proc_reg_write); 101262306a36Sopenharmony_ci return 0; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci#define snd_emu10k1x_shared_spdif_info snd_ctl_boolean_mono_info 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic int snd_emu10k1x_shared_spdif_get(struct snd_kcontrol *kcontrol, 101862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_emu10k1x_ptr_read(emu, SPDIF_SELECT, 0) == 0x700) ? 0 : 1; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol, 102862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 103162306a36Sopenharmony_ci unsigned int val; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci val = ucontrol->value.integer.value[0] ; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (val) { 103662306a36Sopenharmony_ci // enable spdif output 103762306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x000); 103862306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x700); 103962306a36Sopenharmony_ci snd_emu10k1x_gpio_write(emu, 0x1000); 104062306a36Sopenharmony_ci } else { 104162306a36Sopenharmony_ci // disable spdif output 104262306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x700); 104362306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F); 104462306a36Sopenharmony_ci snd_emu10k1x_gpio_write(emu, 0x1080); 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci return 0; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1x_shared_spdif = 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 105262306a36Sopenharmony_ci .name = "Analog/Digital Output Jack", 105362306a36Sopenharmony_ci .info = snd_emu10k1x_shared_spdif_info, 105462306a36Sopenharmony_ci .get = snd_emu10k1x_shared_spdif_get, 105562306a36Sopenharmony_ci .put = snd_emu10k1x_shared_spdif_put 105662306a36Sopenharmony_ci}; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic int snd_emu10k1x_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 106162306a36Sopenharmony_ci uinfo->count = 1; 106262306a36Sopenharmony_ci return 0; 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic int snd_emu10k1x_spdif_get(struct snd_kcontrol *kcontrol, 106662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 106962306a36Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; 107262306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; 107362306a36Sopenharmony_ci ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; 107462306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; 107562306a36Sopenharmony_ci return 0; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic int snd_emu10k1x_spdif_get_mask(struct snd_kcontrol *kcontrol, 107962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 108062306a36Sopenharmony_ci{ 108162306a36Sopenharmony_ci ucontrol->value.iec958.status[0] = 0xff; 108262306a36Sopenharmony_ci ucontrol->value.iec958.status[1] = 0xff; 108362306a36Sopenharmony_ci ucontrol->value.iec958.status[2] = 0xff; 108462306a36Sopenharmony_ci ucontrol->value.iec958.status[3] = 0xff; 108562306a36Sopenharmony_ci return 0; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol, 108962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 109262306a36Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 109362306a36Sopenharmony_ci int change; 109462306a36Sopenharmony_ci unsigned int val; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci val = (ucontrol->value.iec958.status[0] << 0) | 109762306a36Sopenharmony_ci (ucontrol->value.iec958.status[1] << 8) | 109862306a36Sopenharmony_ci (ucontrol->value.iec958.status[2] << 16) | 109962306a36Sopenharmony_ci (ucontrol->value.iec958.status[3] << 24); 110062306a36Sopenharmony_ci change = val != emu->spdif_bits[idx]; 110162306a36Sopenharmony_ci if (change) { 110262306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, SPCS0 + idx, 0, val); 110362306a36Sopenharmony_ci emu->spdif_bits[idx] = val; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci return change; 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control = 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 111162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 111262306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 111362306a36Sopenharmony_ci .count = 3, 111462306a36Sopenharmony_ci .info = snd_emu10k1x_spdif_info, 111562306a36Sopenharmony_ci .get = snd_emu10k1x_spdif_get_mask 111662306a36Sopenharmony_ci}; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1x_spdif_control = 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 112162306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 112262306a36Sopenharmony_ci .count = 3, 112362306a36Sopenharmony_ci .info = snd_emu10k1x_spdif_info, 112462306a36Sopenharmony_ci .get = snd_emu10k1x_spdif_get, 112562306a36Sopenharmony_ci .put = snd_emu10k1x_spdif_put 112662306a36Sopenharmony_ci}; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic int snd_emu10k1x_mixer(struct emu10k1x *emu) 112962306a36Sopenharmony_ci{ 113062306a36Sopenharmony_ci int err; 113162306a36Sopenharmony_ci struct snd_kcontrol *kctl; 113262306a36Sopenharmony_ci struct snd_card *card = emu->card; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu); 113562306a36Sopenharmony_ci if (!kctl) 113662306a36Sopenharmony_ci return -ENOMEM; 113762306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 113862306a36Sopenharmony_ci if (err) 113962306a36Sopenharmony_ci return err; 114062306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu); 114162306a36Sopenharmony_ci if (!kctl) 114262306a36Sopenharmony_ci return -ENOMEM; 114362306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 114462306a36Sopenharmony_ci if (err) 114562306a36Sopenharmony_ci return err; 114662306a36Sopenharmony_ci kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu); 114762306a36Sopenharmony_ci if (!kctl) 114862306a36Sopenharmony_ci return -ENOMEM; 114962306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 115062306a36Sopenharmony_ci if (err) 115162306a36Sopenharmony_ci return err; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci return 0; 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci#define EMU10K1X_MIDI_MODE_INPUT (1<<0) 115762306a36Sopenharmony_ci#define EMU10K1X_MIDI_MODE_OUTPUT (1<<1) 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic inline unsigned char mpu401_read(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int idx) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci return (unsigned char)snd_emu10k1x_ptr_read(emu, mpu->port + idx, 0); 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic inline void mpu401_write(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int data, int idx) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci snd_emu10k1x_ptr_write(emu, mpu->port + idx, 0, data); 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0) 117062306a36Sopenharmony_ci#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1) 117162306a36Sopenharmony_ci#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0) 117262306a36Sopenharmony_ci#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1) 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80)) 117562306a36Sopenharmony_ci#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40)) 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci#define MPU401_RESET 0xff 117862306a36Sopenharmony_ci#define MPU401_ENTER_UART 0x3f 117962306a36Sopenharmony_ci#define MPU401_ACK 0xfe 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_cistatic void mpu401_clear_rx(struct emu10k1x *emu, struct emu10k1x_midi *mpu) 118262306a36Sopenharmony_ci{ 118362306a36Sopenharmony_ci int timeout = 100000; 118462306a36Sopenharmony_ci for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--) 118562306a36Sopenharmony_ci mpu401_read_data(emu, mpu); 118662306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 118762306a36Sopenharmony_ci if (timeout <= 0) 118862306a36Sopenharmony_ci dev_err(emu->card->dev, 118962306a36Sopenharmony_ci "cmd: clear rx timeout (status = 0x%x)\n", 119062306a36Sopenharmony_ci mpu401_read_stat(emu, mpu)); 119162306a36Sopenharmony_ci#endif 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci/* 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci */ 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic void do_emu10k1x_midi_interrupt(struct emu10k1x *emu, 119962306a36Sopenharmony_ci struct emu10k1x_midi *midi, unsigned int status) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci unsigned char byte; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (midi->rmidi == NULL) { 120462306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable | midi->rx_enable); 120562306a36Sopenharmony_ci return; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci spin_lock(&midi->input_lock); 120962306a36Sopenharmony_ci if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) { 121062306a36Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) { 121162306a36Sopenharmony_ci mpu401_clear_rx(emu, midi); 121262306a36Sopenharmony_ci } else { 121362306a36Sopenharmony_ci byte = mpu401_read_data(emu, midi); 121462306a36Sopenharmony_ci if (midi->substream_input) 121562306a36Sopenharmony_ci snd_rawmidi_receive(midi->substream_input, &byte, 1); 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci spin_unlock(&midi->input_lock); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci spin_lock(&midi->output_lock); 122162306a36Sopenharmony_ci if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) { 122262306a36Sopenharmony_ci if (midi->substream_output && 122362306a36Sopenharmony_ci snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { 122462306a36Sopenharmony_ci mpu401_write_data(emu, midi, byte); 122562306a36Sopenharmony_ci } else { 122662306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable); 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci spin_unlock(&midi->output_lock); 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int status) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci do_emu10k1x_midi_interrupt(emu, &emu->midi, status); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic int snd_emu10k1x_midi_cmd(struct emu10k1x * emu, 123862306a36Sopenharmony_ci struct emu10k1x_midi *midi, unsigned char cmd, int ack) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci unsigned long flags; 124162306a36Sopenharmony_ci int timeout, ok; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci spin_lock_irqsave(&midi->input_lock, flags); 124462306a36Sopenharmony_ci mpu401_write_data(emu, midi, 0x00); 124562306a36Sopenharmony_ci /* mpu401_clear_rx(emu, midi); */ 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci mpu401_write_cmd(emu, midi, cmd); 124862306a36Sopenharmony_ci if (ack) { 124962306a36Sopenharmony_ci ok = 0; 125062306a36Sopenharmony_ci timeout = 10000; 125162306a36Sopenharmony_ci while (!ok && timeout-- > 0) { 125262306a36Sopenharmony_ci if (mpu401_input_avail(emu, midi)) { 125362306a36Sopenharmony_ci if (mpu401_read_data(emu, midi) == MPU401_ACK) 125462306a36Sopenharmony_ci ok = 1; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK) 125862306a36Sopenharmony_ci ok = 1; 125962306a36Sopenharmony_ci } else { 126062306a36Sopenharmony_ci ok = 1; 126162306a36Sopenharmony_ci } 126262306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->input_lock, flags); 126362306a36Sopenharmony_ci if (!ok) { 126462306a36Sopenharmony_ci dev_err(emu->card->dev, 126562306a36Sopenharmony_ci "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n", 126662306a36Sopenharmony_ci cmd, emu->port, 126762306a36Sopenharmony_ci mpu401_read_stat(emu, midi), 126862306a36Sopenharmony_ci mpu401_read_data(emu, midi)); 126962306a36Sopenharmony_ci return 1; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci return 0; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci struct emu10k1x *emu; 127762306a36Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 127862306a36Sopenharmony_ci unsigned long flags; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci emu = midi->emu; 128162306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 128262306a36Sopenharmony_ci return -ENXIO; 128362306a36Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 128462306a36Sopenharmony_ci midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT; 128562306a36Sopenharmony_ci midi->substream_input = substream; 128662306a36Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) { 128762306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 128862306a36Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1)) 128962306a36Sopenharmony_ci goto error_out; 129062306a36Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) 129162306a36Sopenharmony_ci goto error_out; 129262306a36Sopenharmony_ci } else { 129362306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci return 0; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cierror_out: 129862306a36Sopenharmony_ci return -EIO; 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci struct emu10k1x *emu; 130462306a36Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 130562306a36Sopenharmony_ci unsigned long flags; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci emu = midi->emu; 130862306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 130962306a36Sopenharmony_ci return -ENXIO; 131062306a36Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 131162306a36Sopenharmony_ci midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT; 131262306a36Sopenharmony_ci midi->substream_output = substream; 131362306a36Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) { 131462306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 131562306a36Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1)) 131662306a36Sopenharmony_ci goto error_out; 131762306a36Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) 131862306a36Sopenharmony_ci goto error_out; 131962306a36Sopenharmony_ci } else { 132062306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci return 0; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cierror_out: 132562306a36Sopenharmony_ci return -EIO; 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_cistatic int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci struct emu10k1x *emu; 133162306a36Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 133262306a36Sopenharmony_ci unsigned long flags; 133362306a36Sopenharmony_ci int err = 0; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci emu = midi->emu; 133662306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 133762306a36Sopenharmony_ci return -ENXIO; 133862306a36Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 133962306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->rx_enable); 134062306a36Sopenharmony_ci midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT; 134162306a36Sopenharmony_ci midi->substream_input = NULL; 134262306a36Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) { 134362306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 134462306a36Sopenharmony_ci err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0); 134562306a36Sopenharmony_ci } else { 134662306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci return err; 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substream) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci struct emu10k1x *emu; 135462306a36Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 135562306a36Sopenharmony_ci unsigned long flags; 135662306a36Sopenharmony_ci int err = 0; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci emu = midi->emu; 135962306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 136062306a36Sopenharmony_ci return -ENXIO; 136162306a36Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 136262306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable); 136362306a36Sopenharmony_ci midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT; 136462306a36Sopenharmony_ci midi->substream_output = NULL; 136562306a36Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) { 136662306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 136762306a36Sopenharmony_ci err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0); 136862306a36Sopenharmony_ci } else { 136962306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci return err; 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_cistatic void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct emu10k1x *emu; 137762306a36Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 137862306a36Sopenharmony_ci emu = midi->emu; 137962306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 138062306a36Sopenharmony_ci return; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci if (up) 138362306a36Sopenharmony_ci snd_emu10k1x_intr_enable(emu, midi->rx_enable); 138462306a36Sopenharmony_ci else 138562306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->rx_enable); 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 138962306a36Sopenharmony_ci{ 139062306a36Sopenharmony_ci struct emu10k1x *emu; 139162306a36Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 139262306a36Sopenharmony_ci unsigned long flags; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci emu = midi->emu; 139562306a36Sopenharmony_ci if (snd_BUG_ON(!emu)) 139662306a36Sopenharmony_ci return; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci if (up) { 139962306a36Sopenharmony_ci int max = 4; 140062306a36Sopenharmony_ci unsigned char byte; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci /* try to send some amount of bytes here before interrupts */ 140362306a36Sopenharmony_ci spin_lock_irqsave(&midi->output_lock, flags); 140462306a36Sopenharmony_ci while (max > 0) { 140562306a36Sopenharmony_ci if (mpu401_output_ready(emu, midi)) { 140662306a36Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) || 140762306a36Sopenharmony_ci snd_rawmidi_transmit(substream, &byte, 1) != 1) { 140862306a36Sopenharmony_ci /* no more data */ 140962306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->output_lock, flags); 141062306a36Sopenharmony_ci return; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci mpu401_write_data(emu, midi, byte); 141362306a36Sopenharmony_ci max--; 141462306a36Sopenharmony_ci } else { 141562306a36Sopenharmony_ci break; 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci spin_unlock_irqrestore(&midi->output_lock, flags); 141962306a36Sopenharmony_ci snd_emu10k1x_intr_enable(emu, midi->tx_enable); 142062306a36Sopenharmony_ci } else { 142162306a36Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable); 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci/* 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci */ 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_emu10k1x_midi_output = 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci .open = snd_emu10k1x_midi_output_open, 143262306a36Sopenharmony_ci .close = snd_emu10k1x_midi_output_close, 143362306a36Sopenharmony_ci .trigger = snd_emu10k1x_midi_output_trigger, 143462306a36Sopenharmony_ci}; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_emu10k1x_midi_input = 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci .open = snd_emu10k1x_midi_input_open, 143962306a36Sopenharmony_ci .close = snd_emu10k1x_midi_input_close, 144062306a36Sopenharmony_ci .trigger = snd_emu10k1x_midi_input_trigger, 144162306a36Sopenharmony_ci}; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic void snd_emu10k1x_midi_free(struct snd_rawmidi *rmidi) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct emu10k1x_midi *midi = rmidi->private_data; 144662306a36Sopenharmony_ci midi->interrupt = NULL; 144762306a36Sopenharmony_ci midi->rmidi = NULL; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic int emu10k1x_midi_init(struct emu10k1x *emu, 145162306a36Sopenharmony_ci struct emu10k1x_midi *midi, int device, 145262306a36Sopenharmony_ci char *name) 145362306a36Sopenharmony_ci{ 145462306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 145562306a36Sopenharmony_ci int err; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi); 145862306a36Sopenharmony_ci if (err < 0) 145962306a36Sopenharmony_ci return err; 146062306a36Sopenharmony_ci midi->emu = emu; 146162306a36Sopenharmony_ci spin_lock_init(&midi->open_lock); 146262306a36Sopenharmony_ci spin_lock_init(&midi->input_lock); 146362306a36Sopenharmony_ci spin_lock_init(&midi->output_lock); 146462306a36Sopenharmony_ci strcpy(rmidi->name, name); 146562306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output); 146662306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input); 146762306a36Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 146862306a36Sopenharmony_ci SNDRV_RAWMIDI_INFO_INPUT | 146962306a36Sopenharmony_ci SNDRV_RAWMIDI_INFO_DUPLEX; 147062306a36Sopenharmony_ci rmidi->private_data = midi; 147162306a36Sopenharmony_ci rmidi->private_free = snd_emu10k1x_midi_free; 147262306a36Sopenharmony_ci midi->rmidi = rmidi; 147362306a36Sopenharmony_ci return 0; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic int snd_emu10k1x_midi(struct emu10k1x *emu) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci struct emu10k1x_midi *midi = &emu->midi; 147962306a36Sopenharmony_ci int err; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)"); 148262306a36Sopenharmony_ci if (err < 0) 148362306a36Sopenharmony_ci return err; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci midi->tx_enable = INTE_MIDITXENABLE; 148662306a36Sopenharmony_ci midi->rx_enable = INTE_MIDIRXENABLE; 148762306a36Sopenharmony_ci midi->port = MUDATA; 148862306a36Sopenharmony_ci midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; 148962306a36Sopenharmony_ci midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; 149062306a36Sopenharmony_ci midi->interrupt = snd_emu10k1x_midi_interrupt; 149162306a36Sopenharmony_ci return 0; 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic int __snd_emu10k1x_probe(struct pci_dev *pci, 149562306a36Sopenharmony_ci const struct pci_device_id *pci_id) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci static int dev; 149862306a36Sopenharmony_ci struct snd_card *card; 149962306a36Sopenharmony_ci struct emu10k1x *chip; 150062306a36Sopenharmony_ci int err; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 150362306a36Sopenharmony_ci return -ENODEV; 150462306a36Sopenharmony_ci if (!enable[dev]) { 150562306a36Sopenharmony_ci dev++; 150662306a36Sopenharmony_ci return -ENOENT; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci err = snd_devm_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 151062306a36Sopenharmony_ci sizeof(*chip), &card); 151162306a36Sopenharmony_ci if (err < 0) 151262306a36Sopenharmony_ci return err; 151362306a36Sopenharmony_ci chip = card->private_data; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci err = snd_emu10k1x_create(card, pci); 151662306a36Sopenharmony_ci if (err < 0) 151762306a36Sopenharmony_ci return err; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci err = snd_emu10k1x_pcm(chip, 0); 152062306a36Sopenharmony_ci if (err < 0) 152162306a36Sopenharmony_ci return err; 152262306a36Sopenharmony_ci err = snd_emu10k1x_pcm(chip, 1); 152362306a36Sopenharmony_ci if (err < 0) 152462306a36Sopenharmony_ci return err; 152562306a36Sopenharmony_ci err = snd_emu10k1x_pcm(chip, 2); 152662306a36Sopenharmony_ci if (err < 0) 152762306a36Sopenharmony_ci return err; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci err = snd_emu10k1x_ac97(chip); 153062306a36Sopenharmony_ci if (err < 0) 153162306a36Sopenharmony_ci return err; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci err = snd_emu10k1x_mixer(chip); 153462306a36Sopenharmony_ci if (err < 0) 153562306a36Sopenharmony_ci return err; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci err = snd_emu10k1x_midi(chip); 153862306a36Sopenharmony_ci if (err < 0) 153962306a36Sopenharmony_ci return err; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci snd_emu10k1x_proc_init(chip); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci strcpy(card->driver, "EMU10K1X"); 154462306a36Sopenharmony_ci strcpy(card->shortname, "Dell Sound Blaster Live!"); 154562306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx irq %i", 154662306a36Sopenharmony_ci card->shortname, chip->port, chip->irq); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci err = snd_card_register(card); 154962306a36Sopenharmony_ci if (err < 0) 155062306a36Sopenharmony_ci return err; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci pci_set_drvdata(pci, card); 155362306a36Sopenharmony_ci dev++; 155462306a36Sopenharmony_ci return 0; 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic int snd_emu10k1x_probe(struct pci_dev *pci, 155862306a36Sopenharmony_ci const struct pci_device_id *pci_id) 155962306a36Sopenharmony_ci{ 156062306a36Sopenharmony_ci return snd_card_free_on_error(&pci->dev, __snd_emu10k1x_probe(pci, pci_id)); 156162306a36Sopenharmony_ci} 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci// PCI IDs 156462306a36Sopenharmony_cistatic const struct pci_device_id snd_emu10k1x_ids[] = { 156562306a36Sopenharmony_ci { PCI_VDEVICE(CREATIVE, 0x0006), 0 }, /* Dell OEM version (EMU10K1) */ 156662306a36Sopenharmony_ci { 0, } 156762306a36Sopenharmony_ci}; 156862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci// pci_driver definition 157162306a36Sopenharmony_cistatic struct pci_driver emu10k1x_driver = { 157262306a36Sopenharmony_ci .name = KBUILD_MODNAME, 157362306a36Sopenharmony_ci .id_table = snd_emu10k1x_ids, 157462306a36Sopenharmony_ci .probe = snd_emu10k1x_probe, 157562306a36Sopenharmony_ci}; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_cimodule_pci_driver(emu10k1x_driver); 1578