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