xref: /openbmc/linux/sound/pci/nm256/nm256.c (revision 3f05f868)
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