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", &reg, &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