18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> 48c2ecf20Sopenharmony_ci * Driver EMU10K1X chips 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Parts of this code were adapted from audigyls.c driver which is 78c2ecf20Sopenharmony_ci * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * BUGS: 108c2ecf20Sopenharmony_ci * -- 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * TODO: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Chips (SB0200 model): 158c2ecf20Sopenharmony_ci * - EMU10K1X-DBQ 168c2ecf20Sopenharmony_ci * - STAC 9708T 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/module.h> 248c2ecf20Sopenharmony_ci#include <sound/core.h> 258c2ecf20Sopenharmony_ci#include <sound/initval.h> 268c2ecf20Sopenharmony_ci#include <sound/pcm.h> 278c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h> 288c2ecf20Sopenharmony_ci#include <sound/info.h> 298c2ecf20Sopenharmony_ci#include <sound/rawmidi.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>"); 328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EMU10K1X"); 338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 348c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci// module parameters (see "Module Parameters") 378c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 388c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 398c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard."); 438c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for the EMU10K1X soundcard."); 458c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard."); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci// some definitions were borrowed from emu10k1 driver as they seem to be the same 508c2ecf20Sopenharmony_ci/************************************************************************************************/ 518c2ecf20Sopenharmony_ci/* PCI function 0 registers, address = <val> + PCIBASE0 */ 528c2ecf20Sopenharmony_ci/************************************************************************************************/ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define PTR 0x00 /* Indexed register set pointer register */ 558c2ecf20Sopenharmony_ci /* NOTE: The CHANNELNUM and ADDRESS words can */ 568c2ecf20Sopenharmony_ci /* be modified independently of each other. */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define DATA 0x04 /* Indexed register set data register */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define IPR 0x08 /* Global interrupt pending register */ 618c2ecf20Sopenharmony_ci /* Clear pending interrupts by writing a 1 to */ 628c2ecf20Sopenharmony_ci /* the relevant bits and zero to the other bits */ 638c2ecf20Sopenharmony_ci#define IPR_MIDITRANSBUFEMPTY 0x00000001 /* MIDI UART transmit buffer empty */ 648c2ecf20Sopenharmony_ci#define IPR_MIDIRECVBUFEMPTY 0x00000002 /* MIDI UART receive buffer empty */ 658c2ecf20Sopenharmony_ci#define IPR_CH_0_LOOP 0x00000800 /* Channel 0 loop */ 668c2ecf20Sopenharmony_ci#define IPR_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */ 678c2ecf20Sopenharmony_ci#define IPR_CAP_0_LOOP 0x00080000 /* Channel capture loop */ 688c2ecf20Sopenharmony_ci#define IPR_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define INTE 0x0c /* Interrupt enable register */ 718c2ecf20Sopenharmony_ci#define INTE_MIDITXENABLE 0x00000001 /* Enable MIDI transmit-buffer-empty interrupts */ 728c2ecf20Sopenharmony_ci#define INTE_MIDIRXENABLE 0x00000002 /* Enable MIDI receive-buffer-empty interrupts */ 738c2ecf20Sopenharmony_ci#define INTE_CH_0_LOOP 0x00000800 /* Channel 0 loop */ 748c2ecf20Sopenharmony_ci#define INTE_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */ 758c2ecf20Sopenharmony_ci#define INTE_CAP_0_LOOP 0x00080000 /* Channel capture loop */ 768c2ecf20Sopenharmony_ci#define INTE_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define HCFG 0x14 /* Hardware config register */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ 818c2ecf20Sopenharmony_ci /* NOTE: This should generally never be used. */ 828c2ecf20Sopenharmony_ci#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ 838c2ecf20Sopenharmony_ci /* Should be set to 1 when the EMU10K1 is */ 848c2ecf20Sopenharmony_ci /* completely initialized. */ 858c2ecf20Sopenharmony_ci#define GPIO 0x18 /* Defaults: 00001080-Analog, 00001000-SPDIF. */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) */ 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/********************************************************************************************************/ 938c2ecf20Sopenharmony_ci/* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers */ 948c2ecf20Sopenharmony_ci/********************************************************************************************************/ 958c2ecf20Sopenharmony_ci#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */ 968c2ecf20Sopenharmony_ci /* One list entry: 4 bytes for DMA address, 978c2ecf20Sopenharmony_ci * 4 bytes for period_size << 16. 988c2ecf20Sopenharmony_ci * One list entry is 8 bytes long. 998c2ecf20Sopenharmony_ci * One list entry for each period in the buffer. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */ 1028c2ecf20Sopenharmony_ci#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */ 1038c2ecf20Sopenharmony_ci#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */ 1048c2ecf20Sopenharmony_ci#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size */ 1058c2ecf20Sopenharmony_ci#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Sample currently in DAC */ 1068c2ecf20Sopenharmony_ci#define PLAYBACK_UNKNOWN1 0x07 1078c2ecf20Sopenharmony_ci#define PLAYBACK_UNKNOWN2 0x08 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* Only one capture channel supported */ 1108c2ecf20Sopenharmony_ci#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */ 1118c2ecf20Sopenharmony_ci#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */ 1128c2ecf20Sopenharmony_ci#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */ 1138c2ecf20Sopenharmony_ci#define CAPTURE_UNKNOWN 0x13 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* From 0x20 - 0x3f, last samples played on each channel */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define TRIGGER_CHANNEL 0x40 /* Trigger channel playback */ 1188c2ecf20Sopenharmony_ci#define TRIGGER_CHANNEL_0 0x00000001 /* Trigger channel 0 */ 1198c2ecf20Sopenharmony_ci#define TRIGGER_CHANNEL_1 0x00000002 /* Trigger channel 1 */ 1208c2ecf20Sopenharmony_ci#define TRIGGER_CHANNEL_2 0x00000004 /* Trigger channel 2 */ 1218c2ecf20Sopenharmony_ci#define TRIGGER_CAPTURE 0x00000100 /* Trigger capture channel */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define ROUTING 0x41 /* Setup sound routing ? */ 1248c2ecf20Sopenharmony_ci#define ROUTING_FRONT_LEFT 0x00000001 1258c2ecf20Sopenharmony_ci#define ROUTING_FRONT_RIGHT 0x00000002 1268c2ecf20Sopenharmony_ci#define ROUTING_REAR_LEFT 0x00000004 1278c2ecf20Sopenharmony_ci#define ROUTING_REAR_RIGHT 0x00000008 1288c2ecf20Sopenharmony_ci#define ROUTING_CENTER_LFE 0x00010000 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define SPCS0 0x42 /* SPDIF output Channel Status 0 register */ 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define SPCS1 0x43 /* SPDIF output Channel Status 1 register */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci#define SPCS2 0x44 /* SPDIF output Channel Status 2 register */ 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */ 1378c2ecf20Sopenharmony_ci#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */ 1388c2ecf20Sopenharmony_ci#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */ 1398c2ecf20Sopenharmony_ci#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */ 1408c2ecf20Sopenharmony_ci#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */ 1418c2ecf20Sopenharmony_ci#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */ 1428c2ecf20Sopenharmony_ci#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */ 1438c2ecf20Sopenharmony_ci#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */ 1448c2ecf20Sopenharmony_ci#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */ 1458c2ecf20Sopenharmony_ci#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */ 1468c2ecf20Sopenharmony_ci#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */ 1478c2ecf20Sopenharmony_ci#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */ 1488c2ecf20Sopenharmony_ci#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */ 1498c2ecf20Sopenharmony_ci#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */ 1508c2ecf20Sopenharmony_ci#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */ 1518c2ecf20Sopenharmony_ci#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */ 1528c2ecf20Sopenharmony_ci#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */ 1538c2ecf20Sopenharmony_ci#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */ 1548c2ecf20Sopenharmony_ci#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */ 1558c2ecf20Sopenharmony_ci#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */ 1568c2ecf20Sopenharmony_ci#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */ 1578c2ecf20Sopenharmony_ci#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */ 1588c2ecf20Sopenharmony_ci#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#define SPDIF_SELECT 0x45 /* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* This is the MPU port on the card */ 1638c2ecf20Sopenharmony_ci#define MUDATA 0x47 1648c2ecf20Sopenharmony_ci#define MUCMD 0x48 1658c2ecf20Sopenharmony_ci#define MUSTAT MUCMD 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* From 0x50 - 0x5f, last samples captured */ 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* 1708c2ecf20Sopenharmony_ci * The hardware has 3 channels for playback and 1 for capture. 1718c2ecf20Sopenharmony_ci * - channel 0 is the front channel 1728c2ecf20Sopenharmony_ci * - channel 1 is the rear channel 1738c2ecf20Sopenharmony_ci * - channel 2 is the center/lfe channel 1748c2ecf20Sopenharmony_ci * Volume is controlled by the AC97 for the front and rear channels by 1758c2ecf20Sopenharmony_ci * the PCM Playback Volume, Sigmatel Surround Playback Volume and 1768c2ecf20Sopenharmony_ci * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects 1778c2ecf20Sopenharmony_ci * the front/rear channel mixing in the REAR OUT jack. When using the 1788c2ecf20Sopenharmony_ci * 4-Speaker Stereo, both front and rear channels will be mixed in the 1798c2ecf20Sopenharmony_ci * REAR OUT. 1808c2ecf20Sopenharmony_ci * The center/lfe channel has no volume control and cannot be muted during 1818c2ecf20Sopenharmony_ci * playback. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistruct emu10k1x_voice { 1858c2ecf20Sopenharmony_ci struct emu10k1x *emu; 1868c2ecf20Sopenharmony_ci int number; 1878c2ecf20Sopenharmony_ci int use; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm; 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistruct emu10k1x_pcm { 1938c2ecf20Sopenharmony_ci struct emu10k1x *emu; 1948c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1958c2ecf20Sopenharmony_ci struct emu10k1x_voice *voice; 1968c2ecf20Sopenharmony_ci unsigned short running; 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistruct emu10k1x_midi { 2008c2ecf20Sopenharmony_ci struct emu10k1x *emu; 2018c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 2028c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *substream_input; 2038c2ecf20Sopenharmony_ci struct snd_rawmidi_substream *substream_output; 2048c2ecf20Sopenharmony_ci unsigned int midi_mode; 2058c2ecf20Sopenharmony_ci spinlock_t input_lock; 2068c2ecf20Sopenharmony_ci spinlock_t output_lock; 2078c2ecf20Sopenharmony_ci spinlock_t open_lock; 2088c2ecf20Sopenharmony_ci int tx_enable, rx_enable; 2098c2ecf20Sopenharmony_ci int port; 2108c2ecf20Sopenharmony_ci int ipr_tx, ipr_rx; 2118c2ecf20Sopenharmony_ci void (*interrupt)(struct emu10k1x *emu, unsigned int status); 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci// definition of the chip-specific record 2158c2ecf20Sopenharmony_cistruct emu10k1x { 2168c2ecf20Sopenharmony_ci struct snd_card *card; 2178c2ecf20Sopenharmony_ci struct pci_dev *pci; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci unsigned long port; 2208c2ecf20Sopenharmony_ci struct resource *res_port; 2218c2ecf20Sopenharmony_ci int irq; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci unsigned char revision; /* chip revision */ 2248c2ecf20Sopenharmony_ci unsigned int serial; /* serial number */ 2258c2ecf20Sopenharmony_ci unsigned short model; /* subsystem id */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci spinlock_t emu_lock; 2288c2ecf20Sopenharmony_ci spinlock_t voice_lock; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci struct snd_ac97 *ac97; 2318c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci struct emu10k1x_voice voices[3]; 2348c2ecf20Sopenharmony_ci struct emu10k1x_voice capture_voice; 2358c2ecf20Sopenharmony_ci u32 spdif_bits[3]; // SPDIF out setup 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci struct snd_dma_buffer dma_buffer; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci struct emu10k1x_midi midi; 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* hardware definition */ 2438c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1x_playback_hw = { 2448c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 2458c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 2468c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 2478c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 2488c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 2498c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 2508c2ecf20Sopenharmony_ci .rate_min = 48000, 2518c2ecf20Sopenharmony_ci .rate_max = 48000, 2528c2ecf20Sopenharmony_ci .channels_min = 2, 2538c2ecf20Sopenharmony_ci .channels_max = 2, 2548c2ecf20Sopenharmony_ci .buffer_bytes_max = (32*1024), 2558c2ecf20Sopenharmony_ci .period_bytes_min = 64, 2568c2ecf20Sopenharmony_ci .period_bytes_max = (16*1024), 2578c2ecf20Sopenharmony_ci .periods_min = 2, 2588c2ecf20Sopenharmony_ci .periods_max = 8, 2598c2ecf20Sopenharmony_ci .fifo_size = 0, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_emu10k1x_capture_hw = { 2638c2ecf20Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 2648c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 2658c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 2668c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 2678c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE, 2688c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 2698c2ecf20Sopenharmony_ci .rate_min = 48000, 2708c2ecf20Sopenharmony_ci .rate_max = 48000, 2718c2ecf20Sopenharmony_ci .channels_min = 2, 2728c2ecf20Sopenharmony_ci .channels_max = 2, 2738c2ecf20Sopenharmony_ci .buffer_bytes_max = (32*1024), 2748c2ecf20Sopenharmony_ci .period_bytes_min = 64, 2758c2ecf20Sopenharmony_ci .period_bytes_max = (16*1024), 2768c2ecf20Sopenharmony_ci .periods_min = 2, 2778c2ecf20Sopenharmony_ci .periods_max = 2, 2788c2ecf20Sopenharmony_ci .fifo_size = 0, 2798c2ecf20Sopenharmony_ci}; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic unsigned int snd_emu10k1x_ptr_read(struct emu10k1x * emu, 2828c2ecf20Sopenharmony_ci unsigned int reg, 2838c2ecf20Sopenharmony_ci unsigned int chn) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci unsigned long flags; 2868c2ecf20Sopenharmony_ci unsigned int regptr, val; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci regptr = (reg << 16) | chn; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 2918c2ecf20Sopenharmony_ci outl(regptr, emu->port + PTR); 2928c2ecf20Sopenharmony_ci val = inl(emu->port + DATA); 2938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 2948c2ecf20Sopenharmony_ci return val; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void snd_emu10k1x_ptr_write(struct emu10k1x *emu, 2988c2ecf20Sopenharmony_ci unsigned int reg, 2998c2ecf20Sopenharmony_ci unsigned int chn, 3008c2ecf20Sopenharmony_ci unsigned int data) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci unsigned int regptr; 3038c2ecf20Sopenharmony_ci unsigned long flags; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci regptr = (reg << 16) | chn; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3088c2ecf20Sopenharmony_ci outl(regptr, emu->port + PTR); 3098c2ecf20Sopenharmony_ci outl(data, emu->port + DATA); 3108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void snd_emu10k1x_intr_enable(struct emu10k1x *emu, unsigned int intrenb) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci unsigned long flags; 3168c2ecf20Sopenharmony_ci unsigned int intr_enable; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3198c2ecf20Sopenharmony_ci intr_enable = inl(emu->port + INTE) | intrenb; 3208c2ecf20Sopenharmony_ci outl(intr_enable, emu->port + INTE); 3218c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void snd_emu10k1x_intr_disable(struct emu10k1x *emu, unsigned int intrenb) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci unsigned long flags; 3278c2ecf20Sopenharmony_ci unsigned int intr_enable; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3308c2ecf20Sopenharmony_ci intr_enable = inl(emu->port + INTE) & ~intrenb; 3318c2ecf20Sopenharmony_ci outl(intr_enable, emu->port + INTE); 3328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic void snd_emu10k1x_gpio_write(struct emu10k1x *emu, unsigned int value) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci unsigned long flags; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 3408c2ecf20Sopenharmony_ci outl(value, emu->port + GPIO); 3418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void snd_emu10k1x_pcm_free_substream(struct snd_pcm_runtime *runtime) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci kfree(runtime->private_data); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void snd_emu10k1x_pcm_interrupt(struct emu10k1x *emu, struct emu10k1x_voice *voice) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if ((epcm = voice->epcm) == NULL) 3548c2ecf20Sopenharmony_ci return; 3558c2ecf20Sopenharmony_ci if (epcm->substream == NULL) 3568c2ecf20Sopenharmony_ci return; 3578c2ecf20Sopenharmony_ci#if 0 3588c2ecf20Sopenharmony_ci dev_info(emu->card->dev, 3598c2ecf20Sopenharmony_ci "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", 3608c2ecf20Sopenharmony_ci epcm->substream->ops->pointer(epcm->substream), 3618c2ecf20Sopenharmony_ci snd_pcm_lib_period_bytes(epcm->substream), 3628c2ecf20Sopenharmony_ci snd_pcm_lib_buffer_bytes(epcm->substream)); 3638c2ecf20Sopenharmony_ci#endif 3648c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(epcm->substream); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* open callback */ 3688c2ecf20Sopenharmony_cistatic int snd_emu10k1x_playback_open(struct snd_pcm_substream *substream) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct emu10k1x *chip = snd_pcm_substream_chip(substream); 3718c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm; 3728c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3738c2ecf20Sopenharmony_ci int err; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { 3768c2ecf20Sopenharmony_ci return err; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0) 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 3828c2ecf20Sopenharmony_ci if (epcm == NULL) 3838c2ecf20Sopenharmony_ci return -ENOMEM; 3848c2ecf20Sopenharmony_ci epcm->emu = chip; 3858c2ecf20Sopenharmony_ci epcm->substream = substream; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci runtime->private_data = epcm; 3888c2ecf20Sopenharmony_ci runtime->private_free = snd_emu10k1x_pcm_free_substream; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1x_playback_hw; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* close callback */ 3968c2ecf20Sopenharmony_cistatic int snd_emu10k1x_playback_close(struct snd_pcm_substream *substream) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/* hw_params callback */ 4028c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_params(struct snd_pcm_substream *substream, 4038c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4068c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (! epcm->voice) { 4098c2ecf20Sopenharmony_ci epcm->voice = &epcm->emu->voices[substream->pcm->device]; 4108c2ecf20Sopenharmony_ci epcm->voice->use = 1; 4118c2ecf20Sopenharmony_ci epcm->voice->epcm = epcm; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/* hw_free callback */ 4188c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_free(struct snd_pcm_substream *substream) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4218c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (runtime->private_data == NULL) 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci epcm = runtime->private_data; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (epcm->voice) { 4298c2ecf20Sopenharmony_ci epcm->voice->use = 0; 4308c2ecf20Sopenharmony_ci epcm->voice->epcm = NULL; 4318c2ecf20Sopenharmony_ci epcm->voice = NULL; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/* prepare callback */ 4388c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_prepare(struct snd_pcm_substream *substream) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 4418c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4428c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 4438c2ecf20Sopenharmony_ci int voice = epcm->voice->number; 4448c2ecf20Sopenharmony_ci u32 *table_base = (u32 *)(emu->dma_buffer.area+1024*voice); 4458c2ecf20Sopenharmony_ci u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size); 4468c2ecf20Sopenharmony_ci int i; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci for(i = 0; i < runtime->periods; i++) { 4498c2ecf20Sopenharmony_ci *table_base++=runtime->dma_addr+(i*period_size_bytes); 4508c2ecf20Sopenharmony_ci *table_base++=period_size_bytes<<16; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_ADDR, voice, emu->dma_buffer.addr+1024*voice); 4548c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_SIZE, voice, (runtime->periods - 1) << 19); 4558c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_LIST_PTR, voice, 0); 4568c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_POINTER, voice, 0); 4578c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN1, voice, 0); 4588c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_UNKNOWN2, voice, 0); 4598c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_DMA_ADDR, voice, runtime->dma_addr); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, PLAYBACK_PERIOD_SIZE, voice, frames_to_bytes(runtime, runtime->period_size)<<16); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* trigger callback */ 4678c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_trigger(struct snd_pcm_substream *substream, 4688c2ecf20Sopenharmony_ci int cmd) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 4718c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4728c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 4738c2ecf20Sopenharmony_ci int channel = epcm->voice->number; 4748c2ecf20Sopenharmony_ci int result = 0; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* 4778c2ecf20Sopenharmony_ci dev_dbg(emu->card->dev, 4788c2ecf20Sopenharmony_ci "trigger - emu10k1x = 0x%x, cmd = %i, pointer = %d\n", 4798c2ecf20Sopenharmony_ci (int)emu, cmd, (int)substream->ops->pointer(substream)); 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci switch (cmd) { 4838c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 4848c2ecf20Sopenharmony_ci if(runtime->periods == 2) 4858c2ecf20Sopenharmony_ci snd_emu10k1x_intr_enable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel); 4868c2ecf20Sopenharmony_ci else 4878c2ecf20Sopenharmony_ci snd_emu10k1x_intr_enable(emu, INTE_CH_0_LOOP << channel); 4888c2ecf20Sopenharmony_ci epcm->running = 1; 4898c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|(TRIGGER_CHANNEL_0<<channel)); 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 4928c2ecf20Sopenharmony_ci epcm->running = 0; 4938c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, (INTE_CH_0_LOOP | INTE_CH_0_HALF_LOOP) << channel); 4948c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CHANNEL_0<<channel)); 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci default: 4978c2ecf20Sopenharmony_ci result = -EINVAL; 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci return result; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/* pointer callback */ 5048c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 5058c2ecf20Sopenharmony_cisnd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 5088c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5098c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 5108c2ecf20Sopenharmony_ci int channel = epcm->voice->number; 5118c2ecf20Sopenharmony_ci snd_pcm_uframes_t ptr = 0, ptr1 = 0, ptr2= 0,ptr3 = 0,ptr4 = 0; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (!epcm->running) 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci ptr3 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel); 5178c2ecf20Sopenharmony_ci ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel); 5188c2ecf20Sopenharmony_ci ptr4 = snd_emu10k1x_ptr_read(emu, PLAYBACK_LIST_PTR, channel); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if(ptr4 == 0 && ptr1 == frames_to_bytes(runtime, runtime->buffer_size)) 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (ptr3 != ptr4) 5248c2ecf20Sopenharmony_ci ptr1 = snd_emu10k1x_ptr_read(emu, PLAYBACK_POINTER, channel); 5258c2ecf20Sopenharmony_ci ptr2 = bytes_to_frames(runtime, ptr1); 5268c2ecf20Sopenharmony_ci ptr2 += (ptr4 >> 3) * runtime->period_size; 5278c2ecf20Sopenharmony_ci ptr = ptr2; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci if (ptr >= runtime->buffer_size) 5308c2ecf20Sopenharmony_ci ptr -= runtime->buffer_size; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return ptr; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/* operators */ 5368c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1x_playback_ops = { 5378c2ecf20Sopenharmony_ci .open = snd_emu10k1x_playback_open, 5388c2ecf20Sopenharmony_ci .close = snd_emu10k1x_playback_close, 5398c2ecf20Sopenharmony_ci .hw_params = snd_emu10k1x_pcm_hw_params, 5408c2ecf20Sopenharmony_ci .hw_free = snd_emu10k1x_pcm_hw_free, 5418c2ecf20Sopenharmony_ci .prepare = snd_emu10k1x_pcm_prepare, 5428c2ecf20Sopenharmony_ci .trigger = snd_emu10k1x_pcm_trigger, 5438c2ecf20Sopenharmony_ci .pointer = snd_emu10k1x_pcm_pointer, 5448c2ecf20Sopenharmony_ci}; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* open_capture callback */ 5478c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_open_capture(struct snd_pcm_substream *substream) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct emu10k1x *chip = snd_pcm_substream_chip(substream); 5508c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm; 5518c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5528c2ecf20Sopenharmony_ci int err; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) 5558c2ecf20Sopenharmony_ci return err; 5568c2ecf20Sopenharmony_ci if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0) 5578c2ecf20Sopenharmony_ci return err; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); 5608c2ecf20Sopenharmony_ci if (epcm == NULL) 5618c2ecf20Sopenharmony_ci return -ENOMEM; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci epcm->emu = chip; 5648c2ecf20Sopenharmony_ci epcm->substream = substream; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci runtime->private_data = epcm; 5678c2ecf20Sopenharmony_ci runtime->private_free = snd_emu10k1x_pcm_free_substream; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci runtime->hw = snd_emu10k1x_capture_hw; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci/* close callback */ 5758c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_close_capture(struct snd_pcm_substream *substream) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci/* hw_params callback */ 5818c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_params_capture(struct snd_pcm_substream *substream, 5828c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5858c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (! epcm->voice) { 5888c2ecf20Sopenharmony_ci if (epcm->emu->capture_voice.use) 5898c2ecf20Sopenharmony_ci return -EBUSY; 5908c2ecf20Sopenharmony_ci epcm->voice = &epcm->emu->capture_voice; 5918c2ecf20Sopenharmony_ci epcm->voice->epcm = epcm; 5928c2ecf20Sopenharmony_ci epcm->voice->use = 1; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci/* hw_free callback */ 5998c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_hw_free_capture(struct snd_pcm_substream *substream) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (runtime->private_data == NULL) 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci epcm = runtime->private_data; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (epcm->voice) { 6108c2ecf20Sopenharmony_ci epcm->voice->use = 0; 6118c2ecf20Sopenharmony_ci epcm->voice->epcm = NULL; 6128c2ecf20Sopenharmony_ci epcm->voice = NULL; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci/* prepare capture callback */ 6198c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_prepare_capture(struct snd_pcm_substream *substream) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 6228c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_DMA_ADDR, 0, runtime->dma_addr); 6258c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_BUFFER_SIZE, 0, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes 6268c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_POINTER, 0, 0); 6278c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, CAPTURE_UNKNOWN, 0, 0); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return 0; 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci/* trigger_capture callback */ 6338c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm_trigger_capture(struct snd_pcm_substream *substream, 6348c2ecf20Sopenharmony_ci int cmd) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 6378c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6388c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 6398c2ecf20Sopenharmony_ci int result = 0; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci switch (cmd) { 6428c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 6438c2ecf20Sopenharmony_ci snd_emu10k1x_intr_enable(emu, INTE_CAP_0_LOOP | 6448c2ecf20Sopenharmony_ci INTE_CAP_0_HALF_LOOP); 6458c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0)|TRIGGER_CAPTURE); 6468c2ecf20Sopenharmony_ci epcm->running = 1; 6478c2ecf20Sopenharmony_ci break; 6488c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 6498c2ecf20Sopenharmony_ci epcm->running = 0; 6508c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, INTE_CAP_0_LOOP | 6518c2ecf20Sopenharmony_ci INTE_CAP_0_HALF_LOOP); 6528c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, TRIGGER_CHANNEL, 0, snd_emu10k1x_ptr_read(emu, TRIGGER_CHANNEL, 0) & ~(TRIGGER_CAPTURE)); 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci default: 6558c2ecf20Sopenharmony_ci result = -EINVAL; 6568c2ecf20Sopenharmony_ci break; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci return result; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/* pointer_capture callback */ 6628c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 6638c2ecf20Sopenharmony_cisnd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_pcm_substream_chip(substream); 6668c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 6678c2ecf20Sopenharmony_ci struct emu10k1x_pcm *epcm = runtime->private_data; 6688c2ecf20Sopenharmony_ci snd_pcm_uframes_t ptr; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (!epcm->running) 6718c2ecf20Sopenharmony_ci return 0; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci ptr = bytes_to_frames(runtime, snd_emu10k1x_ptr_read(emu, CAPTURE_POINTER, 0)); 6748c2ecf20Sopenharmony_ci if (ptr >= runtime->buffer_size) 6758c2ecf20Sopenharmony_ci ptr -= runtime->buffer_size; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return ptr; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_emu10k1x_capture_ops = { 6818c2ecf20Sopenharmony_ci .open = snd_emu10k1x_pcm_open_capture, 6828c2ecf20Sopenharmony_ci .close = snd_emu10k1x_pcm_close_capture, 6838c2ecf20Sopenharmony_ci .hw_params = snd_emu10k1x_pcm_hw_params_capture, 6848c2ecf20Sopenharmony_ci .hw_free = snd_emu10k1x_pcm_hw_free_capture, 6858c2ecf20Sopenharmony_ci .prepare = snd_emu10k1x_pcm_prepare_capture, 6868c2ecf20Sopenharmony_ci .trigger = snd_emu10k1x_pcm_trigger_capture, 6878c2ecf20Sopenharmony_ci .pointer = snd_emu10k1x_pcm_pointer_capture, 6888c2ecf20Sopenharmony_ci}; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic unsigned short snd_emu10k1x_ac97_read(struct snd_ac97 *ac97, 6918c2ecf20Sopenharmony_ci unsigned short reg) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct emu10k1x *emu = ac97->private_data; 6948c2ecf20Sopenharmony_ci unsigned long flags; 6958c2ecf20Sopenharmony_ci unsigned short val; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 6988c2ecf20Sopenharmony_ci outb(reg, emu->port + AC97ADDRESS); 6998c2ecf20Sopenharmony_ci val = inw(emu->port + AC97DATA); 7008c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 7018c2ecf20Sopenharmony_ci return val; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic void snd_emu10k1x_ac97_write(struct snd_ac97 *ac97, 7058c2ecf20Sopenharmony_ci unsigned short reg, unsigned short val) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci struct emu10k1x *emu = ac97->private_data; 7088c2ecf20Sopenharmony_ci unsigned long flags; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 7118c2ecf20Sopenharmony_ci outb(reg, emu->port + AC97ADDRESS); 7128c2ecf20Sopenharmony_ci outw(val, emu->port + AC97DATA); 7138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int snd_emu10k1x_ac97(struct emu10k1x *chip) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci struct snd_ac97_bus *pbus; 7198c2ecf20Sopenharmony_ci struct snd_ac97_template ac97; 7208c2ecf20Sopenharmony_ci int err; 7218c2ecf20Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 7228c2ecf20Sopenharmony_ci .write = snd_emu10k1x_ac97_write, 7238c2ecf20Sopenharmony_ci .read = snd_emu10k1x_ac97_read, 7248c2ecf20Sopenharmony_ci }; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) 7278c2ecf20Sopenharmony_ci return err; 7288c2ecf20Sopenharmony_ci pbus->no_vra = 1; /* we don't need VRA */ 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 7318c2ecf20Sopenharmony_ci ac97.private_data = chip; 7328c2ecf20Sopenharmony_ci ac97.scaps = AC97_SCAP_NO_SPDIF; 7338c2ecf20Sopenharmony_ci return snd_ac97_mixer(pbus, &ac97, &chip->ac97); 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic int snd_emu10k1x_free(struct emu10k1x *chip) 7378c2ecf20Sopenharmony_ci{ 7388c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(chip, TRIGGER_CHANNEL, 0, 0); 7398c2ecf20Sopenharmony_ci // disable interrupts 7408c2ecf20Sopenharmony_ci outl(0, chip->port + INTE); 7418c2ecf20Sopenharmony_ci // disable audio 7428c2ecf20Sopenharmony_ci outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* release the irq */ 7458c2ecf20Sopenharmony_ci if (chip->irq >= 0) 7468c2ecf20Sopenharmony_ci free_irq(chip->irq, chip); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci // release the i/o port 7498c2ecf20Sopenharmony_ci release_and_free_resource(chip->res_port); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci // release the DMA 7528c2ecf20Sopenharmony_ci if (chip->dma_buffer.area) { 7538c2ecf20Sopenharmony_ci snd_dma_free_pages(&chip->dma_buffer); 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci pci_disable_device(chip->pci); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci // release the data 7598c2ecf20Sopenharmony_ci kfree(chip); 7608c2ecf20Sopenharmony_ci return 0; 7618c2ecf20Sopenharmony_ci} 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic int snd_emu10k1x_dev_free(struct snd_device *device) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct emu10k1x *chip = device->device_data; 7668c2ecf20Sopenharmony_ci return snd_emu10k1x_free(chip); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic irqreturn_t snd_emu10k1x_interrupt(int irq, void *dev_id) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci unsigned int status; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci struct emu10k1x *chip = dev_id; 7748c2ecf20Sopenharmony_ci struct emu10k1x_voice *pvoice = chip->voices; 7758c2ecf20Sopenharmony_ci int i; 7768c2ecf20Sopenharmony_ci int mask; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci status = inl(chip->port + IPR); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (! status) 7818c2ecf20Sopenharmony_ci return IRQ_NONE; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci // capture interrupt 7848c2ecf20Sopenharmony_ci if (status & (IPR_CAP_0_LOOP | IPR_CAP_0_HALF_LOOP)) { 7858c2ecf20Sopenharmony_ci struct emu10k1x_voice *cap_voice = &chip->capture_voice; 7868c2ecf20Sopenharmony_ci if (cap_voice->use) 7878c2ecf20Sopenharmony_ci snd_emu10k1x_pcm_interrupt(chip, cap_voice); 7888c2ecf20Sopenharmony_ci else 7898c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(chip, 7908c2ecf20Sopenharmony_ci INTE_CAP_0_LOOP | 7918c2ecf20Sopenharmony_ci INTE_CAP_0_HALF_LOOP); 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci mask = IPR_CH_0_LOOP|IPR_CH_0_HALF_LOOP; 7958c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 7968c2ecf20Sopenharmony_ci if (status & mask) { 7978c2ecf20Sopenharmony_ci if (pvoice->use) 7988c2ecf20Sopenharmony_ci snd_emu10k1x_pcm_interrupt(chip, pvoice); 7998c2ecf20Sopenharmony_ci else 8008c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(chip, mask); 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci pvoice++; 8038c2ecf20Sopenharmony_ci mask <<= 1; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) { 8078c2ecf20Sopenharmony_ci if (chip->midi.interrupt) 8088c2ecf20Sopenharmony_ci chip->midi.interrupt(chip, status); 8098c2ecf20Sopenharmony_ci else 8108c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(chip, INTE_MIDITXENABLE|INTE_MIDIRXENABLE); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci // acknowledge the interrupt if necessary 8148c2ecf20Sopenharmony_ci outl(status, chip->port + IPR); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* dev_dbg(chip->card->dev, "interrupt %08x\n", status); */ 8178c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic const struct snd_pcm_chmap_elem surround_map[] = { 8218c2ecf20Sopenharmony_ci { .channels = 2, 8228c2ecf20Sopenharmony_ci .map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } }, 8238c2ecf20Sopenharmony_ci { } 8248c2ecf20Sopenharmony_ci}; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic const struct snd_pcm_chmap_elem clfe_map[] = { 8278c2ecf20Sopenharmony_ci { .channels = 2, 8288c2ecf20Sopenharmony_ci .map = { SNDRV_CHMAP_FC, SNDRV_CHMAP_LFE } }, 8298c2ecf20Sopenharmony_ci { } 8308c2ecf20Sopenharmony_ci}; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_cistatic int snd_emu10k1x_pcm(struct emu10k1x *emu, int device) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 8358c2ecf20Sopenharmony_ci const struct snd_pcm_chmap_elem *map = NULL; 8368c2ecf20Sopenharmony_ci int err; 8378c2ecf20Sopenharmony_ci int capture = 0; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (device == 0) 8408c2ecf20Sopenharmony_ci capture = 1; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci if ((err = snd_pcm_new(emu->card, "emu10k1x", device, 1, capture, &pcm)) < 0) 8438c2ecf20Sopenharmony_ci return err; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci pcm->private_data = emu; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci switch(device) { 8488c2ecf20Sopenharmony_ci case 0: 8498c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops); 8508c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1x_capture_ops); 8518c2ecf20Sopenharmony_ci break; 8528c2ecf20Sopenharmony_ci case 1: 8538c2ecf20Sopenharmony_ci case 2: 8548c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1x_playback_ops); 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci pcm->info_flags = 0; 8598c2ecf20Sopenharmony_ci switch(device) { 8608c2ecf20Sopenharmony_ci case 0: 8618c2ecf20Sopenharmony_ci strcpy(pcm->name, "EMU10K1X Front"); 8628c2ecf20Sopenharmony_ci map = snd_pcm_std_chmaps; 8638c2ecf20Sopenharmony_ci break; 8648c2ecf20Sopenharmony_ci case 1: 8658c2ecf20Sopenharmony_ci strcpy(pcm->name, "EMU10K1X Rear"); 8668c2ecf20Sopenharmony_ci map = surround_map; 8678c2ecf20Sopenharmony_ci break; 8688c2ecf20Sopenharmony_ci case 2: 8698c2ecf20Sopenharmony_ci strcpy(pcm->name, "EMU10K1X Center/LFE"); 8708c2ecf20Sopenharmony_ci map = clfe_map; 8718c2ecf20Sopenharmony_ci break; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci emu->pcm = pcm; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 8768c2ecf20Sopenharmony_ci &emu->pci->dev, 32*1024, 32*1024); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2, 8798c2ecf20Sopenharmony_ci 1 << 2, NULL); 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic int snd_emu10k1x_create(struct snd_card *card, 8838c2ecf20Sopenharmony_ci struct pci_dev *pci, 8848c2ecf20Sopenharmony_ci struct emu10k1x **rchip) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct emu10k1x *chip; 8878c2ecf20Sopenharmony_ci int err; 8888c2ecf20Sopenharmony_ci int ch; 8898c2ecf20Sopenharmony_ci static const struct snd_device_ops ops = { 8908c2ecf20Sopenharmony_ci .dev_free = snd_emu10k1x_dev_free, 8918c2ecf20Sopenharmony_ci }; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci *rchip = NULL; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if ((err = pci_enable_device(pci)) < 0) 8968c2ecf20Sopenharmony_ci return err; 8978c2ecf20Sopenharmony_ci if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 || 8988c2ecf20Sopenharmony_ci pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) { 8998c2ecf20Sopenharmony_ci dev_err(card->dev, "error to set 28bit mask DMA\n"); 9008c2ecf20Sopenharmony_ci pci_disable_device(pci); 9018c2ecf20Sopenharmony_ci return -ENXIO; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 9058c2ecf20Sopenharmony_ci if (chip == NULL) { 9068c2ecf20Sopenharmony_ci pci_disable_device(pci); 9078c2ecf20Sopenharmony_ci return -ENOMEM; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci chip->card = card; 9118c2ecf20Sopenharmony_ci chip->pci = pci; 9128c2ecf20Sopenharmony_ci chip->irq = -1; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci spin_lock_init(&chip->emu_lock); 9158c2ecf20Sopenharmony_ci spin_lock_init(&chip->voice_lock); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci chip->port = pci_resource_start(pci, 0); 9188c2ecf20Sopenharmony_ci if ((chip->res_port = request_region(chip->port, 8, 9198c2ecf20Sopenharmony_ci "EMU10K1X")) == NULL) { 9208c2ecf20Sopenharmony_ci dev_err(card->dev, "cannot allocate the port 0x%lx\n", 9218c2ecf20Sopenharmony_ci chip->port); 9228c2ecf20Sopenharmony_ci snd_emu10k1x_free(chip); 9238c2ecf20Sopenharmony_ci return -EBUSY; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (request_irq(pci->irq, snd_emu10k1x_interrupt, 9278c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip)) { 9288c2ecf20Sopenharmony_ci dev_err(card->dev, "cannot grab irq %d\n", pci->irq); 9298c2ecf20Sopenharmony_ci snd_emu10k1x_free(chip); 9308c2ecf20Sopenharmony_ci return -EBUSY; 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci chip->irq = pci->irq; 9338c2ecf20Sopenharmony_ci card->sync_irq = chip->irq; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &pci->dev, 9368c2ecf20Sopenharmony_ci 4 * 1024, &chip->dma_buffer) < 0) { 9378c2ecf20Sopenharmony_ci snd_emu10k1x_free(chip); 9388c2ecf20Sopenharmony_ci return -ENOMEM; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci pci_set_master(pci); 9428c2ecf20Sopenharmony_ci /* read revision & serial */ 9438c2ecf20Sopenharmony_ci chip->revision = pci->revision; 9448c2ecf20Sopenharmony_ci pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->serial); 9458c2ecf20Sopenharmony_ci pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->model); 9468c2ecf20Sopenharmony_ci dev_info(card->dev, "Model %04x Rev %08x Serial %08x\n", chip->model, 9478c2ecf20Sopenharmony_ci chip->revision, chip->serial); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci outl(0, chip->port + INTE); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci for(ch = 0; ch < 3; ch++) { 9528c2ecf20Sopenharmony_ci chip->voices[ch].emu = chip; 9538c2ecf20Sopenharmony_ci chip->voices[ch].number = ch; 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci /* 9578c2ecf20Sopenharmony_ci * Init to 0x02109204 : 9588c2ecf20Sopenharmony_ci * Clock accuracy = 0 (1000ppm) 9598c2ecf20Sopenharmony_ci * Sample Rate = 2 (48kHz) 9608c2ecf20Sopenharmony_ci * Audio Channel = 1 (Left of 2) 9618c2ecf20Sopenharmony_ci * Source Number = 0 (Unspecified) 9628c2ecf20Sopenharmony_ci * Generation Status = 1 (Original for Cat Code 12) 9638c2ecf20Sopenharmony_ci * Cat Code = 12 (Digital Signal Mixer) 9648c2ecf20Sopenharmony_ci * Mode = 0 (Mode 0) 9658c2ecf20Sopenharmony_ci * Emphasis = 0 (None) 9668c2ecf20Sopenharmony_ci * CP = 1 (Copyright unasserted) 9678c2ecf20Sopenharmony_ci * AN = 0 (Audio data) 9688c2ecf20Sopenharmony_ci * P = 0 (Consumer) 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPCS0, 0, 9718c2ecf20Sopenharmony_ci chip->spdif_bits[0] = 9728c2ecf20Sopenharmony_ci SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 9738c2ecf20Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | 9748c2ecf20Sopenharmony_ci SPCS_GENERATIONSTATUS | 0x00001200 | 9758c2ecf20Sopenharmony_ci 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); 9768c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPCS1, 0, 9778c2ecf20Sopenharmony_ci chip->spdif_bits[1] = 9788c2ecf20Sopenharmony_ci SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 9798c2ecf20Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | 9808c2ecf20Sopenharmony_ci SPCS_GENERATIONSTATUS | 0x00001200 | 9818c2ecf20Sopenharmony_ci 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); 9828c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPCS2, 0, 9838c2ecf20Sopenharmony_ci chip->spdif_bits[2] = 9848c2ecf20Sopenharmony_ci SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | 9858c2ecf20Sopenharmony_ci SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | 9868c2ecf20Sopenharmony_ci SPCS_GENERATIONSTATUS | 0x00001200 | 9878c2ecf20Sopenharmony_ci 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(chip, SPDIF_SELECT, 0, 0x700); // disable SPDIF 9908c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(chip, ROUTING, 0, 0x1003F); // routing 9918c2ecf20Sopenharmony_ci snd_emu10k1x_gpio_write(chip, 0x1080); // analog mode 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci outl(HCFG_LOCKSOUNDCACHE|HCFG_AUDIOENABLE, chip->port+HCFG); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, 9968c2ecf20Sopenharmony_ci chip, &ops)) < 0) { 9978c2ecf20Sopenharmony_ci snd_emu10k1x_free(chip); 9988c2ecf20Sopenharmony_ci return err; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci *rchip = chip; 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic void snd_emu10k1x_proc_reg_read(struct snd_info_entry *entry, 10058c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct emu10k1x *emu = entry->private_data; 10088c2ecf20Sopenharmony_ci unsigned long value,value1,value2; 10098c2ecf20Sopenharmony_ci unsigned long flags; 10108c2ecf20Sopenharmony_ci int i; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Registers:\n\n"); 10138c2ecf20Sopenharmony_ci for(i = 0; i < 0x20; i+=4) { 10148c2ecf20Sopenharmony_ci spin_lock_irqsave(&emu->emu_lock, flags); 10158c2ecf20Sopenharmony_ci value = inl(emu->port + i); 10168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&emu->emu_lock, flags); 10178c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Register %02X: %08lX\n", i, value); 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci snd_iprintf(buffer, "\nRegisters\n\n"); 10208c2ecf20Sopenharmony_ci for(i = 0; i <= 0x48; i++) { 10218c2ecf20Sopenharmony_ci value = snd_emu10k1x_ptr_read(emu, i, 0); 10228c2ecf20Sopenharmony_ci if(i < 0x10 || (i >= 0x20 && i < 0x40)) { 10238c2ecf20Sopenharmony_ci value1 = snd_emu10k1x_ptr_read(emu, i, 1); 10248c2ecf20Sopenharmony_ci value2 = snd_emu10k1x_ptr_read(emu, i, 2); 10258c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%02X: %08lX %08lX %08lX\n", i, value, value1, value2); 10268c2ecf20Sopenharmony_ci } else { 10278c2ecf20Sopenharmony_ci snd_iprintf(buffer, "%02X: %08lX\n", i, value); 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci} 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, 10338c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct emu10k1x *emu = entry->private_data; 10368c2ecf20Sopenharmony_ci char line[64]; 10378c2ecf20Sopenharmony_ci unsigned int reg, channel_id , val; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci while (!snd_info_get_line(buffer, line, sizeof(line))) { 10408c2ecf20Sopenharmony_ci if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) 10418c2ecf20Sopenharmony_ci continue; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (reg < 0x49 && channel_id <= 2) 10448c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, reg, channel_id, val); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_cistatic int snd_emu10k1x_proc_init(struct emu10k1x *emu) 10498c2ecf20Sopenharmony_ci{ 10508c2ecf20Sopenharmony_ci snd_card_rw_proc_new(emu->card, "emu10k1x_regs", emu, 10518c2ecf20Sopenharmony_ci snd_emu10k1x_proc_reg_read, 10528c2ecf20Sopenharmony_ci snd_emu10k1x_proc_reg_write); 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci#define snd_emu10k1x_shared_spdif_info snd_ctl_boolean_mono_info 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic int snd_emu10k1x_shared_spdif_get(struct snd_kcontrol *kcontrol, 10598c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = (snd_emu10k1x_ptr_read(emu, SPDIF_SELECT, 0) == 0x700) ? 0 : 1; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci return 0; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_cistatic int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol, 10698c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 10708c2ecf20Sopenharmony_ci{ 10718c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 10728c2ecf20Sopenharmony_ci unsigned int val; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci val = ucontrol->value.integer.value[0] ; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (val) { 10778c2ecf20Sopenharmony_ci // enable spdif output 10788c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x000); 10798c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x700); 10808c2ecf20Sopenharmony_ci snd_emu10k1x_gpio_write(emu, 0x1000); 10818c2ecf20Sopenharmony_ci } else { 10828c2ecf20Sopenharmony_ci // disable spdif output 10838c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, SPDIF_SELECT, 0, 0x700); 10848c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, ROUTING, 0, 0x1003F); 10858c2ecf20Sopenharmony_ci snd_emu10k1x_gpio_write(emu, 0x1080); 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci return 0; 10888c2ecf20Sopenharmony_ci} 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1x_shared_spdif = 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10938c2ecf20Sopenharmony_ci .name = "Analog/Digital Output Jack", 10948c2ecf20Sopenharmony_ci .info = snd_emu10k1x_shared_spdif_info, 10958c2ecf20Sopenharmony_ci .get = snd_emu10k1x_shared_spdif_get, 10968c2ecf20Sopenharmony_ci .put = snd_emu10k1x_shared_spdif_put 10978c2ecf20Sopenharmony_ci}; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic int snd_emu10k1x_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 11028c2ecf20Sopenharmony_ci uinfo->count = 1; 11038c2ecf20Sopenharmony_ci return 0; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic int snd_emu10k1x_spdif_get(struct snd_kcontrol *kcontrol, 11078c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 11108c2ecf20Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff; 11138c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff; 11148c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff; 11158c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff; 11168c2ecf20Sopenharmony_ci return 0; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic int snd_emu10k1x_spdif_get_mask(struct snd_kcontrol *kcontrol, 11208c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[0] = 0xff; 11238c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[1] = 0xff; 11248c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[2] = 0xff; 11258c2ecf20Sopenharmony_ci ucontrol->value.iec958.status[3] = 0xff; 11268c2ecf20Sopenharmony_ci return 0; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol, 11308c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct emu10k1x *emu = snd_kcontrol_chip(kcontrol); 11338c2ecf20Sopenharmony_ci unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 11348c2ecf20Sopenharmony_ci int change; 11358c2ecf20Sopenharmony_ci unsigned int val; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci val = (ucontrol->value.iec958.status[0] << 0) | 11388c2ecf20Sopenharmony_ci (ucontrol->value.iec958.status[1] << 8) | 11398c2ecf20Sopenharmony_ci (ucontrol->value.iec958.status[2] << 16) | 11408c2ecf20Sopenharmony_ci (ucontrol->value.iec958.status[3] << 24); 11418c2ecf20Sopenharmony_ci change = val != emu->spdif_bits[idx]; 11428c2ecf20Sopenharmony_ci if (change) { 11438c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, SPCS0 + idx, 0, val); 11448c2ecf20Sopenharmony_ci emu->spdif_bits[idx] = val; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci return change; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control = 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 11528c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 11538c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), 11548c2ecf20Sopenharmony_ci .count = 3, 11558c2ecf20Sopenharmony_ci .info = snd_emu10k1x_spdif_info, 11568c2ecf20Sopenharmony_ci .get = snd_emu10k1x_spdif_get_mask 11578c2ecf20Sopenharmony_ci}; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_emu10k1x_spdif_control = 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 11628c2ecf20Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), 11638c2ecf20Sopenharmony_ci .count = 3, 11648c2ecf20Sopenharmony_ci .info = snd_emu10k1x_spdif_info, 11658c2ecf20Sopenharmony_ci .get = snd_emu10k1x_spdif_get, 11668c2ecf20Sopenharmony_ci .put = snd_emu10k1x_spdif_put 11678c2ecf20Sopenharmony_ci}; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_cistatic int snd_emu10k1x_mixer(struct emu10k1x *emu) 11708c2ecf20Sopenharmony_ci{ 11718c2ecf20Sopenharmony_ci int err; 11728c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 11738c2ecf20Sopenharmony_ci struct snd_card *card = emu->card; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_mask_control, emu)) == NULL) 11768c2ecf20Sopenharmony_ci return -ENOMEM; 11778c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl))) 11788c2ecf20Sopenharmony_ci return err; 11798c2ecf20Sopenharmony_ci if ((kctl = snd_ctl_new1(&snd_emu10k1x_shared_spdif, emu)) == NULL) 11808c2ecf20Sopenharmony_ci return -ENOMEM; 11818c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl))) 11828c2ecf20Sopenharmony_ci return err; 11838c2ecf20Sopenharmony_ci if ((kctl = snd_ctl_new1(&snd_emu10k1x_spdif_control, emu)) == NULL) 11848c2ecf20Sopenharmony_ci return -ENOMEM; 11858c2ecf20Sopenharmony_ci if ((err = snd_ctl_add(card, kctl))) 11868c2ecf20Sopenharmony_ci return err; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci return 0; 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci#define EMU10K1X_MIDI_MODE_INPUT (1<<0) 11928c2ecf20Sopenharmony_ci#define EMU10K1X_MIDI_MODE_OUTPUT (1<<1) 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic inline unsigned char mpu401_read(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int idx) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci return (unsigned char)snd_emu10k1x_ptr_read(emu, mpu->port + idx, 0); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic inline void mpu401_write(struct emu10k1x *emu, struct emu10k1x_midi *mpu, int data, int idx) 12008c2ecf20Sopenharmony_ci{ 12018c2ecf20Sopenharmony_ci snd_emu10k1x_ptr_write(emu, mpu->port + idx, 0, data); 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0) 12058c2ecf20Sopenharmony_ci#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1) 12068c2ecf20Sopenharmony_ci#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0) 12078c2ecf20Sopenharmony_ci#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1) 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80)) 12108c2ecf20Sopenharmony_ci#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40)) 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci#define MPU401_RESET 0xff 12138c2ecf20Sopenharmony_ci#define MPU401_ENTER_UART 0x3f 12148c2ecf20Sopenharmony_ci#define MPU401_ACK 0xfe 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic void mpu401_clear_rx(struct emu10k1x *emu, struct emu10k1x_midi *mpu) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci int timeout = 100000; 12198c2ecf20Sopenharmony_ci for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--) 12208c2ecf20Sopenharmony_ci mpu401_read_data(emu, mpu); 12218c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 12228c2ecf20Sopenharmony_ci if (timeout <= 0) 12238c2ecf20Sopenharmony_ci dev_err(emu->card->dev, 12248c2ecf20Sopenharmony_ci "cmd: clear rx timeout (status = 0x%x)\n", 12258c2ecf20Sopenharmony_ci mpu401_read_stat(emu, mpu)); 12268c2ecf20Sopenharmony_ci#endif 12278c2ecf20Sopenharmony_ci} 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci/* 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_cistatic void do_emu10k1x_midi_interrupt(struct emu10k1x *emu, 12348c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi, unsigned int status) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci unsigned char byte; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (midi->rmidi == NULL) { 12398c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable | midi->rx_enable); 12408c2ecf20Sopenharmony_ci return; 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci spin_lock(&midi->input_lock); 12448c2ecf20Sopenharmony_ci if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) { 12458c2ecf20Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) { 12468c2ecf20Sopenharmony_ci mpu401_clear_rx(emu, midi); 12478c2ecf20Sopenharmony_ci } else { 12488c2ecf20Sopenharmony_ci byte = mpu401_read_data(emu, midi); 12498c2ecf20Sopenharmony_ci if (midi->substream_input) 12508c2ecf20Sopenharmony_ci snd_rawmidi_receive(midi->substream_input, &byte, 1); 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci spin_unlock(&midi->input_lock); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci spin_lock(&midi->output_lock); 12568c2ecf20Sopenharmony_ci if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) { 12578c2ecf20Sopenharmony_ci if (midi->substream_output && 12588c2ecf20Sopenharmony_ci snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) { 12598c2ecf20Sopenharmony_ci mpu401_write_data(emu, midi, byte); 12608c2ecf20Sopenharmony_ci } else { 12618c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable); 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci spin_unlock(&midi->output_lock); 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistatic void snd_emu10k1x_midi_interrupt(struct emu10k1x *emu, unsigned int status) 12688c2ecf20Sopenharmony_ci{ 12698c2ecf20Sopenharmony_ci do_emu10k1x_midi_interrupt(emu, &emu->midi, status); 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic int snd_emu10k1x_midi_cmd(struct emu10k1x * emu, 12738c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi, unsigned char cmd, int ack) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci unsigned long flags; 12768c2ecf20Sopenharmony_ci int timeout, ok; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci spin_lock_irqsave(&midi->input_lock, flags); 12798c2ecf20Sopenharmony_ci mpu401_write_data(emu, midi, 0x00); 12808c2ecf20Sopenharmony_ci /* mpu401_clear_rx(emu, midi); */ 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci mpu401_write_cmd(emu, midi, cmd); 12838c2ecf20Sopenharmony_ci if (ack) { 12848c2ecf20Sopenharmony_ci ok = 0; 12858c2ecf20Sopenharmony_ci timeout = 10000; 12868c2ecf20Sopenharmony_ci while (!ok && timeout-- > 0) { 12878c2ecf20Sopenharmony_ci if (mpu401_input_avail(emu, midi)) { 12888c2ecf20Sopenharmony_ci if (mpu401_read_data(emu, midi) == MPU401_ACK) 12898c2ecf20Sopenharmony_ci ok = 1; 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK) 12938c2ecf20Sopenharmony_ci ok = 1; 12948c2ecf20Sopenharmony_ci } else { 12958c2ecf20Sopenharmony_ci ok = 1; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->input_lock, flags); 12988c2ecf20Sopenharmony_ci if (!ok) { 12998c2ecf20Sopenharmony_ci dev_err(emu->card->dev, 13008c2ecf20Sopenharmony_ci "midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n", 13018c2ecf20Sopenharmony_ci cmd, emu->port, 13028c2ecf20Sopenharmony_ci mpu401_read_stat(emu, midi), 13038c2ecf20Sopenharmony_ci mpu401_read_data(emu, midi)); 13048c2ecf20Sopenharmony_ci return 1; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci return 0; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_cistatic int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct emu10k1x *emu; 13128c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 13138c2ecf20Sopenharmony_ci unsigned long flags; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci emu = midi->emu; 13168c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 13178c2ecf20Sopenharmony_ci return -ENXIO; 13188c2ecf20Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 13198c2ecf20Sopenharmony_ci midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT; 13208c2ecf20Sopenharmony_ci midi->substream_input = substream; 13218c2ecf20Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) { 13228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 13238c2ecf20Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1)) 13248c2ecf20Sopenharmony_ci goto error_out; 13258c2ecf20Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) 13268c2ecf20Sopenharmony_ci goto error_out; 13278c2ecf20Sopenharmony_ci } else { 13288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci return 0; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cierror_out: 13338c2ecf20Sopenharmony_ci return -EIO; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistatic int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream) 13378c2ecf20Sopenharmony_ci{ 13388c2ecf20Sopenharmony_ci struct emu10k1x *emu; 13398c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 13408c2ecf20Sopenharmony_ci unsigned long flags; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci emu = midi->emu; 13438c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 13448c2ecf20Sopenharmony_ci return -ENXIO; 13458c2ecf20Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 13468c2ecf20Sopenharmony_ci midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT; 13478c2ecf20Sopenharmony_ci midi->substream_output = substream; 13488c2ecf20Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) { 13498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 13508c2ecf20Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 1)) 13518c2ecf20Sopenharmony_ci goto error_out; 13528c2ecf20Sopenharmony_ci if (snd_emu10k1x_midi_cmd(emu, midi, MPU401_ENTER_UART, 1)) 13538c2ecf20Sopenharmony_ci goto error_out; 13548c2ecf20Sopenharmony_ci } else { 13558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 13568c2ecf20Sopenharmony_ci } 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cierror_out: 13608c2ecf20Sopenharmony_ci return -EIO; 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream) 13648c2ecf20Sopenharmony_ci{ 13658c2ecf20Sopenharmony_ci struct emu10k1x *emu; 13668c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 13678c2ecf20Sopenharmony_ci unsigned long flags; 13688c2ecf20Sopenharmony_ci int err = 0; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci emu = midi->emu; 13718c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 13728c2ecf20Sopenharmony_ci return -ENXIO; 13738c2ecf20Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 13748c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->rx_enable); 13758c2ecf20Sopenharmony_ci midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT; 13768c2ecf20Sopenharmony_ci midi->substream_input = NULL; 13778c2ecf20Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT)) { 13788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 13798c2ecf20Sopenharmony_ci err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0); 13808c2ecf20Sopenharmony_ci } else { 13818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci return err; 13848c2ecf20Sopenharmony_ci} 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_cistatic int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substream) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci struct emu10k1x *emu; 13898c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 13908c2ecf20Sopenharmony_ci unsigned long flags; 13918c2ecf20Sopenharmony_ci int err = 0; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci emu = midi->emu; 13948c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 13958c2ecf20Sopenharmony_ci return -ENXIO; 13968c2ecf20Sopenharmony_ci spin_lock_irqsave(&midi->open_lock, flags); 13978c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable); 13988c2ecf20Sopenharmony_ci midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT; 13998c2ecf20Sopenharmony_ci midi->substream_output = NULL; 14008c2ecf20Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_INPUT)) { 14018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 14028c2ecf20Sopenharmony_ci err = snd_emu10k1x_midi_cmd(emu, midi, MPU401_RESET, 0); 14038c2ecf20Sopenharmony_ci } else { 14048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->open_lock, flags); 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci return err; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_cistatic void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substream, int up) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci struct emu10k1x *emu; 14128c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 14138c2ecf20Sopenharmony_ci emu = midi->emu; 14148c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 14158c2ecf20Sopenharmony_ci return; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (up) 14188c2ecf20Sopenharmony_ci snd_emu10k1x_intr_enable(emu, midi->rx_enable); 14198c2ecf20Sopenharmony_ci else 14208c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->rx_enable); 14218c2ecf20Sopenharmony_ci} 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_cistatic void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *substream, int up) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct emu10k1x *emu; 14268c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = substream->rmidi->private_data; 14278c2ecf20Sopenharmony_ci unsigned long flags; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci emu = midi->emu; 14308c2ecf20Sopenharmony_ci if (snd_BUG_ON(!emu)) 14318c2ecf20Sopenharmony_ci return; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (up) { 14348c2ecf20Sopenharmony_ci int max = 4; 14358c2ecf20Sopenharmony_ci unsigned char byte; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* try to send some amount of bytes here before interrupts */ 14388c2ecf20Sopenharmony_ci spin_lock_irqsave(&midi->output_lock, flags); 14398c2ecf20Sopenharmony_ci while (max > 0) { 14408c2ecf20Sopenharmony_ci if (mpu401_output_ready(emu, midi)) { 14418c2ecf20Sopenharmony_ci if (!(midi->midi_mode & EMU10K1X_MIDI_MODE_OUTPUT) || 14428c2ecf20Sopenharmony_ci snd_rawmidi_transmit(substream, &byte, 1) != 1) { 14438c2ecf20Sopenharmony_ci /* no more data */ 14448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->output_lock, flags); 14458c2ecf20Sopenharmony_ci return; 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci mpu401_write_data(emu, midi, byte); 14488c2ecf20Sopenharmony_ci max--; 14498c2ecf20Sopenharmony_ci } else { 14508c2ecf20Sopenharmony_ci break; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&midi->output_lock, flags); 14548c2ecf20Sopenharmony_ci snd_emu10k1x_intr_enable(emu, midi->tx_enable); 14558c2ecf20Sopenharmony_ci } else { 14568c2ecf20Sopenharmony_ci snd_emu10k1x_intr_disable(emu, midi->tx_enable); 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci/* 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_emu10k1x_midi_output = 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci .open = snd_emu10k1x_midi_output_open, 14678c2ecf20Sopenharmony_ci .close = snd_emu10k1x_midi_output_close, 14688c2ecf20Sopenharmony_ci .trigger = snd_emu10k1x_midi_output_trigger, 14698c2ecf20Sopenharmony_ci}; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_cistatic const struct snd_rawmidi_ops snd_emu10k1x_midi_input = 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci .open = snd_emu10k1x_midi_input_open, 14748c2ecf20Sopenharmony_ci .close = snd_emu10k1x_midi_input_close, 14758c2ecf20Sopenharmony_ci .trigger = snd_emu10k1x_midi_input_trigger, 14768c2ecf20Sopenharmony_ci}; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic void snd_emu10k1x_midi_free(struct snd_rawmidi *rmidi) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = rmidi->private_data; 14818c2ecf20Sopenharmony_ci midi->interrupt = NULL; 14828c2ecf20Sopenharmony_ci midi->rmidi = NULL; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cistatic int emu10k1x_midi_init(struct emu10k1x *emu, 14868c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi, int device, 14878c2ecf20Sopenharmony_ci char *name) 14888c2ecf20Sopenharmony_ci{ 14898c2ecf20Sopenharmony_ci struct snd_rawmidi *rmidi; 14908c2ecf20Sopenharmony_ci int err; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0) 14938c2ecf20Sopenharmony_ci return err; 14948c2ecf20Sopenharmony_ci midi->emu = emu; 14958c2ecf20Sopenharmony_ci spin_lock_init(&midi->open_lock); 14968c2ecf20Sopenharmony_ci spin_lock_init(&midi->input_lock); 14978c2ecf20Sopenharmony_ci spin_lock_init(&midi->output_lock); 14988c2ecf20Sopenharmony_ci strcpy(rmidi->name, name); 14998c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1x_midi_output); 15008c2ecf20Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1x_midi_input); 15018c2ecf20Sopenharmony_ci rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | 15028c2ecf20Sopenharmony_ci SNDRV_RAWMIDI_INFO_INPUT | 15038c2ecf20Sopenharmony_ci SNDRV_RAWMIDI_INFO_DUPLEX; 15048c2ecf20Sopenharmony_ci rmidi->private_data = midi; 15058c2ecf20Sopenharmony_ci rmidi->private_free = snd_emu10k1x_midi_free; 15068c2ecf20Sopenharmony_ci midi->rmidi = rmidi; 15078c2ecf20Sopenharmony_ci return 0; 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic int snd_emu10k1x_midi(struct emu10k1x *emu) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci struct emu10k1x_midi *midi = &emu->midi; 15138c2ecf20Sopenharmony_ci int err; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if ((err = emu10k1x_midi_init(emu, midi, 0, "EMU10K1X MPU-401 (UART)")) < 0) 15168c2ecf20Sopenharmony_ci return err; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci midi->tx_enable = INTE_MIDITXENABLE; 15198c2ecf20Sopenharmony_ci midi->rx_enable = INTE_MIDIRXENABLE; 15208c2ecf20Sopenharmony_ci midi->port = MUDATA; 15218c2ecf20Sopenharmony_ci midi->ipr_tx = IPR_MIDITRANSBUFEMPTY; 15228c2ecf20Sopenharmony_ci midi->ipr_rx = IPR_MIDIRECVBUFEMPTY; 15238c2ecf20Sopenharmony_ci midi->interrupt = snd_emu10k1x_midi_interrupt; 15248c2ecf20Sopenharmony_ci return 0; 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic int snd_emu10k1x_probe(struct pci_dev *pci, 15288c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci static int dev; 15318c2ecf20Sopenharmony_ci struct snd_card *card; 15328c2ecf20Sopenharmony_ci struct emu10k1x *chip; 15338c2ecf20Sopenharmony_ci int err; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 15368c2ecf20Sopenharmony_ci return -ENODEV; 15378c2ecf20Sopenharmony_ci if (!enable[dev]) { 15388c2ecf20Sopenharmony_ci dev++; 15398c2ecf20Sopenharmony_ci return -ENOENT; 15408c2ecf20Sopenharmony_ci } 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 15438c2ecf20Sopenharmony_ci 0, &card); 15448c2ecf20Sopenharmony_ci if (err < 0) 15458c2ecf20Sopenharmony_ci return err; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci if ((err = snd_emu10k1x_create(card, pci, &chip)) < 0) { 15488c2ecf20Sopenharmony_ci snd_card_free(card); 15498c2ecf20Sopenharmony_ci return err; 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if ((err = snd_emu10k1x_pcm(chip, 0)) < 0) { 15538c2ecf20Sopenharmony_ci snd_card_free(card); 15548c2ecf20Sopenharmony_ci return err; 15558c2ecf20Sopenharmony_ci } 15568c2ecf20Sopenharmony_ci if ((err = snd_emu10k1x_pcm(chip, 1)) < 0) { 15578c2ecf20Sopenharmony_ci snd_card_free(card); 15588c2ecf20Sopenharmony_ci return err; 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci if ((err = snd_emu10k1x_pcm(chip, 2)) < 0) { 15618c2ecf20Sopenharmony_ci snd_card_free(card); 15628c2ecf20Sopenharmony_ci return err; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci if ((err = snd_emu10k1x_ac97(chip)) < 0) { 15668c2ecf20Sopenharmony_ci snd_card_free(card); 15678c2ecf20Sopenharmony_ci return err; 15688c2ecf20Sopenharmony_ci } 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci if ((err = snd_emu10k1x_mixer(chip)) < 0) { 15718c2ecf20Sopenharmony_ci snd_card_free(card); 15728c2ecf20Sopenharmony_ci return err; 15738c2ecf20Sopenharmony_ci } 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci if ((err = snd_emu10k1x_midi(chip)) < 0) { 15768c2ecf20Sopenharmony_ci snd_card_free(card); 15778c2ecf20Sopenharmony_ci return err; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci snd_emu10k1x_proc_init(chip); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci strcpy(card->driver, "EMU10K1X"); 15838c2ecf20Sopenharmony_ci strcpy(card->shortname, "Dell Sound Blaster Live!"); 15848c2ecf20Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx irq %i", 15858c2ecf20Sopenharmony_ci card->shortname, chip->port, chip->irq); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci if ((err = snd_card_register(card)) < 0) { 15888c2ecf20Sopenharmony_ci snd_card_free(card); 15898c2ecf20Sopenharmony_ci return err; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci pci_set_drvdata(pci, card); 15938c2ecf20Sopenharmony_ci dev++; 15948c2ecf20Sopenharmony_ci return 0; 15958c2ecf20Sopenharmony_ci} 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cistatic void snd_emu10k1x_remove(struct pci_dev *pci) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci snd_card_free(pci_get_drvdata(pci)); 16008c2ecf20Sopenharmony_ci} 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci// PCI IDs 16038c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_emu10k1x_ids[] = { 16048c2ecf20Sopenharmony_ci { PCI_VDEVICE(CREATIVE, 0x0006), 0 }, /* Dell OEM version (EMU10K1) */ 16058c2ecf20Sopenharmony_ci { 0, } 16068c2ecf20Sopenharmony_ci}; 16078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci// pci_driver definition 16108c2ecf20Sopenharmony_cistatic struct pci_driver emu10k1x_driver = { 16118c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 16128c2ecf20Sopenharmony_ci .id_table = snd_emu10k1x_ids, 16138c2ecf20Sopenharmony_ci .probe = snd_emu10k1x_probe, 16148c2ecf20Sopenharmony_ci .remove = snd_emu10k1x_remove, 16158c2ecf20Sopenharmony_ci}; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cimodule_pci_driver(emu10k1x_driver); 1618