162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for NeoMagic 256AV and 256ZX chipsets. 462306a36Sopenharmony_ci * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based on nm256_audio.c OSS driver in linux kernel. 762306a36Sopenharmony_ci * The original author of OSS nm256 driver wishes to remain anonymous, 862306a36Sopenharmony_ci * so I just put my acknoledgment to him/her here. 962306a36Sopenharmony_ci * The original author's web page is found at 1062306a36Sopenharmony_ci * http://www.uglx.org/sony.html 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/pci.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/mutex.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <sound/core.h> 2362306a36Sopenharmony_ci#include <sound/info.h> 2462306a36Sopenharmony_ci#include <sound/control.h> 2562306a36Sopenharmony_ci#include <sound/pcm.h> 2662306a36Sopenharmony_ci#include <sound/ac97_codec.h> 2762306a36Sopenharmony_ci#include <sound/initval.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define CARD_NAME "NeoMagic 256AV/ZX" 3062306a36Sopenharmony_ci#define DRIVER_NAME "NM256" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 3362306a36Sopenharmony_ciMODULE_DESCRIPTION("NeoMagic NM256AV/ZX"); 3462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * some compile conditions. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; /* Index */ 4162306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 4262306a36Sopenharmony_cistatic int playback_bufsize = 16; 4362306a36Sopenharmony_cistatic int capture_bufsize = 16; 4462306a36Sopenharmony_cistatic bool force_ac97; /* disabled as default */ 4562306a36Sopenharmony_cistatic int buffer_top; /* not specified */ 4662306a36Sopenharmony_cistatic bool use_cache; /* disabled */ 4762306a36Sopenharmony_cistatic bool vaio_hack; /* disabled */ 4862306a36Sopenharmony_cistatic bool reset_workaround; 4962306a36Sopenharmony_cistatic bool reset_workaround_2; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cimodule_param(index, int, 0444); 5262306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); 5362306a36Sopenharmony_cimodule_param(id, charp, 0444); 5462306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); 5562306a36Sopenharmony_cimodule_param(playback_bufsize, int, 0444); 5662306a36Sopenharmony_ciMODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard."); 5762306a36Sopenharmony_cimodule_param(capture_bufsize, int, 0444); 5862306a36Sopenharmony_ciMODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard."); 5962306a36Sopenharmony_cimodule_param(force_ac97, bool, 0444); 6062306a36Sopenharmony_ciMODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard."); 6162306a36Sopenharmony_cimodule_param(buffer_top, int, 0444); 6262306a36Sopenharmony_ciMODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard."); 6362306a36Sopenharmony_cimodule_param(use_cache, bool, 0444); 6462306a36Sopenharmony_ciMODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access."); 6562306a36Sopenharmony_cimodule_param(vaio_hack, bool, 0444); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks."); 6762306a36Sopenharmony_cimodule_param(reset_workaround, bool, 0444); 6862306a36Sopenharmony_ciMODULE_PARM_DESC(reset_workaround, "Enable AC97 RESET workaround for some laptops."); 6962306a36Sopenharmony_cimodule_param(reset_workaround_2, bool, 0444); 7062306a36Sopenharmony_ciMODULE_PARM_DESC(reset_workaround_2, "Enable extended AC97 RESET workaround for some other laptops."); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* just for backward compatibility */ 7362306a36Sopenharmony_cistatic bool enable; 7462306a36Sopenharmony_cimodule_param(enable, bool, 0444); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * hw definitions 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* The BIOS signature. */ 8362306a36Sopenharmony_ci#define NM_SIGNATURE 0x4e4d0000 8462306a36Sopenharmony_ci/* Signature mask. */ 8562306a36Sopenharmony_ci#define NM_SIG_MASK 0xffff0000 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Size of the second memory area. */ 8862306a36Sopenharmony_ci#define NM_PORT2_SIZE 4096 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* The base offset of the mixer in the second memory area. */ 9162306a36Sopenharmony_ci#define NM_MIXER_OFFSET 0x600 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* The maximum size of a coefficient entry. */ 9462306a36Sopenharmony_ci#define NM_MAX_PLAYBACK_COEF_SIZE 0x5000 9562306a36Sopenharmony_ci#define NM_MAX_RECORD_COEF_SIZE 0x1260 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* The interrupt register. */ 9862306a36Sopenharmony_ci#define NM_INT_REG 0xa04 9962306a36Sopenharmony_ci/* And its bits. */ 10062306a36Sopenharmony_ci#define NM_PLAYBACK_INT 0x40 10162306a36Sopenharmony_ci#define NM_RECORD_INT 0x100 10262306a36Sopenharmony_ci#define NM_MISC_INT_1 0x4000 10362306a36Sopenharmony_ci#define NM_MISC_INT_2 0x1 10462306a36Sopenharmony_ci#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* The AV's "mixer ready" status bit and location. */ 10762306a36Sopenharmony_ci#define NM_MIXER_STATUS_OFFSET 0xa04 10862306a36Sopenharmony_ci#define NM_MIXER_READY_MASK 0x0800 10962306a36Sopenharmony_ci#define NM_MIXER_PRESENCE 0xa06 11062306a36Sopenharmony_ci#define NM_PRESENCE_MASK 0x0050 11162306a36Sopenharmony_ci#define NM_PRESENCE_VALUE 0x0040 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci * For the ZX. It uses the same interrupt register, but it holds 32 11562306a36Sopenharmony_ci * bits instead of 16. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci#define NM2_PLAYBACK_INT 0x10000 11862306a36Sopenharmony_ci#define NM2_RECORD_INT 0x80000 11962306a36Sopenharmony_ci#define NM2_MISC_INT_1 0x8 12062306a36Sopenharmony_ci#define NM2_MISC_INT_2 0x2 12162306a36Sopenharmony_ci#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X)) 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* The ZX's "mixer ready" status bit and location. */ 12462306a36Sopenharmony_ci#define NM2_MIXER_STATUS_OFFSET 0xa06 12562306a36Sopenharmony_ci#define NM2_MIXER_READY_MASK 0x0800 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* The playback registers start from here. */ 12862306a36Sopenharmony_ci#define NM_PLAYBACK_REG_OFFSET 0x0 12962306a36Sopenharmony_ci/* The record registers start from here. */ 13062306a36Sopenharmony_ci#define NM_RECORD_REG_OFFSET 0x200 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* The rate register is located 2 bytes from the start of the register area. */ 13362306a36Sopenharmony_ci#define NM_RATE_REG_OFFSET 2 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Mono/stereo flag, number of bits on playback, and rate mask. */ 13662306a36Sopenharmony_ci#define NM_RATE_STEREO 1 13762306a36Sopenharmony_ci#define NM_RATE_BITS_16 2 13862306a36Sopenharmony_ci#define NM_RATE_MASK 0xf0 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* Playback enable register. */ 14162306a36Sopenharmony_ci#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) 14262306a36Sopenharmony_ci#define NM_PLAYBACK_ENABLE_FLAG 1 14362306a36Sopenharmony_ci#define NM_PLAYBACK_ONESHOT 2 14462306a36Sopenharmony_ci#define NM_PLAYBACK_FREERUN 4 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* Mutes the audio output. */ 14762306a36Sopenharmony_ci#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) 14862306a36Sopenharmony_ci#define NM_AUDIO_MUTE_LEFT 0x8000 14962306a36Sopenharmony_ci#define NM_AUDIO_MUTE_RIGHT 0x0080 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* Recording enable register. */ 15262306a36Sopenharmony_ci#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) 15362306a36Sopenharmony_ci#define NM_RECORD_ENABLE_FLAG 1 15462306a36Sopenharmony_ci#define NM_RECORD_FREERUN 2 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* coefficient buffer pointer */ 15762306a36Sopenharmony_ci#define NM_COEFF_START_OFFSET 0x1c 15862306a36Sopenharmony_ci#define NM_COEFF_END_OFFSET 0x20 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* DMA buffer offsets */ 16162306a36Sopenharmony_ci#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) 16262306a36Sopenharmony_ci#define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) 16362306a36Sopenharmony_ci#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) 16462306a36Sopenharmony_ci#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) 16762306a36Sopenharmony_ci#define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) 16862306a36Sopenharmony_ci#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) 16962306a36Sopenharmony_ci#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistruct nm256_stream { 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci struct nm256 *chip; 17462306a36Sopenharmony_ci struct snd_pcm_substream *substream; 17562306a36Sopenharmony_ci int running; 17662306a36Sopenharmony_ci int suspended; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci u32 buf; /* offset from chip->buffer */ 17962306a36Sopenharmony_ci int bufsize; /* buffer size in bytes */ 18062306a36Sopenharmony_ci void __iomem *bufptr; /* mapped pointer */ 18162306a36Sopenharmony_ci unsigned long bufptr_addr; /* physical address of the mapped pointer */ 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci int dma_size; /* buffer size of the substream in bytes */ 18462306a36Sopenharmony_ci int period_size; /* period size in bytes */ 18562306a36Sopenharmony_ci int periods; /* # of periods */ 18662306a36Sopenharmony_ci int shift; /* bit shifts */ 18762306a36Sopenharmony_ci int cur_period; /* current period # */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci}; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistruct nm256 { 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci struct snd_card *card; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci void __iomem *cport; /* control port */ 19662306a36Sopenharmony_ci unsigned long cport_addr; /* physical address */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci void __iomem *buffer; /* buffer */ 19962306a36Sopenharmony_ci unsigned long buffer_addr; /* buffer phyiscal address */ 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci u32 buffer_start; /* start offset from pci resource 0 */ 20262306a36Sopenharmony_ci u32 buffer_end; /* end offset */ 20362306a36Sopenharmony_ci u32 buffer_size; /* total buffer size */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci u32 all_coeff_buf; /* coefficient buffer */ 20662306a36Sopenharmony_ci u32 coeff_buf[2]; /* coefficient buffer for each stream */ 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci unsigned int coeffs_current: 1; /* coeff. table is loaded? */ 20962306a36Sopenharmony_ci unsigned int use_cache: 1; /* use one big coef. table */ 21062306a36Sopenharmony_ci unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */ 21162306a36Sopenharmony_ci unsigned int reset_workaround_2: 1; /* Extended workaround for some other laptops to avoid freeze */ 21262306a36Sopenharmony_ci unsigned int in_resume: 1; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci int mixer_base; /* register offset of ac97 mixer */ 21562306a36Sopenharmony_ci int mixer_status_offset; /* offset of mixer status reg. */ 21662306a36Sopenharmony_ci int mixer_status_mask; /* bit mask to test the mixer status */ 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci int irq; 21962306a36Sopenharmony_ci int irq_acks; 22062306a36Sopenharmony_ci irq_handler_t interrupt; 22162306a36Sopenharmony_ci int badintrcount; /* counter to check bogus interrupts */ 22262306a36Sopenharmony_ci struct mutex irq_mutex; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci struct nm256_stream streams[2]; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci struct snd_ac97 *ac97; 22762306a36Sopenharmony_ci unsigned short *ac97_regs; /* register caches, only for valid regs */ 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci struct snd_pcm *pcm; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci struct pci_dev *pci; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci spinlock_t reg_lock; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * include coefficient table 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci#include "nm256_coef.c" 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* 24562306a36Sopenharmony_ci * PCI ids 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic const struct pci_device_id snd_nm256_ids[] = { 24862306a36Sopenharmony_ci {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO), 0}, 24962306a36Sopenharmony_ci {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO), 0}, 25062306a36Sopenharmony_ci {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO), 0}, 25162306a36Sopenharmony_ci {0,}, 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_nm256_ids); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* 25862306a36Sopenharmony_ci * lowlvel stuffs 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic inline u8 26262306a36Sopenharmony_cisnd_nm256_readb(struct nm256 *chip, int offset) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci return readb(chip->cport + offset); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic inline u16 26862306a36Sopenharmony_cisnd_nm256_readw(struct nm256 *chip, int offset) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci return readw(chip->cport + offset); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic inline u32 27462306a36Sopenharmony_cisnd_nm256_readl(struct nm256 *chip, int offset) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return readl(chip->cport + offset); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic inline void 28062306a36Sopenharmony_cisnd_nm256_writeb(struct nm256 *chip, int offset, u8 val) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci writeb(val, chip->cport + offset); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic inline void 28662306a36Sopenharmony_cisnd_nm256_writew(struct nm256 *chip, int offset, u16 val) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci writew(val, chip->cport + offset); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic inline void 29262306a36Sopenharmony_cisnd_nm256_writel(struct nm256 *chip, int offset, u32 val) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci writel(val, chip->cport + offset); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic inline void 29862306a36Sopenharmony_cisnd_nm256_write_buffer(struct nm256 *chip, const void *src, int offset, int size) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci offset -= chip->buffer_start; 30162306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG 30262306a36Sopenharmony_ci if (offset < 0 || offset >= chip->buffer_size) { 30362306a36Sopenharmony_ci dev_err(chip->card->dev, 30462306a36Sopenharmony_ci "write_buffer invalid offset = %d size = %d\n", 30562306a36Sopenharmony_ci offset, size); 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci#endif 30962306a36Sopenharmony_ci memcpy_toio(chip->buffer + offset, src, size); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* 31362306a36Sopenharmony_ci * coefficient handlers -- what a magic! 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic u16 31762306a36Sopenharmony_cisnd_nm256_get_start_offset(int which) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci u16 offset = 0; 32062306a36Sopenharmony_ci while (which-- > 0) 32162306a36Sopenharmony_ci offset += coefficient_sizes[which]; 32262306a36Sopenharmony_ci return offset; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void 32662306a36Sopenharmony_cisnd_nm256_load_one_coefficient(struct nm256 *chip, int stream, u32 port, int which) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci u32 coeff_buf = chip->coeff_buf[stream]; 32962306a36Sopenharmony_ci u16 offset = snd_nm256_get_start_offset(which); 33062306a36Sopenharmony_ci u16 size = coefficient_sizes[which]; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size); 33362306a36Sopenharmony_ci snd_nm256_writel(chip, port, coeff_buf); 33462306a36Sopenharmony_ci /* ??? Record seems to behave differently than playback. */ 33562306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 33662306a36Sopenharmony_ci size--; 33762306a36Sopenharmony_ci snd_nm256_writel(chip, port + 4, coeff_buf + size); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void 34162306a36Sopenharmony_cisnd_nm256_load_coefficient(struct nm256 *chip, int stream, int number) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci /* The enable register for the specified engine. */ 34462306a36Sopenharmony_ci u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? 34562306a36Sopenharmony_ci NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG); 34662306a36Sopenharmony_ci u32 addr = NM_COEFF_START_OFFSET; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? 34962306a36Sopenharmony_ci NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (snd_nm256_readb(chip, poffset) & 1) { 35262306a36Sopenharmony_ci dev_dbg(chip->card->dev, 35362306a36Sopenharmony_ci "NM256: Engine was enabled while loading coefficients!\n"); 35462306a36Sopenharmony_ci return; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* The recording engine uses coefficient values 8-15. */ 35862306a36Sopenharmony_ci number &= 7; 35962306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_CAPTURE) 36062306a36Sopenharmony_ci number += 8; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (! chip->use_cache) { 36362306a36Sopenharmony_ci snd_nm256_load_one_coefficient(chip, stream, addr, number); 36462306a36Sopenharmony_ci return; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci if (! chip->coeffs_current) { 36762306a36Sopenharmony_ci snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf, 36862306a36Sopenharmony_ci NM_TOTAL_COEFF_COUNT * 4); 36962306a36Sopenharmony_ci chip->coeffs_current = 1; 37062306a36Sopenharmony_ci } else { 37162306a36Sopenharmony_ci u32 base = chip->all_coeff_buf; 37262306a36Sopenharmony_ci u32 offset = snd_nm256_get_start_offset(number); 37362306a36Sopenharmony_ci u32 end_offset = offset + coefficient_sizes[number]; 37462306a36Sopenharmony_ci snd_nm256_writel(chip, addr, base + offset); 37562306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_PLAYBACK) 37662306a36Sopenharmony_ci end_offset--; 37762306a36Sopenharmony_ci snd_nm256_writel(chip, addr + 4, base + end_offset); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/* The actual rates supported by the card. */ 38362306a36Sopenharmony_cistatic const unsigned int samplerates[8] = { 38462306a36Sopenharmony_ci 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list constraints_rates = { 38762306a36Sopenharmony_ci .count = ARRAY_SIZE(samplerates), 38862306a36Sopenharmony_ci .list = samplerates, 38962306a36Sopenharmony_ci .mask = 0, 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* 39362306a36Sopenharmony_ci * return the index of the target rate 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic int 39662306a36Sopenharmony_cisnd_nm256_fixed_rate(unsigned int rate) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci unsigned int i; 39962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(samplerates); i++) { 40062306a36Sopenharmony_ci if (rate == samplerates[i]) 40162306a36Sopenharmony_ci return i; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci snd_BUG(); 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* 40862306a36Sopenharmony_ci * set sample rate and format 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic void 41162306a36Sopenharmony_cisnd_nm256_set_format(struct nm256 *chip, struct nm256_stream *s, 41262306a36Sopenharmony_ci struct snd_pcm_substream *substream) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 41562306a36Sopenharmony_ci int rate_index = snd_nm256_fixed_rate(runtime->rate); 41662306a36Sopenharmony_ci unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci s->shift = 0; 41962306a36Sopenharmony_ci if (snd_pcm_format_width(runtime->format) == 16) { 42062306a36Sopenharmony_ci ratebits |= NM_RATE_BITS_16; 42162306a36Sopenharmony_ci s->shift++; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci if (runtime->channels > 1) { 42462306a36Sopenharmony_ci ratebits |= NM_RATE_STEREO; 42562306a36Sopenharmony_ci s->shift++; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci runtime->rate = samplerates[rate_index]; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci switch (substream->stream) { 43162306a36Sopenharmony_ci case SNDRV_PCM_STREAM_PLAYBACK: 43262306a36Sopenharmony_ci snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */ 43362306a36Sopenharmony_ci snd_nm256_writeb(chip, 43462306a36Sopenharmony_ci NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, 43562306a36Sopenharmony_ci ratebits); 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci case SNDRV_PCM_STREAM_CAPTURE: 43862306a36Sopenharmony_ci snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */ 43962306a36Sopenharmony_ci snd_nm256_writeb(chip, 44062306a36Sopenharmony_ci NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, 44162306a36Sopenharmony_ci ratebits); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* acquire interrupt */ 44762306a36Sopenharmony_cistatic int snd_nm256_acquire_irq(struct nm256 *chip) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci mutex_lock(&chip->irq_mutex); 45062306a36Sopenharmony_ci if (chip->irq < 0) { 45162306a36Sopenharmony_ci if (request_irq(chip->pci->irq, chip->interrupt, IRQF_SHARED, 45262306a36Sopenharmony_ci KBUILD_MODNAME, chip)) { 45362306a36Sopenharmony_ci dev_err(chip->card->dev, 45462306a36Sopenharmony_ci "unable to grab IRQ %d\n", chip->pci->irq); 45562306a36Sopenharmony_ci mutex_unlock(&chip->irq_mutex); 45662306a36Sopenharmony_ci return -EBUSY; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci chip->irq = chip->pci->irq; 45962306a36Sopenharmony_ci chip->card->sync_irq = chip->irq; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci chip->irq_acks++; 46262306a36Sopenharmony_ci mutex_unlock(&chip->irq_mutex); 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* release interrupt */ 46762306a36Sopenharmony_cistatic void snd_nm256_release_irq(struct nm256 *chip) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci mutex_lock(&chip->irq_mutex); 47062306a36Sopenharmony_ci if (chip->irq_acks > 0) 47162306a36Sopenharmony_ci chip->irq_acks--; 47262306a36Sopenharmony_ci if (chip->irq_acks == 0 && chip->irq >= 0) { 47362306a36Sopenharmony_ci free_irq(chip->irq, chip); 47462306a36Sopenharmony_ci chip->irq = -1; 47562306a36Sopenharmony_ci chip->card->sync_irq = -1; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci mutex_unlock(&chip->irq_mutex); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/* 48162306a36Sopenharmony_ci * start / stop 48262306a36Sopenharmony_ci */ 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci/* update the watermark (current period) */ 48562306a36Sopenharmony_cistatic void snd_nm256_pcm_mark(struct nm256 *chip, struct nm256_stream *s, int reg) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci s->cur_period++; 48862306a36Sopenharmony_ci s->cur_period %= s->periods; 48962306a36Sopenharmony_ci snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK) 49362306a36Sopenharmony_ci#define snd_nm256_capture_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK) 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void 49662306a36Sopenharmony_cisnd_nm256_playback_start(struct nm256 *chip, struct nm256_stream *s, 49762306a36Sopenharmony_ci struct snd_pcm_substream *substream) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci /* program buffer pointers */ 50062306a36Sopenharmony_ci snd_nm256_writel(chip, NM_PBUFFER_START, s->buf); 50162306a36Sopenharmony_ci snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift)); 50262306a36Sopenharmony_ci snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf); 50362306a36Sopenharmony_ci snd_nm256_playback_mark(chip, s); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Enable playback engine and interrupts. */ 50662306a36Sopenharmony_ci snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 50762306a36Sopenharmony_ci NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); 50862306a36Sopenharmony_ci /* Enable both channels. */ 50962306a36Sopenharmony_ci snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic void 51362306a36Sopenharmony_cisnd_nm256_capture_start(struct nm256 *chip, struct nm256_stream *s, 51462306a36Sopenharmony_ci struct snd_pcm_substream *substream) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci /* program buffer pointers */ 51762306a36Sopenharmony_ci snd_nm256_writel(chip, NM_RBUFFER_START, s->buf); 51862306a36Sopenharmony_ci snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size); 51962306a36Sopenharmony_ci snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf); 52062306a36Sopenharmony_ci snd_nm256_capture_mark(chip, s); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Enable playback engine and interrupts. */ 52362306a36Sopenharmony_ci snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 52462306a36Sopenharmony_ci NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/* Stop the play engine. */ 52862306a36Sopenharmony_cistatic void 52962306a36Sopenharmony_cisnd_nm256_playback_stop(struct nm256 *chip) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci /* Shut off sound from both channels. */ 53262306a36Sopenharmony_ci snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 53362306a36Sopenharmony_ci NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); 53462306a36Sopenharmony_ci /* Disable play engine. */ 53562306a36Sopenharmony_ci snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic void 53962306a36Sopenharmony_cisnd_nm256_capture_stop(struct nm256 *chip) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci /* Disable recording engine. */ 54262306a36Sopenharmony_ci snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int 54662306a36Sopenharmony_cisnd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 54962306a36Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 55062306a36Sopenharmony_ci int err = 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (snd_BUG_ON(!s)) 55362306a36Sopenharmony_ci return -ENXIO; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 55662306a36Sopenharmony_ci switch (cmd) { 55762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 55862306a36Sopenharmony_ci s->suspended = 0; 55962306a36Sopenharmony_ci fallthrough; 56062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 56162306a36Sopenharmony_ci if (! s->running) { 56262306a36Sopenharmony_ci snd_nm256_playback_start(chip, s, substream); 56362306a36Sopenharmony_ci s->running = 1; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 56762306a36Sopenharmony_ci s->suspended = 1; 56862306a36Sopenharmony_ci fallthrough; 56962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 57062306a36Sopenharmony_ci if (s->running) { 57162306a36Sopenharmony_ci snd_nm256_playback_stop(chip); 57262306a36Sopenharmony_ci s->running = 0; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci default: 57662306a36Sopenharmony_ci err = -EINVAL; 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 58062306a36Sopenharmony_ci return err; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic int 58462306a36Sopenharmony_cisnd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 58762306a36Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 58862306a36Sopenharmony_ci int err = 0; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (snd_BUG_ON(!s)) 59162306a36Sopenharmony_ci return -ENXIO; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 59462306a36Sopenharmony_ci switch (cmd) { 59562306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 59662306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 59762306a36Sopenharmony_ci if (! s->running) { 59862306a36Sopenharmony_ci snd_nm256_capture_start(chip, s, substream); 59962306a36Sopenharmony_ci s->running = 1; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci break; 60262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 60362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 60462306a36Sopenharmony_ci if (s->running) { 60562306a36Sopenharmony_ci snd_nm256_capture_stop(chip); 60662306a36Sopenharmony_ci s->running = 0; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci default: 61062306a36Sopenharmony_ci err = -EINVAL; 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 61462306a36Sopenharmony_ci return err; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* 61962306a36Sopenharmony_ci * prepare playback/capture channel 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_cistatic int snd_nm256_pcm_prepare(struct snd_pcm_substream *substream) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 62462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 62562306a36Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (snd_BUG_ON(!s)) 62862306a36Sopenharmony_ci return -ENXIO; 62962306a36Sopenharmony_ci s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size); 63062306a36Sopenharmony_ci s->period_size = frames_to_bytes(runtime, substream->runtime->period_size); 63162306a36Sopenharmony_ci s->periods = substream->runtime->periods; 63262306a36Sopenharmony_ci s->cur_period = 0; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 63562306a36Sopenharmony_ci s->running = 0; 63662306a36Sopenharmony_ci snd_nm256_set_format(chip, s, substream); 63762306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* 64462306a36Sopenharmony_ci * get the current pointer 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_cistatic snd_pcm_uframes_t 64762306a36Sopenharmony_cisnd_nm256_playback_pointer(struct snd_pcm_substream *substream) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 65062306a36Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 65162306a36Sopenharmony_ci unsigned long curp; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (snd_BUG_ON(!s)) 65462306a36Sopenharmony_ci return 0; 65562306a36Sopenharmony_ci curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf; 65662306a36Sopenharmony_ci curp %= s->dma_size; 65762306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, curp); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic snd_pcm_uframes_t 66162306a36Sopenharmony_cisnd_nm256_capture_pointer(struct snd_pcm_substream *substream) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 66462306a36Sopenharmony_ci struct nm256_stream *s = substream->runtime->private_data; 66562306a36Sopenharmony_ci unsigned long curp; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (snd_BUG_ON(!s)) 66862306a36Sopenharmony_ci return 0; 66962306a36Sopenharmony_ci curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf; 67062306a36Sopenharmony_ci curp %= s->dma_size; 67162306a36Sopenharmony_ci return bytes_to_frames(substream->runtime, curp); 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/* Remapped I/O space can be accessible as pointer on i386 */ 67562306a36Sopenharmony_ci/* This might be changed in the future */ 67662306a36Sopenharmony_ci#ifndef __i386__ 67762306a36Sopenharmony_ci/* 67862306a36Sopenharmony_ci * silence / copy for playback 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_cistatic int 68162306a36Sopenharmony_cisnd_nm256_playback_silence(struct snd_pcm_substream *substream, 68262306a36Sopenharmony_ci int channel, unsigned long pos, unsigned long count) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 68562306a36Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci memset_io(s->bufptr + pos, 0, count); 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int 69262306a36Sopenharmony_cisnd_nm256_playback_copy(struct snd_pcm_substream *substream, 69362306a36Sopenharmony_ci int channel, unsigned long pos, 69462306a36Sopenharmony_ci struct iov_iter *src, unsigned long count) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 69762306a36Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return copy_from_iter_toio(s->bufptr + pos, src, count); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci/* 70362306a36Sopenharmony_ci * copy to user 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic int 70662306a36Sopenharmony_cisnd_nm256_capture_copy(struct snd_pcm_substream *substream, 70762306a36Sopenharmony_ci int channel, unsigned long pos, 70862306a36Sopenharmony_ci struct iov_iter *dst, unsigned long count) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 71162306a36Sopenharmony_ci struct nm256_stream *s = runtime->private_data; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return copy_to_iter_fromio(dst, s->bufptr + pos, count); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci#endif /* !__i386__ */ 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci/* 72062306a36Sopenharmony_ci * update playback/capture watermarks 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci/* spinlock held! */ 72462306a36Sopenharmony_cistatic void 72562306a36Sopenharmony_cisnd_nm256_playback_update(struct nm256 *chip) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct nm256_stream *s; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK]; 73062306a36Sopenharmony_ci if (s->running && s->substream) { 73162306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 73262306a36Sopenharmony_ci snd_pcm_period_elapsed(s->substream); 73362306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 73462306a36Sopenharmony_ci snd_nm256_playback_mark(chip, s); 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci/* spinlock held! */ 73962306a36Sopenharmony_cistatic void 74062306a36Sopenharmony_cisnd_nm256_capture_update(struct nm256 *chip) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct nm256_stream *s; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE]; 74562306a36Sopenharmony_ci if (s->running && s->substream) { 74662306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 74762306a36Sopenharmony_ci snd_pcm_period_elapsed(s->substream); 74862306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 74962306a36Sopenharmony_ci snd_nm256_capture_mark(chip, s); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci/* 75462306a36Sopenharmony_ci * hardware info 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_nm256_playback = 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID | 75962306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 76062306a36Sopenharmony_ci /*SNDRV_PCM_INFO_PAUSE |*/ 76162306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 76262306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 76362306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, 76462306a36Sopenharmony_ci .rate_min = 8000, 76562306a36Sopenharmony_ci .rate_max = 48000, 76662306a36Sopenharmony_ci .channels_min = 1, 76762306a36Sopenharmony_ci .channels_max = 2, 76862306a36Sopenharmony_ci .periods_min = 2, 76962306a36Sopenharmony_ci .periods_max = 1024, 77062306a36Sopenharmony_ci .buffer_bytes_max = 128 * 1024, 77162306a36Sopenharmony_ci .period_bytes_min = 256, 77262306a36Sopenharmony_ci .period_bytes_max = 128 * 1024, 77362306a36Sopenharmony_ci}; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_nm256_capture = 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci .info = SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | 77862306a36Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 77962306a36Sopenharmony_ci /*SNDRV_PCM_INFO_PAUSE |*/ 78062306a36Sopenharmony_ci SNDRV_PCM_INFO_RESUME, 78162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 78262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, 78362306a36Sopenharmony_ci .rate_min = 8000, 78462306a36Sopenharmony_ci .rate_max = 48000, 78562306a36Sopenharmony_ci .channels_min = 1, 78662306a36Sopenharmony_ci .channels_max = 2, 78762306a36Sopenharmony_ci .periods_min = 2, 78862306a36Sopenharmony_ci .periods_max = 1024, 78962306a36Sopenharmony_ci .buffer_bytes_max = 128 * 1024, 79062306a36Sopenharmony_ci .period_bytes_min = 256, 79162306a36Sopenharmony_ci .period_bytes_max = 128 * 1024, 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* set dma transfer size */ 79662306a36Sopenharmony_cistatic int snd_nm256_pcm_hw_params(struct snd_pcm_substream *substream, 79762306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci /* area and addr are already set and unchanged */ 80062306a36Sopenharmony_ci substream->runtime->dma_bytes = params_buffer_bytes(hw_params); 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci/* 80562306a36Sopenharmony_ci * open 80662306a36Sopenharmony_ci */ 80762306a36Sopenharmony_cistatic void snd_nm256_setup_stream(struct nm256 *chip, struct nm256_stream *s, 80862306a36Sopenharmony_ci struct snd_pcm_substream *substream, 80962306a36Sopenharmony_ci const struct snd_pcm_hardware *hw_ptr) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci s->running = 0; 81462306a36Sopenharmony_ci runtime->hw = *hw_ptr; 81562306a36Sopenharmony_ci runtime->hw.buffer_bytes_max = s->bufsize; 81662306a36Sopenharmony_ci runtime->hw.period_bytes_max = s->bufsize / 2; 81762306a36Sopenharmony_ci runtime->dma_area = (void __force *) s->bufptr; 81862306a36Sopenharmony_ci runtime->dma_addr = s->bufptr_addr; 81962306a36Sopenharmony_ci runtime->dma_bytes = s->bufsize; 82062306a36Sopenharmony_ci runtime->private_data = s; 82162306a36Sopenharmony_ci s->substream = substream; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 82462306a36Sopenharmony_ci &constraints_rates); 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int 82862306a36Sopenharmony_cisnd_nm256_playback_open(struct snd_pcm_substream *substream) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (snd_nm256_acquire_irq(chip) < 0) 83362306a36Sopenharmony_ci return -EBUSY; 83462306a36Sopenharmony_ci snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], 83562306a36Sopenharmony_ci substream, &snd_nm256_playback); 83662306a36Sopenharmony_ci return 0; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic int 84062306a36Sopenharmony_cisnd_nm256_capture_open(struct snd_pcm_substream *substream) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci if (snd_nm256_acquire_irq(chip) < 0) 84562306a36Sopenharmony_ci return -EBUSY; 84662306a36Sopenharmony_ci snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], 84762306a36Sopenharmony_ci substream, &snd_nm256_capture); 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/* 85262306a36Sopenharmony_ci * close - we don't have to do special.. 85362306a36Sopenharmony_ci */ 85462306a36Sopenharmony_cistatic int 85562306a36Sopenharmony_cisnd_nm256_playback_close(struct snd_pcm_substream *substream) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci snd_nm256_release_irq(chip); 86062306a36Sopenharmony_ci return 0; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int 86562306a36Sopenharmony_cisnd_nm256_capture_close(struct snd_pcm_substream *substream) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct nm256 *chip = snd_pcm_substream_chip(substream); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci snd_nm256_release_irq(chip); 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/* 87462306a36Sopenharmony_ci * create a pcm instance 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_nm256_playback_ops = { 87762306a36Sopenharmony_ci .open = snd_nm256_playback_open, 87862306a36Sopenharmony_ci .close = snd_nm256_playback_close, 87962306a36Sopenharmony_ci .hw_params = snd_nm256_pcm_hw_params, 88062306a36Sopenharmony_ci .prepare = snd_nm256_pcm_prepare, 88162306a36Sopenharmony_ci .trigger = snd_nm256_playback_trigger, 88262306a36Sopenharmony_ci .pointer = snd_nm256_playback_pointer, 88362306a36Sopenharmony_ci#ifndef __i386__ 88462306a36Sopenharmony_ci .copy = snd_nm256_playback_copy, 88562306a36Sopenharmony_ci .fill_silence = snd_nm256_playback_silence, 88662306a36Sopenharmony_ci#endif 88762306a36Sopenharmony_ci .mmap = snd_pcm_lib_mmap_iomem, 88862306a36Sopenharmony_ci}; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_nm256_capture_ops = { 89162306a36Sopenharmony_ci .open = snd_nm256_capture_open, 89262306a36Sopenharmony_ci .close = snd_nm256_capture_close, 89362306a36Sopenharmony_ci .hw_params = snd_nm256_pcm_hw_params, 89462306a36Sopenharmony_ci .prepare = snd_nm256_pcm_prepare, 89562306a36Sopenharmony_ci .trigger = snd_nm256_capture_trigger, 89662306a36Sopenharmony_ci .pointer = snd_nm256_capture_pointer, 89762306a36Sopenharmony_ci#ifndef __i386__ 89862306a36Sopenharmony_ci .copy = snd_nm256_capture_copy, 89962306a36Sopenharmony_ci#endif 90062306a36Sopenharmony_ci .mmap = snd_pcm_lib_mmap_iomem, 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int 90462306a36Sopenharmony_cisnd_nm256_pcm(struct nm256 *chip, int device) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct snd_pcm *pcm; 90762306a36Sopenharmony_ci int i, err; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 91062306a36Sopenharmony_ci struct nm256_stream *s = &chip->streams[i]; 91162306a36Sopenharmony_ci s->bufptr = chip->buffer + (s->buf - chip->buffer_start); 91262306a36Sopenharmony_ci s->bufptr_addr = chip->buffer_addr + (s->buf - chip->buffer_start); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci err = snd_pcm_new(chip->card, chip->card->driver, device, 91662306a36Sopenharmony_ci 1, 1, &pcm); 91762306a36Sopenharmony_ci if (err < 0) 91862306a36Sopenharmony_ci return err; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops); 92162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci pcm->private_data = chip; 92462306a36Sopenharmony_ci pcm->info_flags = 0; 92562306a36Sopenharmony_ci chip->pcm = pcm; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci/* 93262306a36Sopenharmony_ci * Initialize the hardware. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_cistatic void 93562306a36Sopenharmony_cisnd_nm256_init_chip(struct nm256 *chip) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci /* Reset everything. */ 93862306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x0, 0x11); 93962306a36Sopenharmony_ci snd_nm256_writew(chip, 0x214, 0); 94062306a36Sopenharmony_ci /* stop sounds.. */ 94162306a36Sopenharmony_ci //snd_nm256_playback_stop(chip); 94262306a36Sopenharmony_ci //snd_nm256_capture_stop(chip); 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic irqreturn_t 94762306a36Sopenharmony_cisnd_nm256_intr_check(struct nm256 *chip) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci if (chip->badintrcount++ > 1000) { 95062306a36Sopenharmony_ci /* 95162306a36Sopenharmony_ci * I'm not sure if the best thing is to stop the card from 95262306a36Sopenharmony_ci * playing or just release the interrupt (after all, we're in 95362306a36Sopenharmony_ci * a bad situation, so doing fancy stuff may not be such a good 95462306a36Sopenharmony_ci * idea). 95562306a36Sopenharmony_ci * 95662306a36Sopenharmony_ci * I worry about the card engine continuing to play noise 95762306a36Sopenharmony_ci * over and over, however--that could become a very 95862306a36Sopenharmony_ci * obnoxious problem. And we know that when this usually 95962306a36Sopenharmony_ci * happens things are fairly safe, it just means the user's 96062306a36Sopenharmony_ci * inserted a PCMCIA card and someone's spamming us with IRQ 9s. 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) 96362306a36Sopenharmony_ci snd_nm256_playback_stop(chip); 96462306a36Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) 96562306a36Sopenharmony_ci snd_nm256_capture_stop(chip); 96662306a36Sopenharmony_ci chip->badintrcount = 0; 96762306a36Sopenharmony_ci return IRQ_HANDLED; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci return IRQ_NONE; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci/* 97362306a36Sopenharmony_ci * Handle a potential interrupt for the device referred to by DEV_ID. 97462306a36Sopenharmony_ci * 97562306a36Sopenharmony_ci * I don't like the cut-n-paste job here either between the two routines, 97662306a36Sopenharmony_ci * but there are sufficient differences between the two interrupt handlers 97762306a36Sopenharmony_ci * that parameterizing it isn't all that great either. (Could use a macro, 97862306a36Sopenharmony_ci * I suppose...yucky bleah.) 97962306a36Sopenharmony_ci */ 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic irqreturn_t 98262306a36Sopenharmony_cisnd_nm256_interrupt(int irq, void *dev_id) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct nm256 *chip = dev_id; 98562306a36Sopenharmony_ci u16 status; 98662306a36Sopenharmony_ci u8 cbyte; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci status = snd_nm256_readw(chip, NM_INT_REG); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* Not ours. */ 99162306a36Sopenharmony_ci if (status == 0) 99262306a36Sopenharmony_ci return snd_nm256_intr_check(chip); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci chip->badintrcount = 0; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* Rather boring; check for individual interrupts and process them. */ 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 99962306a36Sopenharmony_ci if (status & NM_PLAYBACK_INT) { 100062306a36Sopenharmony_ci status &= ~NM_PLAYBACK_INT; 100162306a36Sopenharmony_ci NM_ACK_INT(chip, NM_PLAYBACK_INT); 100262306a36Sopenharmony_ci snd_nm256_playback_update(chip); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if (status & NM_RECORD_INT) { 100662306a36Sopenharmony_ci status &= ~NM_RECORD_INT; 100762306a36Sopenharmony_ci NM_ACK_INT(chip, NM_RECORD_INT); 100862306a36Sopenharmony_ci snd_nm256_capture_update(chip); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (status & NM_MISC_INT_1) { 101262306a36Sopenharmony_ci status &= ~NM_MISC_INT_1; 101362306a36Sopenharmony_ci NM_ACK_INT(chip, NM_MISC_INT_1); 101462306a36Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n"); 101562306a36Sopenharmony_ci snd_nm256_writew(chip, NM_INT_REG, 0x8000); 101662306a36Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 101762306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte | 2); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (status & NM_MISC_INT_2) { 102162306a36Sopenharmony_ci status &= ~NM_MISC_INT_2; 102262306a36Sopenharmony_ci NM_ACK_INT(chip, NM_MISC_INT_2); 102362306a36Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n"); 102462306a36Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 102562306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte & ~2); 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci /* Unknown interrupt. */ 102962306a36Sopenharmony_ci if (status) { 103062306a36Sopenharmony_ci dev_dbg(chip->card->dev, 103162306a36Sopenharmony_ci "NM256: Fire in the hole! Unknown status 0x%x\n", 103262306a36Sopenharmony_ci status); 103362306a36Sopenharmony_ci /* Pray. */ 103462306a36Sopenharmony_ci NM_ACK_INT(chip, status); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 103862306a36Sopenharmony_ci return IRQ_HANDLED; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci/* 104262306a36Sopenharmony_ci * Handle a potential interrupt for the device referred to by DEV_ID. 104362306a36Sopenharmony_ci * This handler is for the 256ZX, and is very similar to the non-ZX 104462306a36Sopenharmony_ci * routine. 104562306a36Sopenharmony_ci */ 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic irqreturn_t 104862306a36Sopenharmony_cisnd_nm256_interrupt_zx(int irq, void *dev_id) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci struct nm256 *chip = dev_id; 105162306a36Sopenharmony_ci u32 status; 105262306a36Sopenharmony_ci u8 cbyte; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci status = snd_nm256_readl(chip, NM_INT_REG); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci /* Not ours. */ 105762306a36Sopenharmony_ci if (status == 0) 105862306a36Sopenharmony_ci return snd_nm256_intr_check(chip); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci chip->badintrcount = 0; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci /* Rather boring; check for individual interrupts and process them. */ 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci spin_lock(&chip->reg_lock); 106562306a36Sopenharmony_ci if (status & NM2_PLAYBACK_INT) { 106662306a36Sopenharmony_ci status &= ~NM2_PLAYBACK_INT; 106762306a36Sopenharmony_ci NM2_ACK_INT(chip, NM2_PLAYBACK_INT); 106862306a36Sopenharmony_ci snd_nm256_playback_update(chip); 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (status & NM2_RECORD_INT) { 107262306a36Sopenharmony_ci status &= ~NM2_RECORD_INT; 107362306a36Sopenharmony_ci NM2_ACK_INT(chip, NM2_RECORD_INT); 107462306a36Sopenharmony_ci snd_nm256_capture_update(chip); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (status & NM2_MISC_INT_1) { 107862306a36Sopenharmony_ci status &= ~NM2_MISC_INT_1; 107962306a36Sopenharmony_ci NM2_ACK_INT(chip, NM2_MISC_INT_1); 108062306a36Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n"); 108162306a36Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 108262306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte | 2); 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (status & NM2_MISC_INT_2) { 108662306a36Sopenharmony_ci status &= ~NM2_MISC_INT_2; 108762306a36Sopenharmony_ci NM2_ACK_INT(chip, NM2_MISC_INT_2); 108862306a36Sopenharmony_ci dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n"); 108962306a36Sopenharmony_ci cbyte = snd_nm256_readb(chip, 0x400); 109062306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x400, cbyte & ~2); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* Unknown interrupt. */ 109462306a36Sopenharmony_ci if (status) { 109562306a36Sopenharmony_ci dev_dbg(chip->card->dev, 109662306a36Sopenharmony_ci "NM256: Fire in the hole! Unknown status 0x%x\n", 109762306a36Sopenharmony_ci status); 109862306a36Sopenharmony_ci /* Pray. */ 109962306a36Sopenharmony_ci NM2_ACK_INT(chip, status); 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci spin_unlock(&chip->reg_lock); 110362306a36Sopenharmony_ci return IRQ_HANDLED; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci/* 110762306a36Sopenharmony_ci * AC97 interface 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci/* 111162306a36Sopenharmony_ci * Waits for the mixer to become ready to be written; returns a zero value 111262306a36Sopenharmony_ci * if it timed out. 111362306a36Sopenharmony_ci */ 111462306a36Sopenharmony_cistatic int 111562306a36Sopenharmony_cisnd_nm256_ac97_ready(struct nm256 *chip) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci int timeout = 10; 111862306a36Sopenharmony_ci u32 testaddr; 111962306a36Sopenharmony_ci u16 testb; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci testaddr = chip->mixer_status_offset; 112262306a36Sopenharmony_ci testb = chip->mixer_status_mask; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci /* 112562306a36Sopenharmony_ci * Loop around waiting for the mixer to become ready. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci while (timeout-- > 0) { 112862306a36Sopenharmony_ci if ((snd_nm256_readw(chip, testaddr) & testb) == 0) 112962306a36Sopenharmony_ci return 1; 113062306a36Sopenharmony_ci udelay(100); 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci/* 113662306a36Sopenharmony_ci * Initial register values to be written to the AC97 mixer. 113762306a36Sopenharmony_ci * While most of these are identical to the reset values, we do this 113862306a36Sopenharmony_ci * so that we have most of the register contents cached--this avoids 113962306a36Sopenharmony_ci * reading from the mixer directly (which seems to be problematic, 114062306a36Sopenharmony_ci * probably due to ignorance). 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistruct initialValues { 114462306a36Sopenharmony_ci unsigned short reg; 114562306a36Sopenharmony_ci unsigned short value; 114662306a36Sopenharmony_ci}; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic const struct initialValues nm256_ac97_init_val[] = 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci { AC97_MASTER, 0x8000 }, 115162306a36Sopenharmony_ci { AC97_HEADPHONE, 0x8000 }, 115262306a36Sopenharmony_ci { AC97_MASTER_MONO, 0x8000 }, 115362306a36Sopenharmony_ci { AC97_PC_BEEP, 0x8000 }, 115462306a36Sopenharmony_ci { AC97_PHONE, 0x8008 }, 115562306a36Sopenharmony_ci { AC97_MIC, 0x8000 }, 115662306a36Sopenharmony_ci { AC97_LINE, 0x8808 }, 115762306a36Sopenharmony_ci { AC97_CD, 0x8808 }, 115862306a36Sopenharmony_ci { AC97_VIDEO, 0x8808 }, 115962306a36Sopenharmony_ci { AC97_AUX, 0x8808 }, 116062306a36Sopenharmony_ci { AC97_PCM, 0x8808 }, 116162306a36Sopenharmony_ci { AC97_REC_SEL, 0x0000 }, 116262306a36Sopenharmony_ci { AC97_REC_GAIN, 0x0B0B }, 116362306a36Sopenharmony_ci { AC97_GENERAL_PURPOSE, 0x0000 }, 116462306a36Sopenharmony_ci { AC97_3D_CONTROL, 0x8000 }, 116562306a36Sopenharmony_ci { AC97_VENDOR_ID1, 0x8384 }, 116662306a36Sopenharmony_ci { AC97_VENDOR_ID2, 0x7609 }, 116762306a36Sopenharmony_ci}; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic int nm256_ac97_idx(unsigned short reg) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci int i; 117262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) 117362306a36Sopenharmony_ci if (nm256_ac97_init_val[i].reg == reg) 117462306a36Sopenharmony_ci return i; 117562306a36Sopenharmony_ci return -1; 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci/* 117962306a36Sopenharmony_ci * some nm256 easily crash when reading from mixer registers 118062306a36Sopenharmony_ci * thus we're treating it as a write-only mixer and cache the 118162306a36Sopenharmony_ci * written values 118262306a36Sopenharmony_ci */ 118362306a36Sopenharmony_cistatic unsigned short 118462306a36Sopenharmony_cisnd_nm256_ac97_read(struct snd_ac97 *ac97, unsigned short reg) 118562306a36Sopenharmony_ci{ 118662306a36Sopenharmony_ci struct nm256 *chip = ac97->private_data; 118762306a36Sopenharmony_ci int idx = nm256_ac97_idx(reg); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (idx < 0) 119062306a36Sopenharmony_ci return 0; 119162306a36Sopenharmony_ci return chip->ac97_regs[idx]; 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci/* 119562306a36Sopenharmony_ci */ 119662306a36Sopenharmony_cistatic void 119762306a36Sopenharmony_cisnd_nm256_ac97_write(struct snd_ac97 *ac97, 119862306a36Sopenharmony_ci unsigned short reg, unsigned short val) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci struct nm256 *chip = ac97->private_data; 120162306a36Sopenharmony_ci int tries = 2; 120262306a36Sopenharmony_ci int idx = nm256_ac97_idx(reg); 120362306a36Sopenharmony_ci u32 base; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (idx < 0) 120662306a36Sopenharmony_ci return; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci base = chip->mixer_base; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci snd_nm256_ac97_ready(chip); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* Wait for the write to take, too. */ 121362306a36Sopenharmony_ci while (tries-- > 0) { 121462306a36Sopenharmony_ci snd_nm256_writew(chip, base + reg, val); 121562306a36Sopenharmony_ci msleep(1); /* a little delay here seems better.. */ 121662306a36Sopenharmony_ci if (snd_nm256_ac97_ready(chip)) { 121762306a36Sopenharmony_ci /* successful write: set cache */ 121862306a36Sopenharmony_ci chip->ac97_regs[idx] = val; 121962306a36Sopenharmony_ci return; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci dev_dbg(chip->card->dev, "nm256: ac97 codec not ready..\n"); 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci/* static resolution table */ 122662306a36Sopenharmony_cistatic const struct snd_ac97_res_table nm256_res_table[] = { 122762306a36Sopenharmony_ci { AC97_MASTER, 0x1f1f }, 122862306a36Sopenharmony_ci { AC97_HEADPHONE, 0x1f1f }, 122962306a36Sopenharmony_ci { AC97_MASTER_MONO, 0x001f }, 123062306a36Sopenharmony_ci { AC97_PC_BEEP, 0x001f }, 123162306a36Sopenharmony_ci { AC97_PHONE, 0x001f }, 123262306a36Sopenharmony_ci { AC97_MIC, 0x001f }, 123362306a36Sopenharmony_ci { AC97_LINE, 0x1f1f }, 123462306a36Sopenharmony_ci { AC97_CD, 0x1f1f }, 123562306a36Sopenharmony_ci { AC97_VIDEO, 0x1f1f }, 123662306a36Sopenharmony_ci { AC97_AUX, 0x1f1f }, 123762306a36Sopenharmony_ci { AC97_PCM, 0x1f1f }, 123862306a36Sopenharmony_ci { AC97_REC_GAIN, 0x0f0f }, 123962306a36Sopenharmony_ci { } /* terminator */ 124062306a36Sopenharmony_ci}; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci/* initialize the ac97 into a known state */ 124362306a36Sopenharmony_cistatic void 124462306a36Sopenharmony_cisnd_nm256_ac97_reset(struct snd_ac97 *ac97) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct nm256 *chip = ac97->private_data; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci /* Reset the mixer. 'Tis magic! */ 124962306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x6c0, 1); 125062306a36Sopenharmony_ci if (! chip->reset_workaround) { 125162306a36Sopenharmony_ci /* Dell latitude LS will lock up by this */ 125262306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x6cc, 0x87); 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci if (! chip->reset_workaround_2) { 125562306a36Sopenharmony_ci /* Dell latitude CSx will lock up by this */ 125662306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x6cc, 0x80); 125762306a36Sopenharmony_ci snd_nm256_writeb(chip, 0x6cc, 0x0); 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci if (! chip->in_resume) { 126062306a36Sopenharmony_ci int i; 126162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) { 126262306a36Sopenharmony_ci /* preload the cache, so as to avoid even a single 126362306a36Sopenharmony_ci * read of the mixer regs 126462306a36Sopenharmony_ci */ 126562306a36Sopenharmony_ci snd_nm256_ac97_write(ac97, nm256_ac97_init_val[i].reg, 126662306a36Sopenharmony_ci nm256_ac97_init_val[i].value); 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci} 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci/* create an ac97 mixer interface */ 127262306a36Sopenharmony_cistatic int 127362306a36Sopenharmony_cisnd_nm256_mixer(struct nm256 *chip) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci struct snd_ac97_bus *pbus; 127662306a36Sopenharmony_ci struct snd_ac97_template ac97; 127762306a36Sopenharmony_ci int err; 127862306a36Sopenharmony_ci static const struct snd_ac97_bus_ops ops = { 127962306a36Sopenharmony_ci .reset = snd_nm256_ac97_reset, 128062306a36Sopenharmony_ci .write = snd_nm256_ac97_write, 128162306a36Sopenharmony_ci .read = snd_nm256_ac97_read, 128262306a36Sopenharmony_ci }; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci chip->ac97_regs = devm_kcalloc(chip->card->dev, 128562306a36Sopenharmony_ci ARRAY_SIZE(nm256_ac97_init_val), 128662306a36Sopenharmony_ci sizeof(short), GFP_KERNEL); 128762306a36Sopenharmony_ci if (! chip->ac97_regs) 128862306a36Sopenharmony_ci return -ENOMEM; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus); 129162306a36Sopenharmony_ci if (err < 0) 129262306a36Sopenharmony_ci return err; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci memset(&ac97, 0, sizeof(ac97)); 129562306a36Sopenharmony_ci ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */ 129662306a36Sopenharmony_ci ac97.private_data = chip; 129762306a36Sopenharmony_ci ac97.res_table = nm256_res_table; 129862306a36Sopenharmony_ci pbus->no_vra = 1; 129962306a36Sopenharmony_ci err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); 130062306a36Sopenharmony_ci if (err < 0) 130162306a36Sopenharmony_ci return err; 130262306a36Sopenharmony_ci if (! (chip->ac97->id & (0xf0000000))) { 130362306a36Sopenharmony_ci /* looks like an invalid id */ 130462306a36Sopenharmony_ci sprintf(chip->card->mixername, "%s AC97", chip->card->driver); 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci return 0; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci/* 131062306a36Sopenharmony_ci * See if the signature left by the NM256 BIOS is intact; if so, we use 131162306a36Sopenharmony_ci * the associated address as the end of our audio buffer in the video 131262306a36Sopenharmony_ci * RAM. 131362306a36Sopenharmony_ci */ 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic int 131662306a36Sopenharmony_cisnd_nm256_peek_for_sig(struct nm256 *chip) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci /* The signature is located 1K below the end of video RAM. */ 131962306a36Sopenharmony_ci void __iomem *temp; 132062306a36Sopenharmony_ci /* Default buffer end is 5120 bytes below the top of RAM. */ 132162306a36Sopenharmony_ci unsigned long pointer_found = chip->buffer_end - 0x1400; 132262306a36Sopenharmony_ci u32 sig; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci temp = ioremap(chip->buffer_addr + chip->buffer_end - 0x400, 16); 132562306a36Sopenharmony_ci if (temp == NULL) { 132662306a36Sopenharmony_ci dev_err(chip->card->dev, 132762306a36Sopenharmony_ci "Unable to scan for card signature in video RAM\n"); 132862306a36Sopenharmony_ci return -EBUSY; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci sig = readl(temp); 133262306a36Sopenharmony_ci if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { 133362306a36Sopenharmony_ci u32 pointer = readl(temp + 4); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* 133662306a36Sopenharmony_ci * If it's obviously invalid, don't use it 133762306a36Sopenharmony_ci */ 133862306a36Sopenharmony_ci if (pointer == 0xffffffff || 133962306a36Sopenharmony_ci pointer < chip->buffer_size || 134062306a36Sopenharmony_ci pointer > chip->buffer_end) { 134162306a36Sopenharmony_ci dev_err(chip->card->dev, 134262306a36Sopenharmony_ci "invalid signature found: 0x%x\n", pointer); 134362306a36Sopenharmony_ci iounmap(temp); 134462306a36Sopenharmony_ci return -ENODEV; 134562306a36Sopenharmony_ci } else { 134662306a36Sopenharmony_ci pointer_found = pointer; 134762306a36Sopenharmony_ci dev_info(chip->card->dev, 134862306a36Sopenharmony_ci "found card signature in video RAM: 0x%x\n", 134962306a36Sopenharmony_ci pointer); 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci } 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci iounmap(temp); 135462306a36Sopenharmony_ci chip->buffer_end = pointer_found; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci return 0; 135762306a36Sopenharmony_ci} 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 136062306a36Sopenharmony_ci/* 136162306a36Sopenharmony_ci * APM event handler, so the card is properly reinitialized after a power 136262306a36Sopenharmony_ci * event. 136362306a36Sopenharmony_ci */ 136462306a36Sopenharmony_cistatic int nm256_suspend(struct device *dev) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 136762306a36Sopenharmony_ci struct nm256 *chip = card->private_data; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); 137062306a36Sopenharmony_ci snd_ac97_suspend(chip->ac97); 137162306a36Sopenharmony_ci chip->coeffs_current = 0; 137262306a36Sopenharmony_ci return 0; 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic int nm256_resume(struct device *dev) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci struct snd_card *card = dev_get_drvdata(dev); 137862306a36Sopenharmony_ci struct nm256 *chip = card->private_data; 137962306a36Sopenharmony_ci int i; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci /* Perform a full reset on the hardware */ 138262306a36Sopenharmony_ci chip->in_resume = 1; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci snd_nm256_init_chip(chip); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci /* restore ac97 */ 138762306a36Sopenharmony_ci snd_ac97_resume(chip->ac97); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 139062306a36Sopenharmony_ci struct nm256_stream *s = &chip->streams[i]; 139162306a36Sopenharmony_ci if (s->substream && s->suspended) { 139262306a36Sopenharmony_ci spin_lock_irq(&chip->reg_lock); 139362306a36Sopenharmony_ci snd_nm256_set_format(chip, s, s->substream); 139462306a36Sopenharmony_ci spin_unlock_irq(&chip->reg_lock); 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci snd_power_change_state(card, SNDRV_CTL_POWER_D0); 139962306a36Sopenharmony_ci chip->in_resume = 0; 140062306a36Sopenharmony_ci return 0; 140162306a36Sopenharmony_ci} 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume); 140462306a36Sopenharmony_ci#define NM256_PM_OPS &nm256_pm 140562306a36Sopenharmony_ci#else 140662306a36Sopenharmony_ci#define NM256_PM_OPS NULL 140762306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic void snd_nm256_free(struct snd_card *card) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci struct nm256 *chip = card->private_data; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) 141462306a36Sopenharmony_ci snd_nm256_playback_stop(chip); 141562306a36Sopenharmony_ci if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) 141662306a36Sopenharmony_ci snd_nm256_capture_stop(chip); 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_cistatic int 142062306a36Sopenharmony_cisnd_nm256_create(struct snd_card *card, struct pci_dev *pci) 142162306a36Sopenharmony_ci{ 142262306a36Sopenharmony_ci struct nm256 *chip = card->private_data; 142362306a36Sopenharmony_ci int err, pval; 142462306a36Sopenharmony_ci u32 addr; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci err = pcim_enable_device(pci); 142762306a36Sopenharmony_ci if (err < 0) 142862306a36Sopenharmony_ci return err; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci chip->card = card; 143162306a36Sopenharmony_ci chip->pci = pci; 143262306a36Sopenharmony_ci chip->use_cache = use_cache; 143362306a36Sopenharmony_ci spin_lock_init(&chip->reg_lock); 143462306a36Sopenharmony_ci chip->irq = -1; 143562306a36Sopenharmony_ci mutex_init(&chip->irq_mutex); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci /* store buffer sizes in bytes */ 143862306a36Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = playback_bufsize * 1024; 143962306a36Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capture_bufsize * 1024; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* 144262306a36Sopenharmony_ci * The NM256 has two memory ports. The first port is nothing 144362306a36Sopenharmony_ci * more than a chunk of video RAM, which is used as the I/O ring 144462306a36Sopenharmony_ci * buffer. The second port has the actual juicy stuff (like the 144562306a36Sopenharmony_ci * mixer and the playback engine control registers). 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci chip->buffer_addr = pci_resource_start(pci, 0); 144962306a36Sopenharmony_ci chip->cport_addr = pci_resource_start(pci, 1); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci err = pci_request_regions(pci, card->driver); 145262306a36Sopenharmony_ci if (err < 0) 145362306a36Sopenharmony_ci return err; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci /* Init the memory port info. */ 145662306a36Sopenharmony_ci /* remap control port (#2) */ 145762306a36Sopenharmony_ci chip->cport = devm_ioremap(&pci->dev, chip->cport_addr, NM_PORT2_SIZE); 145862306a36Sopenharmony_ci if (!chip->cport) { 145962306a36Sopenharmony_ci dev_err(card->dev, "unable to map control port %lx\n", 146062306a36Sopenharmony_ci chip->cport_addr); 146162306a36Sopenharmony_ci return -ENOMEM; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci if (!strcmp(card->driver, "NM256AV")) { 146562306a36Sopenharmony_ci /* Ok, try to see if this is a non-AC97 version of the hardware. */ 146662306a36Sopenharmony_ci pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); 146762306a36Sopenharmony_ci if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { 146862306a36Sopenharmony_ci if (! force_ac97) { 146962306a36Sopenharmony_ci dev_err(card->dev, 147062306a36Sopenharmony_ci "no ac97 is found!\n"); 147162306a36Sopenharmony_ci dev_err(card->dev, 147262306a36Sopenharmony_ci "force the driver to load by passing in the module parameter\n"); 147362306a36Sopenharmony_ci dev_err(card->dev, 147462306a36Sopenharmony_ci " force_ac97=1\n"); 147562306a36Sopenharmony_ci dev_err(card->dev, 147662306a36Sopenharmony_ci "or try sb16, opl3sa2, or cs423x drivers instead.\n"); 147762306a36Sopenharmony_ci return -ENXIO; 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci chip->buffer_end = 2560 * 1024; 148162306a36Sopenharmony_ci chip->interrupt = snd_nm256_interrupt; 148262306a36Sopenharmony_ci chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET; 148362306a36Sopenharmony_ci chip->mixer_status_mask = NM_MIXER_READY_MASK; 148462306a36Sopenharmony_ci } else { 148562306a36Sopenharmony_ci /* Not sure if there is any relevant detect for the ZX or not. */ 148662306a36Sopenharmony_ci if (snd_nm256_readb(chip, 0xa0b) != 0) 148762306a36Sopenharmony_ci chip->buffer_end = 6144 * 1024; 148862306a36Sopenharmony_ci else 148962306a36Sopenharmony_ci chip->buffer_end = 4096 * 1024; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci chip->interrupt = snd_nm256_interrupt_zx; 149262306a36Sopenharmony_ci chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; 149362306a36Sopenharmony_ci chip->mixer_status_mask = NM2_MIXER_READY_MASK; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + 149762306a36Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; 149862306a36Sopenharmony_ci if (chip->use_cache) 149962306a36Sopenharmony_ci chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4; 150062306a36Sopenharmony_ci else 150162306a36Sopenharmony_ci chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (buffer_top >= chip->buffer_size && buffer_top < chip->buffer_end) 150462306a36Sopenharmony_ci chip->buffer_end = buffer_top; 150562306a36Sopenharmony_ci else { 150662306a36Sopenharmony_ci /* get buffer end pointer from signature */ 150762306a36Sopenharmony_ci err = snd_nm256_peek_for_sig(chip); 150862306a36Sopenharmony_ci if (err < 0) 150962306a36Sopenharmony_ci return err; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci chip->buffer_start = chip->buffer_end - chip->buffer_size; 151362306a36Sopenharmony_ci chip->buffer_addr += chip->buffer_start; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci dev_info(card->dev, "Mapping port 1 from 0x%x - 0x%x\n", 151662306a36Sopenharmony_ci chip->buffer_start, chip->buffer_end); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci chip->buffer = devm_ioremap(&pci->dev, chip->buffer_addr, 151962306a36Sopenharmony_ci chip->buffer_size); 152062306a36Sopenharmony_ci if (!chip->buffer) { 152162306a36Sopenharmony_ci dev_err(card->dev, "unable to map ring buffer at %lx\n", 152262306a36Sopenharmony_ci chip->buffer_addr); 152362306a36Sopenharmony_ci return -ENOMEM; 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci /* set offsets */ 152762306a36Sopenharmony_ci addr = chip->buffer_start; 152862306a36Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr; 152962306a36Sopenharmony_ci addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize; 153062306a36Sopenharmony_ci chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr; 153162306a36Sopenharmony_ci addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; 153262306a36Sopenharmony_ci if (chip->use_cache) { 153362306a36Sopenharmony_ci chip->all_coeff_buf = addr; 153462306a36Sopenharmony_ci } else { 153562306a36Sopenharmony_ci chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr; 153662306a36Sopenharmony_ci addr += NM_MAX_PLAYBACK_COEF_SIZE; 153762306a36Sopenharmony_ci chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci /* Fixed setting. */ 154162306a36Sopenharmony_ci chip->mixer_base = NM_MIXER_OFFSET; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci chip->coeffs_current = 0; 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci snd_nm256_init_chip(chip); 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci // pci_set_master(pci); /* needed? */ 154862306a36Sopenharmony_ci return 0; 154962306a36Sopenharmony_ci} 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_cienum { NM_IGNORED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 }; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic const struct snd_pci_quirk nm256_quirks[] = { 155562306a36Sopenharmony_ci /* HP omnibook 4150 has cs4232 codec internally */ 155662306a36Sopenharmony_ci SND_PCI_QUIRK(0x103c, 0x0007, "HP omnibook 4150", NM_IGNORED), 155762306a36Sopenharmony_ci /* Reset workarounds to avoid lock-ups */ 155862306a36Sopenharmony_ci SND_PCI_QUIRK(0x104d, 0x8041, "Sony PCG-F305", NM_RESET_WORKAROUND), 155962306a36Sopenharmony_ci SND_PCI_QUIRK(0x1028, 0x0080, "Dell Latitude LS", NM_RESET_WORKAROUND), 156062306a36Sopenharmony_ci SND_PCI_QUIRK(0x1028, 0x0091, "Dell Latitude CSx", NM_RESET_WORKAROUND_2), 156162306a36Sopenharmony_ci { } /* terminator */ 156262306a36Sopenharmony_ci}; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_cistatic int snd_nm256_probe(struct pci_dev *pci, 156662306a36Sopenharmony_ci const struct pci_device_id *pci_id) 156762306a36Sopenharmony_ci{ 156862306a36Sopenharmony_ci struct snd_card *card; 156962306a36Sopenharmony_ci struct nm256 *chip; 157062306a36Sopenharmony_ci int err; 157162306a36Sopenharmony_ci const struct snd_pci_quirk *q; 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci q = snd_pci_quirk_lookup(pci, nm256_quirks); 157462306a36Sopenharmony_ci if (q) { 157562306a36Sopenharmony_ci dev_dbg(&pci->dev, "Enabled quirk for %s.\n", 157662306a36Sopenharmony_ci snd_pci_quirk_name(q)); 157762306a36Sopenharmony_ci switch (q->value) { 157862306a36Sopenharmony_ci case NM_IGNORED: 157962306a36Sopenharmony_ci dev_info(&pci->dev, 158062306a36Sopenharmony_ci "The device is on the denylist. Loading stopped\n"); 158162306a36Sopenharmony_ci return -ENODEV; 158262306a36Sopenharmony_ci case NM_RESET_WORKAROUND_2: 158362306a36Sopenharmony_ci reset_workaround_2 = 1; 158462306a36Sopenharmony_ci fallthrough; 158562306a36Sopenharmony_ci case NM_RESET_WORKAROUND: 158662306a36Sopenharmony_ci reset_workaround = 1; 158762306a36Sopenharmony_ci break; 158862306a36Sopenharmony_ci } 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci err = snd_devm_card_new(&pci->dev, index, id, THIS_MODULE, 159262306a36Sopenharmony_ci sizeof(*chip), &card); 159362306a36Sopenharmony_ci if (err < 0) 159462306a36Sopenharmony_ci return err; 159562306a36Sopenharmony_ci chip = card->private_data; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci switch (pci->device) { 159862306a36Sopenharmony_ci case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: 159962306a36Sopenharmony_ci strcpy(card->driver, "NM256AV"); 160062306a36Sopenharmony_ci break; 160162306a36Sopenharmony_ci case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: 160262306a36Sopenharmony_ci strcpy(card->driver, "NM256ZX"); 160362306a36Sopenharmony_ci break; 160462306a36Sopenharmony_ci case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO: 160562306a36Sopenharmony_ci strcpy(card->driver, "NM256XL+"); 160662306a36Sopenharmony_ci break; 160762306a36Sopenharmony_ci default: 160862306a36Sopenharmony_ci dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device); 160962306a36Sopenharmony_ci return -EINVAL; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (vaio_hack) 161362306a36Sopenharmony_ci buffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */ 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci if (playback_bufsize < 4) 161662306a36Sopenharmony_ci playback_bufsize = 4; 161762306a36Sopenharmony_ci if (playback_bufsize > 128) 161862306a36Sopenharmony_ci playback_bufsize = 128; 161962306a36Sopenharmony_ci if (capture_bufsize < 4) 162062306a36Sopenharmony_ci capture_bufsize = 4; 162162306a36Sopenharmony_ci if (capture_bufsize > 128) 162262306a36Sopenharmony_ci capture_bufsize = 128; 162362306a36Sopenharmony_ci err = snd_nm256_create(card, pci); 162462306a36Sopenharmony_ci if (err < 0) 162562306a36Sopenharmony_ci return err; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci if (reset_workaround) { 162862306a36Sopenharmony_ci dev_dbg(&pci->dev, "reset_workaround activated\n"); 162962306a36Sopenharmony_ci chip->reset_workaround = 1; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci if (reset_workaround_2) { 163362306a36Sopenharmony_ci dev_dbg(&pci->dev, "reset_workaround_2 activated\n"); 163462306a36Sopenharmony_ci chip->reset_workaround_2 = 1; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci err = snd_nm256_pcm(chip, 0); 163862306a36Sopenharmony_ci if (err < 0) 163962306a36Sopenharmony_ci return err; 164062306a36Sopenharmony_ci err = snd_nm256_mixer(chip); 164162306a36Sopenharmony_ci if (err < 0) 164262306a36Sopenharmony_ci return err; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci sprintf(card->shortname, "NeoMagic %s", card->driver); 164562306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d", 164662306a36Sopenharmony_ci card->shortname, 164762306a36Sopenharmony_ci chip->buffer_addr, chip->cport_addr, chip->irq); 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci err = snd_card_register(card); 165062306a36Sopenharmony_ci if (err < 0) 165162306a36Sopenharmony_ci return err; 165262306a36Sopenharmony_ci card->private_free = snd_nm256_free; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci pci_set_drvdata(pci, card); 165562306a36Sopenharmony_ci return 0; 165662306a36Sopenharmony_ci} 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_cistatic struct pci_driver nm256_driver = { 165962306a36Sopenharmony_ci .name = KBUILD_MODNAME, 166062306a36Sopenharmony_ci .id_table = snd_nm256_ids, 166162306a36Sopenharmony_ci .probe = snd_nm256_probe, 166262306a36Sopenharmony_ci .driver = { 166362306a36Sopenharmony_ci .pm = NM256_PM_OPS, 166462306a36Sopenharmony_ci }, 166562306a36Sopenharmony_ci}; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_cimodule_pci_driver(nm256_driver); 1668