18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for NeoMagic 256AV and 256ZX chipsets.
48c2ecf20Sopenharmony_ci * Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Based on nm256_audio.c OSS driver in linux kernel.
78c2ecf20Sopenharmony_ci * The original author of OSS nm256 driver wishes to remain anonymous,
88c2ecf20Sopenharmony_ci * so I just put my acknoledgment to him/her here.
98c2ecf20Sopenharmony_ci * The original author's web page is found at
108c2ecf20Sopenharmony_ci *	http://www.uglx.org/sony.html
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/pci.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/mutex.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <sound/core.h>
238c2ecf20Sopenharmony_ci#include <sound/info.h>
248c2ecf20Sopenharmony_ci#include <sound/control.h>
258c2ecf20Sopenharmony_ci#include <sound/pcm.h>
268c2ecf20Sopenharmony_ci#include <sound/ac97_codec.h>
278c2ecf20Sopenharmony_ci#include <sound/initval.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define CARD_NAME "NeoMagic 256AV/ZX"
308c2ecf20Sopenharmony_ci#define DRIVER_NAME "NM256"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciMODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NeoMagic NM256AV/ZX");
348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
358c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{NeoMagic,NM256AV},"
368c2ecf20Sopenharmony_ci		"{NeoMagic,NM256ZX}}");
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * some compile conditions.
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1;	/* Index */
438c2ecf20Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */
448c2ecf20Sopenharmony_cistatic int playback_bufsize = 16;
458c2ecf20Sopenharmony_cistatic int capture_bufsize = 16;
468c2ecf20Sopenharmony_cistatic bool force_ac97;			/* disabled as default */
478c2ecf20Sopenharmony_cistatic int buffer_top;			/* not specified */
488c2ecf20Sopenharmony_cistatic bool use_cache;			/* disabled */
498c2ecf20Sopenharmony_cistatic bool vaio_hack;			/* disabled */
508c2ecf20Sopenharmony_cistatic bool reset_workaround;
518c2ecf20Sopenharmony_cistatic bool reset_workaround_2;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cimodule_param(index, int, 0444);
548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
558c2ecf20Sopenharmony_cimodule_param(id, charp, 0444);
568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
578c2ecf20Sopenharmony_cimodule_param(playback_bufsize, int, 0444);
588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(playback_bufsize, "DAC frame size in kB for " CARD_NAME " soundcard.");
598c2ecf20Sopenharmony_cimodule_param(capture_bufsize, int, 0444);
608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(capture_bufsize, "ADC frame size in kB for " CARD_NAME " soundcard.");
618c2ecf20Sopenharmony_cimodule_param(force_ac97, bool, 0444);
628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_ac97, "Force to use AC97 codec for " CARD_NAME " soundcard.");
638c2ecf20Sopenharmony_cimodule_param(buffer_top, int, 0444);
648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(buffer_top, "Set the top address of audio buffer for " CARD_NAME " soundcard.");
658c2ecf20Sopenharmony_cimodule_param(use_cache, bool, 0444);
668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_cache, "Enable the cache for coefficient table access.");
678c2ecf20Sopenharmony_cimodule_param(vaio_hack, bool, 0444);
688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vaio_hack, "Enable workaround for Sony VAIO notebooks.");
698c2ecf20Sopenharmony_cimodule_param(reset_workaround, bool, 0444);
708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset_workaround, "Enable AC97 RESET workaround for some laptops.");
718c2ecf20Sopenharmony_cimodule_param(reset_workaround_2, bool, 0444);
728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(reset_workaround_2, "Enable extended AC97 RESET workaround for some other laptops.");
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* just for backward compatibility */
758c2ecf20Sopenharmony_cistatic bool enable;
768c2ecf20Sopenharmony_cimodule_param(enable, bool, 0444);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * hw definitions
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* The BIOS signature. */
858c2ecf20Sopenharmony_ci#define NM_SIGNATURE 0x4e4d0000
868c2ecf20Sopenharmony_ci/* Signature mask. */
878c2ecf20Sopenharmony_ci#define NM_SIG_MASK 0xffff0000
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* Size of the second memory area. */
908c2ecf20Sopenharmony_ci#define NM_PORT2_SIZE 4096
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* The base offset of the mixer in the second memory area. */
938c2ecf20Sopenharmony_ci#define NM_MIXER_OFFSET 0x600
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* The maximum size of a coefficient entry. */
968c2ecf20Sopenharmony_ci#define NM_MAX_PLAYBACK_COEF_SIZE	0x5000
978c2ecf20Sopenharmony_ci#define NM_MAX_RECORD_COEF_SIZE		0x1260
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/* The interrupt register. */
1008c2ecf20Sopenharmony_ci#define NM_INT_REG 0xa04
1018c2ecf20Sopenharmony_ci/* And its bits. */
1028c2ecf20Sopenharmony_ci#define NM_PLAYBACK_INT 0x40
1038c2ecf20Sopenharmony_ci#define NM_RECORD_INT 0x100
1048c2ecf20Sopenharmony_ci#define NM_MISC_INT_1 0x4000
1058c2ecf20Sopenharmony_ci#define NM_MISC_INT_2 0x1
1068c2ecf20Sopenharmony_ci#define NM_ACK_INT(chip, X) snd_nm256_writew(chip, NM_INT_REG, (X) << 1)
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* The AV's "mixer ready" status bit and location. */
1098c2ecf20Sopenharmony_ci#define NM_MIXER_STATUS_OFFSET 0xa04
1108c2ecf20Sopenharmony_ci#define NM_MIXER_READY_MASK 0x0800
1118c2ecf20Sopenharmony_ci#define NM_MIXER_PRESENCE 0xa06
1128c2ecf20Sopenharmony_ci#define NM_PRESENCE_MASK 0x0050
1138c2ecf20Sopenharmony_ci#define NM_PRESENCE_VALUE 0x0040
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * For the ZX.  It uses the same interrupt register, but it holds 32
1178c2ecf20Sopenharmony_ci * bits instead of 16.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_ci#define NM2_PLAYBACK_INT 0x10000
1208c2ecf20Sopenharmony_ci#define NM2_RECORD_INT 0x80000
1218c2ecf20Sopenharmony_ci#define NM2_MISC_INT_1 0x8
1228c2ecf20Sopenharmony_ci#define NM2_MISC_INT_2 0x2
1238c2ecf20Sopenharmony_ci#define NM2_ACK_INT(chip, X) snd_nm256_writel(chip, NM_INT_REG, (X))
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* The ZX's "mixer ready" status bit and location. */
1268c2ecf20Sopenharmony_ci#define NM2_MIXER_STATUS_OFFSET 0xa06
1278c2ecf20Sopenharmony_ci#define NM2_MIXER_READY_MASK 0x0800
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* The playback registers start from here. */
1308c2ecf20Sopenharmony_ci#define NM_PLAYBACK_REG_OFFSET 0x0
1318c2ecf20Sopenharmony_ci/* The record registers start from here. */
1328c2ecf20Sopenharmony_ci#define NM_RECORD_REG_OFFSET 0x200
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/* The rate register is located 2 bytes from the start of the register area. */
1358c2ecf20Sopenharmony_ci#define NM_RATE_REG_OFFSET 2
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/* Mono/stereo flag, number of bits on playback, and rate mask. */
1388c2ecf20Sopenharmony_ci#define NM_RATE_STEREO 1
1398c2ecf20Sopenharmony_ci#define NM_RATE_BITS_16 2
1408c2ecf20Sopenharmony_ci#define NM_RATE_MASK 0xf0
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/* Playback enable register. */
1438c2ecf20Sopenharmony_ci#define NM_PLAYBACK_ENABLE_REG (NM_PLAYBACK_REG_OFFSET + 0x1)
1448c2ecf20Sopenharmony_ci#define NM_PLAYBACK_ENABLE_FLAG 1
1458c2ecf20Sopenharmony_ci#define NM_PLAYBACK_ONESHOT 2
1468c2ecf20Sopenharmony_ci#define NM_PLAYBACK_FREERUN 4
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* Mutes the audio output. */
1498c2ecf20Sopenharmony_ci#define NM_AUDIO_MUTE_REG (NM_PLAYBACK_REG_OFFSET + 0x18)
1508c2ecf20Sopenharmony_ci#define NM_AUDIO_MUTE_LEFT 0x8000
1518c2ecf20Sopenharmony_ci#define NM_AUDIO_MUTE_RIGHT 0x0080
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/* Recording enable register. */
1548c2ecf20Sopenharmony_ci#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
1558c2ecf20Sopenharmony_ci#define NM_RECORD_ENABLE_FLAG 1
1568c2ecf20Sopenharmony_ci#define NM_RECORD_FREERUN 2
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/* coefficient buffer pointer */
1598c2ecf20Sopenharmony_ci#define NM_COEFF_START_OFFSET	0x1c
1608c2ecf20Sopenharmony_ci#define NM_COEFF_END_OFFSET	0x20
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* DMA buffer offsets */
1638c2ecf20Sopenharmony_ci#define NM_RBUFFER_START (NM_RECORD_REG_OFFSET + 0x4)
1648c2ecf20Sopenharmony_ci#define NM_RBUFFER_END   (NM_RECORD_REG_OFFSET + 0x10)
1658c2ecf20Sopenharmony_ci#define NM_RBUFFER_WMARK (NM_RECORD_REG_OFFSET + 0xc)
1668c2ecf20Sopenharmony_ci#define NM_RBUFFER_CURRP (NM_RECORD_REG_OFFSET + 0x8)
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci#define NM_PBUFFER_START (NM_PLAYBACK_REG_OFFSET + 0x4)
1698c2ecf20Sopenharmony_ci#define NM_PBUFFER_END   (NM_PLAYBACK_REG_OFFSET + 0x14)
1708c2ecf20Sopenharmony_ci#define NM_PBUFFER_WMARK (NM_PLAYBACK_REG_OFFSET + 0xc)
1718c2ecf20Sopenharmony_ci#define NM_PBUFFER_CURRP (NM_PLAYBACK_REG_OFFSET + 0x8)
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistruct nm256_stream {
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	struct nm256 *chip;
1768c2ecf20Sopenharmony_ci	struct snd_pcm_substream *substream;
1778c2ecf20Sopenharmony_ci	int running;
1788c2ecf20Sopenharmony_ci	int suspended;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	u32 buf;	/* offset from chip->buffer */
1818c2ecf20Sopenharmony_ci	int bufsize;	/* buffer size in bytes */
1828c2ecf20Sopenharmony_ci	void __iomem *bufptr;		/* mapped pointer */
1838c2ecf20Sopenharmony_ci	unsigned long bufptr_addr;	/* physical address of the mapped pointer */
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	int dma_size;		/* buffer size of the substream in bytes */
1868c2ecf20Sopenharmony_ci	int period_size;	/* period size in bytes */
1878c2ecf20Sopenharmony_ci	int periods;		/* # of periods */
1888c2ecf20Sopenharmony_ci	int shift;		/* bit shifts */
1898c2ecf20Sopenharmony_ci	int cur_period;		/* current period # */
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistruct nm256 {
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	struct snd_card *card;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	void __iomem *cport;		/* control port */
1988c2ecf20Sopenharmony_ci	struct resource *res_cport;	/* its resource */
1998c2ecf20Sopenharmony_ci	unsigned long cport_addr;	/* physical address */
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	void __iomem *buffer;		/* buffer */
2028c2ecf20Sopenharmony_ci	struct resource *res_buffer;	/* its resource */
2038c2ecf20Sopenharmony_ci	unsigned long buffer_addr;	/* buffer phyiscal address */
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	u32 buffer_start;		/* start offset from pci resource 0 */
2068c2ecf20Sopenharmony_ci	u32 buffer_end;			/* end offset */
2078c2ecf20Sopenharmony_ci	u32 buffer_size;		/* total buffer size */
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	u32 all_coeff_buf;		/* coefficient buffer */
2108c2ecf20Sopenharmony_ci	u32 coeff_buf[2];		/* coefficient buffer for each stream */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	unsigned int coeffs_current: 1;	/* coeff. table is loaded? */
2138c2ecf20Sopenharmony_ci	unsigned int use_cache: 1;	/* use one big coef. table */
2148c2ecf20Sopenharmony_ci	unsigned int reset_workaround: 1; /* Workaround for some laptops to avoid freeze */
2158c2ecf20Sopenharmony_ci	unsigned int reset_workaround_2: 1; /* Extended workaround for some other laptops to avoid freeze */
2168c2ecf20Sopenharmony_ci	unsigned int in_resume: 1;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	int mixer_base;			/* register offset of ac97 mixer */
2198c2ecf20Sopenharmony_ci	int mixer_status_offset;	/* offset of mixer status reg. */
2208c2ecf20Sopenharmony_ci	int mixer_status_mask;		/* bit mask to test the mixer status */
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	int irq;
2238c2ecf20Sopenharmony_ci	int irq_acks;
2248c2ecf20Sopenharmony_ci	irq_handler_t interrupt;
2258c2ecf20Sopenharmony_ci	int badintrcount;		/* counter to check bogus interrupts */
2268c2ecf20Sopenharmony_ci	struct mutex irq_mutex;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	struct nm256_stream streams[2];
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	struct snd_ac97 *ac97;
2318c2ecf20Sopenharmony_ci	unsigned short *ac97_regs; /* register caches, only for valid regs */
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	struct pci_dev *pci;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	spinlock_t reg_lock;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/*
2438c2ecf20Sopenharmony_ci * include coefficient table
2448c2ecf20Sopenharmony_ci */
2458c2ecf20Sopenharmony_ci#include "nm256_coef.c"
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/*
2498c2ecf20Sopenharmony_ci * PCI ids
2508c2ecf20Sopenharmony_ci */
2518c2ecf20Sopenharmony_cistatic const struct pci_device_id snd_nm256_ids[] = {
2528c2ecf20Sopenharmony_ci	{PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO), 0},
2538c2ecf20Sopenharmony_ci	{PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO), 0},
2548c2ecf20Sopenharmony_ci	{PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO), 0},
2558c2ecf20Sopenharmony_ci	{0,},
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, snd_nm256_ids);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/*
2628c2ecf20Sopenharmony_ci * lowlvel stuffs
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic inline u8
2668c2ecf20Sopenharmony_cisnd_nm256_readb(struct nm256 *chip, int offset)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	return readb(chip->cport + offset);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic inline u16
2728c2ecf20Sopenharmony_cisnd_nm256_readw(struct nm256 *chip, int offset)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	return readw(chip->cport + offset);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic inline u32
2788c2ecf20Sopenharmony_cisnd_nm256_readl(struct nm256 *chip, int offset)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	return readl(chip->cport + offset);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic inline void
2848c2ecf20Sopenharmony_cisnd_nm256_writeb(struct nm256 *chip, int offset, u8 val)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	writeb(val, chip->cport + offset);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic inline void
2908c2ecf20Sopenharmony_cisnd_nm256_writew(struct nm256 *chip, int offset, u16 val)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	writew(val, chip->cport + offset);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic inline void
2968c2ecf20Sopenharmony_cisnd_nm256_writel(struct nm256 *chip, int offset, u32 val)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	writel(val, chip->cport + offset);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic inline void
3028c2ecf20Sopenharmony_cisnd_nm256_write_buffer(struct nm256 *chip, const void *src, int offset, int size)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	offset -= chip->buffer_start;
3058c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
3068c2ecf20Sopenharmony_ci	if (offset < 0 || offset >= chip->buffer_size) {
3078c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
3088c2ecf20Sopenharmony_ci			"write_buffer invalid offset = %d size = %d\n",
3098c2ecf20Sopenharmony_ci			   offset, size);
3108c2ecf20Sopenharmony_ci		return;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci#endif
3138c2ecf20Sopenharmony_ci	memcpy_toio(chip->buffer + offset, src, size);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci/*
3178c2ecf20Sopenharmony_ci * coefficient handlers -- what a magic!
3188c2ecf20Sopenharmony_ci */
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_cistatic u16
3218c2ecf20Sopenharmony_cisnd_nm256_get_start_offset(int which)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	u16 offset = 0;
3248c2ecf20Sopenharmony_ci	while (which-- > 0)
3258c2ecf20Sopenharmony_ci		offset += coefficient_sizes[which];
3268c2ecf20Sopenharmony_ci	return offset;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void
3308c2ecf20Sopenharmony_cisnd_nm256_load_one_coefficient(struct nm256 *chip, int stream, u32 port, int which)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u32 coeff_buf = chip->coeff_buf[stream];
3338c2ecf20Sopenharmony_ci	u16 offset = snd_nm256_get_start_offset(which);
3348c2ecf20Sopenharmony_ci	u16 size = coefficient_sizes[which];
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	snd_nm256_write_buffer(chip, coefficients + offset, coeff_buf, size);
3378c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, port, coeff_buf);
3388c2ecf20Sopenharmony_ci	/* ???  Record seems to behave differently than playback.  */
3398c2ecf20Sopenharmony_ci	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
3408c2ecf20Sopenharmony_ci		size--;
3418c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, port + 4, coeff_buf + size);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic void
3458c2ecf20Sopenharmony_cisnd_nm256_load_coefficient(struct nm256 *chip, int stream, int number)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	/* The enable register for the specified engine.  */
3488c2ecf20Sopenharmony_ci	u32 poffset = (stream == SNDRV_PCM_STREAM_CAPTURE ?
3498c2ecf20Sopenharmony_ci		       NM_RECORD_ENABLE_REG : NM_PLAYBACK_ENABLE_REG);
3508c2ecf20Sopenharmony_ci	u32 addr = NM_COEFF_START_OFFSET;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	addr += (stream == SNDRV_PCM_STREAM_CAPTURE ?
3538c2ecf20Sopenharmony_ci		 NM_RECORD_REG_OFFSET : NM_PLAYBACK_REG_OFFSET);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (snd_nm256_readb(chip, poffset) & 1) {
3568c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev,
3578c2ecf20Sopenharmony_ci			"NM256: Engine was enabled while loading coefficients!\n");
3588c2ecf20Sopenharmony_ci		return;
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	/* The recording engine uses coefficient values 8-15.  */
3628c2ecf20Sopenharmony_ci	number &= 7;
3638c2ecf20Sopenharmony_ci	if (stream == SNDRV_PCM_STREAM_CAPTURE)
3648c2ecf20Sopenharmony_ci		number += 8;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (! chip->use_cache) {
3678c2ecf20Sopenharmony_ci		snd_nm256_load_one_coefficient(chip, stream, addr, number);
3688c2ecf20Sopenharmony_ci		return;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	if (! chip->coeffs_current) {
3718c2ecf20Sopenharmony_ci		snd_nm256_write_buffer(chip, coefficients, chip->all_coeff_buf,
3728c2ecf20Sopenharmony_ci				       NM_TOTAL_COEFF_COUNT * 4);
3738c2ecf20Sopenharmony_ci		chip->coeffs_current = 1;
3748c2ecf20Sopenharmony_ci	} else {
3758c2ecf20Sopenharmony_ci		u32 base = chip->all_coeff_buf;
3768c2ecf20Sopenharmony_ci		u32 offset = snd_nm256_get_start_offset(number);
3778c2ecf20Sopenharmony_ci		u32 end_offset = offset + coefficient_sizes[number];
3788c2ecf20Sopenharmony_ci		snd_nm256_writel(chip, addr, base + offset);
3798c2ecf20Sopenharmony_ci		if (stream == SNDRV_PCM_STREAM_PLAYBACK)
3808c2ecf20Sopenharmony_ci			end_offset--;
3818c2ecf20Sopenharmony_ci		snd_nm256_writel(chip, addr + 4, base + end_offset);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci/* The actual rates supported by the card. */
3878c2ecf20Sopenharmony_cistatic const unsigned int samplerates[8] = {
3888c2ecf20Sopenharmony_ci	8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000,
3898c2ecf20Sopenharmony_ci};
3908c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list constraints_rates = {
3918c2ecf20Sopenharmony_ci	.count = ARRAY_SIZE(samplerates),
3928c2ecf20Sopenharmony_ci	.list = samplerates,
3938c2ecf20Sopenharmony_ci	.mask = 0,
3948c2ecf20Sopenharmony_ci};
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci/*
3978c2ecf20Sopenharmony_ci * return the index of the target rate
3988c2ecf20Sopenharmony_ci */
3998c2ecf20Sopenharmony_cistatic int
4008c2ecf20Sopenharmony_cisnd_nm256_fixed_rate(unsigned int rate)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	unsigned int i;
4038c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(samplerates); i++) {
4048c2ecf20Sopenharmony_ci		if (rate == samplerates[i])
4058c2ecf20Sopenharmony_ci			return i;
4068c2ecf20Sopenharmony_ci	}
4078c2ecf20Sopenharmony_ci	snd_BUG();
4088c2ecf20Sopenharmony_ci	return 0;
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/*
4128c2ecf20Sopenharmony_ci * set sample rate and format
4138c2ecf20Sopenharmony_ci */
4148c2ecf20Sopenharmony_cistatic void
4158c2ecf20Sopenharmony_cisnd_nm256_set_format(struct nm256 *chip, struct nm256_stream *s,
4168c2ecf20Sopenharmony_ci		     struct snd_pcm_substream *substream)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
4198c2ecf20Sopenharmony_ci	int rate_index = snd_nm256_fixed_rate(runtime->rate);
4208c2ecf20Sopenharmony_ci	unsigned char ratebits = (rate_index << 4) & NM_RATE_MASK;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	s->shift = 0;
4238c2ecf20Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 16) {
4248c2ecf20Sopenharmony_ci		ratebits |= NM_RATE_BITS_16;
4258c2ecf20Sopenharmony_ci		s->shift++;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci	if (runtime->channels > 1) {
4288c2ecf20Sopenharmony_ci		ratebits |= NM_RATE_STEREO;
4298c2ecf20Sopenharmony_ci		s->shift++;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	runtime->rate = samplerates[rate_index];
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	switch (substream->stream) {
4358c2ecf20Sopenharmony_ci	case SNDRV_PCM_STREAM_PLAYBACK:
4368c2ecf20Sopenharmony_ci		snd_nm256_load_coefficient(chip, 0, rate_index); /* 0 = playback */
4378c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip,
4388c2ecf20Sopenharmony_ci				 NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET,
4398c2ecf20Sopenharmony_ci				 ratebits);
4408c2ecf20Sopenharmony_ci		break;
4418c2ecf20Sopenharmony_ci	case SNDRV_PCM_STREAM_CAPTURE:
4428c2ecf20Sopenharmony_ci		snd_nm256_load_coefficient(chip, 1, rate_index); /* 1 = record */
4438c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip,
4448c2ecf20Sopenharmony_ci				 NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET,
4458c2ecf20Sopenharmony_ci				 ratebits);
4468c2ecf20Sopenharmony_ci		break;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/* acquire interrupt */
4518c2ecf20Sopenharmony_cistatic int snd_nm256_acquire_irq(struct nm256 *chip)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	mutex_lock(&chip->irq_mutex);
4548c2ecf20Sopenharmony_ci	if (chip->irq < 0) {
4558c2ecf20Sopenharmony_ci		if (request_irq(chip->pci->irq, chip->interrupt, IRQF_SHARED,
4568c2ecf20Sopenharmony_ci				KBUILD_MODNAME, chip)) {
4578c2ecf20Sopenharmony_ci			dev_err(chip->card->dev,
4588c2ecf20Sopenharmony_ci				"unable to grab IRQ %d\n", chip->pci->irq);
4598c2ecf20Sopenharmony_ci			mutex_unlock(&chip->irq_mutex);
4608c2ecf20Sopenharmony_ci			return -EBUSY;
4618c2ecf20Sopenharmony_ci		}
4628c2ecf20Sopenharmony_ci		chip->irq = chip->pci->irq;
4638c2ecf20Sopenharmony_ci		chip->card->sync_irq = chip->irq;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci	chip->irq_acks++;
4668c2ecf20Sopenharmony_ci	mutex_unlock(&chip->irq_mutex);
4678c2ecf20Sopenharmony_ci	return 0;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/* release interrupt */
4718c2ecf20Sopenharmony_cistatic void snd_nm256_release_irq(struct nm256 *chip)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	mutex_lock(&chip->irq_mutex);
4748c2ecf20Sopenharmony_ci	if (chip->irq_acks > 0)
4758c2ecf20Sopenharmony_ci		chip->irq_acks--;
4768c2ecf20Sopenharmony_ci	if (chip->irq_acks == 0 && chip->irq >= 0) {
4778c2ecf20Sopenharmony_ci		free_irq(chip->irq, chip);
4788c2ecf20Sopenharmony_ci		chip->irq = -1;
4798c2ecf20Sopenharmony_ci		chip->card->sync_irq = -1;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci	mutex_unlock(&chip->irq_mutex);
4828c2ecf20Sopenharmony_ci}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci/*
4858c2ecf20Sopenharmony_ci * start / stop
4868c2ecf20Sopenharmony_ci */
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci/* update the watermark (current period) */
4898c2ecf20Sopenharmony_cistatic void snd_nm256_pcm_mark(struct nm256 *chip, struct nm256_stream *s, int reg)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	s->cur_period++;
4928c2ecf20Sopenharmony_ci	s->cur_period %= s->periods;
4938c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, reg, s->buf + s->cur_period * s->period_size);
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci#define snd_nm256_playback_mark(chip, s) snd_nm256_pcm_mark(chip, s, NM_PBUFFER_WMARK)
4978c2ecf20Sopenharmony_ci#define snd_nm256_capture_mark(chip, s)  snd_nm256_pcm_mark(chip, s, NM_RBUFFER_WMARK)
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic void
5008c2ecf20Sopenharmony_cisnd_nm256_playback_start(struct nm256 *chip, struct nm256_stream *s,
5018c2ecf20Sopenharmony_ci			 struct snd_pcm_substream *substream)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	/* program buffer pointers */
5048c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, NM_PBUFFER_START, s->buf);
5058c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, NM_PBUFFER_END, s->buf + s->dma_size - (1 << s->shift));
5068c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, NM_PBUFFER_CURRP, s->buf);
5078c2ecf20Sopenharmony_ci	snd_nm256_playback_mark(chip, s);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/* Enable playback engine and interrupts. */
5108c2ecf20Sopenharmony_ci	snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG,
5118c2ecf20Sopenharmony_ci			 NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN);
5128c2ecf20Sopenharmony_ci	/* Enable both channels. */
5138c2ecf20Sopenharmony_ci	snd_nm256_writew(chip, NM_AUDIO_MUTE_REG, 0x0);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic void
5178c2ecf20Sopenharmony_cisnd_nm256_capture_start(struct nm256 *chip, struct nm256_stream *s,
5188c2ecf20Sopenharmony_ci			struct snd_pcm_substream *substream)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	/* program buffer pointers */
5218c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, NM_RBUFFER_START, s->buf);
5228c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, NM_RBUFFER_END, s->buf + s->dma_size);
5238c2ecf20Sopenharmony_ci	snd_nm256_writel(chip, NM_RBUFFER_CURRP, s->buf);
5248c2ecf20Sopenharmony_ci	snd_nm256_capture_mark(chip, s);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* Enable playback engine and interrupts. */
5278c2ecf20Sopenharmony_ci	snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG,
5288c2ecf20Sopenharmony_ci			 NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/* Stop the play engine. */
5328c2ecf20Sopenharmony_cistatic void
5338c2ecf20Sopenharmony_cisnd_nm256_playback_stop(struct nm256 *chip)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	/* Shut off sound from both channels. */
5368c2ecf20Sopenharmony_ci	snd_nm256_writew(chip, NM_AUDIO_MUTE_REG,
5378c2ecf20Sopenharmony_ci			 NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT);
5388c2ecf20Sopenharmony_ci	/* Disable play engine. */
5398c2ecf20Sopenharmony_ci	snd_nm256_writeb(chip, NM_PLAYBACK_ENABLE_REG, 0);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic void
5438c2ecf20Sopenharmony_cisnd_nm256_capture_stop(struct nm256 *chip)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	/* Disable recording engine. */
5468c2ecf20Sopenharmony_ci	snd_nm256_writeb(chip, NM_RECORD_ENABLE_REG, 0);
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic int
5508c2ecf20Sopenharmony_cisnd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
5538c2ecf20Sopenharmony_ci	struct nm256_stream *s = substream->runtime->private_data;
5548c2ecf20Sopenharmony_ci	int err = 0;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!s))
5578c2ecf20Sopenharmony_ci		return -ENXIO;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
5608c2ecf20Sopenharmony_ci	switch (cmd) {
5618c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
5628c2ecf20Sopenharmony_ci		s->suspended = 0;
5638c2ecf20Sopenharmony_ci		fallthrough;
5648c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
5658c2ecf20Sopenharmony_ci		if (! s->running) {
5668c2ecf20Sopenharmony_ci			snd_nm256_playback_start(chip, s, substream);
5678c2ecf20Sopenharmony_ci			s->running = 1;
5688c2ecf20Sopenharmony_ci		}
5698c2ecf20Sopenharmony_ci		break;
5708c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
5718c2ecf20Sopenharmony_ci		s->suspended = 1;
5728c2ecf20Sopenharmony_ci		fallthrough;
5738c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
5748c2ecf20Sopenharmony_ci		if (s->running) {
5758c2ecf20Sopenharmony_ci			snd_nm256_playback_stop(chip);
5768c2ecf20Sopenharmony_ci			s->running = 0;
5778c2ecf20Sopenharmony_ci		}
5788c2ecf20Sopenharmony_ci		break;
5798c2ecf20Sopenharmony_ci	default:
5808c2ecf20Sopenharmony_ci		err = -EINVAL;
5818c2ecf20Sopenharmony_ci		break;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
5848c2ecf20Sopenharmony_ci	return err;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int
5888c2ecf20Sopenharmony_cisnd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
5918c2ecf20Sopenharmony_ci	struct nm256_stream *s = substream->runtime->private_data;
5928c2ecf20Sopenharmony_ci	int err = 0;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!s))
5958c2ecf20Sopenharmony_ci		return -ENXIO;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
5988c2ecf20Sopenharmony_ci	switch (cmd) {
5998c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
6008c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
6018c2ecf20Sopenharmony_ci		if (! s->running) {
6028c2ecf20Sopenharmony_ci			snd_nm256_capture_start(chip, s, substream);
6038c2ecf20Sopenharmony_ci			s->running = 1;
6048c2ecf20Sopenharmony_ci		}
6058c2ecf20Sopenharmony_ci		break;
6068c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
6078c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
6088c2ecf20Sopenharmony_ci		if (s->running) {
6098c2ecf20Sopenharmony_ci			snd_nm256_capture_stop(chip);
6108c2ecf20Sopenharmony_ci			s->running = 0;
6118c2ecf20Sopenharmony_ci		}
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci	default:
6148c2ecf20Sopenharmony_ci		err = -EINVAL;
6158c2ecf20Sopenharmony_ci		break;
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
6188c2ecf20Sopenharmony_ci	return err;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci/*
6238c2ecf20Sopenharmony_ci * prepare playback/capture channel
6248c2ecf20Sopenharmony_ci */
6258c2ecf20Sopenharmony_cistatic int snd_nm256_pcm_prepare(struct snd_pcm_substream *substream)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
6288c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6298c2ecf20Sopenharmony_ci	struct nm256_stream *s = runtime->private_data;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!s))
6328c2ecf20Sopenharmony_ci		return -ENXIO;
6338c2ecf20Sopenharmony_ci	s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size);
6348c2ecf20Sopenharmony_ci	s->period_size = frames_to_bytes(runtime, substream->runtime->period_size);
6358c2ecf20Sopenharmony_ci	s->periods = substream->runtime->periods;
6368c2ecf20Sopenharmony_ci	s->cur_period = 0;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
6398c2ecf20Sopenharmony_ci	s->running = 0;
6408c2ecf20Sopenharmony_ci	snd_nm256_set_format(chip, s, substream);
6418c2ecf20Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	return 0;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/*
6488c2ecf20Sopenharmony_ci * get the current pointer
6498c2ecf20Sopenharmony_ci */
6508c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
6518c2ecf20Sopenharmony_cisnd_nm256_playback_pointer(struct snd_pcm_substream *substream)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
6548c2ecf20Sopenharmony_ci	struct nm256_stream *s = substream->runtime->private_data;
6558c2ecf20Sopenharmony_ci	unsigned long curp;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!s))
6588c2ecf20Sopenharmony_ci		return 0;
6598c2ecf20Sopenharmony_ci	curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf;
6608c2ecf20Sopenharmony_ci	curp %= s->dma_size;
6618c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, curp);
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t
6658c2ecf20Sopenharmony_cisnd_nm256_capture_pointer(struct snd_pcm_substream *substream)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
6688c2ecf20Sopenharmony_ci	struct nm256_stream *s = substream->runtime->private_data;
6698c2ecf20Sopenharmony_ci	unsigned long curp;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!s))
6728c2ecf20Sopenharmony_ci		return 0;
6738c2ecf20Sopenharmony_ci	curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf;
6748c2ecf20Sopenharmony_ci	curp %= s->dma_size;
6758c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, curp);
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci/* Remapped I/O space can be accessible as pointer on i386 */
6798c2ecf20Sopenharmony_ci/* This might be changed in the future */
6808c2ecf20Sopenharmony_ci#ifndef __i386__
6818c2ecf20Sopenharmony_ci/*
6828c2ecf20Sopenharmony_ci * silence / copy for playback
6838c2ecf20Sopenharmony_ci */
6848c2ecf20Sopenharmony_cistatic int
6858c2ecf20Sopenharmony_cisnd_nm256_playback_silence(struct snd_pcm_substream *substream,
6868c2ecf20Sopenharmony_ci			   int channel, unsigned long pos, unsigned long count)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
6898c2ecf20Sopenharmony_ci	struct nm256_stream *s = runtime->private_data;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	memset_io(s->bufptr + pos, 0, count);
6928c2ecf20Sopenharmony_ci	return 0;
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic int
6968c2ecf20Sopenharmony_cisnd_nm256_playback_copy(struct snd_pcm_substream *substream,
6978c2ecf20Sopenharmony_ci			int channel, unsigned long pos,
6988c2ecf20Sopenharmony_ci			void __user *src, unsigned long count)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7018c2ecf20Sopenharmony_ci	struct nm256_stream *s = runtime->private_data;
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	if (copy_from_user_toio(s->bufptr + pos, src, count))
7048c2ecf20Sopenharmony_ci		return -EFAULT;
7058c2ecf20Sopenharmony_ci	return 0;
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_cistatic int
7098c2ecf20Sopenharmony_cisnd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream,
7108c2ecf20Sopenharmony_ci			       int channel, unsigned long pos,
7118c2ecf20Sopenharmony_ci			       void *src, unsigned long count)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7148c2ecf20Sopenharmony_ci	struct nm256_stream *s = runtime->private_data;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	memcpy_toio(s->bufptr + pos, src, count);
7178c2ecf20Sopenharmony_ci	return 0;
7188c2ecf20Sopenharmony_ci}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci/*
7218c2ecf20Sopenharmony_ci * copy to user
7228c2ecf20Sopenharmony_ci */
7238c2ecf20Sopenharmony_cistatic int
7248c2ecf20Sopenharmony_cisnd_nm256_capture_copy(struct snd_pcm_substream *substream,
7258c2ecf20Sopenharmony_ci		       int channel, unsigned long pos,
7268c2ecf20Sopenharmony_ci		       void __user *dst, unsigned long count)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7298c2ecf20Sopenharmony_ci	struct nm256_stream *s = runtime->private_data;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (copy_to_user_fromio(dst, s->bufptr + pos, count))
7328c2ecf20Sopenharmony_ci		return -EFAULT;
7338c2ecf20Sopenharmony_ci	return 0;
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_cistatic int
7378c2ecf20Sopenharmony_cisnd_nm256_capture_copy_kernel(struct snd_pcm_substream *substream,
7388c2ecf20Sopenharmony_ci			      int channel, unsigned long pos,
7398c2ecf20Sopenharmony_ci			      void *dst, unsigned long count)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
7428c2ecf20Sopenharmony_ci	struct nm256_stream *s = runtime->private_data;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	memcpy_fromio(dst, s->bufptr + pos, count);
7458c2ecf20Sopenharmony_ci	return 0;
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci#endif /* !__i386__ */
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci/*
7528c2ecf20Sopenharmony_ci * update playback/capture watermarks
7538c2ecf20Sopenharmony_ci */
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci/* spinlock held! */
7568c2ecf20Sopenharmony_cistatic void
7578c2ecf20Sopenharmony_cisnd_nm256_playback_update(struct nm256 *chip)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci	struct nm256_stream *s;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	s = &chip->streams[SNDRV_PCM_STREAM_PLAYBACK];
7628c2ecf20Sopenharmony_ci	if (s->running && s->substream) {
7638c2ecf20Sopenharmony_ci		spin_unlock(&chip->reg_lock);
7648c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(s->substream);
7658c2ecf20Sopenharmony_ci		spin_lock(&chip->reg_lock);
7668c2ecf20Sopenharmony_ci		snd_nm256_playback_mark(chip, s);
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci/* spinlock held! */
7718c2ecf20Sopenharmony_cistatic void
7728c2ecf20Sopenharmony_cisnd_nm256_capture_update(struct nm256 *chip)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct nm256_stream *s;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	s = &chip->streams[SNDRV_PCM_STREAM_CAPTURE];
7778c2ecf20Sopenharmony_ci	if (s->running && s->substream) {
7788c2ecf20Sopenharmony_ci		spin_unlock(&chip->reg_lock);
7798c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(s->substream);
7808c2ecf20Sopenharmony_ci		spin_lock(&chip->reg_lock);
7818c2ecf20Sopenharmony_ci		snd_nm256_capture_mark(chip, s);
7828c2ecf20Sopenharmony_ci	}
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci/*
7868c2ecf20Sopenharmony_ci * hardware info
7878c2ecf20Sopenharmony_ci */
7888c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_nm256_playback =
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	.info =			SNDRV_PCM_INFO_MMAP_IOMEM |SNDRV_PCM_INFO_MMAP_VALID |
7918c2ecf20Sopenharmony_ci				SNDRV_PCM_INFO_INTERLEAVED |
7928c2ecf20Sopenharmony_ci				/*SNDRV_PCM_INFO_PAUSE |*/
7938c2ecf20Sopenharmony_ci				SNDRV_PCM_INFO_RESUME,
7948c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
7958c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
7968c2ecf20Sopenharmony_ci	.rate_min =		8000,
7978c2ecf20Sopenharmony_ci	.rate_max =		48000,
7988c2ecf20Sopenharmony_ci	.channels_min =		1,
7998c2ecf20Sopenharmony_ci	.channels_max =		2,
8008c2ecf20Sopenharmony_ci	.periods_min =		2,
8018c2ecf20Sopenharmony_ci	.periods_max =		1024,
8028c2ecf20Sopenharmony_ci	.buffer_bytes_max =	128 * 1024,
8038c2ecf20Sopenharmony_ci	.period_bytes_min =	256,
8048c2ecf20Sopenharmony_ci	.period_bytes_max =	128 * 1024,
8058c2ecf20Sopenharmony_ci};
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_nm256_capture =
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	.info =			SNDRV_PCM_INFO_MMAP_IOMEM | SNDRV_PCM_INFO_MMAP_VALID |
8108c2ecf20Sopenharmony_ci				SNDRV_PCM_INFO_INTERLEAVED |
8118c2ecf20Sopenharmony_ci				/*SNDRV_PCM_INFO_PAUSE |*/
8128c2ecf20Sopenharmony_ci				SNDRV_PCM_INFO_RESUME,
8138c2ecf20Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
8148c2ecf20Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
8158c2ecf20Sopenharmony_ci	.rate_min =		8000,
8168c2ecf20Sopenharmony_ci	.rate_max =		48000,
8178c2ecf20Sopenharmony_ci	.channels_min =		1,
8188c2ecf20Sopenharmony_ci	.channels_max =		2,
8198c2ecf20Sopenharmony_ci	.periods_min =		2,
8208c2ecf20Sopenharmony_ci	.periods_max =		1024,
8218c2ecf20Sopenharmony_ci	.buffer_bytes_max =	128 * 1024,
8228c2ecf20Sopenharmony_ci	.period_bytes_min =	256,
8238c2ecf20Sopenharmony_ci	.period_bytes_max =	128 * 1024,
8248c2ecf20Sopenharmony_ci};
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci/* set dma transfer size */
8288c2ecf20Sopenharmony_cistatic int snd_nm256_pcm_hw_params(struct snd_pcm_substream *substream,
8298c2ecf20Sopenharmony_ci				   struct snd_pcm_hw_params *hw_params)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	/* area and addr are already set and unchanged */
8328c2ecf20Sopenharmony_ci	substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
8338c2ecf20Sopenharmony_ci	return 0;
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci/*
8378c2ecf20Sopenharmony_ci * open
8388c2ecf20Sopenharmony_ci */
8398c2ecf20Sopenharmony_cistatic void snd_nm256_setup_stream(struct nm256 *chip, struct nm256_stream *s,
8408c2ecf20Sopenharmony_ci				   struct snd_pcm_substream *substream,
8418c2ecf20Sopenharmony_ci				   const struct snd_pcm_hardware *hw_ptr)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	s->running = 0;
8468c2ecf20Sopenharmony_ci	runtime->hw = *hw_ptr;
8478c2ecf20Sopenharmony_ci	runtime->hw.buffer_bytes_max = s->bufsize;
8488c2ecf20Sopenharmony_ci	runtime->hw.period_bytes_max = s->bufsize / 2;
8498c2ecf20Sopenharmony_ci	runtime->dma_area = (void __force *) s->bufptr;
8508c2ecf20Sopenharmony_ci	runtime->dma_addr = s->bufptr_addr;
8518c2ecf20Sopenharmony_ci	runtime->dma_bytes = s->bufsize;
8528c2ecf20Sopenharmony_ci	runtime->private_data = s;
8538c2ecf20Sopenharmony_ci	s->substream = substream;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
8568c2ecf20Sopenharmony_ci				   &constraints_rates);
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic int
8608c2ecf20Sopenharmony_cisnd_nm256_playback_open(struct snd_pcm_substream *substream)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (snd_nm256_acquire_irq(chip) < 0)
8658c2ecf20Sopenharmony_ci		return -EBUSY;
8668c2ecf20Sopenharmony_ci	snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK],
8678c2ecf20Sopenharmony_ci			       substream, &snd_nm256_playback);
8688c2ecf20Sopenharmony_ci	return 0;
8698c2ecf20Sopenharmony_ci}
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_cistatic int
8728c2ecf20Sopenharmony_cisnd_nm256_capture_open(struct snd_pcm_substream *substream)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	if (snd_nm256_acquire_irq(chip) < 0)
8778c2ecf20Sopenharmony_ci		return -EBUSY;
8788c2ecf20Sopenharmony_ci	snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE],
8798c2ecf20Sopenharmony_ci			       substream, &snd_nm256_capture);
8808c2ecf20Sopenharmony_ci	return 0;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci/*
8848c2ecf20Sopenharmony_ci * close - we don't have to do special..
8858c2ecf20Sopenharmony_ci */
8868c2ecf20Sopenharmony_cistatic int
8878c2ecf20Sopenharmony_cisnd_nm256_playback_close(struct snd_pcm_substream *substream)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	snd_nm256_release_irq(chip);
8928c2ecf20Sopenharmony_ci	return 0;
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic int
8978c2ecf20Sopenharmony_cisnd_nm256_capture_close(struct snd_pcm_substream *substream)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct nm256 *chip = snd_pcm_substream_chip(substream);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	snd_nm256_release_irq(chip);
9028c2ecf20Sopenharmony_ci	return 0;
9038c2ecf20Sopenharmony_ci}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci/*
9068c2ecf20Sopenharmony_ci * create a pcm instance
9078c2ecf20Sopenharmony_ci */
9088c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_nm256_playback_ops = {
9098c2ecf20Sopenharmony_ci	.open =		snd_nm256_playback_open,
9108c2ecf20Sopenharmony_ci	.close =	snd_nm256_playback_close,
9118c2ecf20Sopenharmony_ci	.hw_params =	snd_nm256_pcm_hw_params,
9128c2ecf20Sopenharmony_ci	.prepare =	snd_nm256_pcm_prepare,
9138c2ecf20Sopenharmony_ci	.trigger =	snd_nm256_playback_trigger,
9148c2ecf20Sopenharmony_ci	.pointer =	snd_nm256_playback_pointer,
9158c2ecf20Sopenharmony_ci#ifndef __i386__
9168c2ecf20Sopenharmony_ci	.copy_user =	snd_nm256_playback_copy,
9178c2ecf20Sopenharmony_ci	.copy_kernel =	snd_nm256_playback_copy_kernel,
9188c2ecf20Sopenharmony_ci	.fill_silence =	snd_nm256_playback_silence,
9198c2ecf20Sopenharmony_ci#endif
9208c2ecf20Sopenharmony_ci	.mmap =		snd_pcm_lib_mmap_iomem,
9218c2ecf20Sopenharmony_ci};
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_nm256_capture_ops = {
9248c2ecf20Sopenharmony_ci	.open =		snd_nm256_capture_open,
9258c2ecf20Sopenharmony_ci	.close =	snd_nm256_capture_close,
9268c2ecf20Sopenharmony_ci	.hw_params =	snd_nm256_pcm_hw_params,
9278c2ecf20Sopenharmony_ci	.prepare =	snd_nm256_pcm_prepare,
9288c2ecf20Sopenharmony_ci	.trigger =	snd_nm256_capture_trigger,
9298c2ecf20Sopenharmony_ci	.pointer =	snd_nm256_capture_pointer,
9308c2ecf20Sopenharmony_ci#ifndef __i386__
9318c2ecf20Sopenharmony_ci	.copy_user =	snd_nm256_capture_copy,
9328c2ecf20Sopenharmony_ci	.copy_kernel =	snd_nm256_capture_copy_kernel,
9338c2ecf20Sopenharmony_ci#endif
9348c2ecf20Sopenharmony_ci	.mmap =		snd_pcm_lib_mmap_iomem,
9358c2ecf20Sopenharmony_ci};
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_cistatic int
9388c2ecf20Sopenharmony_cisnd_nm256_pcm(struct nm256 *chip, int device)
9398c2ecf20Sopenharmony_ci{
9408c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
9418c2ecf20Sopenharmony_ci	int i, err;
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
9448c2ecf20Sopenharmony_ci		struct nm256_stream *s = &chip->streams[i];
9458c2ecf20Sopenharmony_ci		s->bufptr = chip->buffer + (s->buf - chip->buffer_start);
9468c2ecf20Sopenharmony_ci		s->bufptr_addr = chip->buffer_addr + (s->buf - chip->buffer_start);
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	err = snd_pcm_new(chip->card, chip->card->driver, device,
9508c2ecf20Sopenharmony_ci			  1, 1, &pcm);
9518c2ecf20Sopenharmony_ci	if (err < 0)
9528c2ecf20Sopenharmony_ci		return err;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_nm256_playback_ops);
9558c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_nm256_capture_ops);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	pcm->private_data = chip;
9588c2ecf20Sopenharmony_ci	pcm->info_flags = 0;
9598c2ecf20Sopenharmony_ci	chip->pcm = pcm;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	return 0;
9628c2ecf20Sopenharmony_ci}
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci/*
9668c2ecf20Sopenharmony_ci * Initialize the hardware.
9678c2ecf20Sopenharmony_ci */
9688c2ecf20Sopenharmony_cistatic void
9698c2ecf20Sopenharmony_cisnd_nm256_init_chip(struct nm256 *chip)
9708c2ecf20Sopenharmony_ci{
9718c2ecf20Sopenharmony_ci	/* Reset everything. */
9728c2ecf20Sopenharmony_ci	snd_nm256_writeb(chip, 0x0, 0x11);
9738c2ecf20Sopenharmony_ci	snd_nm256_writew(chip, 0x214, 0);
9748c2ecf20Sopenharmony_ci	/* stop sounds.. */
9758c2ecf20Sopenharmony_ci	//snd_nm256_playback_stop(chip);
9768c2ecf20Sopenharmony_ci	//snd_nm256_capture_stop(chip);
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_cistatic irqreturn_t
9818c2ecf20Sopenharmony_cisnd_nm256_intr_check(struct nm256 *chip)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	if (chip->badintrcount++ > 1000) {
9848c2ecf20Sopenharmony_ci		/*
9858c2ecf20Sopenharmony_ci		 * I'm not sure if the best thing is to stop the card from
9868c2ecf20Sopenharmony_ci		 * playing or just release the interrupt (after all, we're in
9878c2ecf20Sopenharmony_ci		 * a bad situation, so doing fancy stuff may not be such a good
9888c2ecf20Sopenharmony_ci		 * idea).
9898c2ecf20Sopenharmony_ci		 *
9908c2ecf20Sopenharmony_ci		 * I worry about the card engine continuing to play noise
9918c2ecf20Sopenharmony_ci		 * over and over, however--that could become a very
9928c2ecf20Sopenharmony_ci		 * obnoxious problem.  And we know that when this usually
9938c2ecf20Sopenharmony_ci		 * happens things are fairly safe, it just means the user's
9948c2ecf20Sopenharmony_ci		 * inserted a PCMCIA card and someone's spamming us with IRQ 9s.
9958c2ecf20Sopenharmony_ci		 */
9968c2ecf20Sopenharmony_ci		if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
9978c2ecf20Sopenharmony_ci			snd_nm256_playback_stop(chip);
9988c2ecf20Sopenharmony_ci		if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
9998c2ecf20Sopenharmony_ci			snd_nm256_capture_stop(chip);
10008c2ecf20Sopenharmony_ci		chip->badintrcount = 0;
10018c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
10028c2ecf20Sopenharmony_ci	}
10038c2ecf20Sopenharmony_ci	return IRQ_NONE;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci/*
10078c2ecf20Sopenharmony_ci * Handle a potential interrupt for the device referred to by DEV_ID.
10088c2ecf20Sopenharmony_ci *
10098c2ecf20Sopenharmony_ci * I don't like the cut-n-paste job here either between the two routines,
10108c2ecf20Sopenharmony_ci * but there are sufficient differences between the two interrupt handlers
10118c2ecf20Sopenharmony_ci * that parameterizing it isn't all that great either.  (Could use a macro,
10128c2ecf20Sopenharmony_ci * I suppose...yucky bleah.)
10138c2ecf20Sopenharmony_ci */
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_cistatic irqreturn_t
10168c2ecf20Sopenharmony_cisnd_nm256_interrupt(int irq, void *dev_id)
10178c2ecf20Sopenharmony_ci{
10188c2ecf20Sopenharmony_ci	struct nm256 *chip = dev_id;
10198c2ecf20Sopenharmony_ci	u16 status;
10208c2ecf20Sopenharmony_ci	u8 cbyte;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	status = snd_nm256_readw(chip, NM_INT_REG);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	/* Not ours. */
10258c2ecf20Sopenharmony_ci	if (status == 0)
10268c2ecf20Sopenharmony_ci		return snd_nm256_intr_check(chip);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	chip->badintrcount = 0;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	/* Rather boring; check for individual interrupts and process them. */
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
10338c2ecf20Sopenharmony_ci	if (status & NM_PLAYBACK_INT) {
10348c2ecf20Sopenharmony_ci		status &= ~NM_PLAYBACK_INT;
10358c2ecf20Sopenharmony_ci		NM_ACK_INT(chip, NM_PLAYBACK_INT);
10368c2ecf20Sopenharmony_ci		snd_nm256_playback_update(chip);
10378c2ecf20Sopenharmony_ci	}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	if (status & NM_RECORD_INT) {
10408c2ecf20Sopenharmony_ci		status &= ~NM_RECORD_INT;
10418c2ecf20Sopenharmony_ci		NM_ACK_INT(chip, NM_RECORD_INT);
10428c2ecf20Sopenharmony_ci		snd_nm256_capture_update(chip);
10438c2ecf20Sopenharmony_ci	}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	if (status & NM_MISC_INT_1) {
10468c2ecf20Sopenharmony_ci		status &= ~NM_MISC_INT_1;
10478c2ecf20Sopenharmony_ci		NM_ACK_INT(chip, NM_MISC_INT_1);
10488c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n");
10498c2ecf20Sopenharmony_ci		snd_nm256_writew(chip, NM_INT_REG, 0x8000);
10508c2ecf20Sopenharmony_ci		cbyte = snd_nm256_readb(chip, 0x400);
10518c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip, 0x400, cbyte | 2);
10528c2ecf20Sopenharmony_ci	}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	if (status & NM_MISC_INT_2) {
10558c2ecf20Sopenharmony_ci		status &= ~NM_MISC_INT_2;
10568c2ecf20Sopenharmony_ci		NM_ACK_INT(chip, NM_MISC_INT_2);
10578c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n");
10588c2ecf20Sopenharmony_ci		cbyte = snd_nm256_readb(chip, 0x400);
10598c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip, 0x400, cbyte & ~2);
10608c2ecf20Sopenharmony_ci	}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	/* Unknown interrupt. */
10638c2ecf20Sopenharmony_ci	if (status) {
10648c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev,
10658c2ecf20Sopenharmony_ci			"NM256: Fire in the hole! Unknown status 0x%x\n",
10668c2ecf20Sopenharmony_ci			   status);
10678c2ecf20Sopenharmony_ci		/* Pray. */
10688c2ecf20Sopenharmony_ci		NM_ACK_INT(chip, status);
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
10728c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10738c2ecf20Sopenharmony_ci}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci/*
10768c2ecf20Sopenharmony_ci * Handle a potential interrupt for the device referred to by DEV_ID.
10778c2ecf20Sopenharmony_ci * This handler is for the 256ZX, and is very similar to the non-ZX
10788c2ecf20Sopenharmony_ci * routine.
10798c2ecf20Sopenharmony_ci */
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cistatic irqreturn_t
10828c2ecf20Sopenharmony_cisnd_nm256_interrupt_zx(int irq, void *dev_id)
10838c2ecf20Sopenharmony_ci{
10848c2ecf20Sopenharmony_ci	struct nm256 *chip = dev_id;
10858c2ecf20Sopenharmony_ci	u32 status;
10868c2ecf20Sopenharmony_ci	u8 cbyte;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	status = snd_nm256_readl(chip, NM_INT_REG);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	/* Not ours. */
10918c2ecf20Sopenharmony_ci	if (status == 0)
10928c2ecf20Sopenharmony_ci		return snd_nm256_intr_check(chip);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	chip->badintrcount = 0;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	/* Rather boring; check for individual interrupts and process them. */
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	spin_lock(&chip->reg_lock);
10998c2ecf20Sopenharmony_ci	if (status & NM2_PLAYBACK_INT) {
11008c2ecf20Sopenharmony_ci		status &= ~NM2_PLAYBACK_INT;
11018c2ecf20Sopenharmony_ci		NM2_ACK_INT(chip, NM2_PLAYBACK_INT);
11028c2ecf20Sopenharmony_ci		snd_nm256_playback_update(chip);
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (status & NM2_RECORD_INT) {
11068c2ecf20Sopenharmony_ci		status &= ~NM2_RECORD_INT;
11078c2ecf20Sopenharmony_ci		NM2_ACK_INT(chip, NM2_RECORD_INT);
11088c2ecf20Sopenharmony_ci		snd_nm256_capture_update(chip);
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	if (status & NM2_MISC_INT_1) {
11128c2ecf20Sopenharmony_ci		status &= ~NM2_MISC_INT_1;
11138c2ecf20Sopenharmony_ci		NM2_ACK_INT(chip, NM2_MISC_INT_1);
11148c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "NM256: Got misc interrupt #1\n");
11158c2ecf20Sopenharmony_ci		cbyte = snd_nm256_readb(chip, 0x400);
11168c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip, 0x400, cbyte | 2);
11178c2ecf20Sopenharmony_ci	}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	if (status & NM2_MISC_INT_2) {
11208c2ecf20Sopenharmony_ci		status &= ~NM2_MISC_INT_2;
11218c2ecf20Sopenharmony_ci		NM2_ACK_INT(chip, NM2_MISC_INT_2);
11228c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev, "NM256: Got misc interrupt #2\n");
11238c2ecf20Sopenharmony_ci		cbyte = snd_nm256_readb(chip, 0x400);
11248c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip, 0x400, cbyte & ~2);
11258c2ecf20Sopenharmony_ci	}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	/* Unknown interrupt. */
11288c2ecf20Sopenharmony_ci	if (status) {
11298c2ecf20Sopenharmony_ci		dev_dbg(chip->card->dev,
11308c2ecf20Sopenharmony_ci			"NM256: Fire in the hole! Unknown status 0x%x\n",
11318c2ecf20Sopenharmony_ci			   status);
11328c2ecf20Sopenharmony_ci		/* Pray. */
11338c2ecf20Sopenharmony_ci		NM2_ACK_INT(chip, status);
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	spin_unlock(&chip->reg_lock);
11378c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
11388c2ecf20Sopenharmony_ci}
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci/*
11418c2ecf20Sopenharmony_ci * AC97 interface
11428c2ecf20Sopenharmony_ci */
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci/*
11458c2ecf20Sopenharmony_ci * Waits for the mixer to become ready to be written; returns a zero value
11468c2ecf20Sopenharmony_ci * if it timed out.
11478c2ecf20Sopenharmony_ci */
11488c2ecf20Sopenharmony_cistatic int
11498c2ecf20Sopenharmony_cisnd_nm256_ac97_ready(struct nm256 *chip)
11508c2ecf20Sopenharmony_ci{
11518c2ecf20Sopenharmony_ci	int timeout = 10;
11528c2ecf20Sopenharmony_ci	u32 testaddr;
11538c2ecf20Sopenharmony_ci	u16 testb;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	testaddr = chip->mixer_status_offset;
11568c2ecf20Sopenharmony_ci	testb = chip->mixer_status_mask;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	/*
11598c2ecf20Sopenharmony_ci	 * Loop around waiting for the mixer to become ready.
11608c2ecf20Sopenharmony_ci	 */
11618c2ecf20Sopenharmony_ci	while (timeout-- > 0) {
11628c2ecf20Sopenharmony_ci		if ((snd_nm256_readw(chip, testaddr) & testb) == 0)
11638c2ecf20Sopenharmony_ci			return 1;
11648c2ecf20Sopenharmony_ci		udelay(100);
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci	return 0;
11678c2ecf20Sopenharmony_ci}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci/*
11708c2ecf20Sopenharmony_ci * Initial register values to be written to the AC97 mixer.
11718c2ecf20Sopenharmony_ci * While most of these are identical to the reset values, we do this
11728c2ecf20Sopenharmony_ci * so that we have most of the register contents cached--this avoids
11738c2ecf20Sopenharmony_ci * reading from the mixer directly (which seems to be problematic,
11748c2ecf20Sopenharmony_ci * probably due to ignorance).
11758c2ecf20Sopenharmony_ci */
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cistruct initialValues {
11788c2ecf20Sopenharmony_ci	unsigned short reg;
11798c2ecf20Sopenharmony_ci	unsigned short value;
11808c2ecf20Sopenharmony_ci};
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_cistatic const struct initialValues nm256_ac97_init_val[] =
11838c2ecf20Sopenharmony_ci{
11848c2ecf20Sopenharmony_ci	{ AC97_MASTER, 		0x8000 },
11858c2ecf20Sopenharmony_ci	{ AC97_HEADPHONE,	0x8000 },
11868c2ecf20Sopenharmony_ci	{ AC97_MASTER_MONO,	0x8000 },
11878c2ecf20Sopenharmony_ci	{ AC97_PC_BEEP,		0x8000 },
11888c2ecf20Sopenharmony_ci	{ AC97_PHONE,		0x8008 },
11898c2ecf20Sopenharmony_ci	{ AC97_MIC,		0x8000 },
11908c2ecf20Sopenharmony_ci	{ AC97_LINE,		0x8808 },
11918c2ecf20Sopenharmony_ci	{ AC97_CD,		0x8808 },
11928c2ecf20Sopenharmony_ci	{ AC97_VIDEO,		0x8808 },
11938c2ecf20Sopenharmony_ci	{ AC97_AUX,		0x8808 },
11948c2ecf20Sopenharmony_ci	{ AC97_PCM,		0x8808 },
11958c2ecf20Sopenharmony_ci	{ AC97_REC_SEL,		0x0000 },
11968c2ecf20Sopenharmony_ci	{ AC97_REC_GAIN,	0x0B0B },
11978c2ecf20Sopenharmony_ci	{ AC97_GENERAL_PURPOSE,	0x0000 },
11988c2ecf20Sopenharmony_ci	{ AC97_3D_CONTROL,	0x8000 },
11998c2ecf20Sopenharmony_ci	{ AC97_VENDOR_ID1, 	0x8384 },
12008c2ecf20Sopenharmony_ci	{ AC97_VENDOR_ID2,	0x7609 },
12018c2ecf20Sopenharmony_ci};
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_cistatic int nm256_ac97_idx(unsigned short reg)
12048c2ecf20Sopenharmony_ci{
12058c2ecf20Sopenharmony_ci	int i;
12068c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++)
12078c2ecf20Sopenharmony_ci		if (nm256_ac97_init_val[i].reg == reg)
12088c2ecf20Sopenharmony_ci			return i;
12098c2ecf20Sopenharmony_ci	return -1;
12108c2ecf20Sopenharmony_ci}
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci/*
12138c2ecf20Sopenharmony_ci * some nm256 easily crash when reading from mixer registers
12148c2ecf20Sopenharmony_ci * thus we're treating it as a write-only mixer and cache the
12158c2ecf20Sopenharmony_ci * written values
12168c2ecf20Sopenharmony_ci */
12178c2ecf20Sopenharmony_cistatic unsigned short
12188c2ecf20Sopenharmony_cisnd_nm256_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	struct nm256 *chip = ac97->private_data;
12218c2ecf20Sopenharmony_ci	int idx = nm256_ac97_idx(reg);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	if (idx < 0)
12248c2ecf20Sopenharmony_ci		return 0;
12258c2ecf20Sopenharmony_ci	return chip->ac97_regs[idx];
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci/*
12298c2ecf20Sopenharmony_ci */
12308c2ecf20Sopenharmony_cistatic void
12318c2ecf20Sopenharmony_cisnd_nm256_ac97_write(struct snd_ac97 *ac97,
12328c2ecf20Sopenharmony_ci		     unsigned short reg, unsigned short val)
12338c2ecf20Sopenharmony_ci{
12348c2ecf20Sopenharmony_ci	struct nm256 *chip = ac97->private_data;
12358c2ecf20Sopenharmony_ci	int tries = 2;
12368c2ecf20Sopenharmony_ci	int idx = nm256_ac97_idx(reg);
12378c2ecf20Sopenharmony_ci	u32 base;
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci	if (idx < 0)
12408c2ecf20Sopenharmony_ci		return;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	base = chip->mixer_base;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	snd_nm256_ac97_ready(chip);
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	/* Wait for the write to take, too. */
12478c2ecf20Sopenharmony_ci	while (tries-- > 0) {
12488c2ecf20Sopenharmony_ci		snd_nm256_writew(chip, base + reg, val);
12498c2ecf20Sopenharmony_ci		msleep(1);  /* a little delay here seems better.. */
12508c2ecf20Sopenharmony_ci		if (snd_nm256_ac97_ready(chip)) {
12518c2ecf20Sopenharmony_ci			/* successful write: set cache */
12528c2ecf20Sopenharmony_ci			chip->ac97_regs[idx] = val;
12538c2ecf20Sopenharmony_ci			return;
12548c2ecf20Sopenharmony_ci		}
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci	dev_dbg(chip->card->dev, "nm256: ac97 codec not ready..\n");
12578c2ecf20Sopenharmony_ci}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci/* static resolution table */
12608c2ecf20Sopenharmony_cistatic const struct snd_ac97_res_table nm256_res_table[] = {
12618c2ecf20Sopenharmony_ci	{ AC97_MASTER, 0x1f1f },
12628c2ecf20Sopenharmony_ci	{ AC97_HEADPHONE, 0x1f1f },
12638c2ecf20Sopenharmony_ci	{ AC97_MASTER_MONO, 0x001f },
12648c2ecf20Sopenharmony_ci	{ AC97_PC_BEEP, 0x001f },
12658c2ecf20Sopenharmony_ci	{ AC97_PHONE, 0x001f },
12668c2ecf20Sopenharmony_ci	{ AC97_MIC, 0x001f },
12678c2ecf20Sopenharmony_ci	{ AC97_LINE, 0x1f1f },
12688c2ecf20Sopenharmony_ci	{ AC97_CD, 0x1f1f },
12698c2ecf20Sopenharmony_ci	{ AC97_VIDEO, 0x1f1f },
12708c2ecf20Sopenharmony_ci	{ AC97_AUX, 0x1f1f },
12718c2ecf20Sopenharmony_ci	{ AC97_PCM, 0x1f1f },
12728c2ecf20Sopenharmony_ci	{ AC97_REC_GAIN, 0x0f0f },
12738c2ecf20Sopenharmony_ci	{ } /* terminator */
12748c2ecf20Sopenharmony_ci};
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci/* initialize the ac97 into a known state */
12778c2ecf20Sopenharmony_cistatic void
12788c2ecf20Sopenharmony_cisnd_nm256_ac97_reset(struct snd_ac97 *ac97)
12798c2ecf20Sopenharmony_ci{
12808c2ecf20Sopenharmony_ci	struct nm256 *chip = ac97->private_data;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	/* Reset the mixer.  'Tis magic!  */
12838c2ecf20Sopenharmony_ci	snd_nm256_writeb(chip, 0x6c0, 1);
12848c2ecf20Sopenharmony_ci	if (! chip->reset_workaround) {
12858c2ecf20Sopenharmony_ci		/* Dell latitude LS will lock up by this */
12868c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip, 0x6cc, 0x87);
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci	if (! chip->reset_workaround_2) {
12898c2ecf20Sopenharmony_ci		/* Dell latitude CSx will lock up by this */
12908c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip, 0x6cc, 0x80);
12918c2ecf20Sopenharmony_ci		snd_nm256_writeb(chip, 0x6cc, 0x0);
12928c2ecf20Sopenharmony_ci	}
12938c2ecf20Sopenharmony_ci	if (! chip->in_resume) {
12948c2ecf20Sopenharmony_ci		int i;
12958c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(nm256_ac97_init_val); i++) {
12968c2ecf20Sopenharmony_ci			/* preload the cache, so as to avoid even a single
12978c2ecf20Sopenharmony_ci			 * read of the mixer regs
12988c2ecf20Sopenharmony_ci			 */
12998c2ecf20Sopenharmony_ci			snd_nm256_ac97_write(ac97, nm256_ac97_init_val[i].reg,
13008c2ecf20Sopenharmony_ci					     nm256_ac97_init_val[i].value);
13018c2ecf20Sopenharmony_ci		}
13028c2ecf20Sopenharmony_ci	}
13038c2ecf20Sopenharmony_ci}
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci/* create an ac97 mixer interface */
13068c2ecf20Sopenharmony_cistatic int
13078c2ecf20Sopenharmony_cisnd_nm256_mixer(struct nm256 *chip)
13088c2ecf20Sopenharmony_ci{
13098c2ecf20Sopenharmony_ci	struct snd_ac97_bus *pbus;
13108c2ecf20Sopenharmony_ci	struct snd_ac97_template ac97;
13118c2ecf20Sopenharmony_ci	int err;
13128c2ecf20Sopenharmony_ci	static const struct snd_ac97_bus_ops ops = {
13138c2ecf20Sopenharmony_ci		.reset = snd_nm256_ac97_reset,
13148c2ecf20Sopenharmony_ci		.write = snd_nm256_ac97_write,
13158c2ecf20Sopenharmony_ci		.read = snd_nm256_ac97_read,
13168c2ecf20Sopenharmony_ci	};
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	chip->ac97_regs = kcalloc(ARRAY_SIZE(nm256_ac97_init_val),
13198c2ecf20Sopenharmony_ci				  sizeof(short), GFP_KERNEL);
13208c2ecf20Sopenharmony_ci	if (! chip->ac97_regs)
13218c2ecf20Sopenharmony_ci		return -ENOMEM;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0)
13248c2ecf20Sopenharmony_ci		return err;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	memset(&ac97, 0, sizeof(ac97));
13278c2ecf20Sopenharmony_ci	ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */
13288c2ecf20Sopenharmony_ci	ac97.private_data = chip;
13298c2ecf20Sopenharmony_ci	ac97.res_table = nm256_res_table;
13308c2ecf20Sopenharmony_ci	pbus->no_vra = 1;
13318c2ecf20Sopenharmony_ci	err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
13328c2ecf20Sopenharmony_ci	if (err < 0)
13338c2ecf20Sopenharmony_ci		return err;
13348c2ecf20Sopenharmony_ci	if (! (chip->ac97->id & (0xf0000000))) {
13358c2ecf20Sopenharmony_ci		/* looks like an invalid id */
13368c2ecf20Sopenharmony_ci		sprintf(chip->card->mixername, "%s AC97", chip->card->driver);
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci	return 0;
13398c2ecf20Sopenharmony_ci}
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci/*
13428c2ecf20Sopenharmony_ci * See if the signature left by the NM256 BIOS is intact; if so, we use
13438c2ecf20Sopenharmony_ci * the associated address as the end of our audio buffer in the video
13448c2ecf20Sopenharmony_ci * RAM.
13458c2ecf20Sopenharmony_ci */
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_cistatic int
13488c2ecf20Sopenharmony_cisnd_nm256_peek_for_sig(struct nm256 *chip)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	/* The signature is located 1K below the end of video RAM.  */
13518c2ecf20Sopenharmony_ci	void __iomem *temp;
13528c2ecf20Sopenharmony_ci	/* Default buffer end is 5120 bytes below the top of RAM.  */
13538c2ecf20Sopenharmony_ci	unsigned long pointer_found = chip->buffer_end - 0x1400;
13548c2ecf20Sopenharmony_ci	u32 sig;
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	temp = ioremap(chip->buffer_addr + chip->buffer_end - 0x400, 16);
13578c2ecf20Sopenharmony_ci	if (temp == NULL) {
13588c2ecf20Sopenharmony_ci		dev_err(chip->card->dev,
13598c2ecf20Sopenharmony_ci			"Unable to scan for card signature in video RAM\n");
13608c2ecf20Sopenharmony_ci		return -EBUSY;
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	sig = readl(temp);
13648c2ecf20Sopenharmony_ci	if ((sig & NM_SIG_MASK) == NM_SIGNATURE) {
13658c2ecf20Sopenharmony_ci		u32 pointer = readl(temp + 4);
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci		/*
13688c2ecf20Sopenharmony_ci		 * If it's obviously invalid, don't use it
13698c2ecf20Sopenharmony_ci		 */
13708c2ecf20Sopenharmony_ci		if (pointer == 0xffffffff ||
13718c2ecf20Sopenharmony_ci		    pointer < chip->buffer_size ||
13728c2ecf20Sopenharmony_ci		    pointer > chip->buffer_end) {
13738c2ecf20Sopenharmony_ci			dev_err(chip->card->dev,
13748c2ecf20Sopenharmony_ci				"invalid signature found: 0x%x\n", pointer);
13758c2ecf20Sopenharmony_ci			iounmap(temp);
13768c2ecf20Sopenharmony_ci			return -ENODEV;
13778c2ecf20Sopenharmony_ci		} else {
13788c2ecf20Sopenharmony_ci			pointer_found = pointer;
13798c2ecf20Sopenharmony_ci			dev_info(chip->card->dev,
13808c2ecf20Sopenharmony_ci				 "found card signature in video RAM: 0x%x\n",
13818c2ecf20Sopenharmony_ci			       pointer);
13828c2ecf20Sopenharmony_ci		}
13838c2ecf20Sopenharmony_ci	}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	iounmap(temp);
13868c2ecf20Sopenharmony_ci	chip->buffer_end = pointer_found;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	return 0;
13898c2ecf20Sopenharmony_ci}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
13928c2ecf20Sopenharmony_ci/*
13938c2ecf20Sopenharmony_ci * APM event handler, so the card is properly reinitialized after a power
13948c2ecf20Sopenharmony_ci * event.
13958c2ecf20Sopenharmony_ci */
13968c2ecf20Sopenharmony_cistatic int nm256_suspend(struct device *dev)
13978c2ecf20Sopenharmony_ci{
13988c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
13998c2ecf20Sopenharmony_ci	struct nm256 *chip = card->private_data;
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
14028c2ecf20Sopenharmony_ci	snd_ac97_suspend(chip->ac97);
14038c2ecf20Sopenharmony_ci	chip->coeffs_current = 0;
14048c2ecf20Sopenharmony_ci	return 0;
14058c2ecf20Sopenharmony_ci}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_cistatic int nm256_resume(struct device *dev)
14088c2ecf20Sopenharmony_ci{
14098c2ecf20Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
14108c2ecf20Sopenharmony_ci	struct nm256 *chip = card->private_data;
14118c2ecf20Sopenharmony_ci	int i;
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* Perform a full reset on the hardware */
14148c2ecf20Sopenharmony_ci	chip->in_resume = 1;
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	snd_nm256_init_chip(chip);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	/* restore ac97 */
14198c2ecf20Sopenharmony_ci	snd_ac97_resume(chip->ac97);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
14228c2ecf20Sopenharmony_ci		struct nm256_stream *s = &chip->streams[i];
14238c2ecf20Sopenharmony_ci		if (s->substream && s->suspended) {
14248c2ecf20Sopenharmony_ci			spin_lock_irq(&chip->reg_lock);
14258c2ecf20Sopenharmony_ci			snd_nm256_set_format(chip, s, s->substream);
14268c2ecf20Sopenharmony_ci			spin_unlock_irq(&chip->reg_lock);
14278c2ecf20Sopenharmony_ci		}
14288c2ecf20Sopenharmony_ci	}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
14318c2ecf20Sopenharmony_ci	chip->in_resume = 0;
14328c2ecf20Sopenharmony_ci	return 0;
14338c2ecf20Sopenharmony_ci}
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(nm256_pm, nm256_suspend, nm256_resume);
14368c2ecf20Sopenharmony_ci#define NM256_PM_OPS	&nm256_pm
14378c2ecf20Sopenharmony_ci#else
14388c2ecf20Sopenharmony_ci#define NM256_PM_OPS	NULL
14398c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_cistatic int snd_nm256_free(struct nm256 *chip)
14428c2ecf20Sopenharmony_ci{
14438c2ecf20Sopenharmony_ci	if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running)
14448c2ecf20Sopenharmony_ci		snd_nm256_playback_stop(chip);
14458c2ecf20Sopenharmony_ci	if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
14468c2ecf20Sopenharmony_ci		snd_nm256_capture_stop(chip);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	if (chip->irq >= 0)
14498c2ecf20Sopenharmony_ci		free_irq(chip->irq, chip);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	iounmap(chip->cport);
14528c2ecf20Sopenharmony_ci	iounmap(chip->buffer);
14538c2ecf20Sopenharmony_ci	release_and_free_resource(chip->res_cport);
14548c2ecf20Sopenharmony_ci	release_and_free_resource(chip->res_buffer);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	pci_disable_device(chip->pci);
14578c2ecf20Sopenharmony_ci	kfree(chip->ac97_regs);
14588c2ecf20Sopenharmony_ci	kfree(chip);
14598c2ecf20Sopenharmony_ci	return 0;
14608c2ecf20Sopenharmony_ci}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_cistatic int snd_nm256_dev_free(struct snd_device *device)
14638c2ecf20Sopenharmony_ci{
14648c2ecf20Sopenharmony_ci	struct nm256 *chip = device->device_data;
14658c2ecf20Sopenharmony_ci	return snd_nm256_free(chip);
14668c2ecf20Sopenharmony_ci}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_cistatic int
14698c2ecf20Sopenharmony_cisnd_nm256_create(struct snd_card *card, struct pci_dev *pci,
14708c2ecf20Sopenharmony_ci		 struct nm256 **chip_ret)
14718c2ecf20Sopenharmony_ci{
14728c2ecf20Sopenharmony_ci	struct nm256 *chip;
14738c2ecf20Sopenharmony_ci	int err, pval;
14748c2ecf20Sopenharmony_ci	static const struct snd_device_ops ops = {
14758c2ecf20Sopenharmony_ci		.dev_free =	snd_nm256_dev_free,
14768c2ecf20Sopenharmony_ci	};
14778c2ecf20Sopenharmony_ci	u32 addr;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	*chip_ret = NULL;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	if ((err = pci_enable_device(pci)) < 0)
14828c2ecf20Sopenharmony_ci		return err;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
14858c2ecf20Sopenharmony_ci	if (chip == NULL) {
14868c2ecf20Sopenharmony_ci		pci_disable_device(pci);
14878c2ecf20Sopenharmony_ci		return -ENOMEM;
14888c2ecf20Sopenharmony_ci	}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	chip->card = card;
14918c2ecf20Sopenharmony_ci	chip->pci = pci;
14928c2ecf20Sopenharmony_ci	chip->use_cache = use_cache;
14938c2ecf20Sopenharmony_ci	spin_lock_init(&chip->reg_lock);
14948c2ecf20Sopenharmony_ci	chip->irq = -1;
14958c2ecf20Sopenharmony_ci	mutex_init(&chip->irq_mutex);
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	/* store buffer sizes in bytes */
14988c2ecf20Sopenharmony_ci	chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = playback_bufsize * 1024;
14998c2ecf20Sopenharmony_ci	chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capture_bufsize * 1024;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	/*
15028c2ecf20Sopenharmony_ci	 * The NM256 has two memory ports.  The first port is nothing
15038c2ecf20Sopenharmony_ci	 * more than a chunk of video RAM, which is used as the I/O ring
15048c2ecf20Sopenharmony_ci	 * buffer.  The second port has the actual juicy stuff (like the
15058c2ecf20Sopenharmony_ci	 * mixer and the playback engine control registers).
15068c2ecf20Sopenharmony_ci	 */
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	chip->buffer_addr = pci_resource_start(pci, 0);
15098c2ecf20Sopenharmony_ci	chip->cport_addr = pci_resource_start(pci, 1);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	/* Init the memory port info.  */
15128c2ecf20Sopenharmony_ci	/* remap control port (#2) */
15138c2ecf20Sopenharmony_ci	chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE,
15148c2ecf20Sopenharmony_ci					     card->driver);
15158c2ecf20Sopenharmony_ci	if (chip->res_cport == NULL) {
15168c2ecf20Sopenharmony_ci		dev_err(card->dev, "memory region 0x%lx (size 0x%x) busy\n",
15178c2ecf20Sopenharmony_ci			   chip->cport_addr, NM_PORT2_SIZE);
15188c2ecf20Sopenharmony_ci		err = -EBUSY;
15198c2ecf20Sopenharmony_ci		goto __error;
15208c2ecf20Sopenharmony_ci	}
15218c2ecf20Sopenharmony_ci	chip->cport = ioremap(chip->cport_addr, NM_PORT2_SIZE);
15228c2ecf20Sopenharmony_ci	if (chip->cport == NULL) {
15238c2ecf20Sopenharmony_ci		dev_err(card->dev, "unable to map control port %lx\n",
15248c2ecf20Sopenharmony_ci			chip->cport_addr);
15258c2ecf20Sopenharmony_ci		err = -ENOMEM;
15268c2ecf20Sopenharmony_ci		goto __error;
15278c2ecf20Sopenharmony_ci	}
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	if (!strcmp(card->driver, "NM256AV")) {
15308c2ecf20Sopenharmony_ci		/* Ok, try to see if this is a non-AC97 version of the hardware. */
15318c2ecf20Sopenharmony_ci		pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE);
15328c2ecf20Sopenharmony_ci		if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
15338c2ecf20Sopenharmony_ci			if (! force_ac97) {
15348c2ecf20Sopenharmony_ci				dev_err(card->dev,
15358c2ecf20Sopenharmony_ci					"no ac97 is found!\n");
15368c2ecf20Sopenharmony_ci				dev_err(card->dev,
15378c2ecf20Sopenharmony_ci					"force the driver to load by passing in the module parameter\n");
15388c2ecf20Sopenharmony_ci				dev_err(card->dev,
15398c2ecf20Sopenharmony_ci					" force_ac97=1\n");
15408c2ecf20Sopenharmony_ci				dev_err(card->dev,
15418c2ecf20Sopenharmony_ci					"or try sb16, opl3sa2, or cs423x drivers instead.\n");
15428c2ecf20Sopenharmony_ci				err = -ENXIO;
15438c2ecf20Sopenharmony_ci				goto __error;
15448c2ecf20Sopenharmony_ci			}
15458c2ecf20Sopenharmony_ci		}
15468c2ecf20Sopenharmony_ci		chip->buffer_end = 2560 * 1024;
15478c2ecf20Sopenharmony_ci		chip->interrupt = snd_nm256_interrupt;
15488c2ecf20Sopenharmony_ci		chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET;
15498c2ecf20Sopenharmony_ci		chip->mixer_status_mask = NM_MIXER_READY_MASK;
15508c2ecf20Sopenharmony_ci	} else {
15518c2ecf20Sopenharmony_ci		/* Not sure if there is any relevant detect for the ZX or not.  */
15528c2ecf20Sopenharmony_ci		if (snd_nm256_readb(chip, 0xa0b) != 0)
15538c2ecf20Sopenharmony_ci			chip->buffer_end = 6144 * 1024;
15548c2ecf20Sopenharmony_ci		else
15558c2ecf20Sopenharmony_ci			chip->buffer_end = 4096 * 1024;
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci		chip->interrupt = snd_nm256_interrupt_zx;
15588c2ecf20Sopenharmony_ci		chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET;
15598c2ecf20Sopenharmony_ci		chip->mixer_status_mask = NM2_MIXER_READY_MASK;
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize +
15638c2ecf20Sopenharmony_ci		chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
15648c2ecf20Sopenharmony_ci	if (chip->use_cache)
15658c2ecf20Sopenharmony_ci		chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4;
15668c2ecf20Sopenharmony_ci	else
15678c2ecf20Sopenharmony_ci		chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE;
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci	if (buffer_top >= chip->buffer_size && buffer_top < chip->buffer_end)
15708c2ecf20Sopenharmony_ci		chip->buffer_end = buffer_top;
15718c2ecf20Sopenharmony_ci	else {
15728c2ecf20Sopenharmony_ci		/* get buffer end pointer from signature */
15738c2ecf20Sopenharmony_ci		if ((err = snd_nm256_peek_for_sig(chip)) < 0)
15748c2ecf20Sopenharmony_ci			goto __error;
15758c2ecf20Sopenharmony_ci	}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	chip->buffer_start = chip->buffer_end - chip->buffer_size;
15788c2ecf20Sopenharmony_ci	chip->buffer_addr += chip->buffer_start;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	dev_info(card->dev, "Mapping port 1 from 0x%x - 0x%x\n",
15818c2ecf20Sopenharmony_ci	       chip->buffer_start, chip->buffer_end);
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	chip->res_buffer = request_mem_region(chip->buffer_addr,
15848c2ecf20Sopenharmony_ci					      chip->buffer_size,
15858c2ecf20Sopenharmony_ci					      card->driver);
15868c2ecf20Sopenharmony_ci	if (chip->res_buffer == NULL) {
15878c2ecf20Sopenharmony_ci		dev_err(card->dev, "buffer 0x%lx (size 0x%x) busy\n",
15888c2ecf20Sopenharmony_ci			   chip->buffer_addr, chip->buffer_size);
15898c2ecf20Sopenharmony_ci		err = -EBUSY;
15908c2ecf20Sopenharmony_ci		goto __error;
15918c2ecf20Sopenharmony_ci	}
15928c2ecf20Sopenharmony_ci	chip->buffer = ioremap(chip->buffer_addr, chip->buffer_size);
15938c2ecf20Sopenharmony_ci	if (chip->buffer == NULL) {
15948c2ecf20Sopenharmony_ci		err = -ENOMEM;
15958c2ecf20Sopenharmony_ci		dev_err(card->dev, "unable to map ring buffer at %lx\n",
15968c2ecf20Sopenharmony_ci			chip->buffer_addr);
15978c2ecf20Sopenharmony_ci		goto __error;
15988c2ecf20Sopenharmony_ci	}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	/* set offsets */
16018c2ecf20Sopenharmony_ci	addr = chip->buffer_start;
16028c2ecf20Sopenharmony_ci	chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr;
16038c2ecf20Sopenharmony_ci	addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize;
16048c2ecf20Sopenharmony_ci	chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr;
16058c2ecf20Sopenharmony_ci	addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize;
16068c2ecf20Sopenharmony_ci	if (chip->use_cache) {
16078c2ecf20Sopenharmony_ci		chip->all_coeff_buf = addr;
16088c2ecf20Sopenharmony_ci	} else {
16098c2ecf20Sopenharmony_ci		chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr;
16108c2ecf20Sopenharmony_ci		addr += NM_MAX_PLAYBACK_COEF_SIZE;
16118c2ecf20Sopenharmony_ci		chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr;
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	/* Fixed setting. */
16158c2ecf20Sopenharmony_ci	chip->mixer_base = NM_MIXER_OFFSET;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	chip->coeffs_current = 0;
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	snd_nm256_init_chip(chip);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	// pci_set_master(pci); /* needed? */
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0)
16248c2ecf20Sopenharmony_ci		goto __error;
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	*chip_ret = chip;
16278c2ecf20Sopenharmony_ci	return 0;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci__error:
16308c2ecf20Sopenharmony_ci	snd_nm256_free(chip);
16318c2ecf20Sopenharmony_ci	return err;
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_cienum { NM_IGNORED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 };
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk nm256_quirks[] = {
16388c2ecf20Sopenharmony_ci	/* HP omnibook 4150 has cs4232 codec internally */
16398c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x103c, 0x0007, "HP omnibook 4150", NM_IGNORED),
16408c2ecf20Sopenharmony_ci	/* Reset workarounds to avoid lock-ups */
16418c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x104d, 0x8041, "Sony PCG-F305", NM_RESET_WORKAROUND),
16428c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x1028, 0x0080, "Dell Latitude LS", NM_RESET_WORKAROUND),
16438c2ecf20Sopenharmony_ci	SND_PCI_QUIRK(0x1028, 0x0091, "Dell Latitude CSx", NM_RESET_WORKAROUND_2),
16448c2ecf20Sopenharmony_ci	{ } /* terminator */
16458c2ecf20Sopenharmony_ci};
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_cistatic int snd_nm256_probe(struct pci_dev *pci,
16498c2ecf20Sopenharmony_ci			   const struct pci_device_id *pci_id)
16508c2ecf20Sopenharmony_ci{
16518c2ecf20Sopenharmony_ci	struct snd_card *card;
16528c2ecf20Sopenharmony_ci	struct nm256 *chip;
16538c2ecf20Sopenharmony_ci	int err;
16548c2ecf20Sopenharmony_ci	const struct snd_pci_quirk *q;
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci	q = snd_pci_quirk_lookup(pci, nm256_quirks);
16578c2ecf20Sopenharmony_ci	if (q) {
16588c2ecf20Sopenharmony_ci		dev_dbg(&pci->dev, "Enabled quirk for %s.\n",
16598c2ecf20Sopenharmony_ci			    snd_pci_quirk_name(q));
16608c2ecf20Sopenharmony_ci		switch (q->value) {
16618c2ecf20Sopenharmony_ci		case NM_IGNORED:
16628c2ecf20Sopenharmony_ci			dev_info(&pci->dev,
16638c2ecf20Sopenharmony_ci				 "The device is on the denylist. Loading stopped\n");
16648c2ecf20Sopenharmony_ci			return -ENODEV;
16658c2ecf20Sopenharmony_ci		case NM_RESET_WORKAROUND_2:
16668c2ecf20Sopenharmony_ci			reset_workaround_2 = 1;
16678c2ecf20Sopenharmony_ci			fallthrough;
16688c2ecf20Sopenharmony_ci		case NM_RESET_WORKAROUND:
16698c2ecf20Sopenharmony_ci			reset_workaround = 1;
16708c2ecf20Sopenharmony_ci			break;
16718c2ecf20Sopenharmony_ci		}
16728c2ecf20Sopenharmony_ci	}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	err = snd_card_new(&pci->dev, index, id, THIS_MODULE, 0, &card);
16758c2ecf20Sopenharmony_ci	if (err < 0)
16768c2ecf20Sopenharmony_ci		return err;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	switch (pci->device) {
16798c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO:
16808c2ecf20Sopenharmony_ci		strcpy(card->driver, "NM256AV");
16818c2ecf20Sopenharmony_ci		break;
16828c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO:
16838c2ecf20Sopenharmony_ci		strcpy(card->driver, "NM256ZX");
16848c2ecf20Sopenharmony_ci		break;
16858c2ecf20Sopenharmony_ci	case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO:
16868c2ecf20Sopenharmony_ci		strcpy(card->driver, "NM256XL+");
16878c2ecf20Sopenharmony_ci		break;
16888c2ecf20Sopenharmony_ci	default:
16898c2ecf20Sopenharmony_ci		dev_err(&pci->dev, "invalid device id 0x%x\n", pci->device);
16908c2ecf20Sopenharmony_ci		snd_card_free(card);
16918c2ecf20Sopenharmony_ci		return -EINVAL;
16928c2ecf20Sopenharmony_ci	}
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	if (vaio_hack)
16958c2ecf20Sopenharmony_ci		buffer_top = 0x25a800;	/* this avoids conflicts with XFree86 server */
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	if (playback_bufsize < 4)
16988c2ecf20Sopenharmony_ci		playback_bufsize = 4;
16998c2ecf20Sopenharmony_ci	if (playback_bufsize > 128)
17008c2ecf20Sopenharmony_ci		playback_bufsize = 128;
17018c2ecf20Sopenharmony_ci	if (capture_bufsize < 4)
17028c2ecf20Sopenharmony_ci		capture_bufsize = 4;
17038c2ecf20Sopenharmony_ci	if (capture_bufsize > 128)
17048c2ecf20Sopenharmony_ci		capture_bufsize = 128;
17058c2ecf20Sopenharmony_ci	if ((err = snd_nm256_create(card, pci, &chip)) < 0) {
17068c2ecf20Sopenharmony_ci		snd_card_free(card);
17078c2ecf20Sopenharmony_ci		return err;
17088c2ecf20Sopenharmony_ci	}
17098c2ecf20Sopenharmony_ci	card->private_data = chip;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	if (reset_workaround) {
17128c2ecf20Sopenharmony_ci		dev_dbg(&pci->dev, "reset_workaround activated\n");
17138c2ecf20Sopenharmony_ci		chip->reset_workaround = 1;
17148c2ecf20Sopenharmony_ci	}
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	if (reset_workaround_2) {
17178c2ecf20Sopenharmony_ci		dev_dbg(&pci->dev, "reset_workaround_2 activated\n");
17188c2ecf20Sopenharmony_ci		chip->reset_workaround_2 = 1;
17198c2ecf20Sopenharmony_ci	}
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	if ((err = snd_nm256_pcm(chip, 0)) < 0 ||
17228c2ecf20Sopenharmony_ci	    (err = snd_nm256_mixer(chip)) < 0) {
17238c2ecf20Sopenharmony_ci		snd_card_free(card);
17248c2ecf20Sopenharmony_ci		return err;
17258c2ecf20Sopenharmony_ci	}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_ci	sprintf(card->shortname, "NeoMagic %s", card->driver);
17288c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d",
17298c2ecf20Sopenharmony_ci		card->shortname,
17308c2ecf20Sopenharmony_ci		chip->buffer_addr, chip->cport_addr, chip->irq);
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	if ((err = snd_card_register(card)) < 0) {
17338c2ecf20Sopenharmony_ci		snd_card_free(card);
17348c2ecf20Sopenharmony_ci		return err;
17358c2ecf20Sopenharmony_ci	}
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	pci_set_drvdata(pci, card);
17388c2ecf20Sopenharmony_ci	return 0;
17398c2ecf20Sopenharmony_ci}
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_cistatic void snd_nm256_remove(struct pci_dev *pci)
17428c2ecf20Sopenharmony_ci{
17438c2ecf20Sopenharmony_ci	snd_card_free(pci_get_drvdata(pci));
17448c2ecf20Sopenharmony_ci}
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_cistatic struct pci_driver nm256_driver = {
17488c2ecf20Sopenharmony_ci	.name = KBUILD_MODNAME,
17498c2ecf20Sopenharmony_ci	.id_table = snd_nm256_ids,
17508c2ecf20Sopenharmony_ci	.probe = snd_nm256_probe,
17518c2ecf20Sopenharmony_ci	.remove = snd_nm256_remove,
17528c2ecf20Sopenharmony_ci	.driver = {
17538c2ecf20Sopenharmony_ci		.pm = NM256_PM_OPS,
17548c2ecf20Sopenharmony_ci	},
17558c2ecf20Sopenharmony_ci};
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_cimodule_pci_driver(nm256_driver);
1758