18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  card-als4000.c - driver for Avance Logic ALS4000 based soundcards.
48c2ecf20Sopenharmony_ci *  Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,
58c2ecf20Sopenharmony_ci *			  Jaroslav Kysela <perex@perex.cz>
68c2ecf20Sopenharmony_ci *  Copyright (C) 2002, 2008 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  Framework borrowed from Massimo Piccioni's card-als100.c.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * NOTES
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *  Since Avance does not provide any meaningful documentation, and I
138c2ecf20Sopenharmony_ci *  bought an ALS4000 based soundcard, I was forced to base this driver
148c2ecf20Sopenharmony_ci *  on reverse engineering.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *  Note: this is no longer true (thank you!):
178c2ecf20Sopenharmony_ci *  pretty verbose chip docu (ALS4000a.PDF) can be found on the ALSA web site.
188c2ecf20Sopenharmony_ci *  Page numbers stated anywhere below with the "SPECS_PAGE:" tag
198c2ecf20Sopenharmony_ci *  refer to: ALS4000a.PDF specs Ver 1.0, May 28th, 1998.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *  The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an
228c2ecf20Sopenharmony_ci *  ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport
238c2ecf20Sopenharmony_ci *  interface. These subsystems can be mapped into ISA io-port space,
248c2ecf20Sopenharmony_ci *  using the PCI-interface. In addition, the PCI-bit provides DMA and IRQ
258c2ecf20Sopenharmony_ci *  services to the subsystems.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * While ALS4000 is very similar to a SoundBlaster, the differences in
288c2ecf20Sopenharmony_ci * DMA and capturing require more changes to the SoundBlaster than
298c2ecf20Sopenharmony_ci * desirable, so I made this separate driver.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * The ALS4000 can do real full duplex playback/capture.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * FMDAC:
348c2ecf20Sopenharmony_ci * - 0x4f -> port 0x14
358c2ecf20Sopenharmony_ci * - port 0x15 |= 1
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * Enable/disable 3D sound:
388c2ecf20Sopenharmony_ci * - 0x50 -> port 0x14
398c2ecf20Sopenharmony_ci * - change bit 6 (0x40) of port 0x15
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * Set QSound:
428c2ecf20Sopenharmony_ci * - 0xdb -> port 0x14
438c2ecf20Sopenharmony_ci * - set port 0x15:
448c2ecf20Sopenharmony_ci *   0x3e (mode 3), 0x3c (mode 2), 0x3a (mode 1), 0x38 (mode 0)
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * Set KSound:
478c2ecf20Sopenharmony_ci * - value -> some port 0x0c0d
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * ToDo:
508c2ecf20Sopenharmony_ci * - by default, don't enable legacy game and use PCI game I/O
518c2ecf20Sopenharmony_ci * - power management? (card can do voice wakeup according to datasheet!!)
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include <linux/io.h>
558c2ecf20Sopenharmony_ci#include <linux/init.h>
568c2ecf20Sopenharmony_ci#include <linux/pci.h>
578c2ecf20Sopenharmony_ci#include <linux/gameport.h>
588c2ecf20Sopenharmony_ci#include <linux/module.h>
598c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
608c2ecf20Sopenharmony_ci#include <sound/core.h>
618c2ecf20Sopenharmony_ci#include <sound/pcm.h>
628c2ecf20Sopenharmony_ci#include <sound/rawmidi.h>
638c2ecf20Sopenharmony_ci#include <sound/mpu401.h>
648c2ecf20Sopenharmony_ci#include <sound/opl3.h>
658c2ecf20Sopenharmony_ci#include <sound/sb.h>
668c2ecf20Sopenharmony_ci#include <sound/initval.h>
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>, Andreas Mohr");
698c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Avance Logic ALS4000");
708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
718c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}");
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT)
748c2ecf20Sopenharmony_ci#define SUPPORT_JOYSTICK 1
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
788c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
798c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */
808c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
818c2ecf20Sopenharmony_cistatic int joystick_port[SNDRV_CARDS];
828c2ecf20Sopenharmony_ci#endif
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for ALS4000 soundcard.");
868c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
878c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for ALS4000 soundcard.");
888c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
898c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable ALS4000 soundcard.");
908c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
918c2ecf20Sopenharmony_cimodule_param_hw_array(joystick_port, int, ioport, NULL, 0444);
928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
938c2ecf20Sopenharmony_ci#endif
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct snd_card_als4000 {
968c2ecf20Sopenharmony_ci	/* most frequent access first */
978c2ecf20Sopenharmony_ci	unsigned long iobase;
988c2ecf20Sopenharmony_ci	struct pci_dev *pci;
998c2ecf20Sopenharmony_ci	struct snd_sb *chip;
1008c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
1018c2ecf20Sopenharmony_ci	struct gameport *gameport;
1028c2ecf20Sopenharmony_ci#endif
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_als4000_ids[] = {
1068c2ecf20Sopenharmony_ci	{ 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ALS4000 */
1078c2ecf20Sopenharmony_ci	{ 0, }
1088c2ecf20Sopenharmony_ci};
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_als4000_ids);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cienum als4k_iobase_t {
1138c2ecf20Sopenharmony_ci	/* IOx: B == Byte, W = Word, D = DWord; SPECS_PAGE: 37 */
1148c2ecf20Sopenharmony_ci	ALS4K_IOD_00_AC97_ACCESS = 0x00,
1158c2ecf20Sopenharmony_ci	ALS4K_IOW_04_AC97_READ = 0x04,
1168c2ecf20Sopenharmony_ci	ALS4K_IOB_06_AC97_STATUS = 0x06,
1178c2ecf20Sopenharmony_ci	ALS4K_IOB_07_IRQSTATUS = 0x07,
1188c2ecf20Sopenharmony_ci	ALS4K_IOD_08_GCR_DATA = 0x08,
1198c2ecf20Sopenharmony_ci	ALS4K_IOB_0C_GCR_INDEX = 0x0c,
1208c2ecf20Sopenharmony_ci	ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU = 0x0e,
1218c2ecf20Sopenharmony_ci	ALS4K_IOB_10_ADLIB_ADDR0 = 0x10,
1228c2ecf20Sopenharmony_ci	ALS4K_IOB_11_ADLIB_ADDR1 = 0x11,
1238c2ecf20Sopenharmony_ci	ALS4K_IOB_12_ADLIB_ADDR2 = 0x12,
1248c2ecf20Sopenharmony_ci	ALS4K_IOB_13_ADLIB_ADDR3 = 0x13,
1258c2ecf20Sopenharmony_ci	ALS4K_IOB_14_MIXER_INDEX = 0x14,
1268c2ecf20Sopenharmony_ci	ALS4K_IOB_15_MIXER_DATA = 0x15,
1278c2ecf20Sopenharmony_ci	ALS4K_IOB_16_ESP_RESET = 0x16,
1288c2ecf20Sopenharmony_ci	ALS4K_IOB_16_ACK_FOR_CR1E = 0x16, /* 2nd function */
1298c2ecf20Sopenharmony_ci	ALS4K_IOB_18_OPL_ADDR0 = 0x18,
1308c2ecf20Sopenharmony_ci	ALS4K_IOB_19_OPL_ADDR1 = 0x19,
1318c2ecf20Sopenharmony_ci	ALS4K_IOB_1A_ESP_RD_DATA = 0x1a,
1328c2ecf20Sopenharmony_ci	ALS4K_IOB_1C_ESP_CMD_DATA = 0x1c,
1338c2ecf20Sopenharmony_ci	ALS4K_IOB_1C_ESP_WR_STATUS = 0x1c, /* 2nd function */
1348c2ecf20Sopenharmony_ci	ALS4K_IOB_1E_ESP_RD_STATUS8 = 0x1e,
1358c2ecf20Sopenharmony_ci	ALS4K_IOB_1F_ESP_RD_STATUS16 = 0x1f,
1368c2ecf20Sopenharmony_ci	ALS4K_IOB_20_ESP_GAMEPORT_200 = 0x20,
1378c2ecf20Sopenharmony_ci	ALS4K_IOB_21_ESP_GAMEPORT_201 = 0x21,
1388c2ecf20Sopenharmony_ci	ALS4K_IOB_30_MIDI_DATA = 0x30,
1398c2ecf20Sopenharmony_ci	ALS4K_IOB_31_MIDI_STATUS = 0x31,
1408c2ecf20Sopenharmony_ci	ALS4K_IOB_31_MIDI_COMMAND = 0x31, /* 2nd function */
1418c2ecf20Sopenharmony_ci};
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cienum als4k_iobase_0e_t {
1448c2ecf20Sopenharmony_ci	ALS4K_IOB_0E_MPU_IRQ = 0x10,
1458c2ecf20Sopenharmony_ci	ALS4K_IOB_0E_CR1E_IRQ = 0x40,
1468c2ecf20Sopenharmony_ci	ALS4K_IOB_0E_SB_DMA_IRQ = 0x80,
1478c2ecf20Sopenharmony_ci};
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cienum als4k_gcr_t { /* all registers 32bit wide; SPECS_PAGE: 38 to 42 */
1508c2ecf20Sopenharmony_ci	ALS4K_GCR8C_MISC_CTRL = 0x8c,
1518c2ecf20Sopenharmony_ci	ALS4K_GCR90_TEST_MODE_REG = 0x90,
1528c2ecf20Sopenharmony_ci	ALS4K_GCR91_DMA0_ADDR = 0x91,
1538c2ecf20Sopenharmony_ci	ALS4K_GCR92_DMA0_MODE_COUNT = 0x92,
1548c2ecf20Sopenharmony_ci	ALS4K_GCR93_DMA1_ADDR = 0x93,
1558c2ecf20Sopenharmony_ci	ALS4K_GCR94_DMA1_MODE_COUNT = 0x94,
1568c2ecf20Sopenharmony_ci	ALS4K_GCR95_DMA3_ADDR = 0x95,
1578c2ecf20Sopenharmony_ci	ALS4K_GCR96_DMA3_MODE_COUNT = 0x96,
1588c2ecf20Sopenharmony_ci	ALS4K_GCR99_DMA_EMULATION_CTRL = 0x99,
1598c2ecf20Sopenharmony_ci	ALS4K_GCRA0_FIFO1_CURRENT_ADDR = 0xa0,
1608c2ecf20Sopenharmony_ci	ALS4K_GCRA1_FIFO1_STATUS_BYTECOUNT = 0xa1,
1618c2ecf20Sopenharmony_ci	ALS4K_GCRA2_FIFO2_PCIADDR = 0xa2,
1628c2ecf20Sopenharmony_ci	ALS4K_GCRA3_FIFO2_COUNT = 0xa3,
1638c2ecf20Sopenharmony_ci	ALS4K_GCRA4_FIFO2_CURRENT_ADDR = 0xa4,
1648c2ecf20Sopenharmony_ci	ALS4K_GCRA5_FIFO1_STATUS_BYTECOUNT = 0xa5,
1658c2ecf20Sopenharmony_ci	ALS4K_GCRA6_PM_CTRL = 0xa6,
1668c2ecf20Sopenharmony_ci	ALS4K_GCRA7_PCI_ACCESS_STORAGE = 0xa7,
1678c2ecf20Sopenharmony_ci	ALS4K_GCRA8_LEGACY_CFG1 = 0xa8,
1688c2ecf20Sopenharmony_ci	ALS4K_GCRA9_LEGACY_CFG2 = 0xa9,
1698c2ecf20Sopenharmony_ci	ALS4K_GCRFF_DUMMY_SCRATCH = 0xff,
1708c2ecf20Sopenharmony_ci};
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cienum als4k_gcr8c_t {
1738c2ecf20Sopenharmony_ci	ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE = 0x8000,
1748c2ecf20Sopenharmony_ci	ALS4K_GCR8C_CHIP_REV_MASK = 0xf0000
1758c2ecf20Sopenharmony_ci};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic inline void snd_als4k_iobase_writeb(unsigned long iobase,
1788c2ecf20Sopenharmony_ci						enum als4k_iobase_t reg,
1798c2ecf20Sopenharmony_ci						u8 val)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	outb(val, iobase + reg);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic inline void snd_als4k_iobase_writel(unsigned long iobase,
1858c2ecf20Sopenharmony_ci						enum als4k_iobase_t reg,
1868c2ecf20Sopenharmony_ci						u32 val)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	outl(val, iobase + reg);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic inline u8 snd_als4k_iobase_readb(unsigned long iobase,
1928c2ecf20Sopenharmony_ci						enum als4k_iobase_t reg)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	return inb(iobase + reg);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic inline u32 snd_als4k_iobase_readl(unsigned long iobase,
1988c2ecf20Sopenharmony_ci						enum als4k_iobase_t reg)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return inl(iobase + reg);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic inline void snd_als4k_gcr_write_addr(unsigned long iobase,
2048c2ecf20Sopenharmony_ci						 enum als4k_gcr_t reg,
2058c2ecf20Sopenharmony_ci						 u32 val)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg);
2088c2ecf20Sopenharmony_ci	snd_als4k_iobase_writel(iobase, ALS4K_IOD_08_GCR_DATA, val);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic inline void snd_als4k_gcr_write(struct snd_sb *sb,
2128c2ecf20Sopenharmony_ci					 enum als4k_gcr_t reg,
2138c2ecf20Sopenharmony_ci					 u32 val)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	snd_als4k_gcr_write_addr(sb->alt_port, reg, val);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic inline u32 snd_als4k_gcr_read_addr(unsigned long iobase,
2198c2ecf20Sopenharmony_ci						 enum als4k_gcr_t reg)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	/* SPECS_PAGE: 37/38 */
2228c2ecf20Sopenharmony_ci	snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg);
2238c2ecf20Sopenharmony_ci	return snd_als4k_iobase_readl(iobase, ALS4K_IOD_08_GCR_DATA);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic inline u32 snd_als4k_gcr_read(struct snd_sb *sb, enum als4k_gcr_t reg)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	return snd_als4k_gcr_read_addr(sb->alt_port, reg);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cienum als4k_cr_t { /* all registers 8bit wide; SPECS_PAGE: 20 to 23 */
2328c2ecf20Sopenharmony_ci	ALS4K_CR0_SB_CONFIG = 0x00,
2338c2ecf20Sopenharmony_ci	ALS4K_CR2_MISC_CONTROL = 0x02,
2348c2ecf20Sopenharmony_ci	ALS4K_CR3_CONFIGURATION = 0x03,
2358c2ecf20Sopenharmony_ci	ALS4K_CR17_FIFO_STATUS = 0x17,
2368c2ecf20Sopenharmony_ci	ALS4K_CR18_ESP_MAJOR_VERSION = 0x18,
2378c2ecf20Sopenharmony_ci	ALS4K_CR19_ESP_MINOR_VERSION = 0x19,
2388c2ecf20Sopenharmony_ci	ALS4K_CR1A_MPU401_UART_MODE_CONTROL = 0x1a,
2398c2ecf20Sopenharmony_ci	ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO = 0x1c,
2408c2ecf20Sopenharmony_ci	ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI = 0x1d,
2418c2ecf20Sopenharmony_ci	ALS4K_CR1E_FIFO2_CONTROL = 0x1e, /* secondary PCM FIFO (recording) */
2428c2ecf20Sopenharmony_ci	ALS4K_CR3A_MISC_CONTROL = 0x3a,
2438c2ecf20Sopenharmony_ci	ALS4K_CR3B_CRC32_BYTE0 = 0x3b, /* for testing, activate via CR3A */
2448c2ecf20Sopenharmony_ci	ALS4K_CR3C_CRC32_BYTE1 = 0x3c,
2458c2ecf20Sopenharmony_ci	ALS4K_CR3D_CRC32_BYTE2 = 0x3d,
2468c2ecf20Sopenharmony_ci	ALS4K_CR3E_CRC32_BYTE3 = 0x3e,
2478c2ecf20Sopenharmony_ci};
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cienum als4k_cr0_t {
2508c2ecf20Sopenharmony_ci	ALS4K_CR0_DMA_CONTIN_MODE_CTRL = 0x02, /* IRQ/FIFO controlled for 0/1 */
2518c2ecf20Sopenharmony_ci	ALS4K_CR0_DMA_90H_MODE_CTRL = 0x04, /* IRQ/FIFO controlled for 0/1 */
2528c2ecf20Sopenharmony_ci	ALS4K_CR0_MX80_81_REG_WRITE_ENABLE = 0x80,
2538c2ecf20Sopenharmony_ci};
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic inline void snd_als4_cr_write(struct snd_sb *chip,
2568c2ecf20Sopenharmony_ci					enum als4k_cr_t reg,
2578c2ecf20Sopenharmony_ci					u8 data)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	/* Control Register is reg | 0xc0 (bit 7, 6 set) on sbmixer_index
2608c2ecf20Sopenharmony_ci	 * NOTE: assumes chip->mixer_lock to be locked externally already!
2618c2ecf20Sopenharmony_ci	 * SPECS_PAGE: 6 */
2628c2ecf20Sopenharmony_ci	snd_sbmixer_write(chip, reg | 0xc0, data);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic inline u8 snd_als4_cr_read(struct snd_sb *chip,
2668c2ecf20Sopenharmony_ci					enum als4k_cr_t reg)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	/* NOTE: assumes chip->mixer_lock to be locked externally already! */
2698c2ecf20Sopenharmony_ci	return snd_sbmixer_read(chip, reg | 0xc0);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void snd_als4000_set_rate(struct snd_sb *chip, unsigned int rate)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	if (!(chip->mode & SB_RATE_LOCK)) {
2778c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE_OUT);
2788c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, rate>>8);
2798c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, rate);
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic inline void snd_als4000_set_capture_dma(struct snd_sb *chip,
2848c2ecf20Sopenharmony_ci					       dma_addr_t addr, unsigned size)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	/* SPECS_PAGE: 40 */
2878c2ecf20Sopenharmony_ci	snd_als4k_gcr_write(chip, ALS4K_GCRA2_FIFO2_PCIADDR, addr);
2888c2ecf20Sopenharmony_ci	snd_als4k_gcr_write(chip, ALS4K_GCRA3_FIFO2_COUNT, (size-1));
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic inline void snd_als4000_set_playback_dma(struct snd_sb *chip,
2928c2ecf20Sopenharmony_ci						dma_addr_t addr,
2938c2ecf20Sopenharmony_ci						unsigned size)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	/* SPECS_PAGE: 38 */
2968c2ecf20Sopenharmony_ci	snd_als4k_gcr_write(chip, ALS4K_GCR91_DMA0_ADDR, addr);
2978c2ecf20Sopenharmony_ci	snd_als4k_gcr_write(chip, ALS4K_GCR92_DMA0_MODE_COUNT,
2988c2ecf20Sopenharmony_ci							(size-1)|0x180000);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci#define ALS4000_FORMAT_SIGNED	(1<<0)
3028c2ecf20Sopenharmony_ci#define ALS4000_FORMAT_16BIT	(1<<1)
3038c2ecf20Sopenharmony_ci#define ALS4000_FORMAT_STEREO	(1<<2)
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int snd_als4000_get_format(struct snd_pcm_runtime *runtime)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	int result;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	result = 0;
3108c2ecf20Sopenharmony_ci	if (snd_pcm_format_signed(runtime->format))
3118c2ecf20Sopenharmony_ci		result |= ALS4000_FORMAT_SIGNED;
3128c2ecf20Sopenharmony_ci	if (snd_pcm_format_physical_width(runtime->format) == 16)
3138c2ecf20Sopenharmony_ci		result |= ALS4000_FORMAT_16BIT;
3148c2ecf20Sopenharmony_ci	if (runtime->channels > 1)
3158c2ecf20Sopenharmony_ci		result |= ALS4000_FORMAT_STEREO;
3168c2ecf20Sopenharmony_ci	return result;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci/* structure for setting up playback */
3208c2ecf20Sopenharmony_cistatic const struct {
3218c2ecf20Sopenharmony_ci	unsigned char dsp_cmd, dma_on, dma_off, format;
3228c2ecf20Sopenharmony_ci} playback_cmd_vals[]={
3238c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_U8_MONO */
3248c2ecf20Sopenharmony_ci{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_MONO },
3258c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_S8_MONO */
3268c2ecf20Sopenharmony_ci{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_MONO },
3278c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_U16L_MONO */
3288c2ecf20Sopenharmony_ci{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_MONO },
3298c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_S16L_MONO */
3308c2ecf20Sopenharmony_ci{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_MONO },
3318c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_U8_STEREO */
3328c2ecf20Sopenharmony_ci{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_UNS_STEREO },
3338c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_S8_STEREO */
3348c2ecf20Sopenharmony_ci{ SB_DSP4_OUT8_AI, SB_DSP_DMA8_ON, SB_DSP_DMA8_OFF, SB_DSP4_MODE_SIGN_STEREO },
3358c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_U16L_STEREO */
3368c2ecf20Sopenharmony_ci{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_UNS_STEREO },
3378c2ecf20Sopenharmony_ci/* ALS4000_FORMAT_S16L_STEREO */
3388c2ecf20Sopenharmony_ci{ SB_DSP4_OUT16_AI, SB_DSP_DMA16_ON, SB_DSP_DMA16_OFF, SB_DSP4_MODE_SIGN_STEREO },
3398c2ecf20Sopenharmony_ci};
3408c2ecf20Sopenharmony_ci#define playback_cmd(chip) (playback_cmd_vals[(chip)->playback_format])
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/* structure for setting up capture */
3438c2ecf20Sopenharmony_cienum { CMD_WIDTH8=0x04, CMD_SIGNED=0x10, CMD_MONO=0x80, CMD_STEREO=0xA0 };
3448c2ecf20Sopenharmony_cistatic const unsigned char capture_cmd_vals[]=
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ciCMD_WIDTH8|CMD_MONO,			/* ALS4000_FORMAT_U8_MONO */
3478c2ecf20Sopenharmony_ciCMD_WIDTH8|CMD_SIGNED|CMD_MONO,		/* ALS4000_FORMAT_S8_MONO */
3488c2ecf20Sopenharmony_ciCMD_MONO,				/* ALS4000_FORMAT_U16L_MONO */
3498c2ecf20Sopenharmony_ciCMD_SIGNED|CMD_MONO,			/* ALS4000_FORMAT_S16L_MONO */
3508c2ecf20Sopenharmony_ciCMD_WIDTH8|CMD_STEREO,			/* ALS4000_FORMAT_U8_STEREO */
3518c2ecf20Sopenharmony_ciCMD_WIDTH8|CMD_SIGNED|CMD_STEREO,	/* ALS4000_FORMAT_S8_STEREO */
3528c2ecf20Sopenharmony_ciCMD_STEREO,				/* ALS4000_FORMAT_U16L_STEREO */
3538c2ecf20Sopenharmony_ciCMD_SIGNED|CMD_STEREO,			/* ALS4000_FORMAT_S16L_STEREO */
3548c2ecf20Sopenharmony_ci};
3558c2ecf20Sopenharmony_ci#define capture_cmd(chip) (capture_cmd_vals[(chip)->capture_format])
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
3608c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3618c2ecf20Sopenharmony_ci	unsigned long size;
3628c2ecf20Sopenharmony_ci	unsigned count;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	chip->capture_format = snd_als4000_get_format(runtime);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	size = snd_pcm_lib_buffer_bytes(substream);
3678c2ecf20Sopenharmony_ci	count = snd_pcm_lib_period_bytes(substream);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (chip->capture_format & ALS4000_FORMAT_16BIT)
3708c2ecf20Sopenharmony_ci		count >>= 1;
3718c2ecf20Sopenharmony_ci	count--;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
3748c2ecf20Sopenharmony_ci	snd_als4000_set_rate(chip, runtime->rate);
3758c2ecf20Sopenharmony_ci	snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);
3768c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
3778c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->mixer_lock);
3788c2ecf20Sopenharmony_ci	snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff);
3798c2ecf20Sopenharmony_ci	snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);
3808c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->mixer_lock);
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
3878c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
3888c2ecf20Sopenharmony_ci	unsigned long size;
3898c2ecf20Sopenharmony_ci	unsigned count;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	chip->playback_format = snd_als4000_get_format(runtime);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	size = snd_pcm_lib_buffer_bytes(substream);
3948c2ecf20Sopenharmony_ci	count = snd_pcm_lib_period_bytes(substream);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (chip->playback_format & ALS4000_FORMAT_16BIT)
3978c2ecf20Sopenharmony_ci		count >>= 1;
3988c2ecf20Sopenharmony_ci	count--;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* FIXME: from second playback on, there's a lot more clicks and pops
4018c2ecf20Sopenharmony_ci	 * involved here than on first playback. Fiddling with
4028c2ecf20Sopenharmony_ci	 * tons of different settings didn't help (DMA, speaker on/off,
4038c2ecf20Sopenharmony_ci	 * reordering, ...). Something seems to get enabled on playback
4048c2ecf20Sopenharmony_ci	 * that I haven't found out how to disable again, which then causes
4058c2ecf20Sopenharmony_ci	 * the switching pops to reach the speakers the next time here. */
4068c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
4078c2ecf20Sopenharmony_ci	snd_als4000_set_rate(chip, runtime->rate);
4088c2ecf20Sopenharmony_ci	snd_als4000_set_playback_dma(chip, runtime->dma_addr, size);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	/* SPEAKER_ON not needed, since dma_on seems to also enable speaker */
4118c2ecf20Sopenharmony_ci	/* snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); */
4128c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd);
4138c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, playback_cmd(chip).format);
4148c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, count & 0xff);
4158c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, count >> 8);
4168c2ecf20Sopenharmony_ci	snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
4178c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return 0;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int cmd)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
4258c2ecf20Sopenharmony_ci	int result = 0;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* FIXME race condition in here!!!
4288c2ecf20Sopenharmony_ci	   chip->mode non-atomic update gets consistently protected
4298c2ecf20Sopenharmony_ci	   by reg_lock always, _except_ for this place!!
4308c2ecf20Sopenharmony_ci	   Probably need to take reg_lock as outer (or inner??) lock, too.
4318c2ecf20Sopenharmony_ci	   (or serialize both lock operations? probably not, though... - racy?)
4328c2ecf20Sopenharmony_ci	*/
4338c2ecf20Sopenharmony_ci	spin_lock(&chip->mixer_lock);
4348c2ecf20Sopenharmony_ci	switch (cmd) {
4358c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4368c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
4378c2ecf20Sopenharmony_ci		chip->mode |= SB_RATE_LOCK_CAPTURE;
4388c2ecf20Sopenharmony_ci		snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL,
4398c2ecf20Sopenharmony_ci							 capture_cmd(chip));
4408c2ecf20Sopenharmony_ci		break;
4418c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4428c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
4438c2ecf20Sopenharmony_ci		chip->mode &= ~SB_RATE_LOCK_CAPTURE;
4448c2ecf20Sopenharmony_ci		snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL,
4458c2ecf20Sopenharmony_ci							 capture_cmd(chip));
4468c2ecf20Sopenharmony_ci		break;
4478c2ecf20Sopenharmony_ci	default:
4488c2ecf20Sopenharmony_ci		result = -EINVAL;
4498c2ecf20Sopenharmony_ci		break;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	spin_unlock(&chip->mixer_lock);
4528c2ecf20Sopenharmony_ci	return result;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int snd_als4000_playback_trigger(struct snd_pcm_substream *substream, int cmd)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
4588c2ecf20Sopenharmony_ci	int result = 0;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
4618c2ecf20Sopenharmony_ci	switch (cmd) {
4628c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4638c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
4648c2ecf20Sopenharmony_ci		chip->mode |= SB_RATE_LOCK_PLAYBACK;
4658c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, playback_cmd(chip).dma_on);
4668c2ecf20Sopenharmony_ci		break;
4678c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4688c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
4698c2ecf20Sopenharmony_ci		snd_sbdsp_command(chip, playback_cmd(chip).dma_off);
4708c2ecf20Sopenharmony_ci		chip->mode &= ~SB_RATE_LOCK_PLAYBACK;
4718c2ecf20Sopenharmony_ci		break;
4728c2ecf20Sopenharmony_ci	default:
4738c2ecf20Sopenharmony_ci		result = -EINVAL;
4748c2ecf20Sopenharmony_ci		break;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
4778c2ecf20Sopenharmony_ci	return result;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_als4000_capture_pointer(struct snd_pcm_substream *substream)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
4838c2ecf20Sopenharmony_ci	unsigned int result;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
4868c2ecf20Sopenharmony_ci	result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);
4878c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
4888c2ecf20Sopenharmony_ci	result &= 0xffff;
4898c2ecf20Sopenharmony_ci	return bytes_to_frames( substream->runtime, result );
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_als4000_playback_pointer(struct snd_pcm_substream *substream)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
4958c2ecf20Sopenharmony_ci	unsigned result;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
4988c2ecf20Sopenharmony_ci	result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);
4998c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
5008c2ecf20Sopenharmony_ci	result &= 0xffff;
5018c2ecf20Sopenharmony_ci	return bytes_to_frames( substream->runtime, result );
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/* FIXME: this IRQ routine doesn't really support IRQ sharing (we always
5058c2ecf20Sopenharmony_ci * return IRQ_HANDLED no matter whether we actually had an IRQ flag or not).
5068c2ecf20Sopenharmony_ci * ALS4000a.PDF writes that while ACKing IRQ in PCI block will *not* ACK
5078c2ecf20Sopenharmony_ci * the IRQ in the SB core, ACKing IRQ in SB block *will* ACK the PCI IRQ
5088c2ecf20Sopenharmony_ci * register (alt_port + ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU). Probably something
5098c2ecf20Sopenharmony_ci * could be optimized here to query/write one register only...
5108c2ecf20Sopenharmony_ci * And even if both registers need to be queried, then there's still the
5118c2ecf20Sopenharmony_ci * question of whether it's actually correct to ACK PCI IRQ before reading
5128c2ecf20Sopenharmony_ci * SB IRQ like we do now, since ALS4000a.PDF mentions that PCI IRQ will *clear*
5138c2ecf20Sopenharmony_ci * SB IRQ status.
5148c2ecf20Sopenharmony_ci * (hmm, SPECS_PAGE: 38 mentions it the other way around!)
5158c2ecf20Sopenharmony_ci * And do we *really* need the lock here for *reading* SB_DSP4_IRQSTATUS??
5168c2ecf20Sopenharmony_ci * */
5178c2ecf20Sopenharmony_cistatic irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	struct snd_sb *chip = dev_id;
5208c2ecf20Sopenharmony_ci	unsigned pci_irqstatus;
5218c2ecf20Sopenharmony_ci	unsigned sb_irqstatus;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	/* find out which bit of the ALS4000 PCI block produced the interrupt,
5248c2ecf20Sopenharmony_ci	   SPECS_PAGE: 38, 5 */
5258c2ecf20Sopenharmony_ci	pci_irqstatus = snd_als4k_iobase_readb(chip->alt_port,
5268c2ecf20Sopenharmony_ci				 ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU);
5278c2ecf20Sopenharmony_ci	if ((pci_irqstatus & ALS4K_IOB_0E_SB_DMA_IRQ)
5288c2ecf20Sopenharmony_ci	 && (chip->playback_substream)) /* playback */
5298c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(chip->playback_substream);
5308c2ecf20Sopenharmony_ci	if ((pci_irqstatus & ALS4K_IOB_0E_CR1E_IRQ)
5318c2ecf20Sopenharmony_ci	 && (chip->capture_substream)) /* capturing */
5328c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(chip->capture_substream);
5338c2ecf20Sopenharmony_ci	if ((pci_irqstatus & ALS4K_IOB_0E_MPU_IRQ)
5348c2ecf20Sopenharmony_ci	 && (chip->rmidi)) /* MPU401 interrupt */
5358c2ecf20Sopenharmony_ci		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
5368c2ecf20Sopenharmony_ci	/* ACK the PCI block IRQ */
5378c2ecf20Sopenharmony_ci	snd_als4k_iobase_writeb(chip->alt_port,
5388c2ecf20Sopenharmony_ci			 ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU, pci_irqstatus);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	spin_lock(&chip->mixer_lock);
5418c2ecf20Sopenharmony_ci	/* SPECS_PAGE: 20 */
5428c2ecf20Sopenharmony_ci	sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);
5438c2ecf20Sopenharmony_ci	spin_unlock(&chip->mixer_lock);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (sb_irqstatus & SB_IRQTYPE_8BIT)
5468c2ecf20Sopenharmony_ci		snd_sb_ack_8bit(chip);
5478c2ecf20Sopenharmony_ci	if (sb_irqstatus & SB_IRQTYPE_16BIT)
5488c2ecf20Sopenharmony_ci		snd_sb_ack_16bit(chip);
5498c2ecf20Sopenharmony_ci	if (sb_irqstatus & SB_IRQTYPE_MPUIN)
5508c2ecf20Sopenharmony_ci		inb(chip->mpu_port);
5518c2ecf20Sopenharmony_ci	if (sb_irqstatus & ALS4K_IRQTYPE_CR1E_DMA)
5528c2ecf20Sopenharmony_ci		snd_als4k_iobase_readb(chip->alt_port,
5538c2ecf20Sopenharmony_ci					ALS4K_IOB_16_ACK_FOR_CR1E);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	/* dev_dbg(chip->card->dev, "als4000: irq 0x%04x 0x%04x\n",
5568c2ecf20Sopenharmony_ci					 pci_irqstatus, sb_irqstatus); */
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/* only ack the things we actually handled above */
5598c2ecf20Sopenharmony_ci	return IRQ_RETVAL(
5608c2ecf20Sopenharmony_ci	     (pci_irqstatus & (ALS4K_IOB_0E_SB_DMA_IRQ|ALS4K_IOB_0E_CR1E_IRQ|
5618c2ecf20Sopenharmony_ci				ALS4K_IOB_0E_MPU_IRQ))
5628c2ecf20Sopenharmony_ci	  || (sb_irqstatus & (SB_IRQTYPE_8BIT|SB_IRQTYPE_16BIT|
5638c2ecf20Sopenharmony_ci				SB_IRQTYPE_MPUIN|ALS4K_IRQTYPE_CR1E_DMA))
5648c2ecf20Sopenharmony_ci	);
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci/*****************************************************************/
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_als4000_playback =
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
5728c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
5738c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
5748c2ecf20Sopenharmony_ci				SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
5758c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
5768c2ecf20Sopenharmony_ci	.rate_min =		4000,
5778c2ecf20Sopenharmony_ci	.rate_max =		48000,
5788c2ecf20Sopenharmony_ci	.channels_min =		1,
5798c2ecf20Sopenharmony_ci	.channels_max =		2,
5808c2ecf20Sopenharmony_ci	.buffer_bytes_max =	65536,
5818c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
5828c2ecf20Sopenharmony_ci	.period_bytes_max =	65536,
5838c2ecf20Sopenharmony_ci	.periods_min =		1,
5848c2ecf20Sopenharmony_ci	.periods_max =		1024,
5858c2ecf20Sopenharmony_ci	.fifo_size =		0
5868c2ecf20Sopenharmony_ci};
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_als4000_capture =
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
5918c2ecf20Sopenharmony_ci				 SNDRV_PCM_INFO_MMAP_VALID),
5928c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
5938c2ecf20Sopenharmony_ci				SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
5948c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
5958c2ecf20Sopenharmony_ci	.rate_min =		4000,
5968c2ecf20Sopenharmony_ci	.rate_max =		48000,
5978c2ecf20Sopenharmony_ci	.channels_min =		1,
5988c2ecf20Sopenharmony_ci	.channels_max =		2,
5998c2ecf20Sopenharmony_ci	.buffer_bytes_max =	65536,
6008c2ecf20Sopenharmony_ci	.period_bytes_min =	64,
6018c2ecf20Sopenharmony_ci	.period_bytes_max =	65536,
6028c2ecf20Sopenharmony_ci	.periods_min =		1,
6038c2ecf20Sopenharmony_ci	.periods_max =		1024,
6048c2ecf20Sopenharmony_ci	.fifo_size =		0
6058c2ecf20Sopenharmony_ci};
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci/*****************************************************************/
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int snd_als4000_playback_open(struct snd_pcm_substream *substream)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
6128c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	chip->playback_substream = substream;
6158c2ecf20Sopenharmony_ci	runtime->hw = snd_als4000_playback;
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int snd_als4000_playback_close(struct snd_pcm_substream *substream)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	chip->playback_substream = NULL;
6248c2ecf20Sopenharmony_ci	return 0;
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic int snd_als4000_capture_open(struct snd_pcm_substream *substream)
6288c2ecf20Sopenharmony_ci{
6298c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
6308c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	chip->capture_substream = substream;
6338c2ecf20Sopenharmony_ci	runtime->hw = snd_als4000_capture;
6348c2ecf20Sopenharmony_ci	return 0;
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic int snd_als4000_capture_close(struct snd_pcm_substream *substream)
6388c2ecf20Sopenharmony_ci{
6398c2ecf20Sopenharmony_ci	struct snd_sb *chip = snd_pcm_substream_chip(substream);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	chip->capture_substream = NULL;
6428c2ecf20Sopenharmony_ci	return 0;
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci/******************************************************************/
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_als4000_playback_ops = {
6488c2ecf20Sopenharmony_ci	.open =		snd_als4000_playback_open,
6498c2ecf20Sopenharmony_ci	.close =	snd_als4000_playback_close,
6508c2ecf20Sopenharmony_ci	.prepare =	snd_als4000_playback_prepare,
6518c2ecf20Sopenharmony_ci	.trigger =	snd_als4000_playback_trigger,
6528c2ecf20Sopenharmony_ci	.pointer =	snd_als4000_playback_pointer
6538c2ecf20Sopenharmony_ci};
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_als4000_capture_ops = {
6568c2ecf20Sopenharmony_ci	.open =		snd_als4000_capture_open,
6578c2ecf20Sopenharmony_ci	.close =	snd_als4000_capture_close,
6588c2ecf20Sopenharmony_ci	.prepare =	snd_als4000_capture_prepare,
6598c2ecf20Sopenharmony_ci	.trigger =	snd_als4000_capture_trigger,
6608c2ecf20Sopenharmony_ci	.pointer =	snd_als4000_capture_pointer
6618c2ecf20Sopenharmony_ci};
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic int snd_als4000_pcm(struct snd_sb *chip, int device)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
6668c2ecf20Sopenharmony_ci	int err;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm);
6698c2ecf20Sopenharmony_ci	if (err < 0)
6708c2ecf20Sopenharmony_ci		return err;
6718c2ecf20Sopenharmony_ci	pcm->private_data = chip;
6728c2ecf20Sopenharmony_ci	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
6738c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
6748c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
6778c2ecf20Sopenharmony_ci				       &chip->pci->dev, 64*1024, 64*1024);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	chip->pcm = pcm;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	return 0;
6828c2ecf20Sopenharmony_ci}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci/******************************************************************/
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic void snd_als4000_set_addr(unsigned long iobase,
6878c2ecf20Sopenharmony_ci					unsigned int sb_io,
6888c2ecf20Sopenharmony_ci					unsigned int mpu_io,
6898c2ecf20Sopenharmony_ci					unsigned int opl_io,
6908c2ecf20Sopenharmony_ci					unsigned int game_io)
6918c2ecf20Sopenharmony_ci{
6928c2ecf20Sopenharmony_ci	u32 cfg1 = 0;
6938c2ecf20Sopenharmony_ci	u32 cfg2 = 0;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	if (mpu_io > 0)
6968c2ecf20Sopenharmony_ci		cfg2 |= (mpu_io | 1) << 16;
6978c2ecf20Sopenharmony_ci	if (sb_io > 0)
6988c2ecf20Sopenharmony_ci		cfg2 |= (sb_io | 1);
6998c2ecf20Sopenharmony_ci	if (game_io > 0)
7008c2ecf20Sopenharmony_ci		cfg1 |= (game_io | 1) << 16;
7018c2ecf20Sopenharmony_ci	if (opl_io > 0)
7028c2ecf20Sopenharmony_ci		cfg1 |= (opl_io | 1);
7038c2ecf20Sopenharmony_ci	snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA8_LEGACY_CFG1, cfg1);
7048c2ecf20Sopenharmony_ci	snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA9_LEGACY_CFG2, cfg2);
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic void snd_als4000_configure(struct snd_sb *chip)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	u8 tmp;
7108c2ecf20Sopenharmony_ci	int i;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* do some more configuration */
7138c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->mixer_lock);
7148c2ecf20Sopenharmony_ci	tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG);
7158c2ecf20Sopenharmony_ci	snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
7168c2ecf20Sopenharmony_ci				tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
7178c2ecf20Sopenharmony_ci	/* always select DMA channel 0, since we do not actually use DMA
7188c2ecf20Sopenharmony_ci	 * SPECS_PAGE: 19/20 */
7198c2ecf20Sopenharmony_ci	snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0);
7208c2ecf20Sopenharmony_ci	snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG,
7218c2ecf20Sopenharmony_ci				 tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);
7228c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->mixer_lock);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
7258c2ecf20Sopenharmony_ci	/* enable interrupts */
7268c2ecf20Sopenharmony_ci	snd_als4k_gcr_write(chip, ALS4K_GCR8C_MISC_CTRL,
7278c2ecf20Sopenharmony_ci					ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	/* SPECS_PAGE: 39 */
7308c2ecf20Sopenharmony_ci	for (i = ALS4K_GCR91_DMA0_ADDR; i <= ALS4K_GCR96_DMA3_MODE_COUNT; ++i)
7318c2ecf20Sopenharmony_ci		snd_als4k_gcr_write(chip, i, 0);
7328c2ecf20Sopenharmony_ci	/* enable burst mode to prevent dropouts during high PCI bus usage */
7338c2ecf20Sopenharmony_ci	snd_als4k_gcr_write(chip, ALS4K_GCR99_DMA_EMULATION_CTRL,
7348c2ecf20Sopenharmony_ci		(snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL) & ~0x07) | 0x04);
7358c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
7398c2ecf20Sopenharmony_cistatic int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct gameport *gp;
7428c2ecf20Sopenharmony_ci	struct resource *r;
7438c2ecf20Sopenharmony_ci	int io_port;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	if (joystick_port[dev] == 0)
7468c2ecf20Sopenharmony_ci		return -ENODEV;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (joystick_port[dev] == 1) { /* auto-detect */
7498c2ecf20Sopenharmony_ci		for (io_port = 0x200; io_port <= 0x218; io_port += 8) {
7508c2ecf20Sopenharmony_ci			r = request_region(io_port, 8, "ALS4000 gameport");
7518c2ecf20Sopenharmony_ci			if (r)
7528c2ecf20Sopenharmony_ci				break;
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci	} else {
7558c2ecf20Sopenharmony_ci		io_port = joystick_port[dev];
7568c2ecf20Sopenharmony_ci		r = request_region(io_port, 8, "ALS4000 gameport");
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	if (!r) {
7608c2ecf20Sopenharmony_ci		dev_warn(&acard->pci->dev, "cannot reserve joystick ports\n");
7618c2ecf20Sopenharmony_ci		return -EBUSY;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	acard->gameport = gp = gameport_allocate_port();
7658c2ecf20Sopenharmony_ci	if (!gp) {
7668c2ecf20Sopenharmony_ci		dev_err(&acard->pci->dev, "cannot allocate memory for gameport\n");
7678c2ecf20Sopenharmony_ci		release_and_free_resource(r);
7688c2ecf20Sopenharmony_ci		return -ENOMEM;
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	gameport_set_name(gp, "ALS4000 Gameport");
7728c2ecf20Sopenharmony_ci	gameport_set_phys(gp, "pci%s/gameport0", pci_name(acard->pci));
7738c2ecf20Sopenharmony_ci	gameport_set_dev_parent(gp, &acard->pci->dev);
7748c2ecf20Sopenharmony_ci	gp->io = io_port;
7758c2ecf20Sopenharmony_ci	gameport_set_port_data(gp, r);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	/* Enable legacy joystick port */
7788c2ecf20Sopenharmony_ci	snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	gameport_register_port(acard->gameport);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	return 0;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic void snd_als4000_free_gameport(struct snd_card_als4000 *acard)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	if (acard->gameport) {
7888c2ecf20Sopenharmony_ci		struct resource *r = gameport_get_port_data(acard->gameport);
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci		gameport_unregister_port(acard->gameport);
7918c2ecf20Sopenharmony_ci		acard->gameport = NULL;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		/* disable joystick */
7948c2ecf20Sopenharmony_ci		snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci		release_and_free_resource(r);
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci#else
8008c2ecf20Sopenharmony_cistatic inline int snd_als4000_create_gameport(struct snd_card_als4000 *acard, int dev) { return -ENOSYS; }
8018c2ecf20Sopenharmony_cistatic inline void snd_als4000_free_gameport(struct snd_card_als4000 *acard) { }
8028c2ecf20Sopenharmony_ci#endif
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic void snd_card_als4000_free( struct snd_card *card )
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	struct snd_card_als4000 *acard = card->private_data;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* make sure that interrupts are disabled */
8098c2ecf20Sopenharmony_ci	snd_als4k_gcr_write_addr(acard->iobase, ALS4K_GCR8C_MISC_CTRL, 0);
8108c2ecf20Sopenharmony_ci	/* free resources */
8118c2ecf20Sopenharmony_ci	snd_als4000_free_gameport(acard);
8128c2ecf20Sopenharmony_ci	pci_release_regions(acard->pci);
8138c2ecf20Sopenharmony_ci	pci_disable_device(acard->pci);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_cistatic int snd_card_als4000_probe(struct pci_dev *pci,
8178c2ecf20Sopenharmony_ci				  const struct pci_device_id *pci_id)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	static int dev;
8208c2ecf20Sopenharmony_ci	struct snd_card *card;
8218c2ecf20Sopenharmony_ci	struct snd_card_als4000 *acard;
8228c2ecf20Sopenharmony_ci	unsigned long iobase;
8238c2ecf20Sopenharmony_ci	struct snd_sb *chip;
8248c2ecf20Sopenharmony_ci	struct snd_opl3 *opl3;
8258c2ecf20Sopenharmony_ci	unsigned short word;
8268c2ecf20Sopenharmony_ci	int err;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
8298c2ecf20Sopenharmony_ci		return -ENODEV;
8308c2ecf20Sopenharmony_ci	if (!enable[dev]) {
8318c2ecf20Sopenharmony_ci		dev++;
8328c2ecf20Sopenharmony_ci		return -ENOENT;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	/* enable PCI device */
8368c2ecf20Sopenharmony_ci	if ((err = pci_enable_device(pci)) < 0) {
8378c2ecf20Sopenharmony_ci		return err;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci	/* check, if we can restrict PCI DMA transfers to 24 bits */
8408c2ecf20Sopenharmony_ci	if (dma_set_mask(&pci->dev, DMA_BIT_MASK(24)) < 0 ||
8418c2ecf20Sopenharmony_ci	    dma_set_coherent_mask(&pci->dev, DMA_BIT_MASK(24)) < 0) {
8428c2ecf20Sopenharmony_ci		dev_err(&pci->dev, "architecture does not support 24bit PCI busmaster DMA\n");
8438c2ecf20Sopenharmony_ci		pci_disable_device(pci);
8448c2ecf20Sopenharmony_ci		return -ENXIO;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if ((err = pci_request_regions(pci, "ALS4000")) < 0) {
8488c2ecf20Sopenharmony_ci		pci_disable_device(pci);
8498c2ecf20Sopenharmony_ci		return err;
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci	iobase = pci_resource_start(pci, 0);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	pci_read_config_word(pci, PCI_COMMAND, &word);
8548c2ecf20Sopenharmony_ci	pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);
8558c2ecf20Sopenharmony_ci	pci_set_master(pci);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
8588c2ecf20Sopenharmony_ci			   sizeof(*acard) /* private_data: acard */,
8598c2ecf20Sopenharmony_ci			   &card);
8608c2ecf20Sopenharmony_ci	if (err < 0) {
8618c2ecf20Sopenharmony_ci		pci_release_regions(pci);
8628c2ecf20Sopenharmony_ci		pci_disable_device(pci);
8638c2ecf20Sopenharmony_ci		return err;
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	acard = card->private_data;
8678c2ecf20Sopenharmony_ci	acard->pci = pci;
8688c2ecf20Sopenharmony_ci	acard->iobase = iobase;
8698c2ecf20Sopenharmony_ci	card->private_free = snd_card_als4000_free;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* disable all legacy ISA stuff */
8728c2ecf20Sopenharmony_ci	snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if ((err = snd_sbdsp_create(card,
8758c2ecf20Sopenharmony_ci				    iobase + ALS4K_IOB_10_ADLIB_ADDR0,
8768c2ecf20Sopenharmony_ci				    pci->irq,
8778c2ecf20Sopenharmony_ci		/* internally registered as IRQF_SHARED in case of ALS4000 SB */
8788c2ecf20Sopenharmony_ci				    snd_als4000_interrupt,
8798c2ecf20Sopenharmony_ci				    -1,
8808c2ecf20Sopenharmony_ci				    -1,
8818c2ecf20Sopenharmony_ci				    SB_HW_ALS4000,
8828c2ecf20Sopenharmony_ci				    &chip)) < 0) {
8838c2ecf20Sopenharmony_ci		goto out_err;
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci	acard->chip = chip;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	chip->pci = pci;
8888c2ecf20Sopenharmony_ci	chip->alt_port = iobase;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	snd_als4000_configure(chip);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	strcpy(card->driver, "ALS4000");
8938c2ecf20Sopenharmony_ci	strcpy(card->shortname, "Avance Logic ALS4000");
8948c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx, irq %i",
8958c2ecf20Sopenharmony_ci		card->shortname, chip->alt_port, chip->irq);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
8988c2ecf20Sopenharmony_ci					iobase + ALS4K_IOB_30_MIDI_DATA,
8998c2ecf20Sopenharmony_ci					MPU401_INFO_INTEGRATED |
9008c2ecf20Sopenharmony_ci					MPU401_INFO_IRQ_HOOK,
9018c2ecf20Sopenharmony_ci					-1, &chip->rmidi)) < 0) {
9028c2ecf20Sopenharmony_ci		dev_err(&pci->dev, "no MPU-401 device at 0x%lx?\n",
9038c2ecf20Sopenharmony_ci				iobase + ALS4K_IOB_30_MIDI_DATA);
9048c2ecf20Sopenharmony_ci		goto out_err;
9058c2ecf20Sopenharmony_ci	}
9068c2ecf20Sopenharmony_ci	/* FIXME: ALS4000 has interesting MPU401 configuration features
9078c2ecf20Sopenharmony_ci	 * at ALS4K_CR1A_MPU401_UART_MODE_CONTROL
9088c2ecf20Sopenharmony_ci	 * (pass-thru / UART switching, fast MIDI clock, etc.),
9098c2ecf20Sopenharmony_ci	 * however there doesn't seem to be an ALSA API for this...
9108c2ecf20Sopenharmony_ci	 * SPECS_PAGE: 21 */
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	if ((err = snd_als4000_pcm(chip, 0)) < 0) {
9138c2ecf20Sopenharmony_ci		goto out_err;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci	if ((err = snd_sbmixer_new(chip)) < 0) {
9168c2ecf20Sopenharmony_ci		goto out_err;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	if (snd_opl3_create(card,
9208c2ecf20Sopenharmony_ci				iobase + ALS4K_IOB_10_ADLIB_ADDR0,
9218c2ecf20Sopenharmony_ci				iobase + ALS4K_IOB_12_ADLIB_ADDR2,
9228c2ecf20Sopenharmony_ci			    OPL3_HW_AUTO, 1, &opl3) < 0) {
9238c2ecf20Sopenharmony_ci		dev_err(&pci->dev, "no OPL device at 0x%lx-0x%lx?\n",
9248c2ecf20Sopenharmony_ci			   iobase + ALS4K_IOB_10_ADLIB_ADDR0,
9258c2ecf20Sopenharmony_ci			   iobase + ALS4K_IOB_12_ADLIB_ADDR2);
9268c2ecf20Sopenharmony_ci	} else {
9278c2ecf20Sopenharmony_ci		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
9288c2ecf20Sopenharmony_ci			goto out_err;
9298c2ecf20Sopenharmony_ci		}
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	snd_als4000_create_gameport(acard, dev);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	if ((err = snd_card_register(card)) < 0) {
9358c2ecf20Sopenharmony_ci		goto out_err;
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, card);
9388c2ecf20Sopenharmony_ci	dev++;
9398c2ecf20Sopenharmony_ci	err = 0;
9408c2ecf20Sopenharmony_ci	goto out;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ciout_err:
9438c2ecf20Sopenharmony_ci	snd_card_free(card);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ciout:
9468c2ecf20Sopenharmony_ci	return err;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic void snd_card_als4000_remove(struct pci_dev *pci)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	snd_card_free(pci_get_drvdata(pci));
9528c2ecf20Sopenharmony_ci}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
9558c2ecf20Sopenharmony_cistatic int snd_als4000_suspend(struct device *dev)
9568c2ecf20Sopenharmony_ci{
9578c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
9588c2ecf20Sopenharmony_ci	struct snd_card_als4000 *acard = card->private_data;
9598c2ecf20Sopenharmony_ci	struct snd_sb *chip = acard->chip;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	snd_sbmixer_suspend(chip);
9648c2ecf20Sopenharmony_ci	return 0;
9658c2ecf20Sopenharmony_ci}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_cistatic int snd_als4000_resume(struct device *dev)
9688c2ecf20Sopenharmony_ci{
9698c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
9708c2ecf20Sopenharmony_ci	struct snd_card_als4000 *acard = card->private_data;
9718c2ecf20Sopenharmony_ci	struct snd_sb *chip = acard->chip;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	snd_als4000_configure(chip);
9748c2ecf20Sopenharmony_ci	snd_sbdsp_reset(chip);
9758c2ecf20Sopenharmony_ci	snd_sbmixer_resume(chip);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci#ifdef SUPPORT_JOYSTICK
9788c2ecf20Sopenharmony_ci	if (acard->gameport)
9798c2ecf20Sopenharmony_ci		snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);
9808c2ecf20Sopenharmony_ci#endif
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
9838c2ecf20Sopenharmony_ci	return 0;
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(snd_als4000_pm, snd_als4000_suspend, snd_als4000_resume);
9878c2ecf20Sopenharmony_ci#define SND_ALS4000_PM_OPS	&snd_als4000_pm
9888c2ecf20Sopenharmony_ci#else
9898c2ecf20Sopenharmony_ci#define SND_ALS4000_PM_OPS	NULL
9908c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_cistatic struct pci_driver als4000_driver = {
9938c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
9948c2ecf20Sopenharmony_ci	.id_table = snd_als4000_ids,
9958c2ecf20Sopenharmony_ci	.probe = snd_card_als4000_probe,
9968c2ecf20Sopenharmony_ci	.remove = snd_card_als4000_remove,
9978c2ecf20Sopenharmony_ci	.driver = {
9988c2ecf20Sopenharmony_ci		.pm = SND_ALS4000_PM_OPS,
9998c2ecf20Sopenharmony_ci	},
10008c2ecf20Sopenharmony_ci};
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_cimodule_pci_driver(als4000_driver);
1003