11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Driver for NeoMagic 256AV and 256ZX chipsets. 31da177e4SLinus Torvalds * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Based on nm256_audio.c OSS driver in linux kernel. 61da177e4SLinus Torvalds * The original author of OSS nm256 driver wishes to remain anonymous, 71da177e4SLinus Torvalds * so I just put my acknoledgment to him/her here. 81da177e4SLinus Torvalds * The original author's web page is found at 91da177e4SLinus Torvalds * http://www.uglx.org/sony.html 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * 121da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 131da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 141da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 151da177e4SLinus Torvalds * (at your option) any later version. 161da177e4SLinus Torvalds * 171da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 181da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 191da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 201da177e4SLinus Torvalds * GNU General Public License for more details. 211da177e4SLinus Torvalds * 221da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 231da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 241da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 251da177e4SLinus Torvalds */ 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds #include <sound/driver.h> 281da177e4SLinus Torvalds #include <asm/io.h> 291da177e4SLinus Torvalds #include <linux/delay.h> 301da177e4SLinus Torvalds #include <linux/interrupt.h> 311da177e4SLinus Torvalds #include <linux/init.h> 321da177e4SLinus Torvalds #include <linux/pci.h> 331da177e4SLinus Torvalds #include <linux/slab.h> 341da177e4SLinus Torvalds #include <linux/moduleparam.h> 351da177e4SLinus Torvalds #include <sound/core.h> 361da177e4SLinus Torvalds #include <sound/info.h> 371da177e4SLinus Torvalds #include <sound/control.h> 381da177e4SLinus Torvalds #include <sound/pcm.h> 391da177e4SLinus Torvalds #include <sound/ac97_codec.h> 401da177e4SLinus Torvalds #include <sound/initval.h> 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds #define CARD_NAME "NeoMagic 256AV/ZX" 431da177e4SLinus Torvalds #define DRIVER_NAME "NM256" 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 461da177e4SLinus Torvalds MODULE_DESCRIPTION("NeoMagic NM256AV/ZX"); 471da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 481da177e4SLinus Torvalds MODULE_SUPPORTED_DEVICE("{{NeoMagic,NM256AV}," 491da177e4SLinus Torvalds "{NeoMagic,NM256ZX}}"); 501da177e4SLinus Torvalds 511da177e4SLinus Torvalds /* 521da177e4SLinus Torvalds * some compile conditions. 531da177e4SLinus Torvalds */ 541da177e4SLinus Torvalds 558a3fb4d0STakashi Iwai static int index = SNDRV_DEFAULT_IDX1; /* Index */ 568a3fb4d0STakashi Iwai static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ 578a3fb4d0STakashi Iwai static int playback_bufsize = 16; 588a3fb4d0STakashi Iwai static int capture_bufsize = 16; 598a3fb4d0STakashi Iwai static int force_ac97; /* disabled as default */ 608a3fb4d0STakashi Iwai static int buffer_top; /* not specified */ 618a3fb4d0STakashi Iwai static int use_cache; /* disabled */ 628a3fb4d0STakashi Iwai static int vaio_hack; /* disabled */ 638a3fb4d0STakashi Iwai static int reset_workaround; 648a3fb4d0STakashi Iwai static int reset_workaround_2; 651da177e4SLinus Torvalds 668a3fb4d0STakashi Iwai module_param(index, int, 0444); 671da177e4SLinus Torvalds MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); 688a3fb4d0STakashi Iwai module_param(id, charp, 0444); 691da177e4SLinus Torvalds MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); 708a3fb4d0STakashi Iwai module_param(playback_bufsize, int, 0444); 711da177e4SLinus Torvalds MODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard."); 728a3fb4d0STakashi Iwai module_param(capture_bufsize, int, 0444); 731da177e4SLinus Torvalds MODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard."); 748a3fb4d0STakashi Iwai module_param(force_ac97, bool, 0444); 751da177e4SLinus Torvalds MODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard."); 768a3fb4d0STakashi Iwai module_param(buffer_top, int, 0444); 771da177e4SLinus Torvalds MODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard."); 788a3fb4d0STakashi Iwai module_param(use_cache, bool, 0444); 791da177e4SLinus Torvalds MODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access."); 808a3fb4d0STakashi Iwai module_param(vaio_hack, bool, 0444); 811da177e4SLinus Torvalds MODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks."); 828a3fb4d0STakashi Iwai module_param(reset_workaround, bool, 0444); 831da177e4SLinus Torvalds MODULE_PARM_DESC(reset_workaround, "Enable AC97 RESET workaround for some laptops."); 848a3fb4d0STakashi Iwai module_param(reset_workaround_2, bool, 0444); 8547530cf4SJohn W. Linville MODULE_PARM_DESC(reset_workaround_2, "Enable extended AC97 RESET workaround for some other laptops."); 861da177e4SLinus Torvalds 878a3fb4d0STakashi Iwai /* just for backward compatibility */ 888a3fb4d0STakashi Iwai static int enable; 898a3fb4d0STakashi Iwai module_param(enable, bool, 0444); 908a3fb4d0STakashi Iwai 918a3fb4d0STakashi Iwai 928a3fb4d0STakashi Iwai 931da177e4SLinus Torvalds /* 941da177e4SLinus Torvalds * hw definitions 951da177e4SLinus Torvalds */ 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds /* The BIOS signature. */ 981da177e4SLinus Torvalds #define NM_SIGNATURE 0x4e4d0000 991da177e4SLinus Torvalds /* Signature mask. */ 1001da177e4SLinus Torvalds #define NM_SIG_MASK 0xffff0000 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds /* Size of the second memory area. */ 1031da177e4SLinus Torvalds #define NM_PORT2_SIZE 4096 1041da177e4SLinus Torvalds 1051da177e4SLinus Torvalds /* The base offset of the mixer in the second memory area. */ 1061da177e4SLinus Torvalds #define NM_MIXER_OFFSET 0x600 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds /* The maximum size of a coefficient entry. */ 1091da177e4SLinus Torvalds #define NM_MAX_PLAYBACK_COEF_SIZE 0x5000 1101da177e4SLinus Torvalds #define NM_MAX_RECORD_COEF_SIZE 0x1260 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds /* The interrupt register. */ 1131da177e4SLinus Torvalds #define NM_INT_REG 0xa04 1141da177e4SLinus Torvalds /* And its bits. */ 1151da177e4SLinus Torvalds #define NM_PLAYBACK_INT 0x40 1161da177e4SLinus Torvalds #define NM_RECORD_INT 0x100 1171da177e4SLinus Torvalds #define NM_MISC_INT_1 0x4000 1181da177e4SLinus Torvalds #define NM_MISC_INT_2 0x1 1191da177e4SLinus Torvalds #define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1) 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds /* The AV's "mixer ready" status bit and location. */ 1221da177e4SLinus Torvalds #define NM_MIXER_STATUS_OFFSET 0xa04 1231da177e4SLinus Torvalds #define NM_MIXER_READY_MASK 0x0800 1241da177e4SLinus Torvalds #define NM_MIXER_PRESENCE 0xa06 1251da177e4SLinus Torvalds #define NM_PRESENCE_MASK 0x0050 1261da177e4SLinus Torvalds #define NM_PRESENCE_VALUE 0x0040 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds /* 1291da177e4SLinus Torvalds * For the ZX. It uses the same interrupt register, but it holds 32 1301da177e4SLinus Torvalds * bits instead of 16. 1311da177e4SLinus Torvalds */ 1321da177e4SLinus Torvalds #define NM2_PLAYBACK_INT 0x10000 1331da177e4SLinus Torvalds #define NM2_RECORD_INT 0x80000 1341da177e4SLinus Torvalds #define NM2_MISC_INT_1 0x8 1351da177e4SLinus Torvalds #define NM2_MISC_INT_2 0x2 1361da177e4SLinus Torvalds #define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X)) 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds /* The ZX's "mixer ready" status bit and location. */ 1391da177e4SLinus Torvalds #define NM2_MIXER_STATUS_OFFSET 0xa06 1401da177e4SLinus Torvalds #define NM2_MIXER_READY_MASK 0x0800 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds /* The playback registers start from here. */ 1431da177e4SLinus Torvalds #define NM_PLAYBACK_REG_OFFSET 0x0 1441da177e4SLinus Torvalds /* The record registers start from here. */ 1451da177e4SLinus Torvalds #define NM_RECORD_REG_OFFSET 0x200 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds /* The rate register is located 2 bytes from the start of the register area. */ 1481da177e4SLinus Torvalds #define NM_RATE_REG_OFFSET 2 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds /* Mono/stereo flag, number of bits on playback, and rate mask. */ 1511da177e4SLinus Torvalds #define NM_RATE_STEREO 1 1521da177e4SLinus Torvalds #define NM_RATE_BITS_16 2 1531da177e4SLinus Torvalds #define NM_RATE_MASK 0xf0 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds /* Playback enable register. */ 1561da177e4SLinus Torvalds #define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1) 1571da177e4SLinus Torvalds #define NM_PLAYBACK_ENABLE_FLAG 1 1581da177e4SLinus Torvalds #define NM_PLAYBACK_ONESHOT 2 1591da177e4SLinus Torvalds #define NM_PLAYBACK_FREERUN 4 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds /* Mutes the audio output. */ 1621da177e4SLinus Torvalds #define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18) 1631da177e4SLinus Torvalds #define NM_AUDIO_MUTE_LEFT 0x8000 1641da177e4SLinus Torvalds #define NM_AUDIO_MUTE_RIGHT 0x0080 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds /* Recording enable register. */ 1671da177e4SLinus Torvalds #define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0) 1681da177e4SLinus Torvalds #define NM_RECORD_ENABLE_FLAG 1 1691da177e4SLinus Torvalds #define NM_RECORD_FREERUN 2 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* coefficient buffer pointer */ 1721da177e4SLinus Torvalds #define NM_COEFF_START_OFFSET 0x1c 1731da177e4SLinus Torvalds #define NM_COEFF_END_OFFSET 0x20 1741da177e4SLinus Torvalds 1751da177e4SLinus Torvalds /* DMA buffer offsets */ 1761da177e4SLinus Torvalds #define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4) 1771da177e4SLinus Torvalds #define NM_RBUFFER_END (NM_RECORD_REG_OFFSET + 0x10) 1781da177e4SLinus Torvalds #define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc) 1791da177e4SLinus Torvalds #define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8) 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds #define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4) 1821da177e4SLinus Torvalds #define NM_PBUFFER_END (NM_PLAYBACK_REG_OFFSET + 0x14) 1831da177e4SLinus Torvalds #define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc) 1841da177e4SLinus Torvalds #define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8) 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds /* 1871da177e4SLinus Torvalds * type definitions 1881da177e4SLinus Torvalds */ 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds typedef struct snd_nm256 nm256_t; 1911da177e4SLinus Torvalds typedef struct snd_nm256_stream nm256_stream_t; 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds struct snd_nm256_stream { 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds nm256_t *chip; 1961da177e4SLinus Torvalds snd_pcm_substream_t *substream; 1971da177e4SLinus Torvalds int running; 1981204de32STakashi Iwai int suspended; 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds u32 buf; /* offset from chip->buffer */ 2011da177e4SLinus Torvalds int bufsize; /* buffer size in bytes */ 2021da177e4SLinus Torvalds void __iomem *bufptr; /* mapped pointer */ 2031da177e4SLinus Torvalds unsigned long bufptr_addr; /* physical address of the mapped pointer */ 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds int dma_size; /* buffer size of the substream in bytes */ 2061da177e4SLinus Torvalds int period_size; /* period size in bytes */ 2071da177e4SLinus Torvalds int periods; /* # of periods */ 2081da177e4SLinus Torvalds int shift; /* bit shifts */ 2091da177e4SLinus Torvalds int cur_period; /* current period # */ 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds }; 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds struct snd_nm256 { 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds snd_card_t *card; 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds void __iomem *cport; /* control port */ 2181da177e4SLinus Torvalds struct resource *res_cport; /* its resource */ 2191da177e4SLinus Torvalds unsigned long cport_addr; /* physical address */ 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds void __iomem *buffer; /* buffer */ 2221da177e4SLinus Torvalds struct resource *res_buffer; /* its resource */ 2231da177e4SLinus Torvalds unsigned long buffer_addr; /* buffer phyiscal address */ 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds u32 buffer_start; /* start offset from pci resource 0 */ 2261da177e4SLinus Torvalds u32 buffer_end; /* end offset */ 2271da177e4SLinus Torvalds u32 buffer_size; /* total buffer size */ 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds u32 all_coeff_buf; /* coefficient buffer */ 2301da177e4SLinus Torvalds u32 coeff_buf[2]; /* coefficient buffer for each stream */ 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds unsigned int coeffs_current: 1; /* coeff. table is loaded? */ 2331da177e4SLinus Torvalds unsigned int use_cache: 1; /* use one big coef. table */ 2341da177e4SLinus Torvalds unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */ 23547530cf4SJohn W. Linville unsigned int reset_workaround_2: 1; /* Extended workaround for some other laptops to avoid freeze */ 2361da177e4SLinus Torvalds 2371da177e4SLinus Torvalds int mixer_base; /* register offset of ac97 mixer */ 2381da177e4SLinus Torvalds int mixer_status_offset; /* offset of mixer status reg. */ 2391da177e4SLinus Torvalds int mixer_status_mask; /* bit mask to test the mixer status */ 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds int irq; 2421204de32STakashi Iwai int irq_acks; 2431da177e4SLinus Torvalds irqreturn_t (*interrupt)(int, void *, struct pt_regs *); 2441da177e4SLinus Torvalds int badintrcount; /* counter to check bogus interrupts */ 2451204de32STakashi Iwai struct semaphore irq_mutex; 2461da177e4SLinus Torvalds 2471da177e4SLinus Torvalds nm256_stream_t streams[2]; 2481da177e4SLinus Torvalds 2491da177e4SLinus Torvalds ac97_t *ac97; 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds snd_pcm_t *pcm; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds struct pci_dev *pci; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds spinlock_t reg_lock; 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds }; 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds /* 2611da177e4SLinus Torvalds * include coefficient table 2621da177e4SLinus Torvalds */ 2631da177e4SLinus Torvalds #include "nm256_coef.c" 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds /* 2671da177e4SLinus Torvalds * PCI ids 2681da177e4SLinus Torvalds */ 2691da177e4SLinus Torvalds static struct pci_device_id snd_nm256_ids[] = { 2701da177e4SLinus Torvalds {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 2711da177e4SLinus Torvalds {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 2721da177e4SLinus Torvalds {PCI_VENDOR_ID_NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 2731da177e4SLinus Torvalds {0,}, 2741da177e4SLinus Torvalds }; 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds MODULE_DEVICE_TABLE(pci, snd_nm256_ids); 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds /* 2801da177e4SLinus Torvalds * lowlvel stuffs 2811da177e4SLinus Torvalds */ 2821da177e4SLinus Torvalds 28377933d72SJesper Juhl static inline u8 2841da177e4SLinus Torvalds snd_nm256_readb(nm256_t *chip, int offset) 2851da177e4SLinus Torvalds { 2861da177e4SLinus Torvalds return readb(chip->cport + offset); 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 28977933d72SJesper Juhl static inline u16 2901da177e4SLinus Torvalds snd_nm256_readw(nm256_t *chip, int offset) 2911da177e4SLinus Torvalds { 2921da177e4SLinus Torvalds return readw(chip->cport + offset); 2931da177e4SLinus Torvalds } 2941da177e4SLinus Torvalds 29577933d72SJesper Juhl static inline u32 2961da177e4SLinus Torvalds snd_nm256_readl(nm256_t *chip, int offset) 2971da177e4SLinus Torvalds { 2981da177e4SLinus Torvalds return readl(chip->cport + offset); 2991da177e4SLinus Torvalds } 3001da177e4SLinus Torvalds 30177933d72SJesper Juhl static inline void 3021da177e4SLinus Torvalds snd_nm256_writeb(nm256_t *chip, int offset, u8 val) 3031da177e4SLinus Torvalds { 3041da177e4SLinus Torvalds writeb(val, chip->cport + offset); 3051da177e4SLinus Torvalds } 3061da177e4SLinus Torvalds 30777933d72SJesper Juhl static inline void 3081da177e4SLinus Torvalds snd_nm256_writew(nm256_t *chip, int offset, u16 val) 3091da177e4SLinus Torvalds { 3101da177e4SLinus Torvalds writew(val, chip->cport + offset); 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds 31377933d72SJesper Juhl static inline void 3141da177e4SLinus Torvalds snd_nm256_writel(nm256_t *chip, int offset, u32 val) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds writel(val, chip->cport + offset); 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds 31977933d72SJesper Juhl static inline void 3201da177e4SLinus Torvalds snd_nm256_write_buffer(nm256_t *chip, void *src, int offset, int size) 3211da177e4SLinus Torvalds { 3221da177e4SLinus Torvalds offset -= chip->buffer_start; 32399b359baSTakashi Iwai #ifdef CONFIG_SND_DEBUG 3241da177e4SLinus Torvalds if (offset < 0 || offset >= chip->buffer_size) { 32599b359baSTakashi Iwai snd_printk(KERN_ERR "write_buffer invalid offset = %d size = %d\n", offset, size); 3261da177e4SLinus Torvalds return; 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds #endif 3291da177e4SLinus Torvalds memcpy_toio(chip->buffer + offset, src, size); 3301da177e4SLinus Torvalds } 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds /* 3331da177e4SLinus Torvalds * coefficient handlers -- what a magic! 3341da177e4SLinus Torvalds */ 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds static u16 3371da177e4SLinus Torvalds snd_nm256_get_start_offset(int which) 3381da177e4SLinus Torvalds { 3391da177e4SLinus Torvalds u16 offset = 0; 3401da177e4SLinus Torvalds while (which-- > 0) 3411da177e4SLinus Torvalds offset += coefficient_sizes[which]; 3421da177e4SLinus Torvalds return offset; 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds static void 3461da177e4SLinus Torvalds snd_nm256_load_one_coefficient(nm256_t *chip, int stream, u32 port, int which) 3471da177e4SLinus Torvalds { 3481da177e4SLinus Torvalds u32 coeff_buf = chip->coeff_buf[stream]; 3491da177e4SLinus Torvalds u16 offset = snd_nm256_get_start_offset(which); 3501da177e4SLinus Torvalds u16 size = coefficient_sizes[which]; 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size); 3531da177e4SLinus Torvalds snd_nm256_writel(chip, port, coeff_buf); 3541da177e4SLinus Torvalds /* ??? Record seems to behave differently than playback. */ 3551da177e4SLinus Torvalds if (stream == SNDRV_PCM_STREAM_PLAYBACK) 3561da177e4SLinus Torvalds size--; 3571da177e4SLinus Torvalds snd_nm256_writel(chip, port + 4, coeff_buf + size); 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds 3601da177e4SLinus Torvalds static void 3611da177e4SLinus Torvalds snd_nm256_load_coefficient(nm256_t *chip, int stream, int number) 3621da177e4SLinus Torvalds { 3631da177e4SLinus Torvalds /* The enable register for the specified engine. */ 3641da177e4SLinus Torvalds u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG); 3651da177e4SLinus Torvalds u32 addr = NM_COEFF_START_OFFSET; 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds addr += (stream == SNDRV_PCM_STREAM_CAPTURE ? NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET); 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds if (snd_nm256_readb(chip, poffset) & 1) { 3701da177e4SLinus Torvalds snd_printd("NM256: Engine was enabled while loading coefficients!\n"); 3711da177e4SLinus Torvalds return; 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds /* The recording engine uses coefficient values 8-15. */ 3751da177e4SLinus Torvalds number &= 7; 3761da177e4SLinus Torvalds if (stream == SNDRV_PCM_STREAM_CAPTURE) 3771da177e4SLinus Torvalds number += 8; 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds if (! chip->use_cache) { 3801da177e4SLinus Torvalds snd_nm256_load_one_coefficient(chip, stream, addr, number); 3811da177e4SLinus Torvalds return; 3821da177e4SLinus Torvalds } 3831da177e4SLinus Torvalds if (! chip->coeffs_current) { 3841da177e4SLinus Torvalds snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf, 3851da177e4SLinus Torvalds NM_TOTAL_COEFF_COUNT * 4); 3861da177e4SLinus Torvalds chip->coeffs_current = 1; 3871da177e4SLinus Torvalds } else { 3881da177e4SLinus Torvalds u32 base = chip->all_coeff_buf; 3891da177e4SLinus Torvalds u32 offset = snd_nm256_get_start_offset(number); 3901da177e4SLinus Torvalds u32 end_offset = offset + coefficient_sizes[number]; 3911da177e4SLinus Torvalds snd_nm256_writel(chip, addr, base + offset); 3921da177e4SLinus Torvalds if (stream == SNDRV_PCM_STREAM_PLAYBACK) 3931da177e4SLinus Torvalds end_offset--; 3941da177e4SLinus Torvalds snd_nm256_writel(chip, addr + 4, base + end_offset); 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds /* The actual rates supported by the card. */ 4001da177e4SLinus Torvalds static unsigned int samplerates[8] = { 4011da177e4SLinus Torvalds 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 4021da177e4SLinus Torvalds }; 4031da177e4SLinus Torvalds static snd_pcm_hw_constraint_list_t constraints_rates = { 4041da177e4SLinus Torvalds .count = ARRAY_SIZE(samplerates), 4051da177e4SLinus Torvalds .list = samplerates, 4061da177e4SLinus Torvalds .mask = 0, 4071da177e4SLinus Torvalds }; 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds /* 4101da177e4SLinus Torvalds * return the index of the target rate 4111da177e4SLinus Torvalds */ 4121da177e4SLinus Torvalds static int 4131da177e4SLinus Torvalds snd_nm256_fixed_rate(unsigned int rate) 4141da177e4SLinus Torvalds { 4151da177e4SLinus Torvalds unsigned int i; 4161da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(samplerates); i++) { 4171da177e4SLinus Torvalds if (rate == samplerates[i]) 4181da177e4SLinus Torvalds return i; 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds snd_BUG(); 4211da177e4SLinus Torvalds return 0; 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds 4241da177e4SLinus Torvalds /* 4251da177e4SLinus Torvalds * set sample rate and format 4261da177e4SLinus Torvalds */ 4271da177e4SLinus Torvalds static void 4281da177e4SLinus Torvalds snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) 4291da177e4SLinus Torvalds { 4301da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 4311da177e4SLinus Torvalds int rate_index = snd_nm256_fixed_rate(runtime->rate); 4321da177e4SLinus Torvalds unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK; 4331da177e4SLinus Torvalds 4341da177e4SLinus Torvalds s->shift = 0; 4351da177e4SLinus Torvalds if (snd_pcm_format_width(runtime->format) == 16) { 4361da177e4SLinus Torvalds ratebits |= NM_RATE_BITS_16; 4371da177e4SLinus Torvalds s->shift++; 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds if (runtime->channels > 1) { 4401da177e4SLinus Torvalds ratebits |= NM_RATE_STEREO; 4411da177e4SLinus Torvalds s->shift++; 4421da177e4SLinus Torvalds } 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds runtime->rate = samplerates[rate_index]; 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds switch (substream->stream) { 4471da177e4SLinus Torvalds case SNDRV_PCM_STREAM_PLAYBACK: 4481da177e4SLinus Torvalds snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */ 4491da177e4SLinus Torvalds snd_nm256_writeb(chip, 4501da177e4SLinus Torvalds NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET, 4511da177e4SLinus Torvalds ratebits); 4521da177e4SLinus Torvalds break; 4531da177e4SLinus Torvalds case SNDRV_PCM_STREAM_CAPTURE: 4541da177e4SLinus Torvalds snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */ 4551da177e4SLinus Torvalds snd_nm256_writeb(chip, 4561da177e4SLinus Torvalds NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET, 4571da177e4SLinus Torvalds ratebits); 4581da177e4SLinus Torvalds break; 4591da177e4SLinus Torvalds } 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds 4621204de32STakashi Iwai /* acquire interrupt */ 4631204de32STakashi Iwai static int snd_nm256_acquire_irq(nm256_t *chip) 4641204de32STakashi Iwai { 4651204de32STakashi Iwai down(&chip->irq_mutex); 4661204de32STakashi Iwai if (chip->irq < 0) { 4671204de32STakashi Iwai if (request_irq(chip->pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, 4681204de32STakashi Iwai chip->card->driver, (void*)chip)) { 46999b359baSTakashi Iwai snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->pci->irq); 4701204de32STakashi Iwai up(&chip->irq_mutex); 4711204de32STakashi Iwai return -EBUSY; 4721204de32STakashi Iwai } 4731204de32STakashi Iwai chip->irq = chip->pci->irq; 4741204de32STakashi Iwai } 4751204de32STakashi Iwai chip->irq_acks++; 4761204de32STakashi Iwai up(&chip->irq_mutex); 4771204de32STakashi Iwai return 0; 4781204de32STakashi Iwai } 4791204de32STakashi Iwai 4801204de32STakashi Iwai /* release interrupt */ 4811204de32STakashi Iwai static void snd_nm256_release_irq(nm256_t *chip) 4821204de32STakashi Iwai { 4831204de32STakashi Iwai down(&chip->irq_mutex); 4841204de32STakashi Iwai if (chip->irq_acks > 0) 4851204de32STakashi Iwai chip->irq_acks--; 4861204de32STakashi Iwai if (chip->irq_acks == 0 && chip->irq >= 0) { 4871204de32STakashi Iwai free_irq(chip->irq, (void*)chip); 4881204de32STakashi Iwai chip->irq = -1; 4891204de32STakashi Iwai } 4901204de32STakashi Iwai up(&chip->irq_mutex); 4911204de32STakashi Iwai } 4921204de32STakashi Iwai 4931da177e4SLinus Torvalds /* 4941da177e4SLinus Torvalds * start / stop 4951da177e4SLinus Torvalds */ 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds /* update the watermark (current period) */ 4981da177e4SLinus Torvalds static void snd_nm256_pcm_mark(nm256_t *chip, nm256_stream_t *s, int reg) 4991da177e4SLinus Torvalds { 5001da177e4SLinus Torvalds s->cur_period++; 5011da177e4SLinus Torvalds s->cur_period %= s->periods; 5021da177e4SLinus Torvalds snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size); 5031da177e4SLinus Torvalds } 5041da177e4SLinus Torvalds 5051da177e4SLinus Torvalds #define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK) 5061da177e4SLinus Torvalds #define snd_nm256_capture_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK) 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds static void 5091da177e4SLinus Torvalds snd_nm256_playback_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) 5101da177e4SLinus Torvalds { 5111da177e4SLinus Torvalds /* program buffer pointers */ 5121da177e4SLinus Torvalds snd_nm256_writel(chip, NM_PBUFFER_START, s->buf); 5131da177e4SLinus Torvalds snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift)); 5141da177e4SLinus Torvalds snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf); 5151da177e4SLinus Torvalds snd_nm256_playback_mark(chip, s); 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds /* Enable playback engine and interrupts. */ 5181da177e4SLinus Torvalds snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 5191da177e4SLinus Torvalds NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN); 5201da177e4SLinus Torvalds /* Enable both channels. */ 5211da177e4SLinus Torvalds snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0); 5221da177e4SLinus Torvalds } 5231da177e4SLinus Torvalds 5241da177e4SLinus Torvalds static void 5251da177e4SLinus Torvalds snd_nm256_capture_start(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *substream) 5261da177e4SLinus Torvalds { 5271da177e4SLinus Torvalds /* program buffer pointers */ 5281da177e4SLinus Torvalds snd_nm256_writel(chip, NM_RBUFFER_START, s->buf); 5291da177e4SLinus Torvalds snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size); 5301da177e4SLinus Torvalds snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf); 5311da177e4SLinus Torvalds snd_nm256_capture_mark(chip, s); 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds /* Enable playback engine and interrupts. */ 5341da177e4SLinus Torvalds snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 5351da177e4SLinus Torvalds NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN); 5361da177e4SLinus Torvalds } 5371da177e4SLinus Torvalds 5381da177e4SLinus Torvalds /* Stop the play engine. */ 5391da177e4SLinus Torvalds static void 5401da177e4SLinus Torvalds snd_nm256_playback_stop(nm256_t *chip) 5411da177e4SLinus Torvalds { 5421da177e4SLinus Torvalds /* Shut off sound from both channels. */ 5431da177e4SLinus Torvalds snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 5441da177e4SLinus Torvalds NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT); 5451da177e4SLinus Torvalds /* Disable play engine. */ 5461da177e4SLinus Torvalds snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0); 5471da177e4SLinus Torvalds } 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds static void 5501da177e4SLinus Torvalds snd_nm256_capture_stop(nm256_t *chip) 5511da177e4SLinus Torvalds { 5521da177e4SLinus Torvalds /* Disable recording engine. */ 5531da177e4SLinus Torvalds snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0); 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 5561da177e4SLinus Torvalds static int 5571da177e4SLinus Torvalds snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd) 5581da177e4SLinus Torvalds { 5591da177e4SLinus Torvalds nm256_t *chip = snd_pcm_substream_chip(substream); 5601da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; 5611da177e4SLinus Torvalds int err = 0; 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds snd_assert(s != NULL, return -ENXIO); 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds spin_lock(&chip->reg_lock); 5661da177e4SLinus Torvalds switch (cmd) { 5671da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_RESUME: 5681204de32STakashi Iwai s->suspended = 0; 5691204de32STakashi Iwai /* fallthru */ 5701204de32STakashi Iwai case SNDRV_PCM_TRIGGER_START: 5711da177e4SLinus Torvalds if (! s->running) { 5721da177e4SLinus Torvalds snd_nm256_playback_start(chip, s, substream); 5731da177e4SLinus Torvalds s->running = 1; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds break; 5761da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND: 5771204de32STakashi Iwai s->suspended = 1; 5781204de32STakashi Iwai /* fallthru */ 5791204de32STakashi Iwai case SNDRV_PCM_TRIGGER_STOP: 5801da177e4SLinus Torvalds if (s->running) { 5811da177e4SLinus Torvalds snd_nm256_playback_stop(chip); 5821da177e4SLinus Torvalds s->running = 0; 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds break; 5851da177e4SLinus Torvalds default: 5861da177e4SLinus Torvalds err = -EINVAL; 5871da177e4SLinus Torvalds break; 5881da177e4SLinus Torvalds } 5891da177e4SLinus Torvalds spin_unlock(&chip->reg_lock); 5901da177e4SLinus Torvalds return err; 5911da177e4SLinus Torvalds } 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds static int 5941da177e4SLinus Torvalds snd_nm256_capture_trigger(snd_pcm_substream_t *substream, int cmd) 5951da177e4SLinus Torvalds { 5961da177e4SLinus Torvalds nm256_t *chip = snd_pcm_substream_chip(substream); 5971da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; 5981da177e4SLinus Torvalds int err = 0; 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds snd_assert(s != NULL, return -ENXIO); 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds spin_lock(&chip->reg_lock); 6031da177e4SLinus Torvalds switch (cmd) { 6041da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_START: 6051da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_RESUME: 6061da177e4SLinus Torvalds if (! s->running) { 6071da177e4SLinus Torvalds snd_nm256_capture_start(chip, s, substream); 6081da177e4SLinus Torvalds s->running = 1; 6091da177e4SLinus Torvalds } 6101da177e4SLinus Torvalds break; 6111da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_STOP: 6121da177e4SLinus Torvalds case SNDRV_PCM_TRIGGER_SUSPEND: 6131da177e4SLinus Torvalds if (s->running) { 6141da177e4SLinus Torvalds snd_nm256_capture_stop(chip); 6151da177e4SLinus Torvalds s->running = 0; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds break; 6181da177e4SLinus Torvalds default: 6191da177e4SLinus Torvalds err = -EINVAL; 6201da177e4SLinus Torvalds break; 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds spin_unlock(&chip->reg_lock); 6231da177e4SLinus Torvalds return err; 6241da177e4SLinus Torvalds } 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds 6271da177e4SLinus Torvalds /* 6281da177e4SLinus Torvalds * prepare playback/capture channel 6291da177e4SLinus Torvalds */ 6301da177e4SLinus Torvalds static int snd_nm256_pcm_prepare(snd_pcm_substream_t *substream) 6311da177e4SLinus Torvalds { 6321da177e4SLinus Torvalds nm256_t *chip = snd_pcm_substream_chip(substream); 6331da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 6341da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds snd_assert(s, return -ENXIO); 6371da177e4SLinus Torvalds s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size); 6381da177e4SLinus Torvalds s->period_size = frames_to_bytes(runtime, substream->runtime->period_size); 6391da177e4SLinus Torvalds s->periods = substream->runtime->periods; 6401da177e4SLinus Torvalds s->cur_period = 0; 6411da177e4SLinus Torvalds 6421da177e4SLinus Torvalds spin_lock_irq(&chip->reg_lock); 6431da177e4SLinus Torvalds s->running = 0; 6441da177e4SLinus Torvalds snd_nm256_set_format(chip, s, substream); 6451da177e4SLinus Torvalds spin_unlock_irq(&chip->reg_lock); 6461da177e4SLinus Torvalds 6471da177e4SLinus Torvalds return 0; 6481da177e4SLinus Torvalds } 6491da177e4SLinus Torvalds 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds /* 6521da177e4SLinus Torvalds * get the current pointer 6531da177e4SLinus Torvalds */ 6541da177e4SLinus Torvalds static snd_pcm_uframes_t 6551da177e4SLinus Torvalds snd_nm256_playback_pointer(snd_pcm_substream_t * substream) 6561da177e4SLinus Torvalds { 6571da177e4SLinus Torvalds nm256_t *chip = snd_pcm_substream_chip(substream); 6581da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; 6591da177e4SLinus Torvalds unsigned long curp; 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds snd_assert(s, return 0); 6621da177e4SLinus Torvalds curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf; 6631da177e4SLinus Torvalds curp %= s->dma_size; 6641da177e4SLinus Torvalds return bytes_to_frames(substream->runtime, curp); 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds static snd_pcm_uframes_t 6681da177e4SLinus Torvalds snd_nm256_capture_pointer(snd_pcm_substream_t * substream) 6691da177e4SLinus Torvalds { 6701da177e4SLinus Torvalds nm256_t *chip = snd_pcm_substream_chip(substream); 6711da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)substream->runtime->private_data; 6721da177e4SLinus Torvalds unsigned long curp; 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds snd_assert(s != NULL, return 0); 6751da177e4SLinus Torvalds curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf; 6761da177e4SLinus Torvalds curp %= s->dma_size; 6771da177e4SLinus Torvalds return bytes_to_frames(substream->runtime, curp); 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds /* Remapped I/O space can be accessible as pointer on i386 */ 6811da177e4SLinus Torvalds /* This might be changed in the future */ 6821da177e4SLinus Torvalds #ifndef __i386__ 6831da177e4SLinus Torvalds /* 6841da177e4SLinus Torvalds * silence / copy for playback 6851da177e4SLinus Torvalds */ 6861da177e4SLinus Torvalds static int 6871da177e4SLinus Torvalds snd_nm256_playback_silence(snd_pcm_substream_t *substream, 6881da177e4SLinus Torvalds int channel, /* not used (interleaved data) */ 6891da177e4SLinus Torvalds snd_pcm_uframes_t pos, 6901da177e4SLinus Torvalds snd_pcm_uframes_t count) 6911da177e4SLinus Torvalds { 6921da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 6931da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; 6941da177e4SLinus Torvalds count = frames_to_bytes(runtime, count); 6951da177e4SLinus Torvalds pos = frames_to_bytes(runtime, pos); 6961da177e4SLinus Torvalds memset_io(s->bufptr + pos, 0, count); 6971da177e4SLinus Torvalds return 0; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds static int 7011da177e4SLinus Torvalds snd_nm256_playback_copy(snd_pcm_substream_t *substream, 7021da177e4SLinus Torvalds int channel, /* not used (interleaved data) */ 7031da177e4SLinus Torvalds snd_pcm_uframes_t pos, 7041da177e4SLinus Torvalds void __user *src, 7051da177e4SLinus Torvalds snd_pcm_uframes_t count) 7061da177e4SLinus Torvalds { 7071da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 7081da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; 7091da177e4SLinus Torvalds count = frames_to_bytes(runtime, count); 7101da177e4SLinus Torvalds pos = frames_to_bytes(runtime, pos); 7111da177e4SLinus Torvalds if (copy_from_user_toio(s->bufptr + pos, src, count)) 7121da177e4SLinus Torvalds return -EFAULT; 7131da177e4SLinus Torvalds return 0; 7141da177e4SLinus Torvalds } 7151da177e4SLinus Torvalds 7161da177e4SLinus Torvalds /* 7171da177e4SLinus Torvalds * copy to user 7181da177e4SLinus Torvalds */ 7191da177e4SLinus Torvalds static int 7201da177e4SLinus Torvalds snd_nm256_capture_copy(snd_pcm_substream_t *substream, 7211da177e4SLinus Torvalds int channel, /* not used (interleaved data) */ 7221da177e4SLinus Torvalds snd_pcm_uframes_t pos, 7231da177e4SLinus Torvalds void __user *dst, 7241da177e4SLinus Torvalds snd_pcm_uframes_t count) 7251da177e4SLinus Torvalds { 7261da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 7271da177e4SLinus Torvalds nm256_stream_t *s = (nm256_stream_t*)runtime->private_data; 7281da177e4SLinus Torvalds count = frames_to_bytes(runtime, count); 7291da177e4SLinus Torvalds pos = frames_to_bytes(runtime, pos); 7301da177e4SLinus Torvalds if (copy_to_user_fromio(dst, s->bufptr + pos, count)) 7311da177e4SLinus Torvalds return -EFAULT; 7321da177e4SLinus Torvalds return 0; 7331da177e4SLinus Torvalds } 7341da177e4SLinus Torvalds 7351da177e4SLinus Torvalds #endif /* !__i386__ */ 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds 7381da177e4SLinus Torvalds /* 7391da177e4SLinus Torvalds * update playback/capture watermarks 7401da177e4SLinus Torvalds */ 7411da177e4SLinus Torvalds 7421da177e4SLinus Torvalds /* spinlock held! */ 7431da177e4SLinus Torvalds static void 7441da177e4SLinus Torvalds snd_nm256_playback_update(nm256_t *chip) 7451da177e4SLinus Torvalds { 7461da177e4SLinus Torvalds nm256_stream_t *s; 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK]; 7491da177e4SLinus Torvalds if (s->running && s->substream) { 7501da177e4SLinus Torvalds spin_unlock(&chip->reg_lock); 7511da177e4SLinus Torvalds snd_pcm_period_elapsed(s->substream); 7521da177e4SLinus Torvalds spin_lock(&chip->reg_lock); 7531da177e4SLinus Torvalds snd_nm256_playback_mark(chip, s); 7541da177e4SLinus Torvalds } 7551da177e4SLinus Torvalds } 7561da177e4SLinus Torvalds 7571da177e4SLinus Torvalds /* spinlock held! */ 7581da177e4SLinus Torvalds static void 7591da177e4SLinus Torvalds snd_nm256_capture_update(nm256_t *chip) 7601da177e4SLinus Torvalds { 7611da177e4SLinus Torvalds nm256_stream_t *s; 7621da177e4SLinus Torvalds 7631da177e4SLinus Torvalds s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE]; 7641da177e4SLinus Torvalds if (s->running && s->substream) { 7651da177e4SLinus Torvalds spin_unlock(&chip->reg_lock); 7661da177e4SLinus Torvalds snd_pcm_period_elapsed(s->substream); 7671da177e4SLinus Torvalds spin_lock(&chip->reg_lock); 7681da177e4SLinus Torvalds snd_nm256_capture_mark(chip, s); 7691da177e4SLinus Torvalds } 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds /* 7731da177e4SLinus Torvalds * hardware info 7741da177e4SLinus Torvalds */ 7751da177e4SLinus Torvalds static snd_pcm_hardware_t snd_nm256_playback = 7761da177e4SLinus Torvalds { 7771da177e4SLinus Torvalds .info = SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID | 7781da177e4SLinus Torvalds SNDRV_PCM_INFO_INTERLEAVED | 7791da177e4SLinus Torvalds /*SNDRV_PCM_INFO_PAUSE |*/ 7801da177e4SLinus Torvalds SNDRV_PCM_INFO_RESUME, 7811da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 7821da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, 7831da177e4SLinus Torvalds .rate_min = 8000, 7841da177e4SLinus Torvalds .rate_max = 48000, 7851da177e4SLinus Torvalds .channels_min = 1, 7861da177e4SLinus Torvalds .channels_max = 2, 7871da177e4SLinus Torvalds .periods_min = 2, 7881da177e4SLinus Torvalds .periods_max = 1024, 7891da177e4SLinus Torvalds .buffer_bytes_max = 128 * 1024, 7901da177e4SLinus Torvalds .period_bytes_min = 256, 7911da177e4SLinus Torvalds .period_bytes_max = 128 * 1024, 7921da177e4SLinus Torvalds }; 7931da177e4SLinus Torvalds 7941da177e4SLinus Torvalds static snd_pcm_hardware_t snd_nm256_capture = 7951da177e4SLinus Torvalds { 7961da177e4SLinus Torvalds .info = SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID | 7971da177e4SLinus Torvalds SNDRV_PCM_INFO_INTERLEAVED | 7981da177e4SLinus Torvalds /*SNDRV_PCM_INFO_PAUSE |*/ 7991da177e4SLinus Torvalds SNDRV_PCM_INFO_RESUME, 8001da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, 8011da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000, 8021da177e4SLinus Torvalds .rate_min = 8000, 8031da177e4SLinus Torvalds .rate_max = 48000, 8041da177e4SLinus Torvalds .channels_min = 1, 8051da177e4SLinus Torvalds .channels_max = 2, 8061da177e4SLinus Torvalds .periods_min = 2, 8071da177e4SLinus Torvalds .periods_max = 1024, 8081da177e4SLinus Torvalds .buffer_bytes_max = 128 * 1024, 8091da177e4SLinus Torvalds .period_bytes_min = 256, 8101da177e4SLinus Torvalds .period_bytes_max = 128 * 1024, 8111da177e4SLinus Torvalds }; 8121da177e4SLinus Torvalds 8131da177e4SLinus Torvalds 8141da177e4SLinus Torvalds /* set dma transfer size */ 8151da177e4SLinus Torvalds static int snd_nm256_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *hw_params) 8161da177e4SLinus Torvalds { 8171da177e4SLinus Torvalds /* area and addr are already set and unchanged */ 8181da177e4SLinus Torvalds substream->runtime->dma_bytes = params_buffer_bytes(hw_params); 8191da177e4SLinus Torvalds return 0; 8201da177e4SLinus Torvalds } 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds /* 8231da177e4SLinus Torvalds * open 8241da177e4SLinus Torvalds */ 8251da177e4SLinus Torvalds static void snd_nm256_setup_stream(nm256_t *chip, nm256_stream_t *s, 8261da177e4SLinus Torvalds snd_pcm_substream_t *substream, 8271da177e4SLinus Torvalds snd_pcm_hardware_t *hw_ptr) 8281da177e4SLinus Torvalds { 8291da177e4SLinus Torvalds snd_pcm_runtime_t *runtime = substream->runtime; 8301da177e4SLinus Torvalds 8311da177e4SLinus Torvalds s->running = 0; 8321da177e4SLinus Torvalds runtime->hw = *hw_ptr; 8331da177e4SLinus Torvalds runtime->hw.buffer_bytes_max = s->bufsize; 8341da177e4SLinus Torvalds runtime->hw.period_bytes_max = s->bufsize / 2; 8354d23359bSClemens Ladisch runtime->dma_area = (void __force *) s->bufptr; 8361da177e4SLinus Torvalds runtime->dma_addr = s->bufptr_addr; 8371da177e4SLinus Torvalds runtime->dma_bytes = s->bufsize; 8381da177e4SLinus Torvalds runtime->private_data = s; 8391da177e4SLinus Torvalds s->substream = substream; 8401da177e4SLinus Torvalds 8411da177e4SLinus Torvalds snd_pcm_set_sync(substream); 8421da177e4SLinus Torvalds snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, 8431da177e4SLinus Torvalds &constraints_rates); 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds 8461da177e4SLinus Torvalds static int 8471da177e4SLinus Torvalds snd_nm256_playback_open(snd_pcm_substream_t *substream) 8481da177e4SLinus Torvalds { 8491da177e4SLinus Torvalds nm256_t *chip = snd_pcm_substream_chip(substream); 8501da177e4SLinus Torvalds 8511204de32STakashi Iwai if (snd_nm256_acquire_irq(chip) < 0) 8521204de32STakashi Iwai return -EBUSY; 8531da177e4SLinus Torvalds snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], 8541da177e4SLinus Torvalds substream, &snd_nm256_playback); 8551da177e4SLinus Torvalds return 0; 8561da177e4SLinus Torvalds } 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds static int 8591da177e4SLinus Torvalds snd_nm256_capture_open(snd_pcm_substream_t *substream) 8601da177e4SLinus Torvalds { 8611da177e4SLinus Torvalds nm256_t *chip = snd_pcm_substream_chip(substream); 8621da177e4SLinus Torvalds 8631204de32STakashi Iwai if (snd_nm256_acquire_irq(chip) < 0) 8641204de32STakashi Iwai return -EBUSY; 8651da177e4SLinus Torvalds snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], 8661da177e4SLinus Torvalds substream, &snd_nm256_capture); 8671da177e4SLinus Torvalds return 0; 8681da177e4SLinus Torvalds } 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds /* 8711da177e4SLinus Torvalds * close - we don't have to do special.. 8721da177e4SLinus Torvalds */ 8731da177e4SLinus Torvalds static int 8741da177e4SLinus Torvalds snd_nm256_playback_close(snd_pcm_substream_t *substream) 8751da177e4SLinus Torvalds { 8761204de32STakashi Iwai nm256_t *chip = snd_pcm_substream_chip(substream); 8771204de32STakashi Iwai 8781204de32STakashi Iwai snd_nm256_release_irq(chip); 8791da177e4SLinus Torvalds return 0; 8801da177e4SLinus Torvalds } 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds static int 8841da177e4SLinus Torvalds snd_nm256_capture_close(snd_pcm_substream_t *substream) 8851da177e4SLinus Torvalds { 8861204de32STakashi Iwai nm256_t *chip = snd_pcm_substream_chip(substream); 8871204de32STakashi Iwai 8881204de32STakashi Iwai snd_nm256_release_irq(chip); 8891da177e4SLinus Torvalds return 0; 8901da177e4SLinus Torvalds } 8911da177e4SLinus Torvalds 8921da177e4SLinus Torvalds /* 8931da177e4SLinus Torvalds * create a pcm instance 8941da177e4SLinus Torvalds */ 8951da177e4SLinus Torvalds static snd_pcm_ops_t snd_nm256_playback_ops = { 8961da177e4SLinus Torvalds .open = snd_nm256_playback_open, 8971da177e4SLinus Torvalds .close = snd_nm256_playback_close, 8981da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 8991da177e4SLinus Torvalds .hw_params = snd_nm256_pcm_hw_params, 9001da177e4SLinus Torvalds .prepare = snd_nm256_pcm_prepare, 9011da177e4SLinus Torvalds .trigger = snd_nm256_playback_trigger, 9021da177e4SLinus Torvalds .pointer = snd_nm256_playback_pointer, 9031da177e4SLinus Torvalds #ifndef __i386__ 9041da177e4SLinus Torvalds .copy = snd_nm256_playback_copy, 9051da177e4SLinus Torvalds .silence = snd_nm256_playback_silence, 9061da177e4SLinus Torvalds #endif 9071da177e4SLinus Torvalds .mmap = snd_pcm_lib_mmap_iomem, 9081da177e4SLinus Torvalds }; 9091da177e4SLinus Torvalds 9101da177e4SLinus Torvalds static snd_pcm_ops_t snd_nm256_capture_ops = { 9111da177e4SLinus Torvalds .open = snd_nm256_capture_open, 9121da177e4SLinus Torvalds .close = snd_nm256_capture_close, 9131da177e4SLinus Torvalds .ioctl = snd_pcm_lib_ioctl, 9141da177e4SLinus Torvalds .hw_params = snd_nm256_pcm_hw_params, 9151da177e4SLinus Torvalds .prepare = snd_nm256_pcm_prepare, 9161da177e4SLinus Torvalds .trigger = snd_nm256_capture_trigger, 9171da177e4SLinus Torvalds .pointer = snd_nm256_capture_pointer, 9181da177e4SLinus Torvalds #ifndef __i386__ 9191da177e4SLinus Torvalds .copy = snd_nm256_capture_copy, 9201da177e4SLinus Torvalds #endif 9211da177e4SLinus Torvalds .mmap = snd_pcm_lib_mmap_iomem, 9221da177e4SLinus Torvalds }; 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds static int __devinit 9251da177e4SLinus Torvalds snd_nm256_pcm(nm256_t *chip, int device) 9261da177e4SLinus Torvalds { 9271da177e4SLinus Torvalds snd_pcm_t *pcm; 9281da177e4SLinus Torvalds int i, err; 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds for (i = 0; i < 2; i++) { 9311da177e4SLinus Torvalds nm256_stream_t *s = &chip->streams[i]; 9321da177e4SLinus Torvalds s->bufptr = chip->buffer + (s->buf - chip->buffer_start); 9331da177e4SLinus Torvalds s->bufptr_addr = chip->buffer_addr + (s->buf - chip->buffer_start); 9341da177e4SLinus Torvalds } 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds err = snd_pcm_new(chip->card, chip->card->driver, device, 9371da177e4SLinus Torvalds 1, 1, &pcm); 9381da177e4SLinus Torvalds if (err < 0) 9391da177e4SLinus Torvalds return err; 9401da177e4SLinus Torvalds 9411da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops); 9421da177e4SLinus Torvalds snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops); 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds pcm->private_data = chip; 9451da177e4SLinus Torvalds pcm->info_flags = 0; 9461da177e4SLinus Torvalds chip->pcm = pcm; 9471da177e4SLinus Torvalds 9481da177e4SLinus Torvalds return 0; 9491da177e4SLinus Torvalds } 9501da177e4SLinus Torvalds 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds /* 9531da177e4SLinus Torvalds * Initialize the hardware. 9541da177e4SLinus Torvalds */ 9551da177e4SLinus Torvalds static void 9561da177e4SLinus Torvalds snd_nm256_init_chip(nm256_t *chip) 9571da177e4SLinus Torvalds { 9581da177e4SLinus Torvalds /* Reset everything. */ 9591da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x0, 0x11); 9601da177e4SLinus Torvalds snd_nm256_writew(chip, 0x214, 0); 9611da177e4SLinus Torvalds /* stop sounds.. */ 9621da177e4SLinus Torvalds //snd_nm256_playback_stop(chip); 9631da177e4SLinus Torvalds //snd_nm256_capture_stop(chip); 9641da177e4SLinus Torvalds } 9651da177e4SLinus Torvalds 9661da177e4SLinus Torvalds 9671204de32STakashi Iwai static irqreturn_t 9681da177e4SLinus Torvalds snd_nm256_intr_check(nm256_t *chip) 9691da177e4SLinus Torvalds { 9701da177e4SLinus Torvalds if (chip->badintrcount++ > 1000) { 9711da177e4SLinus Torvalds /* 9721da177e4SLinus Torvalds * I'm not sure if the best thing is to stop the card from 9731da177e4SLinus Torvalds * playing or just release the interrupt (after all, we're in 9741da177e4SLinus Torvalds * a bad situation, so doing fancy stuff may not be such a good 9751da177e4SLinus Torvalds * idea). 9761da177e4SLinus Torvalds * 9771da177e4SLinus Torvalds * I worry about the card engine continuing to play noise 9781da177e4SLinus Torvalds * over and over, however--that could become a very 9791da177e4SLinus Torvalds * obnoxious problem. And we know that when this usually 9801da177e4SLinus Torvalds * happens things are fairly safe, it just means the user's 9811da177e4SLinus Torvalds * inserted a PCMCIA card and someone's spamming us with IRQ 9s. 9821da177e4SLinus Torvalds */ 9831da177e4SLinus Torvalds if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) 9841da177e4SLinus Torvalds snd_nm256_playback_stop(chip); 9851da177e4SLinus Torvalds if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) 9861da177e4SLinus Torvalds snd_nm256_capture_stop(chip); 9871da177e4SLinus Torvalds chip->badintrcount = 0; 9881204de32STakashi Iwai return IRQ_HANDLED; 9891da177e4SLinus Torvalds } 9901204de32STakashi Iwai return IRQ_NONE; 9911da177e4SLinus Torvalds } 9921da177e4SLinus Torvalds 9931da177e4SLinus Torvalds /* 9941da177e4SLinus Torvalds * Handle a potential interrupt for the device referred to by DEV_ID. 9951da177e4SLinus Torvalds * 9961da177e4SLinus Torvalds * I don't like the cut-n-paste job here either between the two routines, 9971da177e4SLinus Torvalds * but there are sufficient differences between the two interrupt handlers 9981da177e4SLinus Torvalds * that parameterizing it isn't all that great either. (Could use a macro, 9991da177e4SLinus Torvalds * I suppose...yucky bleah.) 10001da177e4SLinus Torvalds */ 10011da177e4SLinus Torvalds 10021da177e4SLinus Torvalds static irqreturn_t 10031da177e4SLinus Torvalds snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy) 10041da177e4SLinus Torvalds { 10051da177e4SLinus Torvalds nm256_t *chip = dev_id; 10061da177e4SLinus Torvalds u16 status; 10071da177e4SLinus Torvalds u8 cbyte; 10081da177e4SLinus Torvalds 10091da177e4SLinus Torvalds status = snd_nm256_readw(chip, NM_INT_REG); 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds /* Not ours. */ 10121204de32STakashi Iwai if (status == 0) 10131204de32STakashi Iwai return snd_nm256_intr_check(chip); 10141da177e4SLinus Torvalds 10151da177e4SLinus Torvalds chip->badintrcount = 0; 10161da177e4SLinus Torvalds 10171da177e4SLinus Torvalds /* Rather boring; check for individual interrupts and process them. */ 10181da177e4SLinus Torvalds 10191da177e4SLinus Torvalds spin_lock(&chip->reg_lock); 10201da177e4SLinus Torvalds if (status & NM_PLAYBACK_INT) { 10211da177e4SLinus Torvalds status &= ~NM_PLAYBACK_INT; 10221da177e4SLinus Torvalds NM_ACK_INT(chip, NM_PLAYBACK_INT); 10231da177e4SLinus Torvalds snd_nm256_playback_update(chip); 10241da177e4SLinus Torvalds } 10251da177e4SLinus Torvalds 10261da177e4SLinus Torvalds if (status & NM_RECORD_INT) { 10271da177e4SLinus Torvalds status &= ~NM_RECORD_INT; 10281da177e4SLinus Torvalds NM_ACK_INT(chip, NM_RECORD_INT); 10291da177e4SLinus Torvalds snd_nm256_capture_update(chip); 10301da177e4SLinus Torvalds } 10311da177e4SLinus Torvalds 10321da177e4SLinus Torvalds if (status & NM_MISC_INT_1) { 10331da177e4SLinus Torvalds status &= ~NM_MISC_INT_1; 10341da177e4SLinus Torvalds NM_ACK_INT(chip, NM_MISC_INT_1); 10351da177e4SLinus Torvalds snd_printd("NM256: Got misc interrupt #1\n"); 10361da177e4SLinus Torvalds snd_nm256_writew(chip, NM_INT_REG, 0x8000); 10371da177e4SLinus Torvalds cbyte = snd_nm256_readb(chip, 0x400); 10381da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x400, cbyte | 2); 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds if (status & NM_MISC_INT_2) { 10421da177e4SLinus Torvalds status &= ~NM_MISC_INT_2; 10431da177e4SLinus Torvalds NM_ACK_INT(chip, NM_MISC_INT_2); 10441da177e4SLinus Torvalds snd_printd("NM256: Got misc interrupt #2\n"); 10451da177e4SLinus Torvalds cbyte = snd_nm256_readb(chip, 0x400); 10461da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x400, cbyte & ~2); 10471da177e4SLinus Torvalds } 10481da177e4SLinus Torvalds 10491da177e4SLinus Torvalds /* Unknown interrupt. */ 10501da177e4SLinus Torvalds if (status) { 10511da177e4SLinus Torvalds snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", 10521da177e4SLinus Torvalds status); 10531da177e4SLinus Torvalds /* Pray. */ 10541da177e4SLinus Torvalds NM_ACK_INT(chip, status); 10551da177e4SLinus Torvalds } 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds spin_unlock(&chip->reg_lock); 10581da177e4SLinus Torvalds return IRQ_HANDLED; 10591da177e4SLinus Torvalds } 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds /* 10621da177e4SLinus Torvalds * Handle a potential interrupt for the device referred to by DEV_ID. 10631da177e4SLinus Torvalds * This handler is for the 256ZX, and is very similar to the non-ZX 10641da177e4SLinus Torvalds * routine. 10651da177e4SLinus Torvalds */ 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds static irqreturn_t 10681da177e4SLinus Torvalds snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy) 10691da177e4SLinus Torvalds { 10701da177e4SLinus Torvalds nm256_t *chip = dev_id; 10711da177e4SLinus Torvalds u32 status; 10721da177e4SLinus Torvalds u8 cbyte; 10731da177e4SLinus Torvalds 10741da177e4SLinus Torvalds status = snd_nm256_readl(chip, NM_INT_REG); 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds /* Not ours. */ 10771204de32STakashi Iwai if (status == 0) 10781204de32STakashi Iwai return snd_nm256_intr_check(chip); 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds chip->badintrcount = 0; 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds /* Rather boring; check for individual interrupts and process them. */ 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds spin_lock(&chip->reg_lock); 10851da177e4SLinus Torvalds if (status & NM2_PLAYBACK_INT) { 10861da177e4SLinus Torvalds status &= ~NM2_PLAYBACK_INT; 10871da177e4SLinus Torvalds NM2_ACK_INT(chip, NM2_PLAYBACK_INT); 10881da177e4SLinus Torvalds snd_nm256_playback_update(chip); 10891da177e4SLinus Torvalds } 10901da177e4SLinus Torvalds 10911da177e4SLinus Torvalds if (status & NM2_RECORD_INT) { 10921da177e4SLinus Torvalds status &= ~NM2_RECORD_INT; 10931da177e4SLinus Torvalds NM2_ACK_INT(chip, NM2_RECORD_INT); 10941da177e4SLinus Torvalds snd_nm256_capture_update(chip); 10951da177e4SLinus Torvalds } 10961da177e4SLinus Torvalds 10971da177e4SLinus Torvalds if (status & NM2_MISC_INT_1) { 10981da177e4SLinus Torvalds status &= ~NM2_MISC_INT_1; 10991da177e4SLinus Torvalds NM2_ACK_INT(chip, NM2_MISC_INT_1); 11001da177e4SLinus Torvalds snd_printd("NM256: Got misc interrupt #1\n"); 11011da177e4SLinus Torvalds cbyte = snd_nm256_readb(chip, 0x400); 11021da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x400, cbyte | 2); 11031da177e4SLinus Torvalds } 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds if (status & NM2_MISC_INT_2) { 11061da177e4SLinus Torvalds status &= ~NM2_MISC_INT_2; 11071da177e4SLinus Torvalds NM2_ACK_INT(chip, NM2_MISC_INT_2); 11081da177e4SLinus Torvalds snd_printd("NM256: Got misc interrupt #2\n"); 11091da177e4SLinus Torvalds cbyte = snd_nm256_readb(chip, 0x400); 11101da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x400, cbyte & ~2); 11111da177e4SLinus Torvalds } 11121da177e4SLinus Torvalds 11131da177e4SLinus Torvalds /* Unknown interrupt. */ 11141da177e4SLinus Torvalds if (status) { 11151da177e4SLinus Torvalds snd_printd("NM256: Fire in the hole! Unknown status 0x%x\n", 11161da177e4SLinus Torvalds status); 11171da177e4SLinus Torvalds /* Pray. */ 11181da177e4SLinus Torvalds NM2_ACK_INT(chip, status); 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds spin_unlock(&chip->reg_lock); 11221da177e4SLinus Torvalds return IRQ_HANDLED; 11231da177e4SLinus Torvalds } 11241da177e4SLinus Torvalds 11251da177e4SLinus Torvalds /* 11261da177e4SLinus Torvalds * AC97 interface 11271da177e4SLinus Torvalds */ 11281da177e4SLinus Torvalds 11291da177e4SLinus Torvalds /* 11301da177e4SLinus Torvalds * Waits for the mixer to become ready to be written; returns a zero value 11311da177e4SLinus Torvalds * if it timed out. 11321da177e4SLinus Torvalds */ 11331da177e4SLinus Torvalds static int 11341da177e4SLinus Torvalds snd_nm256_ac97_ready(nm256_t *chip) 11351da177e4SLinus Torvalds { 11361da177e4SLinus Torvalds int timeout = 10; 11371da177e4SLinus Torvalds u32 testaddr; 11381da177e4SLinus Torvalds u16 testb; 11391da177e4SLinus Torvalds 11401da177e4SLinus Torvalds testaddr = chip->mixer_status_offset; 11411da177e4SLinus Torvalds testb = chip->mixer_status_mask; 11421da177e4SLinus Torvalds 11431da177e4SLinus Torvalds /* 11441da177e4SLinus Torvalds * Loop around waiting for the mixer to become ready. 11451da177e4SLinus Torvalds */ 11461da177e4SLinus Torvalds while (timeout-- > 0) { 11471da177e4SLinus Torvalds if ((snd_nm256_readw(chip, testaddr) & testb) == 0) 11481da177e4SLinus Torvalds return 1; 11491da177e4SLinus Torvalds udelay(100); 11501da177e4SLinus Torvalds } 11511da177e4SLinus Torvalds return 0; 11521da177e4SLinus Torvalds } 11531da177e4SLinus Torvalds 11541da177e4SLinus Torvalds /* 11551da177e4SLinus Torvalds */ 11561da177e4SLinus Torvalds static unsigned short 11571da177e4SLinus Torvalds snd_nm256_ac97_read(ac97_t *ac97, unsigned short reg) 11581da177e4SLinus Torvalds { 11591da177e4SLinus Torvalds nm256_t *chip = ac97->private_data; 11601da177e4SLinus Torvalds int res; 11611da177e4SLinus Torvalds 11621da177e4SLinus Torvalds if (reg >= 128) 11631da177e4SLinus Torvalds return 0; 11641da177e4SLinus Torvalds 11651da177e4SLinus Torvalds if (! snd_nm256_ac97_ready(chip)) 11661da177e4SLinus Torvalds return 0; 11671da177e4SLinus Torvalds res = snd_nm256_readw(chip, chip->mixer_base + reg); 11681da177e4SLinus Torvalds /* Magic delay. Bleah yucky. */ 11691da177e4SLinus Torvalds msleep(1); 11701da177e4SLinus Torvalds return res; 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds 11731da177e4SLinus Torvalds /* 11741da177e4SLinus Torvalds */ 11751da177e4SLinus Torvalds static void 11761da177e4SLinus Torvalds snd_nm256_ac97_write(ac97_t *ac97, 11771da177e4SLinus Torvalds unsigned short reg, unsigned short val) 11781da177e4SLinus Torvalds { 11791da177e4SLinus Torvalds nm256_t *chip = ac97->private_data; 11801da177e4SLinus Torvalds int tries = 2; 11811da177e4SLinus Torvalds u32 base; 11821da177e4SLinus Torvalds 11831da177e4SLinus Torvalds base = chip->mixer_base; 11841da177e4SLinus Torvalds 11851da177e4SLinus Torvalds snd_nm256_ac97_ready(chip); 11861da177e4SLinus Torvalds 11871da177e4SLinus Torvalds /* Wait for the write to take, too. */ 11881da177e4SLinus Torvalds while (tries-- > 0) { 11891da177e4SLinus Torvalds snd_nm256_writew(chip, base + reg, val); 11901da177e4SLinus Torvalds msleep(1); /* a little delay here seems better.. */ 11911da177e4SLinus Torvalds if (snd_nm256_ac97_ready(chip)) 11921da177e4SLinus Torvalds return; 11931da177e4SLinus Torvalds } 11941da177e4SLinus Torvalds snd_printd("nm256: ac97 codec not ready..\n"); 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds 11971da177e4SLinus Torvalds /* initialize the ac97 into a known state */ 11981da177e4SLinus Torvalds static void 11991da177e4SLinus Torvalds snd_nm256_ac97_reset(ac97_t *ac97) 12001da177e4SLinus Torvalds { 12011da177e4SLinus Torvalds nm256_t *chip = ac97->private_data; 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds /* Reset the mixer. 'Tis magic! */ 12041da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x6c0, 1); 12051da177e4SLinus Torvalds if (! chip->reset_workaround) { 12061da177e4SLinus Torvalds /* Dell latitude LS will lock up by this */ 12071da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x6cc, 0x87); 12081da177e4SLinus Torvalds } 120947530cf4SJohn W. Linville if (! chip->reset_workaround_2) { 121047530cf4SJohn W. Linville /* Dell latitude CSx will lock up by this */ 12111da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x6cc, 0x80); 12121da177e4SLinus Torvalds snd_nm256_writeb(chip, 0x6cc, 0x0); 12131da177e4SLinus Torvalds } 121447530cf4SJohn W. Linville } 12151da177e4SLinus Torvalds 12161da177e4SLinus Torvalds /* create an ac97 mixer interface */ 12171da177e4SLinus Torvalds static int __devinit 12181da177e4SLinus Torvalds snd_nm256_mixer(nm256_t *chip) 12191da177e4SLinus Torvalds { 12201da177e4SLinus Torvalds ac97_bus_t *pbus; 12211da177e4SLinus Torvalds ac97_template_t ac97; 12221da177e4SLinus Torvalds int i, err; 12231da177e4SLinus Torvalds static ac97_bus_ops_t ops = { 12241da177e4SLinus Torvalds .reset = snd_nm256_ac97_reset, 12251da177e4SLinus Torvalds .write = snd_nm256_ac97_write, 12261da177e4SLinus Torvalds .read = snd_nm256_ac97_read, 12271da177e4SLinus Torvalds }; 12281da177e4SLinus Torvalds /* looks like nm256 hangs up when unexpected registers are touched... */ 12291da177e4SLinus Torvalds static int mixer_regs[] = { 12301da177e4SLinus Torvalds AC97_MASTER, AC97_HEADPHONE, AC97_MASTER_MONO, 12311da177e4SLinus Torvalds AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD, 12321da177e4SLinus Torvalds AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, 12331da177e4SLinus Torvalds AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, 12341204de32STakashi Iwai /*AC97_EXTENDED_ID,*/ 12351da177e4SLinus Torvalds AC97_VENDOR_ID1, AC97_VENDOR_ID2, 12361da177e4SLinus Torvalds -1 12371da177e4SLinus Torvalds }; 12381da177e4SLinus Torvalds 12391da177e4SLinus Torvalds if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) 12401da177e4SLinus Torvalds return err; 12411da177e4SLinus Torvalds 12421da177e4SLinus Torvalds memset(&ac97, 0, sizeof(ac97)); 12431da177e4SLinus Torvalds ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */ 12441da177e4SLinus Torvalds ac97.limited_regs = 1; 12451da177e4SLinus Torvalds for (i = 0; mixer_regs[i] >= 0; i++) 12461da177e4SLinus Torvalds set_bit(mixer_regs[i], ac97.reg_accessed); 12471da177e4SLinus Torvalds ac97.private_data = chip; 12481204de32STakashi Iwai pbus->no_vra = 1; 12491da177e4SLinus Torvalds err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); 12501da177e4SLinus Torvalds if (err < 0) 12511da177e4SLinus Torvalds return err; 12521da177e4SLinus Torvalds if (! (chip->ac97->id & (0xf0000000))) { 12531da177e4SLinus Torvalds /* looks like an invalid id */ 12541da177e4SLinus Torvalds sprintf(chip->card->mixername, "%s AC97", chip->card->driver); 12551da177e4SLinus Torvalds } 12561da177e4SLinus Torvalds return 0; 12571da177e4SLinus Torvalds } 12581da177e4SLinus Torvalds 12591da177e4SLinus Torvalds /* 12601da177e4SLinus Torvalds * See if the signature left by the NM256 BIOS is intact; if so, we use 12611da177e4SLinus Torvalds * the associated address as the end of our audio buffer in the video 12621da177e4SLinus Torvalds * RAM. 12631da177e4SLinus Torvalds */ 12641da177e4SLinus Torvalds 12651da177e4SLinus Torvalds static int __devinit 12661da177e4SLinus Torvalds snd_nm256_peek_for_sig(nm256_t *chip) 12671da177e4SLinus Torvalds { 12681da177e4SLinus Torvalds /* The signature is located 1K below the end of video RAM. */ 12691da177e4SLinus Torvalds void __iomem *temp; 12701da177e4SLinus Torvalds /* Default buffer end is 5120 bytes below the top of RAM. */ 12711da177e4SLinus Torvalds unsigned long pointer_found = chip->buffer_end - 0x1400; 12721da177e4SLinus Torvalds u32 sig; 12731da177e4SLinus Torvalds 12741da177e4SLinus Torvalds temp = ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16); 12751da177e4SLinus Torvalds if (temp == NULL) { 127699b359baSTakashi Iwai snd_printk(KERN_ERR "Unable to scan for card signature in video RAM\n"); 12771da177e4SLinus Torvalds return -EBUSY; 12781da177e4SLinus Torvalds } 12791da177e4SLinus Torvalds 12801da177e4SLinus Torvalds sig = readl(temp); 12811da177e4SLinus Torvalds if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { 12821da177e4SLinus Torvalds u32 pointer = readl(temp + 4); 12831da177e4SLinus Torvalds 12841da177e4SLinus Torvalds /* 12851da177e4SLinus Torvalds * If it's obviously invalid, don't use it 12861da177e4SLinus Torvalds */ 12871da177e4SLinus Torvalds if (pointer == 0xffffffff || 12881da177e4SLinus Torvalds pointer < chip->buffer_size || 12891da177e4SLinus Torvalds pointer > chip->buffer_end) { 129099b359baSTakashi Iwai snd_printk(KERN_ERR "invalid signature found: 0x%x\n", pointer); 12911da177e4SLinus Torvalds iounmap(temp); 12921da177e4SLinus Torvalds return -ENODEV; 12931da177e4SLinus Torvalds } else { 12941da177e4SLinus Torvalds pointer_found = pointer; 12951da177e4SLinus Torvalds printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n", pointer); 12961da177e4SLinus Torvalds } 12971da177e4SLinus Torvalds } 12981da177e4SLinus Torvalds 12991da177e4SLinus Torvalds iounmap(temp); 13001da177e4SLinus Torvalds chip->buffer_end = pointer_found; 13011da177e4SLinus Torvalds 13021da177e4SLinus Torvalds return 0; 13031da177e4SLinus Torvalds } 13041da177e4SLinus Torvalds 13051da177e4SLinus Torvalds #ifdef CONFIG_PM 13061da177e4SLinus Torvalds /* 13071da177e4SLinus Torvalds * APM event handler, so the card is properly reinitialized after a power 13081da177e4SLinus Torvalds * event. 13091da177e4SLinus Torvalds */ 13101da177e4SLinus Torvalds static int nm256_suspend(snd_card_t *card, pm_message_t state) 13111da177e4SLinus Torvalds { 13121da177e4SLinus Torvalds nm256_t *chip = card->pm_private_data; 13131da177e4SLinus Torvalds 13141da177e4SLinus Torvalds snd_pcm_suspend_all(chip->pcm); 13151da177e4SLinus Torvalds snd_ac97_suspend(chip->ac97); 13161da177e4SLinus Torvalds chip->coeffs_current = 0; 13171da177e4SLinus Torvalds pci_disable_device(chip->pci); 13181da177e4SLinus Torvalds return 0; 13191da177e4SLinus Torvalds } 13201da177e4SLinus Torvalds 13211da177e4SLinus Torvalds static int nm256_resume(snd_card_t *card) 13221da177e4SLinus Torvalds { 13231da177e4SLinus Torvalds nm256_t *chip = card->pm_private_data; 13241204de32STakashi Iwai int i; 13251da177e4SLinus Torvalds 13261da177e4SLinus Torvalds /* Perform a full reset on the hardware */ 13271da177e4SLinus Torvalds pci_enable_device(chip->pci); 13281da177e4SLinus Torvalds snd_nm256_init_chip(chip); 13291da177e4SLinus Torvalds 13301da177e4SLinus Torvalds /* restore ac97 */ 13311da177e4SLinus Torvalds snd_ac97_resume(chip->ac97); 13321da177e4SLinus Torvalds 13331204de32STakashi Iwai for (i = 0; i < 2; i++) { 13341204de32STakashi Iwai nm256_stream_t *s = &chip->streams[i]; 13351204de32STakashi Iwai if (s->substream && s->suspended) { 13361204de32STakashi Iwai spin_lock_irq(&chip->reg_lock); 13371204de32STakashi Iwai snd_nm256_set_format(chip, s, s->substream); 13381204de32STakashi Iwai spin_unlock_irq(&chip->reg_lock); 13391204de32STakashi Iwai } 13401204de32STakashi Iwai } 13411204de32STakashi Iwai 13421da177e4SLinus Torvalds return 0; 13431da177e4SLinus Torvalds } 13441da177e4SLinus Torvalds #endif /* CONFIG_PM */ 13451da177e4SLinus Torvalds 13461da177e4SLinus Torvalds static int snd_nm256_free(nm256_t *chip) 13471da177e4SLinus Torvalds { 13481da177e4SLinus Torvalds if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) 13491da177e4SLinus Torvalds snd_nm256_playback_stop(chip); 13501da177e4SLinus Torvalds if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) 13511da177e4SLinus Torvalds snd_nm256_capture_stop(chip); 13521da177e4SLinus Torvalds 13531da177e4SLinus Torvalds if (chip->irq >= 0) 13541da177e4SLinus Torvalds synchronize_irq(chip->irq); 13551da177e4SLinus Torvalds 13561da177e4SLinus Torvalds if (chip->cport) 13571da177e4SLinus Torvalds iounmap(chip->cport); 13581da177e4SLinus Torvalds if (chip->buffer) 13591da177e4SLinus Torvalds iounmap(chip->buffer); 1360b1d5776dSTakashi Iwai release_and_free_resource(chip->res_cport); 1361b1d5776dSTakashi Iwai release_and_free_resource(chip->res_buffer); 13621da177e4SLinus Torvalds if (chip->irq >= 0) 13631da177e4SLinus Torvalds free_irq(chip->irq, (void*)chip); 13641da177e4SLinus Torvalds 13651da177e4SLinus Torvalds pci_disable_device(chip->pci); 13661da177e4SLinus Torvalds kfree(chip); 13671da177e4SLinus Torvalds return 0; 13681da177e4SLinus Torvalds } 13691da177e4SLinus Torvalds 13701da177e4SLinus Torvalds static int snd_nm256_dev_free(snd_device_t *device) 13711da177e4SLinus Torvalds { 13721da177e4SLinus Torvalds nm256_t *chip = device->device_data; 13731da177e4SLinus Torvalds return snd_nm256_free(chip); 13741da177e4SLinus Torvalds } 13751da177e4SLinus Torvalds 13761da177e4SLinus Torvalds static int __devinit 13771da177e4SLinus Torvalds snd_nm256_create(snd_card_t *card, struct pci_dev *pci, 13781da177e4SLinus Torvalds nm256_t **chip_ret) 13791da177e4SLinus Torvalds { 13801da177e4SLinus Torvalds nm256_t *chip; 13811da177e4SLinus Torvalds int err, pval; 13821da177e4SLinus Torvalds static snd_device_ops_t ops = { 13831da177e4SLinus Torvalds .dev_free = snd_nm256_dev_free, 13841da177e4SLinus Torvalds }; 13851da177e4SLinus Torvalds u32 addr; 13861da177e4SLinus Torvalds 13871da177e4SLinus Torvalds *chip_ret = NULL; 13881da177e4SLinus Torvalds 13891da177e4SLinus Torvalds if ((err = pci_enable_device(pci)) < 0) 13901da177e4SLinus Torvalds return err; 13911da177e4SLinus Torvalds 1392e560d8d8STakashi Iwai chip = kzalloc(sizeof(*chip), GFP_KERNEL); 13931da177e4SLinus Torvalds if (chip == NULL) { 13941da177e4SLinus Torvalds pci_disable_device(pci); 13951da177e4SLinus Torvalds return -ENOMEM; 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds 13981da177e4SLinus Torvalds chip->card = card; 13991da177e4SLinus Torvalds chip->pci = pci; 14003f05f868STakashi Iwai chip->use_cache = use_cache; 14011da177e4SLinus Torvalds spin_lock_init(&chip->reg_lock); 14021da177e4SLinus Torvalds chip->irq = -1; 14031204de32STakashi Iwai init_MUTEX(&chip->irq_mutex); 14041da177e4SLinus Torvalds 14053f05f868STakashi Iwai /* store buffer sizes in bytes */ 14063f05f868STakashi Iwai chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = playback_bufsize * 1024; 14073f05f868STakashi Iwai chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capture_bufsize * 1024; 14081da177e4SLinus Torvalds 14091da177e4SLinus Torvalds /* 14101da177e4SLinus Torvalds * The NM256 has two memory ports. The first port is nothing 14111da177e4SLinus Torvalds * more than a chunk of video RAM, which is used as the I/O ring 14121da177e4SLinus Torvalds * buffer. The second port has the actual juicy stuff (like the 14131da177e4SLinus Torvalds * mixer and the playback engine control registers). 14141da177e4SLinus Torvalds */ 14151da177e4SLinus Torvalds 14161da177e4SLinus Torvalds chip->buffer_addr = pci_resource_start(pci, 0); 14171da177e4SLinus Torvalds chip->cport_addr = pci_resource_start(pci, 1); 14181da177e4SLinus Torvalds 14191da177e4SLinus Torvalds /* Init the memory port info. */ 14201da177e4SLinus Torvalds /* remap control port (#2) */ 14211da177e4SLinus Torvalds chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE, 14221da177e4SLinus Torvalds card->driver); 14231da177e4SLinus Torvalds if (chip->res_cport == NULL) { 142499b359baSTakashi Iwai snd_printk(KERN_ERR "memory region 0x%lx (size 0x%x) busy\n", 14251da177e4SLinus Torvalds chip->cport_addr, NM_PORT2_SIZE); 14261da177e4SLinus Torvalds err = -EBUSY; 14271da177e4SLinus Torvalds goto __error; 14281da177e4SLinus Torvalds } 14291da177e4SLinus Torvalds chip->cport = ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE); 14301da177e4SLinus Torvalds if (chip->cport == NULL) { 143199b359baSTakashi Iwai snd_printk(KERN_ERR "unable to map control port %lx\n", chip->cport_addr); 14321da177e4SLinus Torvalds err = -ENOMEM; 14331da177e4SLinus Torvalds goto __error; 14341da177e4SLinus Torvalds } 14351da177e4SLinus Torvalds 14361da177e4SLinus Torvalds if (!strcmp(card->driver, "NM256AV")) { 14371da177e4SLinus Torvalds /* Ok, try to see if this is a non-AC97 version of the hardware. */ 14381da177e4SLinus Torvalds pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); 14391da177e4SLinus Torvalds if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { 14403f05f868STakashi Iwai if (! force_ac97) { 14411da177e4SLinus Torvalds printk(KERN_ERR "nm256: no ac97 is found!\n"); 14421da177e4SLinus Torvalds printk(KERN_ERR " force the driver to load by passing in the module parameter\n"); 14431da177e4SLinus Torvalds printk(KERN_ERR " force_ac97=1\n"); 14441da177e4SLinus Torvalds printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n"); 14451da177e4SLinus Torvalds err = -ENXIO; 14461da177e4SLinus Torvalds goto __error; 14471da177e4SLinus Torvalds } 14481da177e4SLinus Torvalds } 14491da177e4SLinus Torvalds chip->buffer_end = 2560 * 1024; 14501da177e4SLinus Torvalds chip->interrupt = snd_nm256_interrupt; 14511da177e4SLinus Torvalds chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET; 14521da177e4SLinus Torvalds chip->mixer_status_mask = NM_MIXER_READY_MASK; 14531da177e4SLinus Torvalds } else { 14541da177e4SLinus Torvalds /* Not sure if there is any relevant detect for the ZX or not. */ 14551da177e4SLinus Torvalds if (snd_nm256_readb(chip, 0xa0b) != 0) 14561da177e4SLinus Torvalds chip->buffer_end = 6144 * 1024; 14571da177e4SLinus Torvalds else 14581da177e4SLinus Torvalds chip->buffer_end = 4096 * 1024; 14591da177e4SLinus Torvalds 14601da177e4SLinus Torvalds chip->interrupt = snd_nm256_interrupt_zx; 14611da177e4SLinus Torvalds chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; 14621da177e4SLinus Torvalds chip->mixer_status_mask = NM2_MIXER_READY_MASK; 14631da177e4SLinus Torvalds } 14641da177e4SLinus Torvalds 14651da177e4SLinus Torvalds chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; 14661da177e4SLinus Torvalds if (chip->use_cache) 14671da177e4SLinus Torvalds chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4; 14681da177e4SLinus Torvalds else 14691da177e4SLinus Torvalds chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE; 14701da177e4SLinus Torvalds 14713f05f868STakashi Iwai if (buffer_top >= chip->buffer_size && buffer_top < chip->buffer_end) 14723f05f868STakashi Iwai chip->buffer_end = buffer_top; 14731da177e4SLinus Torvalds else { 14741da177e4SLinus Torvalds /* get buffer end pointer from signature */ 14751da177e4SLinus Torvalds if ((err = snd_nm256_peek_for_sig(chip)) < 0) 14761da177e4SLinus Torvalds goto __error; 14771da177e4SLinus Torvalds } 14781da177e4SLinus Torvalds 14791da177e4SLinus Torvalds chip->buffer_start = chip->buffer_end - chip->buffer_size; 14801da177e4SLinus Torvalds chip->buffer_addr += chip->buffer_start; 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n", 14831da177e4SLinus Torvalds chip->buffer_start, chip->buffer_end); 14841da177e4SLinus Torvalds 14851da177e4SLinus Torvalds chip->res_buffer = request_mem_region(chip->buffer_addr, 14861da177e4SLinus Torvalds chip->buffer_size, 14871da177e4SLinus Torvalds card->driver); 14881da177e4SLinus Torvalds if (chip->res_buffer == NULL) { 148999b359baSTakashi Iwai snd_printk(KERN_ERR "nm256: buffer 0x%lx (size 0x%x) busy\n", 14901da177e4SLinus Torvalds chip->buffer_addr, chip->buffer_size); 14911da177e4SLinus Torvalds err = -EBUSY; 14921da177e4SLinus Torvalds goto __error; 14931da177e4SLinus Torvalds } 14941da177e4SLinus Torvalds chip->buffer = ioremap_nocache(chip->buffer_addr, chip->buffer_size); 14951da177e4SLinus Torvalds if (chip->buffer == NULL) { 14961da177e4SLinus Torvalds err = -ENOMEM; 149799b359baSTakashi Iwai snd_printk(KERN_ERR "unable to map ring buffer at %lx\n", chip->buffer_addr); 14981da177e4SLinus Torvalds goto __error; 14991da177e4SLinus Torvalds } 15001da177e4SLinus Torvalds 15011da177e4SLinus Torvalds /* set offsets */ 15021da177e4SLinus Torvalds addr = chip->buffer_start; 15031da177e4SLinus Torvalds chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr; 15041da177e4SLinus Torvalds addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize; 15051da177e4SLinus Torvalds chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr; 15061da177e4SLinus Torvalds addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; 15071da177e4SLinus Torvalds if (chip->use_cache) { 15081da177e4SLinus Torvalds chip->all_coeff_buf = addr; 15091da177e4SLinus Torvalds } else { 15101da177e4SLinus Torvalds chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr; 15111da177e4SLinus Torvalds addr += NM_MAX_PLAYBACK_COEF_SIZE; 15121da177e4SLinus Torvalds chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; 15131da177e4SLinus Torvalds } 15141da177e4SLinus Torvalds 15151da177e4SLinus Torvalds /* Fixed setting. */ 15161da177e4SLinus Torvalds chip->mixer_base = NM_MIXER_OFFSET; 15171da177e4SLinus Torvalds 15181da177e4SLinus Torvalds chip->coeffs_current = 0; 15191da177e4SLinus Torvalds 15201da177e4SLinus Torvalds snd_nm256_init_chip(chip); 15211da177e4SLinus Torvalds 15221da177e4SLinus Torvalds // pci_set_master(pci); /* needed? */ 15231da177e4SLinus Torvalds 15241da177e4SLinus Torvalds snd_card_set_pm_callback(card, nm256_suspend, nm256_resume, chip); 15251da177e4SLinus Torvalds 15261da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) 15271da177e4SLinus Torvalds goto __error; 15281da177e4SLinus Torvalds 15291da177e4SLinus Torvalds snd_card_set_dev(card, &pci->dev); 15301da177e4SLinus Torvalds 15311da177e4SLinus Torvalds *chip_ret = chip; 15321da177e4SLinus Torvalds return 0; 15331da177e4SLinus Torvalds 15341da177e4SLinus Torvalds __error: 15351da177e4SLinus Torvalds snd_nm256_free(chip); 15361da177e4SLinus Torvalds return err; 15371da177e4SLinus Torvalds } 15381da177e4SLinus Torvalds 15391da177e4SLinus Torvalds 15401da177e4SLinus Torvalds struct nm256_quirk { 15411da177e4SLinus Torvalds unsigned short vendor; 15421da177e4SLinus Torvalds unsigned short device; 15431da177e4SLinus Torvalds int type; 15441da177e4SLinus Torvalds }; 15451da177e4SLinus Torvalds 154647530cf4SJohn W. Linville enum { NM_BLACKLISTED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 }; 15471da177e4SLinus Torvalds 15481da177e4SLinus Torvalds static struct nm256_quirk nm256_quirks[] __devinitdata = { 15491da177e4SLinus Torvalds /* HP omnibook 4150 has cs4232 codec internally */ 15501da177e4SLinus Torvalds { .vendor = 0x103c, .device = 0x0007, .type = NM_BLACKLISTED }, 15511da177e4SLinus Torvalds /* Sony PCG-F305 */ 15521da177e4SLinus Torvalds { .vendor = 0x104d, .device = 0x8041, .type = NM_RESET_WORKAROUND }, 15531da177e4SLinus Torvalds /* Dell Latitude LS */ 15541da177e4SLinus Torvalds { .vendor = 0x1028, .device = 0x0080, .type = NM_RESET_WORKAROUND }, 155547530cf4SJohn W. Linville /* Dell Latitude CSx */ 155647530cf4SJohn W. Linville { .vendor = 0x1028, .device = 0x0091, .type = NM_RESET_WORKAROUND_2 }, 15571da177e4SLinus Torvalds { } /* terminator */ 15581da177e4SLinus Torvalds }; 15591da177e4SLinus Torvalds 15601da177e4SLinus Torvalds 15611da177e4SLinus Torvalds static int __devinit snd_nm256_probe(struct pci_dev *pci, 15621da177e4SLinus Torvalds const struct pci_device_id *pci_id) 15631da177e4SLinus Torvalds { 15641da177e4SLinus Torvalds snd_card_t *card; 15651da177e4SLinus Torvalds nm256_t *chip; 15661da177e4SLinus Torvalds int err; 15671da177e4SLinus Torvalds struct nm256_quirk *q; 15681da177e4SLinus Torvalds u16 subsystem_vendor, subsystem_device; 15691da177e4SLinus Torvalds 15701da177e4SLinus Torvalds pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); 15711da177e4SLinus Torvalds pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device); 15721da177e4SLinus Torvalds 15731da177e4SLinus Torvalds for (q = nm256_quirks; q->vendor; q++) { 15741da177e4SLinus Torvalds if (q->vendor == subsystem_vendor && q->device == subsystem_device) { 15751da177e4SLinus Torvalds switch (q->type) { 15761da177e4SLinus Torvalds case NM_BLACKLISTED: 15771da177e4SLinus Torvalds printk(KERN_INFO "nm256: The device is blacklisted. Loading stopped\n"); 15781da177e4SLinus Torvalds return -ENODEV; 157947530cf4SJohn W. Linville case NM_RESET_WORKAROUND_2: 15808a3fb4d0STakashi Iwai reset_workaround_2 = 1; 158147530cf4SJohn W. Linville /* Fall-through */ 15821da177e4SLinus Torvalds case NM_RESET_WORKAROUND: 15838a3fb4d0STakashi Iwai reset_workaround = 1; 15841da177e4SLinus Torvalds break; 15851da177e4SLinus Torvalds } 15861da177e4SLinus Torvalds } 15871da177e4SLinus Torvalds } 15881da177e4SLinus Torvalds 15898a3fb4d0STakashi Iwai card = snd_card_new(index, id, THIS_MODULE, 0); 15901da177e4SLinus Torvalds if (card == NULL) 15911da177e4SLinus Torvalds return -ENOMEM; 15921da177e4SLinus Torvalds 15931da177e4SLinus Torvalds switch (pci->device) { 15941da177e4SLinus Torvalds case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: 15951da177e4SLinus Torvalds strcpy(card->driver, "NM256AV"); 15961da177e4SLinus Torvalds break; 15971da177e4SLinus Torvalds case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: 15981da177e4SLinus Torvalds strcpy(card->driver, "NM256ZX"); 15991da177e4SLinus Torvalds break; 16001da177e4SLinus Torvalds case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO: 16011da177e4SLinus Torvalds strcpy(card->driver, "NM256XL+"); 16021da177e4SLinus Torvalds break; 16031da177e4SLinus Torvalds default: 160499b359baSTakashi Iwai snd_printk(KERN_ERR "invalid device id 0x%x\n", pci->device); 16051da177e4SLinus Torvalds snd_card_free(card); 16061da177e4SLinus Torvalds return -EINVAL; 16071da177e4SLinus Torvalds } 16081da177e4SLinus Torvalds 16098a3fb4d0STakashi Iwai if (vaio_hack) 16103f05f868STakashi Iwai buffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */ 16111da177e4SLinus Torvalds 16128a3fb4d0STakashi Iwai if (playback_bufsize < 4) 16138a3fb4d0STakashi Iwai playback_bufsize = 4; 16148a3fb4d0STakashi Iwai if (playback_bufsize > 128) 16158a3fb4d0STakashi Iwai playback_bufsize = 128; 16168a3fb4d0STakashi Iwai if (capture_bufsize < 4) 16178a3fb4d0STakashi Iwai capture_bufsize = 4; 16188a3fb4d0STakashi Iwai if (capture_bufsize > 128) 16198a3fb4d0STakashi Iwai capture_bufsize = 128; 16203f05f868STakashi Iwai if ((err = snd_nm256_create(card, pci, &chip)) < 0) { 16211da177e4SLinus Torvalds snd_card_free(card); 16221da177e4SLinus Torvalds return err; 16231da177e4SLinus Torvalds } 16241da177e4SLinus Torvalds 16258a3fb4d0STakashi Iwai if (reset_workaround) { 16261da177e4SLinus Torvalds snd_printdd(KERN_INFO "nm256: reset_workaround activated\n"); 16271da177e4SLinus Torvalds chip->reset_workaround = 1; 16281da177e4SLinus Torvalds } 16291da177e4SLinus Torvalds 16308a3fb4d0STakashi Iwai if (reset_workaround_2) { 163147530cf4SJohn W. Linville snd_printdd(KERN_INFO "nm256: reset_workaround_2 activated\n"); 163247530cf4SJohn W. Linville chip->reset_workaround_2 = 1; 163347530cf4SJohn W. Linville } 163447530cf4SJohn W. Linville 16351da177e4SLinus Torvalds if ((err = snd_nm256_pcm(chip, 0)) < 0 || 16361da177e4SLinus Torvalds (err = snd_nm256_mixer(chip)) < 0) { 16371da177e4SLinus Torvalds snd_card_free(card); 16381da177e4SLinus Torvalds return err; 16391da177e4SLinus Torvalds } 16401da177e4SLinus Torvalds 16411da177e4SLinus Torvalds sprintf(card->shortname, "NeoMagic %s", card->driver); 16421da177e4SLinus Torvalds sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d", 16431da177e4SLinus Torvalds card->shortname, 16441da177e4SLinus Torvalds chip->buffer_addr, chip->cport_addr, chip->irq); 16451da177e4SLinus Torvalds 16461da177e4SLinus Torvalds if ((err = snd_card_register(card)) < 0) { 16471da177e4SLinus Torvalds snd_card_free(card); 16481da177e4SLinus Torvalds return err; 16491da177e4SLinus Torvalds } 16501da177e4SLinus Torvalds 16511da177e4SLinus Torvalds pci_set_drvdata(pci, card); 16521da177e4SLinus Torvalds return 0; 16531da177e4SLinus Torvalds } 16541da177e4SLinus Torvalds 16551da177e4SLinus Torvalds static void __devexit snd_nm256_remove(struct pci_dev *pci) 16561da177e4SLinus Torvalds { 16571da177e4SLinus Torvalds snd_card_free(pci_get_drvdata(pci)); 16581da177e4SLinus Torvalds pci_set_drvdata(pci, NULL); 16591da177e4SLinus Torvalds } 16601da177e4SLinus Torvalds 16611da177e4SLinus Torvalds 16621da177e4SLinus Torvalds static struct pci_driver driver = { 16631da177e4SLinus Torvalds .name = "NeoMagic 256", 16641da177e4SLinus Torvalds .id_table = snd_nm256_ids, 16651da177e4SLinus Torvalds .probe = snd_nm256_probe, 16661da177e4SLinus Torvalds .remove = __devexit_p(snd_nm256_remove), 16671da177e4SLinus Torvalds SND_PCI_PM_CALLBACKS 16681da177e4SLinus Torvalds }; 16691da177e4SLinus Torvalds 16701da177e4SLinus Torvalds 16711da177e4SLinus Torvalds static int __init alsa_card_nm256_init(void) 16721da177e4SLinus Torvalds { 167301d25d46STakashi Iwai return pci_register_driver(&driver); 16741da177e4SLinus Torvalds } 16751da177e4SLinus Torvalds 16761da177e4SLinus Torvalds static void __exit alsa_card_nm256_exit(void) 16771da177e4SLinus Torvalds { 16781da177e4SLinus Torvalds pci_unregister_driver(&driver); 16791da177e4SLinus Torvalds } 16801da177e4SLinus Torvalds 16811da177e4SLinus Torvalds module_init(alsa_card_nm256_init) 16821da177e4SLinus Torvalds module_exit(alsa_card_nm256_exit) 1683