162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
462306a36Sopenharmony_ci *                   Abramo Bagnara <abramo@alsa-project.org>
562306a36Sopenharmony_ci *                   Cirrus Logic, Inc.
662306a36Sopenharmony_ci *  Routines for control of Cirrus Logic CS461x chips
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  KNOWN BUGS:
962306a36Sopenharmony_ci *    - Sometimes the SPDIF input DSP tasks get's unsynchronized
1062306a36Sopenharmony_ci *      and the SPDIF get somewhat "distorcionated", or/and left right channel
1162306a36Sopenharmony_ci *      are swapped. To get around this problem when it happens, mute and unmute
1262306a36Sopenharmony_ci *      the SPDIF input mixer control.
1362306a36Sopenharmony_ci *    - On the Hercules Game Theater XP the amplifier are sometimes turned
1462306a36Sopenharmony_ci *      off on inadecuate moments which causes distorcions on sound.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *  TODO:
1762306a36Sopenharmony_ci *    - Secondary CODEC on some soundcards
1862306a36Sopenharmony_ci *    - SPDIF input support for other sample rates then 48khz
1962306a36Sopenharmony_ci *    - Posibility to mix the SPDIF output with analog sources.
2062306a36Sopenharmony_ci *    - PCM channels for Center and LFE on secondary codec
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci *  NOTE: with CONFIG_SND_CS46XX_NEW_DSP unset uses old DSP image (which
2362306a36Sopenharmony_ci *        is default configuration), no SPDIF, no secondary codec, no
2462306a36Sopenharmony_ci *        multi channel PCM.  But known to work.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci *  FINALLY: A credit to the developers Tom and Jordan
2762306a36Sopenharmony_ci *           at Cirrus for have helping me out with the DSP, however we
2862306a36Sopenharmony_ci *           still don't have sufficient documentation and technical
2962306a36Sopenharmony_ci *           references to be able to implement all fancy feutures
3062306a36Sopenharmony_ci *           supported by the cs46xx DSP's.
3162306a36Sopenharmony_ci *           Benny <benny@hostmobility.com>
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <linux/delay.h>
3562306a36Sopenharmony_ci#include <linux/pci.h>
3662306a36Sopenharmony_ci#include <linux/pm.h>
3762306a36Sopenharmony_ci#include <linux/init.h>
3862306a36Sopenharmony_ci#include <linux/interrupt.h>
3962306a36Sopenharmony_ci#include <linux/slab.h>
4062306a36Sopenharmony_ci#include <linux/gameport.h>
4162306a36Sopenharmony_ci#include <linux/mutex.h>
4262306a36Sopenharmony_ci#include <linux/export.h>
4362306a36Sopenharmony_ci#include <linux/module.h>
4462306a36Sopenharmony_ci#include <linux/firmware.h>
4562306a36Sopenharmony_ci#include <linux/vmalloc.h>
4662306a36Sopenharmony_ci#include <linux/io.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <sound/core.h>
4962306a36Sopenharmony_ci#include <sound/control.h>
5062306a36Sopenharmony_ci#include <sound/info.h>
5162306a36Sopenharmony_ci#include <sound/pcm.h>
5262306a36Sopenharmony_ci#include <sound/pcm_params.h>
5362306a36Sopenharmony_ci#include "cs46xx.h"
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#include "cs46xx_lib.h"
5662306a36Sopenharmony_ci#include "dsp_spos.h"
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void amp_voyetra(struct snd_cs46xx *chip, int change);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
6162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_rear_ops;
6262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops;
6362306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops;
6462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops;
6562306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops;
6662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops;
6762306a36Sopenharmony_ci#endif
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_ops;
7062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops;
7162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_capture_ops;
7262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,
7562306a36Sopenharmony_ci					    unsigned short reg,
7662306a36Sopenharmony_ci					    int codec_index)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int count;
7962306a36Sopenharmony_ci	unsigned short result,tmp;
8062306a36Sopenharmony_ci	u32 offset = 0;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
8362306a36Sopenharmony_ci		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
8462306a36Sopenharmony_ci		return 0xffff;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	chip->active_ctrl(chip, 1);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (codec_index == CS46XX_SECONDARY_CODEC_INDEX)
8962306a36Sopenharmony_ci		offset = CS46XX_SECONDARY_CODEC_OFFSET;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
9362306a36Sopenharmony_ci	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
9462306a36Sopenharmony_ci	 *  3. Write ACCTL = Control Register = 460h for initiating the write7---55
9562306a36Sopenharmony_ci	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 17h
9662306a36Sopenharmony_ci	 *  5. if DCV not cleared, break and return error
9762306a36Sopenharmony_ci	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
9862306a36Sopenharmony_ci	 */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL);
10362306a36Sopenharmony_ci	if ((tmp & ACCTL_VFRM) == 0) {
10462306a36Sopenharmony_ci		dev_warn(chip->card->dev, "ACCTL_VFRM not set 0x%x\n", tmp);
10562306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, (tmp & (~ACCTL_ESYN)) | ACCTL_VFRM );
10662306a36Sopenharmony_ci		msleep(50);
10762306a36Sopenharmony_ci		tmp = snd_cs46xx_peekBA0(chip, BA0_ACCTL + offset);
10862306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, tmp | ACCTL_ESYN | ACCTL_VFRM );
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/*
11362306a36Sopenharmony_ci	 *  Setup the AC97 control registers on the CS461x to send the
11462306a36Sopenharmony_ci	 *  appropriate command to the AC97 to perform the read.
11562306a36Sopenharmony_ci	 *  ACCAD = Command Address Register = 46Ch
11662306a36Sopenharmony_ci	 *  ACCDA = Command Data Register = 470h
11762306a36Sopenharmony_ci	 *  ACCTL = Control Register = 460h
11862306a36Sopenharmony_ci	 *  set DCV - will clear when process completed
11962306a36Sopenharmony_ci	 *  set CRW - Read command
12062306a36Sopenharmony_ci	 *  set VFRM - valid frame enabled
12162306a36Sopenharmony_ci	 *  set ESYN - ASYNC generation enabled
12262306a36Sopenharmony_ci	 *  set RSTN - ARST# inactive, AC97 codec not reset
12362306a36Sopenharmony_ci	 */
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCAD, reg);
12662306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCDA, 0);
12762306a36Sopenharmony_ci	if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {
12862306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL,/* clear ACCTL_DCV */ ACCTL_CRW |
12962306a36Sopenharmony_ci				   ACCTL_VFRM | ACCTL_ESYN |
13062306a36Sopenharmony_ci				   ACCTL_RSTN);
13162306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW |
13262306a36Sopenharmony_ci				   ACCTL_VFRM | ACCTL_ESYN |
13362306a36Sopenharmony_ci				   ACCTL_RSTN);
13462306a36Sopenharmony_ci	} else {
13562306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |
13662306a36Sopenharmony_ci				   ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN |
13762306a36Sopenharmony_ci				   ACCTL_RSTN);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 *  Wait for the read to occur.
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	for (count = 0; count < 1000; count++) {
14462306a36Sopenharmony_ci		/*
14562306a36Sopenharmony_ci		 *  First, we want to wait for a short time.
14662306a36Sopenharmony_ci	 	 */
14762306a36Sopenharmony_ci		udelay(10);
14862306a36Sopenharmony_ci		/*
14962306a36Sopenharmony_ci		 *  Now, check to see if the read has completed.
15062306a36Sopenharmony_ci		 *  ACCTL = 460h, DCV should be reset by now and 460h = 17h
15162306a36Sopenharmony_ci		 */
15262306a36Sopenharmony_ci		if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV))
15362306a36Sopenharmony_ci			goto ok1;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	dev_err(chip->card->dev,
15762306a36Sopenharmony_ci		"AC'97 read problem (ACCTL_DCV), reg = 0x%x\n", reg);
15862306a36Sopenharmony_ci	result = 0xffff;
15962306a36Sopenharmony_ci	goto end;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci ok1:
16262306a36Sopenharmony_ci	/*
16362306a36Sopenharmony_ci	 *  Wait for the valid status bit to go active.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	for (count = 0; count < 100; count++) {
16662306a36Sopenharmony_ci		/*
16762306a36Sopenharmony_ci		 *  Read the AC97 status register.
16862306a36Sopenharmony_ci		 *  ACSTS = Status Register = 464h
16962306a36Sopenharmony_ci		 *  VSTS - Valid Status
17062306a36Sopenharmony_ci		 */
17162306a36Sopenharmony_ci		if (snd_cs46xx_peekBA0(chip, BA0_ACSTS + offset) & ACSTS_VSTS)
17262306a36Sopenharmony_ci			goto ok2;
17362306a36Sopenharmony_ci		udelay(10);
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	dev_err(chip->card->dev,
17762306a36Sopenharmony_ci		"AC'97 read problem (ACSTS_VSTS), codec_index %d, reg = 0x%x\n",
17862306a36Sopenharmony_ci		codec_index, reg);
17962306a36Sopenharmony_ci	result = 0xffff;
18062306a36Sopenharmony_ci	goto end;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci ok2:
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 *  Read the data returned from the AC97 register.
18562306a36Sopenharmony_ci	 *  ACSDA = Status Data Register = 474h
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci#if 0
18862306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
18962306a36Sopenharmony_ci		"e) reg = 0x%x, val = 0x%x, BA0_ACCAD = 0x%x\n", reg,
19062306a36Sopenharmony_ci			snd_cs46xx_peekBA0(chip, BA0_ACSDA),
19162306a36Sopenharmony_ci			snd_cs46xx_peekBA0(chip, BA0_ACCAD));
19262306a36Sopenharmony_ci#endif
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	//snd_cs46xx_peekBA0(chip, BA0_ACCAD);
19562306a36Sopenharmony_ci	result = snd_cs46xx_peekBA0(chip, BA0_ACSDA + offset);
19662306a36Sopenharmony_ci end:
19762306a36Sopenharmony_ci	chip->active_ctrl(chip, -1);
19862306a36Sopenharmony_ci	return result;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic unsigned short snd_cs46xx_ac97_read(struct snd_ac97 * ac97,
20262306a36Sopenharmony_ci					    unsigned short reg)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct snd_cs46xx *chip = ac97->private_data;
20562306a36Sopenharmony_ci	unsigned short val;
20662306a36Sopenharmony_ci	int codec_index = ac97->num;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
20962306a36Sopenharmony_ci		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
21062306a36Sopenharmony_ci		return 0xffff;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	val = snd_cs46xx_codec_read(chip, reg, codec_index);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return val;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void snd_cs46xx_codec_write(struct snd_cs46xx *chip,
21962306a36Sopenharmony_ci				   unsigned short reg,
22062306a36Sopenharmony_ci				   unsigned short val,
22162306a36Sopenharmony_ci				   int codec_index)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	int count;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
22662306a36Sopenharmony_ci		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
22762306a36Sopenharmony_ci		return;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	chip->active_ctrl(chip, 1);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
23362306a36Sopenharmony_ci	 *  2. Write ACCDA = Command Data Register = 470h    for data to write to AC97
23462306a36Sopenharmony_ci	 *  3. Write ACCTL = Control Register = 460h for initiating the write
23562306a36Sopenharmony_ci	 *  4. Read ACCTL = 460h, DCV should be reset by now and 460h = 07h
23662306a36Sopenharmony_ci	 *  5. if DCV not cleared, break and return error
23762306a36Sopenharmony_ci	 */
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/*
24062306a36Sopenharmony_ci	 *  Setup the AC97 control registers on the CS461x to send the
24162306a36Sopenharmony_ci	 *  appropriate command to the AC97 to perform the read.
24262306a36Sopenharmony_ci	 *  ACCAD = Command Address Register = 46Ch
24362306a36Sopenharmony_ci	 *  ACCDA = Command Data Register = 470h
24462306a36Sopenharmony_ci	 *  ACCTL = Control Register = 460h
24562306a36Sopenharmony_ci	 *  set DCV - will clear when process completed
24662306a36Sopenharmony_ci	 *  reset CRW - Write command
24762306a36Sopenharmony_ci	 *  set VFRM - valid frame enabled
24862306a36Sopenharmony_ci	 *  set ESYN - ASYNC generation enabled
24962306a36Sopenharmony_ci	 *  set RSTN - ARST# inactive, AC97 codec not reset
25062306a36Sopenharmony_ci         */
25162306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCAD , reg);
25262306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCDA , val);
25362306a36Sopenharmony_ci	snd_cs46xx_peekBA0(chip, BA0_ACCTL);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (codec_index == CS46XX_PRIMARY_CODEC_INDEX) {
25662306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, /* clear ACCTL_DCV */ ACCTL_VFRM |
25762306a36Sopenharmony_ci				   ACCTL_ESYN | ACCTL_RSTN);
25862306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM |
25962306a36Sopenharmony_ci				   ACCTL_ESYN | ACCTL_RSTN);
26062306a36Sopenharmony_ci	} else {
26162306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_DCV | ACCTL_TC |
26262306a36Sopenharmony_ci				   ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	for (count = 0; count < 4000; count++) {
26662306a36Sopenharmony_ci		/*
26762306a36Sopenharmony_ci		 *  First, we want to wait for a short time.
26862306a36Sopenharmony_ci		 */
26962306a36Sopenharmony_ci		udelay(10);
27062306a36Sopenharmony_ci		/*
27162306a36Sopenharmony_ci		 *  Now, check to see if the write has completed.
27262306a36Sopenharmony_ci		 *  ACCTL = 460h, DCV should be reset by now and 460h = 07h
27362306a36Sopenharmony_ci		 */
27462306a36Sopenharmony_ci		if (!(snd_cs46xx_peekBA0(chip, BA0_ACCTL) & ACCTL_DCV)) {
27562306a36Sopenharmony_ci			goto end;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci	}
27862306a36Sopenharmony_ci	dev_err(chip->card->dev,
27962306a36Sopenharmony_ci		"AC'97 write problem, codec_index = %d, reg = 0x%x, val = 0x%x\n",
28062306a36Sopenharmony_ci		codec_index, reg, val);
28162306a36Sopenharmony_ci end:
28262306a36Sopenharmony_ci	chip->active_ctrl(chip, -1);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void snd_cs46xx_ac97_write(struct snd_ac97 *ac97,
28662306a36Sopenharmony_ci				   unsigned short reg,
28762306a36Sopenharmony_ci				   unsigned short val)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct snd_cs46xx *chip = ac97->private_data;
29062306a36Sopenharmony_ci	int codec_index = ac97->num;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
29362306a36Sopenharmony_ci		       codec_index != CS46XX_SECONDARY_CODEC_INDEX))
29462306a36Sopenharmony_ci		return;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	snd_cs46xx_codec_write(chip, reg, val, codec_index);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/*
30162306a36Sopenharmony_ci *  Chip initialization
30262306a36Sopenharmony_ci */
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ciint snd_cs46xx_download(struct snd_cs46xx *chip,
30562306a36Sopenharmony_ci			u32 *src,
30662306a36Sopenharmony_ci                        unsigned long offset,
30762306a36Sopenharmony_ci                        unsigned long len)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	void __iomem *dst;
31062306a36Sopenharmony_ci	unsigned int bank = offset >> 16;
31162306a36Sopenharmony_ci	offset = offset & 0xffff;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (snd_BUG_ON((offset & 3) || (len & 3)))
31462306a36Sopenharmony_ci		return -EINVAL;
31562306a36Sopenharmony_ci	dst = chip->region.idx[bank+1].remap_addr + offset;
31662306a36Sopenharmony_ci	len /= sizeof(u32);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* writel already converts 32-bit value to right endianess */
31962306a36Sopenharmony_ci	while (len-- > 0) {
32062306a36Sopenharmony_ci		writel(*src++, dst);
32162306a36Sopenharmony_ci		dst += sizeof(u32);
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic inline void memcpy_le32(void *dst, const void *src, unsigned int len)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN
32962306a36Sopenharmony_ci	memcpy(dst, src, len);
33062306a36Sopenharmony_ci#else
33162306a36Sopenharmony_ci	u32 *_dst = dst;
33262306a36Sopenharmony_ci	const __le32 *_src = src;
33362306a36Sopenharmony_ci	len /= 4;
33462306a36Sopenharmony_ci	while (len-- > 0)
33562306a36Sopenharmony_ci		*_dst++ = le32_to_cpu(*_src++);
33662306a36Sopenharmony_ci#endif
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic const char *module_names[CS46XX_DSP_MODULES] = {
34262306a36Sopenharmony_ci	"cwc4630", "cwcasync", "cwcsnoop", "cwcbinhack", "cwcdma"
34362306a36Sopenharmony_ci};
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ciMODULE_FIRMWARE("cs46xx/cwc4630");
34662306a36Sopenharmony_ciMODULE_FIRMWARE("cs46xx/cwcasync");
34762306a36Sopenharmony_ciMODULE_FIRMWARE("cs46xx/cwcsnoop");
34862306a36Sopenharmony_ciMODULE_FIRMWARE("cs46xx/cwcbinhack");
34962306a36Sopenharmony_ciMODULE_FIRMWARE("cs46xx/cwcdma");
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void free_module_desc(struct dsp_module_desc *module)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	if (!module)
35462306a36Sopenharmony_ci		return;
35562306a36Sopenharmony_ci	kfree(module->module_name);
35662306a36Sopenharmony_ci	kfree(module->symbol_table.symbols);
35762306a36Sopenharmony_ci	if (module->segments) {
35862306a36Sopenharmony_ci		int i;
35962306a36Sopenharmony_ci		for (i = 0; i < module->nsegments; i++)
36062306a36Sopenharmony_ci			kfree(module->segments[i].data);
36162306a36Sopenharmony_ci		kfree(module->segments);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	kfree(module);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/* firmware binary format:
36762306a36Sopenharmony_ci * le32 nsymbols;
36862306a36Sopenharmony_ci * struct {
36962306a36Sopenharmony_ci *	le32 address;
37062306a36Sopenharmony_ci *	char symbol_name[DSP_MAX_SYMBOL_NAME];
37162306a36Sopenharmony_ci *	le32 symbol_type;
37262306a36Sopenharmony_ci * } symbols[nsymbols];
37362306a36Sopenharmony_ci * le32 nsegments;
37462306a36Sopenharmony_ci * struct {
37562306a36Sopenharmony_ci *	le32 segment_type;
37662306a36Sopenharmony_ci *	le32 offset;
37762306a36Sopenharmony_ci *	le32 size;
37862306a36Sopenharmony_ci *	le32 data[size];
37962306a36Sopenharmony_ci * } segments[nsegments];
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic int load_firmware(struct snd_cs46xx *chip,
38362306a36Sopenharmony_ci			 struct dsp_module_desc **module_ret,
38462306a36Sopenharmony_ci			 const char *fw_name)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	int i, err;
38762306a36Sopenharmony_ci	unsigned int nums, fwlen, fwsize;
38862306a36Sopenharmony_ci	const __le32 *fwdat;
38962306a36Sopenharmony_ci	struct dsp_module_desc *module = NULL;
39062306a36Sopenharmony_ci	const struct firmware *fw;
39162306a36Sopenharmony_ci	char fw_path[32];
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	sprintf(fw_path, "cs46xx/%s", fw_name);
39462306a36Sopenharmony_ci	err = request_firmware(&fw, fw_path, &chip->pci->dev);
39562306a36Sopenharmony_ci	if (err < 0)
39662306a36Sopenharmony_ci		return err;
39762306a36Sopenharmony_ci	fwsize = fw->size / 4;
39862306a36Sopenharmony_ci	if (fwsize < 2) {
39962306a36Sopenharmony_ci		err = -EINVAL;
40062306a36Sopenharmony_ci		goto error;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	err = -ENOMEM;
40462306a36Sopenharmony_ci	module = kzalloc(sizeof(*module), GFP_KERNEL);
40562306a36Sopenharmony_ci	if (!module)
40662306a36Sopenharmony_ci		goto error;
40762306a36Sopenharmony_ci	module->module_name = kstrdup(fw_name, GFP_KERNEL);
40862306a36Sopenharmony_ci	if (!module->module_name)
40962306a36Sopenharmony_ci		goto error;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	fwlen = 0;
41262306a36Sopenharmony_ci	fwdat = (const __le32 *)fw->data;
41362306a36Sopenharmony_ci	nums = module->symbol_table.nsymbols = le32_to_cpu(fwdat[fwlen++]);
41462306a36Sopenharmony_ci	if (nums >= 40)
41562306a36Sopenharmony_ci		goto error_inval;
41662306a36Sopenharmony_ci	module->symbol_table.symbols =
41762306a36Sopenharmony_ci		kcalloc(nums, sizeof(struct dsp_symbol_entry), GFP_KERNEL);
41862306a36Sopenharmony_ci	if (!module->symbol_table.symbols)
41962306a36Sopenharmony_ci		goto error;
42062306a36Sopenharmony_ci	for (i = 0; i < nums; i++) {
42162306a36Sopenharmony_ci		struct dsp_symbol_entry *entry =
42262306a36Sopenharmony_ci			&module->symbol_table.symbols[i];
42362306a36Sopenharmony_ci		if (fwlen + 2 + DSP_MAX_SYMBOL_NAME / 4 > fwsize)
42462306a36Sopenharmony_ci			goto error_inval;
42562306a36Sopenharmony_ci		entry->address = le32_to_cpu(fwdat[fwlen++]);
42662306a36Sopenharmony_ci		memcpy(entry->symbol_name, &fwdat[fwlen], DSP_MAX_SYMBOL_NAME - 1);
42762306a36Sopenharmony_ci		fwlen += DSP_MAX_SYMBOL_NAME / 4;
42862306a36Sopenharmony_ci		entry->symbol_type = le32_to_cpu(fwdat[fwlen++]);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (fwlen >= fwsize)
43262306a36Sopenharmony_ci		goto error_inval;
43362306a36Sopenharmony_ci	nums = module->nsegments = le32_to_cpu(fwdat[fwlen++]);
43462306a36Sopenharmony_ci	if (nums > 10)
43562306a36Sopenharmony_ci		goto error_inval;
43662306a36Sopenharmony_ci	module->segments =
43762306a36Sopenharmony_ci		kcalloc(nums, sizeof(struct dsp_segment_desc), GFP_KERNEL);
43862306a36Sopenharmony_ci	if (!module->segments)
43962306a36Sopenharmony_ci		goto error;
44062306a36Sopenharmony_ci	for (i = 0; i < nums; i++) {
44162306a36Sopenharmony_ci		struct dsp_segment_desc *entry = &module->segments[i];
44262306a36Sopenharmony_ci		if (fwlen + 3 > fwsize)
44362306a36Sopenharmony_ci			goto error_inval;
44462306a36Sopenharmony_ci		entry->segment_type = le32_to_cpu(fwdat[fwlen++]);
44562306a36Sopenharmony_ci		entry->offset = le32_to_cpu(fwdat[fwlen++]);
44662306a36Sopenharmony_ci		entry->size = le32_to_cpu(fwdat[fwlen++]);
44762306a36Sopenharmony_ci		if (fwlen + entry->size > fwsize)
44862306a36Sopenharmony_ci			goto error_inval;
44962306a36Sopenharmony_ci		entry->data = kmalloc_array(entry->size, 4, GFP_KERNEL);
45062306a36Sopenharmony_ci		if (!entry->data)
45162306a36Sopenharmony_ci			goto error;
45262306a36Sopenharmony_ci		memcpy_le32(entry->data, &fwdat[fwlen], entry->size * 4);
45362306a36Sopenharmony_ci		fwlen += entry->size;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	*module_ret = module;
45762306a36Sopenharmony_ci	release_firmware(fw);
45862306a36Sopenharmony_ci	return 0;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci error_inval:
46162306a36Sopenharmony_ci	err = -EINVAL;
46262306a36Sopenharmony_ci error:
46362306a36Sopenharmony_ci	free_module_desc(module);
46462306a36Sopenharmony_ci	release_firmware(fw);
46562306a36Sopenharmony_ci	return err;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ciint snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,
46962306a36Sopenharmony_ci                         unsigned long offset,
47062306a36Sopenharmony_ci                         unsigned long len)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	void __iomem *dst;
47362306a36Sopenharmony_ci	unsigned int bank = offset >> 16;
47462306a36Sopenharmony_ci	offset = offset & 0xffff;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	if (snd_BUG_ON((offset & 3) || (len & 3)))
47762306a36Sopenharmony_ci		return -EINVAL;
47862306a36Sopenharmony_ci	dst = chip->region.idx[bank+1].remap_addr + offset;
47962306a36Sopenharmony_ci	len /= sizeof(u32);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* writel already converts 32-bit value to right endianess */
48262306a36Sopenharmony_ci	while (len-- > 0) {
48362306a36Sopenharmony_ci		writel(0, dst);
48462306a36Sopenharmony_ci		dst += sizeof(u32);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci#else /* old DSP image */
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistruct ba1_struct {
49262306a36Sopenharmony_ci	struct {
49362306a36Sopenharmony_ci		u32 offset;
49462306a36Sopenharmony_ci		u32 size;
49562306a36Sopenharmony_ci	} memory[BA1_MEMORY_COUNT];
49662306a36Sopenharmony_ci	u32 map[BA1_DWORD_SIZE];
49762306a36Sopenharmony_ci};
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ciMODULE_FIRMWARE("cs46xx/ba1");
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic int load_firmware(struct snd_cs46xx *chip)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	const struct firmware *fw;
50462306a36Sopenharmony_ci	int i, size, err;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	err = request_firmware(&fw, "cs46xx/ba1", &chip->pci->dev);
50762306a36Sopenharmony_ci	if (err < 0)
50862306a36Sopenharmony_ci		return err;
50962306a36Sopenharmony_ci	if (fw->size != sizeof(*chip->ba1)) {
51062306a36Sopenharmony_ci		err = -EINVAL;
51162306a36Sopenharmony_ci		goto error;
51262306a36Sopenharmony_ci	}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	chip->ba1 = vmalloc(sizeof(*chip->ba1));
51562306a36Sopenharmony_ci	if (!chip->ba1) {
51662306a36Sopenharmony_ci		err = -ENOMEM;
51762306a36Sopenharmony_ci		goto error;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	memcpy_le32(chip->ba1, fw->data, sizeof(*chip->ba1));
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* sanity check */
52362306a36Sopenharmony_ci	size = 0;
52462306a36Sopenharmony_ci	for (i = 0; i < BA1_MEMORY_COUNT; i++)
52562306a36Sopenharmony_ci		size += chip->ba1->memory[i].size;
52662306a36Sopenharmony_ci	if (size > BA1_DWORD_SIZE * 4)
52762306a36Sopenharmony_ci		err = -EINVAL;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci error:
53062306a36Sopenharmony_ci	release_firmware(fw);
53162306a36Sopenharmony_ci	return err;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic __maybe_unused int snd_cs46xx_download_image(struct snd_cs46xx *chip)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	int idx, err;
53762306a36Sopenharmony_ci	unsigned int offset = 0;
53862306a36Sopenharmony_ci	struct ba1_struct *ba1 = chip->ba1;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	for (idx = 0; idx < BA1_MEMORY_COUNT; idx++) {
54162306a36Sopenharmony_ci		err = snd_cs46xx_download(chip,
54262306a36Sopenharmony_ci					  &ba1->map[offset],
54362306a36Sopenharmony_ci					  ba1->memory[idx].offset,
54462306a36Sopenharmony_ci					  ba1->memory[idx].size);
54562306a36Sopenharmony_ci		if (err < 0)
54662306a36Sopenharmony_ci			return err;
54762306a36Sopenharmony_ci		offset += ba1->memory[idx].size >> 2;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci#endif /* CONFIG_SND_CS46XX_NEW_DSP */
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci/*
55462306a36Sopenharmony_ci *  Chip reset
55562306a36Sopenharmony_ci */
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic void snd_cs46xx_reset(struct snd_cs46xx *chip)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	int idx;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/*
56262306a36Sopenharmony_ci	 *  Write the reset bit of the SP control register.
56362306a36Sopenharmony_ci	 */
56462306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RSTSP);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/*
56762306a36Sopenharmony_ci	 *  Write the control register.
56862306a36Sopenharmony_ci	 */
56962306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_DRQEN);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/*
57262306a36Sopenharmony_ci	 *  Clear the trap registers.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	for (idx = 0; idx < 8; idx++) {
57562306a36Sopenharmony_ci		snd_cs46xx_poke(chip, BA1_DREG, DREG_REGID_TRAP_SELECT + idx);
57662306a36Sopenharmony_ci		snd_cs46xx_poke(chip, BA1_TWPR, 0xFFFF);
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_DREG, 0);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/*
58162306a36Sopenharmony_ci	 *  Set the frame timer to reflect the number of cycles per frame.
58262306a36Sopenharmony_ci	 */
58362306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int cs46xx_wait_for_fifo(struct snd_cs46xx * chip,int retry_timeout)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	u32 i, status = 0;
58962306a36Sopenharmony_ci	/*
59062306a36Sopenharmony_ci	 * Make sure the previous FIFO write operation has completed.
59162306a36Sopenharmony_ci	 */
59262306a36Sopenharmony_ci	for(i = 0; i < 50; i++){
59362306a36Sopenharmony_ci		status = snd_cs46xx_peekBA0(chip, BA0_SERBST);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if( !(status & SERBST_WBSY) )
59662306a36Sopenharmony_ci			break;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		mdelay(retry_timeout);
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if(status & SERBST_WBSY) {
60262306a36Sopenharmony_ci		dev_err(chip->card->dev,
60362306a36Sopenharmony_ci			"failure waiting for FIFO command to complete\n");
60462306a36Sopenharmony_ci		return -EINVAL;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void snd_cs46xx_clear_serial_FIFOs(struct snd_cs46xx *chip)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	int idx, powerdown = 0;
61362306a36Sopenharmony_ci	unsigned int tmp;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/*
61662306a36Sopenharmony_ci	 *  See if the devices are powered down.  If so, we must power them up first
61762306a36Sopenharmony_ci	 *  or they will not respond.
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
62062306a36Sopenharmony_ci	if (!(tmp & CLKCR1_SWCE)) {
62162306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
62262306a36Sopenharmony_ci		powerdown = 1;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/*
62662306a36Sopenharmony_ci	 *  We want to clear out the serial port FIFOs so we don't end up playing
62762306a36Sopenharmony_ci	 *  whatever random garbage happens to be in them.  We fill the sample FIFOS
62862306a36Sopenharmony_ci	 *  with zero (silence).
62962306a36Sopenharmony_ci	 */
63062306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/*
63362306a36Sopenharmony_ci	 *  Fill all 256 sample FIFO locations.
63462306a36Sopenharmony_ci	 */
63562306a36Sopenharmony_ci	for (idx = 0; idx < 0xFF; idx++) {
63662306a36Sopenharmony_ci		/*
63762306a36Sopenharmony_ci		 *  Make sure the previous FIFO write operation has completed.
63862306a36Sopenharmony_ci		 */
63962306a36Sopenharmony_ci		if (cs46xx_wait_for_fifo(chip,1)) {
64062306a36Sopenharmony_ci			dev_dbg(chip->card->dev,
64162306a36Sopenharmony_ci				"failed waiting for FIFO at addr (%02X)\n",
64262306a36Sopenharmony_ci				idx);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci			if (powerdown)
64562306a36Sopenharmony_ci				snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci			break;
64862306a36Sopenharmony_ci		}
64962306a36Sopenharmony_ci		/*
65062306a36Sopenharmony_ci		 *  Write the serial port FIFO index.
65162306a36Sopenharmony_ci		 */
65262306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
65362306a36Sopenharmony_ci		/*
65462306a36Sopenharmony_ci		 *  Tell the serial port to load the new value into the FIFO location.
65562306a36Sopenharmony_ci		 */
65662306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci	/*
65962306a36Sopenharmony_ci	 *  Now, if we powered up the devices, then power them back down again.
66062306a36Sopenharmony_ci	 *  This is kinda ugly, but should never happen.
66162306a36Sopenharmony_ci	 */
66262306a36Sopenharmony_ci	if (powerdown)
66362306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic void snd_cs46xx_proc_start(struct snd_cs46xx *chip)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	int cnt;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/*
67162306a36Sopenharmony_ci	 *  Set the frame timer to reflect the number of cycles per frame.
67262306a36Sopenharmony_ci	 */
67362306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);
67462306a36Sopenharmony_ci	/*
67562306a36Sopenharmony_ci	 *  Turn on the run, run at frame, and DMA enable bits in the local copy of
67662306a36Sopenharmony_ci	 *  the SP control register.
67762306a36Sopenharmony_ci	 */
67862306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);
67962306a36Sopenharmony_ci	/*
68062306a36Sopenharmony_ci	 *  Wait until the run at frame bit resets itself in the SP control
68162306a36Sopenharmony_ci	 *  register.
68262306a36Sopenharmony_ci	 */
68362306a36Sopenharmony_ci	for (cnt = 0; cnt < 25; cnt++) {
68462306a36Sopenharmony_ci		udelay(50);
68562306a36Sopenharmony_ci		if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR))
68662306a36Sopenharmony_ci			break;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)
69062306a36Sopenharmony_ci		dev_err(chip->card->dev, "SPCR_RUNFR never reset\n");
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic void snd_cs46xx_proc_stop(struct snd_cs46xx *chip)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	/*
69662306a36Sopenharmony_ci	 *  Turn off the run, run at frame, and DMA enable bits in the local copy of
69762306a36Sopenharmony_ci	 *  the SP control register.
69862306a36Sopenharmony_ci	 */
69962306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_SPCR, 0);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci/*
70362306a36Sopenharmony_ci *  Sample rate routines
70462306a36Sopenharmony_ci */
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci#define GOF_PER_SEC 200
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_cistatic void snd_cs46xx_set_play_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	unsigned long flags;
71162306a36Sopenharmony_ci	unsigned int tmp1, tmp2;
71262306a36Sopenharmony_ci	unsigned int phiIncr;
71362306a36Sopenharmony_ci	unsigned int correctionPerGOF, correctionPerSec;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	/*
71662306a36Sopenharmony_ci	 *  Compute the values used to drive the actual sample rate conversion.
71762306a36Sopenharmony_ci	 *  The following formulas are being computed, using inline assembly
71862306a36Sopenharmony_ci	 *  since we need to use 64 bit arithmetic to compute the values:
71962306a36Sopenharmony_ci	 *
72062306a36Sopenharmony_ci	 *  phiIncr = floor((Fs,in * 2^26) / Fs,out)
72162306a36Sopenharmony_ci	 *  correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
72262306a36Sopenharmony_ci         *                                   GOF_PER_SEC)
72362306a36Sopenharmony_ci         *  ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M
72462306a36Sopenharmony_ci         *                       GOF_PER_SEC * correctionPerGOF
72562306a36Sopenharmony_ci	 *
72662306a36Sopenharmony_ci	 *  i.e.
72762306a36Sopenharmony_ci	 *
72862306a36Sopenharmony_ci	 *  phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)
72962306a36Sopenharmony_ci	 *  correctionPerGOF:correctionPerSec =
73062306a36Sopenharmony_ci	 *      dividend:remainder(ulOther / GOF_PER_SEC)
73162306a36Sopenharmony_ci	 */
73262306a36Sopenharmony_ci	tmp1 = rate << 16;
73362306a36Sopenharmony_ci	phiIncr = tmp1 / 48000;
73462306a36Sopenharmony_ci	tmp1 -= phiIncr * 48000;
73562306a36Sopenharmony_ci	tmp1 <<= 10;
73662306a36Sopenharmony_ci	phiIncr <<= 10;
73762306a36Sopenharmony_ci	tmp2 = tmp1 / 48000;
73862306a36Sopenharmony_ci	phiIncr += tmp2;
73962306a36Sopenharmony_ci	tmp1 -= tmp2 * 48000;
74062306a36Sopenharmony_ci	correctionPerGOF = tmp1 / GOF_PER_SEC;
74162306a36Sopenharmony_ci	tmp1 -= correctionPerGOF * GOF_PER_SEC;
74262306a36Sopenharmony_ci	correctionPerSec = tmp1;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/*
74562306a36Sopenharmony_ci	 *  Fill in the SampleRateConverter control block.
74662306a36Sopenharmony_ci	 */
74762306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
74862306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PSRC,
74962306a36Sopenharmony_ci	  ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
75062306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PPI, phiIncr);
75162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void snd_cs46xx_set_capture_sample_rate(struct snd_cs46xx *chip, unsigned int rate)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	unsigned long flags;
75762306a36Sopenharmony_ci	unsigned int phiIncr, coeffIncr, tmp1, tmp2;
75862306a36Sopenharmony_ci	unsigned int correctionPerGOF, correctionPerSec, initialDelay;
75962306a36Sopenharmony_ci	unsigned int frameGroupLength, cnt;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/*
76262306a36Sopenharmony_ci	 *  We can only decimate by up to a factor of 1/9th the hardware rate.
76362306a36Sopenharmony_ci	 *  Correct the value if an attempt is made to stray outside that limit.
76462306a36Sopenharmony_ci	 */
76562306a36Sopenharmony_ci	if ((rate * 9) < 48000)
76662306a36Sopenharmony_ci		rate = 48000 / 9;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/*
76962306a36Sopenharmony_ci	 *  We can not capture at a rate greater than the Input Rate (48000).
77062306a36Sopenharmony_ci	 *  Return an error if an attempt is made to stray outside that limit.
77162306a36Sopenharmony_ci	 */
77262306a36Sopenharmony_ci	if (rate > 48000)
77362306a36Sopenharmony_ci		rate = 48000;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/*
77662306a36Sopenharmony_ci	 *  Compute the values used to drive the actual sample rate conversion.
77762306a36Sopenharmony_ci	 *  The following formulas are being computed, using inline assembly
77862306a36Sopenharmony_ci	 *  since we need to use 64 bit arithmetic to compute the values:
77962306a36Sopenharmony_ci	 *
78062306a36Sopenharmony_ci	 *     coeffIncr = -floor((Fs,out * 2^23) / Fs,in)
78162306a36Sopenharmony_ci	 *     phiIncr = floor((Fs,in * 2^26) / Fs,out)
78262306a36Sopenharmony_ci	 *     correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /
78362306a36Sopenharmony_ci	 *                                GOF_PER_SEC)
78462306a36Sopenharmony_ci	 *     correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -
78562306a36Sopenharmony_ci	 *                          GOF_PER_SEC * correctionPerGOF
78662306a36Sopenharmony_ci	 *     initialDelay = ceil((24 * Fs,in) / Fs,out)
78762306a36Sopenharmony_ci	 *
78862306a36Sopenharmony_ci	 * i.e.
78962306a36Sopenharmony_ci	 *
79062306a36Sopenharmony_ci	 *     coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))
79162306a36Sopenharmony_ci	 *     phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)
79262306a36Sopenharmony_ci	 *     correctionPerGOF:correctionPerSec =
79362306a36Sopenharmony_ci	 * 	    dividend:remainder(ulOther / GOF_PER_SEC)
79462306a36Sopenharmony_ci	 *     initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)
79562306a36Sopenharmony_ci	 */
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	tmp1 = rate << 16;
79862306a36Sopenharmony_ci	coeffIncr = tmp1 / 48000;
79962306a36Sopenharmony_ci	tmp1 -= coeffIncr * 48000;
80062306a36Sopenharmony_ci	tmp1 <<= 7;
80162306a36Sopenharmony_ci	coeffIncr <<= 7;
80262306a36Sopenharmony_ci	coeffIncr += tmp1 / 48000;
80362306a36Sopenharmony_ci	coeffIncr ^= 0xFFFFFFFF;
80462306a36Sopenharmony_ci	coeffIncr++;
80562306a36Sopenharmony_ci	tmp1 = 48000 << 16;
80662306a36Sopenharmony_ci	phiIncr = tmp1 / rate;
80762306a36Sopenharmony_ci	tmp1 -= phiIncr * rate;
80862306a36Sopenharmony_ci	tmp1 <<= 10;
80962306a36Sopenharmony_ci	phiIncr <<= 10;
81062306a36Sopenharmony_ci	tmp2 = tmp1 / rate;
81162306a36Sopenharmony_ci	phiIncr += tmp2;
81262306a36Sopenharmony_ci	tmp1 -= tmp2 * rate;
81362306a36Sopenharmony_ci	correctionPerGOF = tmp1 / GOF_PER_SEC;
81462306a36Sopenharmony_ci	tmp1 -= correctionPerGOF * GOF_PER_SEC;
81562306a36Sopenharmony_ci	correctionPerSec = tmp1;
81662306a36Sopenharmony_ci	initialDelay = DIV_ROUND_UP(48000 * 24, rate);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	/*
81962306a36Sopenharmony_ci	 *  Fill in the VariDecimate control block.
82062306a36Sopenharmony_ci	 */
82162306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
82262306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CSRC,
82362306a36Sopenharmony_ci		((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
82462306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);
82562306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CD,
82662306a36Sopenharmony_ci		(((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);
82762306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CPI, phiIncr);
82862306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/*
83162306a36Sopenharmony_ci	 *  Figure out the frame group length for the write back task.  Basically,
83262306a36Sopenharmony_ci	 *  this is just the factors of 24000 (2^6*3*5^3) that are not present in
83362306a36Sopenharmony_ci	 *  the output sample rate.
83462306a36Sopenharmony_ci	 */
83562306a36Sopenharmony_ci	frameGroupLength = 1;
83662306a36Sopenharmony_ci	for (cnt = 2; cnt <= 64; cnt *= 2) {
83762306a36Sopenharmony_ci		if (((rate / cnt) * cnt) != rate)
83862306a36Sopenharmony_ci			frameGroupLength *= 2;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci	if (((rate / 3) * 3) != rate) {
84162306a36Sopenharmony_ci		frameGroupLength *= 3;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci	for (cnt = 5; cnt <= 125; cnt *= 5) {
84462306a36Sopenharmony_ci		if (((rate / cnt) * cnt) != rate)
84562306a36Sopenharmony_ci			frameGroupLength *= 5;
84662306a36Sopenharmony_ci        }
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/*
84962306a36Sopenharmony_ci	 * Fill in the WriteBack control block.
85062306a36Sopenharmony_ci	 */
85162306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
85262306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength);
85362306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength));
85462306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF);
85562306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000));
85662306a36Sopenharmony_ci	snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF);
85762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci/*
86162306a36Sopenharmony_ci *  PCM part
86262306a36Sopenharmony_ci */
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic void snd_cs46xx_pb_trans_copy(struct snd_pcm_substream *substream,
86562306a36Sopenharmony_ci				     struct snd_pcm_indirect *rec, size_t bytes)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
86862306a36Sopenharmony_ci	struct snd_cs46xx_pcm * cpcm = runtime->private_data;
86962306a36Sopenharmony_ci	memcpy(cpcm->hw_buf.area + rec->hw_data, runtime->dma_area + rec->sw_data, bytes);
87062306a36Sopenharmony_ci}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_cistatic int snd_cs46xx_playback_transfer(struct snd_pcm_substream *substream)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
87562306a36Sopenharmony_ci	struct snd_cs46xx_pcm * cpcm = runtime->private_data;
87662306a36Sopenharmony_ci	return snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec,
87762306a36Sopenharmony_ci						  snd_cs46xx_pb_trans_copy);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic void snd_cs46xx_cp_trans_copy(struct snd_pcm_substream *substream,
88162306a36Sopenharmony_ci				     struct snd_pcm_indirect *rec, size_t bytes)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
88462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
88562306a36Sopenharmony_ci	memcpy(runtime->dma_area + rec->sw_data,
88662306a36Sopenharmony_ci	       chip->capt.hw_buf.area + rec->hw_data, bytes);
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic int snd_cs46xx_capture_transfer(struct snd_pcm_substream *substream)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
89262306a36Sopenharmony_ci	return snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec,
89362306a36Sopenharmony_ci						 snd_cs46xx_cp_trans_copy);
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(struct snd_pcm_substream *substream)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
89962306a36Sopenharmony_ci	size_t ptr;
90062306a36Sopenharmony_ci	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (snd_BUG_ON(!cpcm->pcm_channel))
90362306a36Sopenharmony_ci		return -ENXIO;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
90662306a36Sopenharmony_ci	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
90762306a36Sopenharmony_ci#else
90862306a36Sopenharmony_ci	ptr = snd_cs46xx_peek(chip, BA1_PBA);
90962306a36Sopenharmony_ci#endif
91062306a36Sopenharmony_ci	ptr -= cpcm->hw_buf.addr;
91162306a36Sopenharmony_ci	return ptr >> cpcm->shift;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(struct snd_pcm_substream *substream)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
91762306a36Sopenharmony_ci	size_t ptr;
91862306a36Sopenharmony_ci	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
92162306a36Sopenharmony_ci	if (snd_BUG_ON(!cpcm->pcm_channel))
92262306a36Sopenharmony_ci		return -ENXIO;
92362306a36Sopenharmony_ci	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);
92462306a36Sopenharmony_ci#else
92562306a36Sopenharmony_ci	ptr = snd_cs46xx_peek(chip, BA1_PBA);
92662306a36Sopenharmony_ci#endif
92762306a36Sopenharmony_ci	ptr -= cpcm->hw_buf.addr;
92862306a36Sopenharmony_ci	return snd_pcm_indirect_playback_pointer(substream, &cpcm->pcm_rec, ptr);
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(struct snd_pcm_substream *substream)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
93462306a36Sopenharmony_ci	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;
93562306a36Sopenharmony_ci	return ptr >> chip->capt.shift;
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(struct snd_pcm_substream *substream)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
94162306a36Sopenharmony_ci	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;
94262306a36Sopenharmony_ci	return snd_pcm_indirect_capture_pointer(substream, &chip->capt.pcm_rec, ptr);
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic int snd_cs46xx_playback_trigger(struct snd_pcm_substream *substream,
94662306a36Sopenharmony_ci				       int cmd)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
94962306a36Sopenharmony_ci	/*struct snd_pcm_runtime *runtime = substream->runtime;*/
95062306a36Sopenharmony_ci	int result = 0;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
95362306a36Sopenharmony_ci	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;
95462306a36Sopenharmony_ci	if (! cpcm->pcm_channel) {
95562306a36Sopenharmony_ci		return -ENXIO;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci#endif
95862306a36Sopenharmony_ci	switch (cmd) {
95962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
96062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
96162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
96262306a36Sopenharmony_ci		/* magic value to unmute PCM stream  playback volume */
96362306a36Sopenharmony_ci		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address +
96462306a36Sopenharmony_ci				       SCBVolumeCtrl) << 2, 0x80008000);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci		if (cpcm->pcm_channel->unlinked)
96762306a36Sopenharmony_ci			cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		if (substream->runtime->periods != CS46XX_FRAGS)
97062306a36Sopenharmony_ci			snd_cs46xx_playback_transfer(substream);
97162306a36Sopenharmony_ci#else
97262306a36Sopenharmony_ci		spin_lock(&chip->reg_lock);
97362306a36Sopenharmony_ci		if (substream->runtime->periods != CS46XX_FRAGS)
97462306a36Sopenharmony_ci			snd_cs46xx_playback_transfer(substream);
97562306a36Sopenharmony_ci		{ unsigned int tmp;
97662306a36Sopenharmony_ci		tmp = snd_cs46xx_peek(chip, BA1_PCTL);
97762306a36Sopenharmony_ci		tmp &= 0x0000ffff;
97862306a36Sopenharmony_ci		snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);
97962306a36Sopenharmony_ci		}
98062306a36Sopenharmony_ci		spin_unlock(&chip->reg_lock);
98162306a36Sopenharmony_ci#endif
98262306a36Sopenharmony_ci		break;
98362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
98462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
98562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
98662306a36Sopenharmony_ci		/* magic mute channel */
98762306a36Sopenharmony_ci		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address +
98862306a36Sopenharmony_ci				       SCBVolumeCtrl) << 2, 0xffffffff);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		if (!cpcm->pcm_channel->unlinked)
99162306a36Sopenharmony_ci			cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
99262306a36Sopenharmony_ci#else
99362306a36Sopenharmony_ci		spin_lock(&chip->reg_lock);
99462306a36Sopenharmony_ci		{ unsigned int tmp;
99562306a36Sopenharmony_ci		tmp = snd_cs46xx_peek(chip, BA1_PCTL);
99662306a36Sopenharmony_ci		tmp &= 0x0000ffff;
99762306a36Sopenharmony_ci		snd_cs46xx_poke(chip, BA1_PCTL, tmp);
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci		spin_unlock(&chip->reg_lock);
100062306a36Sopenharmony_ci#endif
100162306a36Sopenharmony_ci		break;
100262306a36Sopenharmony_ci	default:
100362306a36Sopenharmony_ci		result = -EINVAL;
100462306a36Sopenharmony_ci		break;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	return result;
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic int snd_cs46xx_capture_trigger(struct snd_pcm_substream *substream,
101162306a36Sopenharmony_ci				      int cmd)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
101462306a36Sopenharmony_ci	unsigned int tmp;
101562306a36Sopenharmony_ci	int result = 0;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	spin_lock(&chip->reg_lock);
101862306a36Sopenharmony_ci	switch (cmd) {
101962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
102062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
102162306a36Sopenharmony_ci		tmp = snd_cs46xx_peek(chip, BA1_CCTL);
102262306a36Sopenharmony_ci		tmp &= 0xffff0000;
102362306a36Sopenharmony_ci		snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp);
102462306a36Sopenharmony_ci		break;
102562306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
102662306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
102762306a36Sopenharmony_ci		tmp = snd_cs46xx_peek(chip, BA1_CCTL);
102862306a36Sopenharmony_ci		tmp &= 0xffff0000;
102962306a36Sopenharmony_ci		snd_cs46xx_poke(chip, BA1_CCTL, tmp);
103062306a36Sopenharmony_ci		break;
103162306a36Sopenharmony_ci	default:
103262306a36Sopenharmony_ci		result = -EINVAL;
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci	spin_unlock(&chip->reg_lock);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	return result;
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
104162306a36Sopenharmony_cistatic int _cs46xx_adjust_sample_rate (struct snd_cs46xx *chip, struct snd_cs46xx_pcm *cpcm,
104262306a36Sopenharmony_ci				       int sample_rate)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	/* If PCMReaderSCB and SrcTaskSCB not created yet ... */
104662306a36Sopenharmony_ci	if ( cpcm->pcm_channel == NULL) {
104762306a36Sopenharmony_ci		cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate,
104862306a36Sopenharmony_ci								   cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id);
104962306a36Sopenharmony_ci		if (cpcm->pcm_channel == NULL) {
105062306a36Sopenharmony_ci			dev_err(chip->card->dev,
105162306a36Sopenharmony_ci				"failed to create virtual PCM channel\n");
105262306a36Sopenharmony_ci			return -ENOMEM;
105362306a36Sopenharmony_ci		}
105462306a36Sopenharmony_ci		cpcm->pcm_channel->sample_rate = sample_rate;
105562306a36Sopenharmony_ci	} else
105662306a36Sopenharmony_ci	/* if sample rate is changed */
105762306a36Sopenharmony_ci	if ((int)cpcm->pcm_channel->sample_rate != sample_rate) {
105862306a36Sopenharmony_ci		int unlinked = cpcm->pcm_channel->unlinked;
105962306a36Sopenharmony_ci		cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci		cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel(chip, sample_rate, cpcm,
106262306a36Sopenharmony_ci								  cpcm->hw_buf.addr,
106362306a36Sopenharmony_ci								  cpcm->pcm_channel_id);
106462306a36Sopenharmony_ci		if (!cpcm->pcm_channel) {
106562306a36Sopenharmony_ci			dev_err(chip->card->dev,
106662306a36Sopenharmony_ci				"failed to re-create virtual PCM channel\n");
106762306a36Sopenharmony_ci			return -ENOMEM;
106862306a36Sopenharmony_ci		}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci		if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
107162306a36Sopenharmony_ci		cpcm->pcm_channel->sample_rate = sample_rate;
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	return 0;
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci#endif
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,
108062306a36Sopenharmony_ci					 struct snd_pcm_hw_params *hw_params)
108162306a36Sopenharmony_ci{
108262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
108362306a36Sopenharmony_ci	struct snd_cs46xx_pcm *cpcm;
108462306a36Sopenharmony_ci	int err;
108562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
108662306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
108762306a36Sopenharmony_ci	int sample_rate = params_rate(hw_params);
108862306a36Sopenharmony_ci	int period_size = params_period_bytes(hw_params);
108962306a36Sopenharmony_ci#endif
109062306a36Sopenharmony_ci	cpcm = runtime->private_data;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
109362306a36Sopenharmony_ci	if (snd_BUG_ON(!sample_rate))
109462306a36Sopenharmony_ci		return -ENXIO;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) {
109962306a36Sopenharmony_ci		mutex_unlock(&chip->spos_mutex);
110062306a36Sopenharmony_ci		return -ENXIO;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	snd_BUG_ON(!cpcm->pcm_channel);
110462306a36Sopenharmony_ci	if (!cpcm->pcm_channel) {
110562306a36Sopenharmony_ci		mutex_unlock(&chip->spos_mutex);
110662306a36Sopenharmony_ci		return -ENXIO;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	if (cs46xx_dsp_pcm_channel_set_period (chip,cpcm->pcm_channel,period_size)) {
111162306a36Sopenharmony_ci		 mutex_unlock(&chip->spos_mutex);
111262306a36Sopenharmony_ci		 return -EINVAL;
111362306a36Sopenharmony_ci	 }
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	dev_dbg(chip->card->dev,
111662306a36Sopenharmony_ci		"period_size (%d), periods (%d) buffer_size(%d)\n",
111762306a36Sopenharmony_ci		     period_size, params_periods(hw_params),
111862306a36Sopenharmony_ci		     params_buffer_bytes(hw_params));
111962306a36Sopenharmony_ci#endif
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (params_periods(hw_params) == CS46XX_FRAGS) {
112262306a36Sopenharmony_ci		if (runtime->dma_area != cpcm->hw_buf.area)
112362306a36Sopenharmony_ci			snd_pcm_lib_free_pages(substream);
112462306a36Sopenharmony_ci		snd_pcm_set_runtime_buffer(substream, &cpcm->hw_buf);
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
112862306a36Sopenharmony_ci		if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
112962306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_ops;
113062306a36Sopenharmony_ci		} else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
113162306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_rear_ops;
113262306a36Sopenharmony_ci		} else if (cpcm->pcm_channel_id == DSP_PCM_CENTER_LFE_CHANNEL) {
113362306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_clfe_ops;
113462306a36Sopenharmony_ci		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
113562306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_iec958_ops;
113662306a36Sopenharmony_ci		} else {
113762306a36Sopenharmony_ci			snd_BUG();
113862306a36Sopenharmony_ci		}
113962306a36Sopenharmony_ci#else
114062306a36Sopenharmony_ci		substream->ops = &snd_cs46xx_playback_ops;
114162306a36Sopenharmony_ci#endif
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	} else {
114462306a36Sopenharmony_ci		if (runtime->dma_area == cpcm->hw_buf.area)
114562306a36Sopenharmony_ci			snd_pcm_set_runtime_buffer(substream, NULL);
114662306a36Sopenharmony_ci		err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
114762306a36Sopenharmony_ci		if (err < 0) {
114862306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
114962306a36Sopenharmony_ci			mutex_unlock(&chip->spos_mutex);
115062306a36Sopenharmony_ci#endif
115162306a36Sopenharmony_ci			return err;
115262306a36Sopenharmony_ci		}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
115562306a36Sopenharmony_ci		if (cpcm->pcm_channel_id == DSP_PCM_MAIN_CHANNEL) {
115662306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_indirect_ops;
115762306a36Sopenharmony_ci		} else if (cpcm->pcm_channel_id == DSP_PCM_REAR_CHANNEL) {
115862306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_indirect_rear_ops;
115962306a36Sopenharmony_ci		} else if (cpcm->pcm_channel_id == DSP_PCM_CENTER_LFE_CHANNEL) {
116062306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_indirect_clfe_ops;
116162306a36Sopenharmony_ci		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {
116262306a36Sopenharmony_ci			substream->ops = &snd_cs46xx_playback_indirect_iec958_ops;
116362306a36Sopenharmony_ci		} else {
116462306a36Sopenharmony_ci			snd_BUG();
116562306a36Sopenharmony_ci		}
116662306a36Sopenharmony_ci#else
116762306a36Sopenharmony_ci		substream->ops = &snd_cs46xx_playback_indirect_ops;
116862306a36Sopenharmony_ci#endif
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
117362306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
117462306a36Sopenharmony_ci#endif
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	return 0;
117762306a36Sopenharmony_ci}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic int snd_cs46xx_playback_hw_free(struct snd_pcm_substream *substream)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	/*struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);*/
118262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
118362306a36Sopenharmony_ci	struct snd_cs46xx_pcm *cpcm;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	cpcm = runtime->private_data;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	/* if play_back open fails, then this function
118862306a36Sopenharmony_ci	   is called and cpcm can actually be NULL here */
118962306a36Sopenharmony_ci	if (!cpcm) return -ENXIO;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	if (runtime->dma_area != cpcm->hw_buf.area)
119262306a36Sopenharmony_ci		snd_pcm_lib_free_pages(substream);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	snd_pcm_set_runtime_buffer(substream, NULL);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	return 0;
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic int snd_cs46xx_playback_prepare(struct snd_pcm_substream *substream)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	unsigned int tmp;
120262306a36Sopenharmony_ci	unsigned int pfie;
120362306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
120462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
120562306a36Sopenharmony_ci	struct snd_cs46xx_pcm *cpcm;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	cpcm = runtime->private_data;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
121062306a36Sopenharmony_ci	if (snd_BUG_ON(!cpcm->pcm_channel))
121162306a36Sopenharmony_ci		return -ENXIO;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
121462306a36Sopenharmony_ci	pfie &= ~0x0000f03f;
121562306a36Sopenharmony_ci#else
121662306a36Sopenharmony_ci	/* old dsp */
121762306a36Sopenharmony_ci	pfie = snd_cs46xx_peek(chip, BA1_PFIE);
121862306a36Sopenharmony_ci 	pfie &= ~0x0000f03f;
121962306a36Sopenharmony_ci#endif
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	cpcm->shift = 2;
122262306a36Sopenharmony_ci	/* if to convert from stereo to mono */
122362306a36Sopenharmony_ci	if (runtime->channels == 1) {
122462306a36Sopenharmony_ci		cpcm->shift--;
122562306a36Sopenharmony_ci		pfie |= 0x00002000;
122662306a36Sopenharmony_ci	}
122762306a36Sopenharmony_ci	/* if to convert from 8 bit to 16 bit */
122862306a36Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) == 8) {
122962306a36Sopenharmony_ci		cpcm->shift--;
123062306a36Sopenharmony_ci		pfie |= 0x00001000;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci	/* if to convert to unsigned */
123362306a36Sopenharmony_ci	if (snd_pcm_format_unsigned(runtime->format))
123462306a36Sopenharmony_ci		pfie |= 0x00008000;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	/* Never convert byte order when sample stream is 8 bit */
123762306a36Sopenharmony_ci	if (snd_pcm_format_width(runtime->format) != 8) {
123862306a36Sopenharmony_ci		/* convert from big endian to little endian */
123962306a36Sopenharmony_ci		if (snd_pcm_format_big_endian(runtime->format))
124062306a36Sopenharmony_ci			pfie |= 0x00004000;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	memset(&cpcm->pcm_rec, 0, sizeof(cpcm->pcm_rec));
124462306a36Sopenharmony_ci	cpcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
124562306a36Sopenharmony_ci	cpcm->pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2);
125062306a36Sopenharmony_ci	tmp &= ~0x000003ff;
125162306a36Sopenharmony_ci	tmp |= (4 << cpcm->shift) - 1;
125262306a36Sopenharmony_ci	/* playback transaction count register */
125362306a36Sopenharmony_ci	snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2, tmp);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* playback format && interrupt enable */
125662306a36Sopenharmony_ci	snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2, pfie | cpcm->pcm_channel->pcm_slot);
125762306a36Sopenharmony_ci#else
125862306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PBA, cpcm->hw_buf.addr);
125962306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_PDTC);
126062306a36Sopenharmony_ci	tmp &= ~0x000003ff;
126162306a36Sopenharmony_ci	tmp |= (4 << cpcm->shift) - 1;
126262306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PDTC, tmp);
126362306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PFIE, pfie);
126462306a36Sopenharmony_ci	snd_cs46xx_set_play_sample_rate(chip, runtime->rate);
126562306a36Sopenharmony_ci#endif
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	return 0;
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_cistatic int snd_cs46xx_capture_hw_params(struct snd_pcm_substream *substream,
127162306a36Sopenharmony_ci					struct snd_pcm_hw_params *hw_params)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
127462306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
127562306a36Sopenharmony_ci	int err;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
127862306a36Sopenharmony_ci	cs46xx_dsp_pcm_ostream_set_period (chip, params_period_bytes(hw_params));
127962306a36Sopenharmony_ci#endif
128062306a36Sopenharmony_ci	if (runtime->periods == CS46XX_FRAGS) {
128162306a36Sopenharmony_ci		if (runtime->dma_area != chip->capt.hw_buf.area)
128262306a36Sopenharmony_ci			snd_pcm_lib_free_pages(substream);
128362306a36Sopenharmony_ci		snd_pcm_set_runtime_buffer(substream, &chip->capt.hw_buf);
128462306a36Sopenharmony_ci		substream->ops = &snd_cs46xx_capture_ops;
128562306a36Sopenharmony_ci	} else {
128662306a36Sopenharmony_ci		if (runtime->dma_area == chip->capt.hw_buf.area)
128762306a36Sopenharmony_ci			snd_pcm_set_runtime_buffer(substream, NULL);
128862306a36Sopenharmony_ci		err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
128962306a36Sopenharmony_ci		if (err < 0)
129062306a36Sopenharmony_ci			return err;
129162306a36Sopenharmony_ci		substream->ops = &snd_cs46xx_capture_indirect_ops;
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	return 0;
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_cistatic int snd_cs46xx_capture_hw_free(struct snd_pcm_substream *substream)
129862306a36Sopenharmony_ci{
129962306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
130062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (runtime->dma_area != chip->capt.hw_buf.area)
130362306a36Sopenharmony_ci		snd_pcm_lib_free_pages(substream);
130462306a36Sopenharmony_ci	snd_pcm_set_runtime_buffer(substream, NULL);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return 0;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic int snd_cs46xx_capture_prepare(struct snd_pcm_substream *substream)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
131262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CBA, chip->capt.hw_buf.addr);
131562306a36Sopenharmony_ci	chip->capt.shift = 2;
131662306a36Sopenharmony_ci	memset(&chip->capt.pcm_rec, 0, sizeof(chip->capt.pcm_rec));
131762306a36Sopenharmony_ci	chip->capt.pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream);
131862306a36Sopenharmony_ci	chip->capt.pcm_rec.hw_buffer_size = runtime->period_size * CS46XX_FRAGS << 2;
131962306a36Sopenharmony_ci	snd_cs46xx_set_capture_sample_rate(chip, runtime->rate);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	return 0;
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic irqreturn_t snd_cs46xx_interrupt(int irq, void *dev_id)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	struct snd_cs46xx *chip = dev_id;
132762306a36Sopenharmony_ci	u32 status1;
132862306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
132962306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
133062306a36Sopenharmony_ci	u32 status2;
133162306a36Sopenharmony_ci	int i;
133262306a36Sopenharmony_ci	struct snd_cs46xx_pcm *cpcm = NULL;
133362306a36Sopenharmony_ci#endif
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	/*
133662306a36Sopenharmony_ci	 *  Read the Interrupt Status Register to clear the interrupt
133762306a36Sopenharmony_ci	 */
133862306a36Sopenharmony_ci	status1 = snd_cs46xx_peekBA0(chip, BA0_HISR);
133962306a36Sopenharmony_ci	if ((status1 & 0x7fffffff) == 0) {
134062306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
134162306a36Sopenharmony_ci		return IRQ_NONE;
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
134562306a36Sopenharmony_ci	status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) {
134862306a36Sopenharmony_ci		if (i <= 15) {
134962306a36Sopenharmony_ci			if ( status1 & (1 << i) ) {
135062306a36Sopenharmony_ci				if (i == CS46XX_DSP_CAPTURE_CHANNEL) {
135162306a36Sopenharmony_ci					if (chip->capt.substream)
135262306a36Sopenharmony_ci						snd_pcm_period_elapsed(chip->capt.substream);
135362306a36Sopenharmony_ci				} else {
135462306a36Sopenharmony_ci					if (ins->pcm_channels[i].active &&
135562306a36Sopenharmony_ci					    ins->pcm_channels[i].private_data &&
135662306a36Sopenharmony_ci					    !ins->pcm_channels[i].unlinked) {
135762306a36Sopenharmony_ci						cpcm = ins->pcm_channels[i].private_data;
135862306a36Sopenharmony_ci						snd_pcm_period_elapsed(cpcm->substream);
135962306a36Sopenharmony_ci					}
136062306a36Sopenharmony_ci				}
136162306a36Sopenharmony_ci			}
136262306a36Sopenharmony_ci		} else {
136362306a36Sopenharmony_ci			if ( status2 & (1 << (i - 16))) {
136462306a36Sopenharmony_ci				if (ins->pcm_channels[i].active &&
136562306a36Sopenharmony_ci				    ins->pcm_channels[i].private_data &&
136662306a36Sopenharmony_ci				    !ins->pcm_channels[i].unlinked) {
136762306a36Sopenharmony_ci					cpcm = ins->pcm_channels[i].private_data;
136862306a36Sopenharmony_ci					snd_pcm_period_elapsed(cpcm->substream);
136962306a36Sopenharmony_ci				}
137062306a36Sopenharmony_ci			}
137162306a36Sopenharmony_ci		}
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci#else
137562306a36Sopenharmony_ci	/* old dsp */
137662306a36Sopenharmony_ci	if ((status1 & HISR_VC0) && chip->playback_pcm) {
137762306a36Sopenharmony_ci		if (chip->playback_pcm->substream)
137862306a36Sopenharmony_ci			snd_pcm_period_elapsed(chip->playback_pcm->substream);
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci	if ((status1 & HISR_VC1) && chip->pcm) {
138162306a36Sopenharmony_ci		if (chip->capt.substream)
138262306a36Sopenharmony_ci			snd_pcm_period_elapsed(chip->capt.substream);
138362306a36Sopenharmony_ci	}
138462306a36Sopenharmony_ci#endif
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if ((status1 & HISR_MIDI) && chip->rmidi) {
138762306a36Sopenharmony_ci		unsigned char c;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci		spin_lock(&chip->reg_lock);
139062306a36Sopenharmony_ci		while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_RBE) == 0) {
139162306a36Sopenharmony_ci			c = snd_cs46xx_peekBA0(chip, BA0_MIDRP);
139262306a36Sopenharmony_ci			if ((chip->midcr & MIDCR_RIE) == 0)
139362306a36Sopenharmony_ci				continue;
139462306a36Sopenharmony_ci			snd_rawmidi_receive(chip->midi_input, &c, 1);
139562306a36Sopenharmony_ci		}
139662306a36Sopenharmony_ci		while ((snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
139762306a36Sopenharmony_ci			if ((chip->midcr & MIDCR_TIE) == 0)
139862306a36Sopenharmony_ci				break;
139962306a36Sopenharmony_ci			if (snd_rawmidi_transmit(chip->midi_output, &c, 1) != 1) {
140062306a36Sopenharmony_ci				chip->midcr &= ~MIDCR_TIE;
140162306a36Sopenharmony_ci				snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
140262306a36Sopenharmony_ci				break;
140362306a36Sopenharmony_ci			}
140462306a36Sopenharmony_ci			snd_cs46xx_pokeBA0(chip, BA0_MIDWP, c);
140562306a36Sopenharmony_ci		}
140662306a36Sopenharmony_ci		spin_unlock(&chip->reg_lock);
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci	/*
140962306a36Sopenharmony_ci	 *  EOI to the PCI part....reenables interrupts
141062306a36Sopenharmony_ci	 */
141162306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_CHGM | HICR_IEV);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	return IRQ_HANDLED;
141462306a36Sopenharmony_ci}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs46xx_playback =
141762306a36Sopenharmony_ci{
141862306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP |
141962306a36Sopenharmony_ci				 SNDRV_PCM_INFO_INTERLEAVED |
142062306a36Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
142162306a36Sopenharmony_ci				 /*SNDRV_PCM_INFO_RESUME*/ |
142262306a36Sopenharmony_ci				 SNDRV_PCM_INFO_SYNC_APPLPTR),
142362306a36Sopenharmony_ci	.formats =		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
142462306a36Sopenharmony_ci				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
142562306a36Sopenharmony_ci				 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
142662306a36Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
142762306a36Sopenharmony_ci	.rate_min =		5500,
142862306a36Sopenharmony_ci	.rate_max =		48000,
142962306a36Sopenharmony_ci	.channels_min =		1,
143062306a36Sopenharmony_ci	.channels_max =		2,
143162306a36Sopenharmony_ci	.buffer_bytes_max =	(256 * 1024),
143262306a36Sopenharmony_ci	.period_bytes_min =	CS46XX_MIN_PERIOD_SIZE,
143362306a36Sopenharmony_ci	.period_bytes_max =	CS46XX_MAX_PERIOD_SIZE,
143462306a36Sopenharmony_ci	.periods_min =		CS46XX_FRAGS,
143562306a36Sopenharmony_ci	.periods_max =		1024,
143662306a36Sopenharmony_ci	.fifo_size =		0,
143762306a36Sopenharmony_ci};
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs46xx_capture =
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	.info =			(SNDRV_PCM_INFO_MMAP |
144262306a36Sopenharmony_ci				 SNDRV_PCM_INFO_INTERLEAVED |
144362306a36Sopenharmony_ci				 SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/
144462306a36Sopenharmony_ci				 /*SNDRV_PCM_INFO_RESUME*/ |
144562306a36Sopenharmony_ci				 SNDRV_PCM_INFO_SYNC_APPLPTR),
144662306a36Sopenharmony_ci	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
144762306a36Sopenharmony_ci	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
144862306a36Sopenharmony_ci	.rate_min =		5500,
144962306a36Sopenharmony_ci	.rate_max =		48000,
145062306a36Sopenharmony_ci	.channels_min =		2,
145162306a36Sopenharmony_ci	.channels_max =		2,
145262306a36Sopenharmony_ci	.buffer_bytes_max =	(256 * 1024),
145362306a36Sopenharmony_ci	.period_bytes_min =	CS46XX_MIN_PERIOD_SIZE,
145462306a36Sopenharmony_ci	.period_bytes_max =	CS46XX_MAX_PERIOD_SIZE,
145562306a36Sopenharmony_ci	.periods_min =		CS46XX_FRAGS,
145662306a36Sopenharmony_ci	.periods_max =		1024,
145762306a36Sopenharmony_ci	.fifo_size =		0,
145862306a36Sopenharmony_ci};
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic const unsigned int period_sizes[] = { 32, 64, 128, 256, 512, 1024, 2048 };
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_period_sizes = {
146562306a36Sopenharmony_ci	.count = ARRAY_SIZE(period_sizes),
146662306a36Sopenharmony_ci	.list = period_sizes,
146762306a36Sopenharmony_ci	.mask = 0
146862306a36Sopenharmony_ci};
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci#endif
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_cistatic void snd_cs46xx_pcm_free_substream(struct snd_pcm_runtime *runtime)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	kfree(runtime->private_data);
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic int _cs46xx_playback_open_channel (struct snd_pcm_substream *substream,int pcm_channel_id)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
148062306a36Sopenharmony_ci	struct snd_cs46xx_pcm * cpcm;
148162306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	cpcm = kzalloc(sizeof(*cpcm), GFP_KERNEL);
148462306a36Sopenharmony_ci	if (cpcm == NULL)
148562306a36Sopenharmony_ci		return -ENOMEM;
148662306a36Sopenharmony_ci	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
148762306a36Sopenharmony_ci				PAGE_SIZE, &cpcm->hw_buf) < 0) {
148862306a36Sopenharmony_ci		kfree(cpcm);
148962306a36Sopenharmony_ci		return -ENOMEM;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	runtime->hw = snd_cs46xx_playback;
149362306a36Sopenharmony_ci	runtime->private_data = cpcm;
149462306a36Sopenharmony_ci	runtime->private_free = snd_cs46xx_pcm_free_substream;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	cpcm->substream = substream;
149762306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
149862306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
149962306a36Sopenharmony_ci	cpcm->pcm_channel = NULL;
150062306a36Sopenharmony_ci	cpcm->pcm_channel_id = pcm_channel_id;
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	snd_pcm_hw_constraint_list(runtime, 0,
150462306a36Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
150562306a36Sopenharmony_ci				   &hw_constraints_period_sizes);
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
150862306a36Sopenharmony_ci#else
150962306a36Sopenharmony_ci	chip->playback_pcm = cpcm; /* HACK */
151062306a36Sopenharmony_ci#endif
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (chip->accept_valid)
151362306a36Sopenharmony_ci		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
151462306a36Sopenharmony_ci	chip->active_ctrl(chip, 1);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	return 0;
151762306a36Sopenharmony_ci}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_cistatic int snd_cs46xx_playback_open(struct snd_pcm_substream *substream)
152062306a36Sopenharmony_ci{
152162306a36Sopenharmony_ci	dev_dbg(substream->pcm->card->dev, "open front channel\n");
152262306a36Sopenharmony_ci	return _cs46xx_playback_open_channel(substream,DSP_PCM_MAIN_CHANNEL);
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
152662306a36Sopenharmony_cistatic int snd_cs46xx_playback_open_rear(struct snd_pcm_substream *substream)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	dev_dbg(substream->pcm->card->dev, "open rear channel\n");
152962306a36Sopenharmony_ci	return _cs46xx_playback_open_channel(substream,DSP_PCM_REAR_CHANNEL);
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_cistatic int snd_cs46xx_playback_open_clfe(struct snd_pcm_substream *substream)
153362306a36Sopenharmony_ci{
153462306a36Sopenharmony_ci	dev_dbg(substream->pcm->card->dev, "open center - LFE channel\n");
153562306a36Sopenharmony_ci	return _cs46xx_playback_open_channel(substream,DSP_PCM_CENTER_LFE_CHANNEL);
153662306a36Sopenharmony_ci}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_cistatic int snd_cs46xx_playback_open_iec958(struct snd_pcm_substream *substream)
153962306a36Sopenharmony_ci{
154062306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "open raw iec958 channel\n");
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
154562306a36Sopenharmony_ci	cs46xx_iec958_pre_open (chip);
154662306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	return _cs46xx_playback_open_channel(substream,DSP_IEC958_CHANNEL);
154962306a36Sopenharmony_ci}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_cistatic int snd_cs46xx_playback_close(struct snd_pcm_substream *substream);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_cistatic int snd_cs46xx_playback_close_iec958(struct snd_pcm_substream *substream)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	int err;
155662306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "close raw iec958 channel\n");
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	err = snd_cs46xx_playback_close(substream);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
156362306a36Sopenharmony_ci	cs46xx_iec958_post_close (chip);
156462306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	return err;
156762306a36Sopenharmony_ci}
156862306a36Sopenharmony_ci#endif
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_cistatic int snd_cs46xx_capture_open(struct snd_pcm_substream *substream)
157162306a36Sopenharmony_ci{
157262306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
157562306a36Sopenharmony_ci				PAGE_SIZE, &chip->capt.hw_buf) < 0)
157662306a36Sopenharmony_ci		return -ENOMEM;
157762306a36Sopenharmony_ci	chip->capt.substream = substream;
157862306a36Sopenharmony_ci	substream->runtime->hw = snd_cs46xx_capture;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	if (chip->accept_valid)
158162306a36Sopenharmony_ci		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	chip->active_ctrl(chip, 1);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
158662306a36Sopenharmony_ci	snd_pcm_hw_constraint_list(substream->runtime, 0,
158762306a36Sopenharmony_ci				   SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
158862306a36Sopenharmony_ci				   &hw_constraints_period_sizes);
158962306a36Sopenharmony_ci#endif
159062306a36Sopenharmony_ci	return 0;
159162306a36Sopenharmony_ci}
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_cistatic int snd_cs46xx_playback_close(struct snd_pcm_substream *substream)
159462306a36Sopenharmony_ci{
159562306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
159662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
159762306a36Sopenharmony_ci	struct snd_cs46xx_pcm * cpcm;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	cpcm = runtime->private_data;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	/* when playback_open fails, then cpcm can be NULL */
160262306a36Sopenharmony_ci	if (!cpcm) return -ENXIO;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
160562306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
160662306a36Sopenharmony_ci	if (cpcm->pcm_channel) {
160762306a36Sopenharmony_ci		cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
160862306a36Sopenharmony_ci		cpcm->pcm_channel = NULL;
160962306a36Sopenharmony_ci	}
161062306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
161162306a36Sopenharmony_ci#else
161262306a36Sopenharmony_ci	chip->playback_pcm = NULL;
161362306a36Sopenharmony_ci#endif
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci	cpcm->substream = NULL;
161662306a36Sopenharmony_ci	snd_dma_free_pages(&cpcm->hw_buf);
161762306a36Sopenharmony_ci	chip->active_ctrl(chip, -1);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	return 0;
162062306a36Sopenharmony_ci}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cistatic int snd_cs46xx_capture_close(struct snd_pcm_substream *substream)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	chip->capt.substream = NULL;
162762306a36Sopenharmony_ci	snd_dma_free_pages(&chip->capt.hw_buf);
162862306a36Sopenharmony_ci	chip->active_ctrl(chip, -1);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	return 0;
163162306a36Sopenharmony_ci}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
163462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_rear_ops = {
163562306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open_rear,
163662306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close,
163762306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
163862306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
163962306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
164062306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
164162306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_direct_pointer,
164262306a36Sopenharmony_ci};
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = {
164562306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open_rear,
164662306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close,
164762306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
164862306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
164962306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
165062306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
165162306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_indirect_pointer,
165262306a36Sopenharmony_ci	.ack =			snd_cs46xx_playback_transfer,
165362306a36Sopenharmony_ci};
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = {
165662306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open_clfe,
165762306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close,
165862306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
165962306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
166062306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
166162306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
166262306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_direct_pointer,
166362306a36Sopenharmony_ci};
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = {
166662306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open_clfe,
166762306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close,
166862306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
166962306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
167062306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
167162306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
167262306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_indirect_pointer,
167362306a36Sopenharmony_ci	.ack =			snd_cs46xx_playback_transfer,
167462306a36Sopenharmony_ci};
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = {
167762306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open_iec958,
167862306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close_iec958,
167962306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
168062306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
168162306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
168262306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
168362306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_direct_pointer,
168462306a36Sopenharmony_ci};
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = {
168762306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open_iec958,
168862306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close_iec958,
168962306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
169062306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
169162306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
169262306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
169362306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_indirect_pointer,
169462306a36Sopenharmony_ci	.ack =			snd_cs46xx_playback_transfer,
169562306a36Sopenharmony_ci};
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci#endif
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_ops = {
170062306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open,
170162306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close,
170262306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
170362306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
170462306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
170562306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
170662306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_direct_pointer,
170762306a36Sopenharmony_ci};
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = {
171062306a36Sopenharmony_ci	.open =			snd_cs46xx_playback_open,
171162306a36Sopenharmony_ci	.close =		snd_cs46xx_playback_close,
171262306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_playback_hw_params,
171362306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_playback_hw_free,
171462306a36Sopenharmony_ci	.prepare =		snd_cs46xx_playback_prepare,
171562306a36Sopenharmony_ci	.trigger =		snd_cs46xx_playback_trigger,
171662306a36Sopenharmony_ci	.pointer =		snd_cs46xx_playback_indirect_pointer,
171762306a36Sopenharmony_ci	.ack =			snd_cs46xx_playback_transfer,
171862306a36Sopenharmony_ci};
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_capture_ops = {
172162306a36Sopenharmony_ci	.open =			snd_cs46xx_capture_open,
172262306a36Sopenharmony_ci	.close =		snd_cs46xx_capture_close,
172362306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_capture_hw_params,
172462306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_capture_hw_free,
172562306a36Sopenharmony_ci	.prepare =		snd_cs46xx_capture_prepare,
172662306a36Sopenharmony_ci	.trigger =		snd_cs46xx_capture_trigger,
172762306a36Sopenharmony_ci	.pointer =		snd_cs46xx_capture_direct_pointer,
172862306a36Sopenharmony_ci};
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = {
173162306a36Sopenharmony_ci	.open =			snd_cs46xx_capture_open,
173262306a36Sopenharmony_ci	.close =		snd_cs46xx_capture_close,
173362306a36Sopenharmony_ci	.hw_params =		snd_cs46xx_capture_hw_params,
173462306a36Sopenharmony_ci	.hw_free =		snd_cs46xx_capture_hw_free,
173562306a36Sopenharmony_ci	.prepare =		snd_cs46xx_capture_prepare,
173662306a36Sopenharmony_ci	.trigger =		snd_cs46xx_capture_trigger,
173762306a36Sopenharmony_ci	.pointer =		snd_cs46xx_capture_indirect_pointer,
173862306a36Sopenharmony_ci	.ack =			snd_cs46xx_capture_transfer,
173962306a36Sopenharmony_ci};
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
174262306a36Sopenharmony_ci#define MAX_PLAYBACK_CHANNELS	(DSP_MAX_PCM_CHANNELS - 1)
174362306a36Sopenharmony_ci#else
174462306a36Sopenharmony_ci#define MAX_PLAYBACK_CHANNELS	1
174562306a36Sopenharmony_ci#endif
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ciint snd_cs46xx_pcm(struct snd_cs46xx *chip, int device)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	struct snd_pcm *pcm;
175062306a36Sopenharmony_ci	int err;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "CS46xx", device, MAX_PLAYBACK_CHANNELS, 1, &pcm);
175362306a36Sopenharmony_ci	if (err < 0)
175462306a36Sopenharmony_ci		return err;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	pcm->private_data = chip;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_ops);
175962306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs46xx_capture_ops);
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	/* global setup */
176262306a36Sopenharmony_ci	pcm->info_flags = 0;
176362306a36Sopenharmony_ci	strcpy(pcm->name, "CS46xx");
176462306a36Sopenharmony_ci	chip->pcm = pcm;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
176762306a36Sopenharmony_ci					      &chip->pci->dev,
176862306a36Sopenharmony_ci					      64*1024, 256*1024);
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	return 0;
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
177562306a36Sopenharmony_ciint snd_cs46xx_pcm_rear(struct snd_cs46xx *chip, int device)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	struct snd_pcm *pcm;
177862306a36Sopenharmony_ci	int err;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "CS46xx - Rear", device, MAX_PLAYBACK_CHANNELS, 0, &pcm);
178162306a36Sopenharmony_ci	if (err < 0)
178262306a36Sopenharmony_ci		return err;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	pcm->private_data = chip;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_rear_ops);
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci	/* global setup */
178962306a36Sopenharmony_ci	pcm->info_flags = 0;
179062306a36Sopenharmony_ci	strcpy(pcm->name, "CS46xx - Rear");
179162306a36Sopenharmony_ci	chip->pcm_rear = pcm;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
179462306a36Sopenharmony_ci					      &chip->pci->dev,
179562306a36Sopenharmony_ci					      64*1024, 256*1024);
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	return 0;
179862306a36Sopenharmony_ci}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ciint snd_cs46xx_pcm_center_lfe(struct snd_cs46xx *chip, int device)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	struct snd_pcm *pcm;
180362306a36Sopenharmony_ci	int err;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "CS46xx - Center LFE", device, MAX_PLAYBACK_CHANNELS, 0, &pcm);
180662306a36Sopenharmony_ci	if (err < 0)
180762306a36Sopenharmony_ci		return err;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	pcm->private_data = chip;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_clfe_ops);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	/* global setup */
181462306a36Sopenharmony_ci	pcm->info_flags = 0;
181562306a36Sopenharmony_ci	strcpy(pcm->name, "CS46xx - Center LFE");
181662306a36Sopenharmony_ci	chip->pcm_center_lfe = pcm;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
181962306a36Sopenharmony_ci					      &chip->pci->dev,
182062306a36Sopenharmony_ci					      64*1024, 256*1024);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	return 0;
182362306a36Sopenharmony_ci}
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ciint snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
182662306a36Sopenharmony_ci{
182762306a36Sopenharmony_ci	struct snd_pcm *pcm;
182862306a36Sopenharmony_ci	int err;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	err = snd_pcm_new(chip->card, "CS46xx - IEC958", device, 1, 0, &pcm);
183162306a36Sopenharmony_ci	if (err < 0)
183262306a36Sopenharmony_ci		return err;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	pcm->private_data = chip;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs46xx_playback_iec958_ops);
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	/* global setup */
183962306a36Sopenharmony_ci	pcm->info_flags = 0;
184062306a36Sopenharmony_ci	strcpy(pcm->name, "CS46xx - IEC958");
184162306a36Sopenharmony_ci	chip->pcm_iec958 = pcm;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
184462306a36Sopenharmony_ci					      &chip->pci->dev,
184562306a36Sopenharmony_ci					      64*1024, 256*1024);
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	return 0;
184862306a36Sopenharmony_ci}
184962306a36Sopenharmony_ci#endif
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci/*
185262306a36Sopenharmony_ci *  Mixer routines
185362306a36Sopenharmony_ci */
185462306a36Sopenharmony_cistatic void snd_cs46xx_mixer_free_ac97(struct snd_ac97 *ac97)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	struct snd_cs46xx *chip = ac97->private_data;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	if (snd_BUG_ON(ac97 != chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] &&
185962306a36Sopenharmony_ci		       ac97 != chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]))
186062306a36Sopenharmony_ci		return;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) {
186362306a36Sopenharmony_ci		chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL;
186462306a36Sopenharmony_ci		chip->eapd_switch = NULL;
186562306a36Sopenharmony_ci	}
186662306a36Sopenharmony_ci	else
186762306a36Sopenharmony_ci		chip->ac97[CS46XX_SECONDARY_CODEC_INDEX] = NULL;
186862306a36Sopenharmony_ci}
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_cistatic int snd_cs46xx_vol_info(struct snd_kcontrol *kcontrol,
187162306a36Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
187262306a36Sopenharmony_ci{
187362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
187462306a36Sopenharmony_ci	uinfo->count = 2;
187562306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
187662306a36Sopenharmony_ci	uinfo->value.integer.max = 0x7fff;
187762306a36Sopenharmony_ci	return 0;
187862306a36Sopenharmony_ci}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_cistatic int snd_cs46xx_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
188162306a36Sopenharmony_ci{
188262306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
188362306a36Sopenharmony_ci	int reg = kcontrol->private_value;
188462306a36Sopenharmony_ci	unsigned int val = snd_cs46xx_peek(chip, reg);
188562306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = 0xffff - (val >> 16);
188662306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = 0xffff - (val & 0xffff);
188762306a36Sopenharmony_ci	return 0;
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_cistatic int snd_cs46xx_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
189362306a36Sopenharmony_ci	int reg = kcontrol->private_value;
189462306a36Sopenharmony_ci	unsigned int val = ((0xffff - ucontrol->value.integer.value[0]) << 16 |
189562306a36Sopenharmony_ci			    (0xffff - ucontrol->value.integer.value[1]));
189662306a36Sopenharmony_ci	unsigned int old = snd_cs46xx_peek(chip, reg);
189762306a36Sopenharmony_ci	int change = (old != val);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	if (change) {
190062306a36Sopenharmony_ci		snd_cs46xx_poke(chip, reg, val);
190162306a36Sopenharmony_ci	}
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	return change;
190462306a36Sopenharmony_ci}
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_cistatic int snd_cs46xx_vol_dac_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->dsp_spos_instance->dac_volume_left;
191362306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = chip->dsp_spos_instance->dac_volume_right;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	return 0;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_cistatic int snd_cs46xx_vol_dac_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
191962306a36Sopenharmony_ci{
192062306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
192162306a36Sopenharmony_ci	int change = 0;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	if (chip->dsp_spos_instance->dac_volume_right != ucontrol->value.integer.value[0] ||
192462306a36Sopenharmony_ci	    chip->dsp_spos_instance->dac_volume_left != ucontrol->value.integer.value[1]) {
192562306a36Sopenharmony_ci		cs46xx_dsp_set_dac_volume(chip,
192662306a36Sopenharmony_ci					  ucontrol->value.integer.value[0],
192762306a36Sopenharmony_ci					  ucontrol->value.integer.value[1]);
192862306a36Sopenharmony_ci		change = 1;
192962306a36Sopenharmony_ci	}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	return change;
193262306a36Sopenharmony_ci}
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci#if 0
193562306a36Sopenharmony_cistatic int snd_cs46xx_vol_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
193662306a36Sopenharmony_ci{
193762306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_input_volume_left;
194062306a36Sopenharmony_ci	ucontrol->value.integer.value[1] = chip->dsp_spos_instance->spdif_input_volume_right;
194162306a36Sopenharmony_ci	return 0;
194262306a36Sopenharmony_ci}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_cistatic int snd_cs46xx_vol_iec958_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
194562306a36Sopenharmony_ci{
194662306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
194762306a36Sopenharmony_ci	int change = 0;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	if (chip->dsp_spos_instance->spdif_input_volume_left  != ucontrol->value.integer.value[0] ||
195062306a36Sopenharmony_ci	    chip->dsp_spos_instance->spdif_input_volume_right!= ucontrol->value.integer.value[1]) {
195162306a36Sopenharmony_ci		cs46xx_dsp_set_iec958_volume (chip,
195262306a36Sopenharmony_ci					      ucontrol->value.integer.value[0],
195362306a36Sopenharmony_ci					      ucontrol->value.integer.value[1]);
195462306a36Sopenharmony_ci		change = 1;
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	return change;
195862306a36Sopenharmony_ci}
195962306a36Sopenharmony_ci#endif
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci#define snd_mixer_boolean_info		snd_ctl_boolean_mono_info
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_cistatic int snd_cs46xx_iec958_get(struct snd_kcontrol *kcontrol,
196462306a36Sopenharmony_ci                                 struct snd_ctl_elem_value *ucontrol)
196562306a36Sopenharmony_ci{
196662306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
196762306a36Sopenharmony_ci	int reg = kcontrol->private_value;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	if (reg == CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT)
197062306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
197162306a36Sopenharmony_ci	else
197262306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in;
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	return 0;
197562306a36Sopenharmony_ci}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_cistatic int snd_cs46xx_iec958_put(struct snd_kcontrol *kcontrol,
197862306a36Sopenharmony_ci                                  struct snd_ctl_elem_value *ucontrol)
197962306a36Sopenharmony_ci{
198062306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
198162306a36Sopenharmony_ci	int change, res;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	switch (kcontrol->private_value) {
198462306a36Sopenharmony_ci	case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT:
198562306a36Sopenharmony_ci		mutex_lock(&chip->spos_mutex);
198662306a36Sopenharmony_ci		change = (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED);
198762306a36Sopenharmony_ci		if (ucontrol->value.integer.value[0] && !change)
198862306a36Sopenharmony_ci			cs46xx_dsp_enable_spdif_out(chip);
198962306a36Sopenharmony_ci		else if (change && !ucontrol->value.integer.value[0])
199062306a36Sopenharmony_ci			cs46xx_dsp_disable_spdif_out(chip);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci		res = (change != (chip->dsp_spos_instance->spdif_status_out & DSP_SPDIF_STATUS_OUTPUT_ENABLED));
199362306a36Sopenharmony_ci		mutex_unlock(&chip->spos_mutex);
199462306a36Sopenharmony_ci		break;
199562306a36Sopenharmony_ci	case CS46XX_MIXER_SPDIF_INPUT_ELEMENT:
199662306a36Sopenharmony_ci		change = chip->dsp_spos_instance->spdif_status_in;
199762306a36Sopenharmony_ci		if (ucontrol->value.integer.value[0] && !change) {
199862306a36Sopenharmony_ci			cs46xx_dsp_enable_spdif_in(chip);
199962306a36Sopenharmony_ci			/* restore volume */
200062306a36Sopenharmony_ci		}
200162306a36Sopenharmony_ci		else if (change && !ucontrol->value.integer.value[0])
200262306a36Sopenharmony_ci			cs46xx_dsp_disable_spdif_in(chip);
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci		res = (change != chip->dsp_spos_instance->spdif_status_in);
200562306a36Sopenharmony_ci		break;
200662306a36Sopenharmony_ci	default:
200762306a36Sopenharmony_ci		res = -EINVAL;
200862306a36Sopenharmony_ci		snd_BUG(); /* should never happen ... */
200962306a36Sopenharmony_ci	}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	return res;
201262306a36Sopenharmony_ci}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_cistatic int snd_cs46xx_adc_capture_get(struct snd_kcontrol *kcontrol,
201562306a36Sopenharmony_ci                                      struct snd_ctl_elem_value *ucontrol)
201662306a36Sopenharmony_ci{
201762306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
201862306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	if (ins->adc_input != NULL)
202162306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 1;
202262306a36Sopenharmony_ci	else
202362306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 0;
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	return 0;
202662306a36Sopenharmony_ci}
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_cistatic int snd_cs46xx_adc_capture_put(struct snd_kcontrol *kcontrol,
202962306a36Sopenharmony_ci                                      struct snd_ctl_elem_value *ucontrol)
203062306a36Sopenharmony_ci{
203162306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
203262306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
203362306a36Sopenharmony_ci	int change = 0;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	if (ucontrol->value.integer.value[0] && !ins->adc_input) {
203662306a36Sopenharmony_ci		cs46xx_dsp_enable_adc_capture(chip);
203762306a36Sopenharmony_ci		change = 1;
203862306a36Sopenharmony_ci	} else  if (!ucontrol->value.integer.value[0] && ins->adc_input) {
203962306a36Sopenharmony_ci		cs46xx_dsp_disable_adc_capture(chip);
204062306a36Sopenharmony_ci		change = 1;
204162306a36Sopenharmony_ci	}
204262306a36Sopenharmony_ci	return change;
204362306a36Sopenharmony_ci}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_cistatic int snd_cs46xx_pcm_capture_get(struct snd_kcontrol *kcontrol,
204662306a36Sopenharmony_ci                                      struct snd_ctl_elem_value *ucontrol)
204762306a36Sopenharmony_ci{
204862306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
204962306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (ins->pcm_input != NULL)
205262306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 1;
205362306a36Sopenharmony_ci	else
205462306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 0;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	return 0;
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_cistatic int snd_cs46xx_pcm_capture_put(struct snd_kcontrol *kcontrol,
206162306a36Sopenharmony_ci                                      struct snd_ctl_elem_value *ucontrol)
206262306a36Sopenharmony_ci{
206362306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
206462306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
206562306a36Sopenharmony_ci	int change = 0;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	if (ucontrol->value.integer.value[0] && !ins->pcm_input) {
206862306a36Sopenharmony_ci		cs46xx_dsp_enable_pcm_capture(chip);
206962306a36Sopenharmony_ci		change = 1;
207062306a36Sopenharmony_ci	} else  if (!ucontrol->value.integer.value[0] && ins->pcm_input) {
207162306a36Sopenharmony_ci		cs46xx_dsp_disable_pcm_capture(chip);
207262306a36Sopenharmony_ci		change = 1;
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	return change;
207662306a36Sopenharmony_ci}
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_cistatic int snd_herc_spdif_select_get(struct snd_kcontrol *kcontrol,
207962306a36Sopenharmony_ci                                     struct snd_ctl_elem_value *ucontrol)
208062306a36Sopenharmony_ci{
208162306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	if (val1 & EGPIODR_GPOE0)
208662306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 1;
208762306a36Sopenharmony_ci	else
208862306a36Sopenharmony_ci		ucontrol->value.integer.value[0] = 0;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	return 0;
209162306a36Sopenharmony_ci}
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci/*
209462306a36Sopenharmony_ci *	Game Theatre XP card - EGPIO[0] is used to select SPDIF input optical or coaxial.
209562306a36Sopenharmony_ci */
209662306a36Sopenharmony_cistatic int snd_herc_spdif_select_put(struct snd_kcontrol *kcontrol,
209762306a36Sopenharmony_ci                                       struct snd_ctl_elem_value *ucontrol)
209862306a36Sopenharmony_ci{
209962306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
210062306a36Sopenharmony_ci	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
210162306a36Sopenharmony_ci	int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	if (ucontrol->value.integer.value[0]) {
210462306a36Sopenharmony_ci		/* optical is default */
210562306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,
210662306a36Sopenharmony_ci				   EGPIODR_GPOE0 | val1);  /* enable EGPIO0 output */
210762306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR,
210862306a36Sopenharmony_ci				   EGPIOPTR_GPPT0 | val2); /* open-drain on output */
210962306a36Sopenharmony_ci	} else {
211062306a36Sopenharmony_ci		/* coaxial */
211162306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,  val1 & ~EGPIODR_GPOE0); /* disable */
211262306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT0); /* disable */
211362306a36Sopenharmony_ci	}
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	/* checking diff from the EGPIO direction register
211662306a36Sopenharmony_ci	   should be enough */
211762306a36Sopenharmony_ci	return (val1 != (int)snd_cs46xx_peekBA0(chip, BA0_EGPIODR));
211862306a36Sopenharmony_ci}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_cistatic int snd_cs46xx_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
212462306a36Sopenharmony_ci	uinfo->count = 1;
212562306a36Sopenharmony_ci	return 0;
212662306a36Sopenharmony_ci}
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_cistatic int snd_cs46xx_spdif_default_get(struct snd_kcontrol *kcontrol,
212962306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
213062306a36Sopenharmony_ci{
213162306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
213262306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
213562306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_default >> 24) & 0xff);
213662306a36Sopenharmony_ci	ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_default >> 16) & 0xff);
213762306a36Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0;
213862306a36Sopenharmony_ci	ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_default) & 0xff);
213962306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ci	return 0;
214262306a36Sopenharmony_ci}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_cistatic int snd_cs46xx_spdif_default_put(struct snd_kcontrol *kcontrol,
214562306a36Sopenharmony_ci					struct snd_ctl_elem_value *ucontrol)
214662306a36Sopenharmony_ci{
214762306a36Sopenharmony_ci	struct snd_cs46xx * chip = snd_kcontrol_chip(kcontrol);
214862306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
214962306a36Sopenharmony_ci	unsigned int val;
215062306a36Sopenharmony_ci	int change;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
215362306a36Sopenharmony_ci	val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
215462306a36Sopenharmony_ci		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[2]) << 16) |
215562306a36Sopenharmony_ci		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3]))  |
215662306a36Sopenharmony_ci		/* left and right validity bit */
215762306a36Sopenharmony_ci		(1 << 13) | (1 << 12);
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	change = (unsigned int)ins->spdif_csuv_default != val;
216162306a36Sopenharmony_ci	ins->spdif_csuv_default = val;
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	if ( !(ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN) )
216462306a36Sopenharmony_ci		cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	return change;
216962306a36Sopenharmony_ci}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_cistatic int snd_cs46xx_spdif_mask_get(struct snd_kcontrol *kcontrol,
217262306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
217362306a36Sopenharmony_ci{
217462306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = 0xff;
217562306a36Sopenharmony_ci	ucontrol->value.iec958.status[1] = 0xff;
217662306a36Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0x00;
217762306a36Sopenharmony_ci	ucontrol->value.iec958.status[3] = 0xff;
217862306a36Sopenharmony_ci	return 0;
217962306a36Sopenharmony_ci}
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_cistatic int snd_cs46xx_spdif_stream_get(struct snd_kcontrol *kcontrol,
218262306a36Sopenharmony_ci                                         struct snd_ctl_elem_value *ucontrol)
218362306a36Sopenharmony_ci{
218462306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
218562306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
218862306a36Sopenharmony_ci	ucontrol->value.iec958.status[0] = _wrap_all_bits((ins->spdif_csuv_stream >> 24) & 0xff);
218962306a36Sopenharmony_ci	ucontrol->value.iec958.status[1] = _wrap_all_bits((ins->spdif_csuv_stream >> 16) & 0xff);
219062306a36Sopenharmony_ci	ucontrol->value.iec958.status[2] = 0;
219162306a36Sopenharmony_ci	ucontrol->value.iec958.status[3] = _wrap_all_bits((ins->spdif_csuv_stream) & 0xff);
219262306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	return 0;
219562306a36Sopenharmony_ci}
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_cistatic int snd_cs46xx_spdif_stream_put(struct snd_kcontrol *kcontrol,
219862306a36Sopenharmony_ci                                        struct snd_ctl_elem_value *ucontrol)
219962306a36Sopenharmony_ci{
220062306a36Sopenharmony_ci	struct snd_cs46xx * chip = snd_kcontrol_chip(kcontrol);
220162306a36Sopenharmony_ci	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
220262306a36Sopenharmony_ci	unsigned int val;
220362306a36Sopenharmony_ci	int change;
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	mutex_lock(&chip->spos_mutex);
220662306a36Sopenharmony_ci	val = ((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[0]) << 24) |
220762306a36Sopenharmony_ci		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[1]) << 16) |
220862306a36Sopenharmony_ci		((unsigned int)_wrap_all_bits(ucontrol->value.iec958.status[3])) |
220962306a36Sopenharmony_ci		/* left and right validity bit */
221062306a36Sopenharmony_ci		(1 << 13) | (1 << 12);
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	change = ins->spdif_csuv_stream != val;
221462306a36Sopenharmony_ci	ins->spdif_csuv_stream = val;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	if ( ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN )
221762306a36Sopenharmony_ci		cs46xx_poke_via_dsp (chip,SP_SPDOUT_CSUV,val);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	mutex_unlock(&chip->spos_mutex);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	return change;
222262306a36Sopenharmony_ci}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci#endif /* CONFIG_SND_CS46XX_NEW_DSP */
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs46xx_controls[] = {
222862306a36Sopenharmony_ci{
222962306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
223062306a36Sopenharmony_ci	.name = "DAC Volume",
223162306a36Sopenharmony_ci	.info = snd_cs46xx_vol_info,
223262306a36Sopenharmony_ci#ifndef CONFIG_SND_CS46XX_NEW_DSP
223362306a36Sopenharmony_ci	.get = snd_cs46xx_vol_get,
223462306a36Sopenharmony_ci	.put = snd_cs46xx_vol_put,
223562306a36Sopenharmony_ci	.private_value = BA1_PVOL,
223662306a36Sopenharmony_ci#else
223762306a36Sopenharmony_ci	.get = snd_cs46xx_vol_dac_get,
223862306a36Sopenharmony_ci	.put = snd_cs46xx_vol_dac_put,
223962306a36Sopenharmony_ci#endif
224062306a36Sopenharmony_ci},
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci{
224362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
224462306a36Sopenharmony_ci	.name = "ADC Volume",
224562306a36Sopenharmony_ci	.info = snd_cs46xx_vol_info,
224662306a36Sopenharmony_ci	.get = snd_cs46xx_vol_get,
224762306a36Sopenharmony_ci	.put = snd_cs46xx_vol_put,
224862306a36Sopenharmony_ci#ifndef CONFIG_SND_CS46XX_NEW_DSP
224962306a36Sopenharmony_ci	.private_value = BA1_CVOL,
225062306a36Sopenharmony_ci#else
225162306a36Sopenharmony_ci	.private_value = (VARIDECIMATE_SCB_ADDR + 0xE) << 2,
225262306a36Sopenharmony_ci#endif
225362306a36Sopenharmony_ci},
225462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
225762306a36Sopenharmony_ci	.name = "ADC Capture Switch",
225862306a36Sopenharmony_ci	.info = snd_mixer_boolean_info,
225962306a36Sopenharmony_ci	.get = snd_cs46xx_adc_capture_get,
226062306a36Sopenharmony_ci	.put = snd_cs46xx_adc_capture_put
226162306a36Sopenharmony_ci},
226262306a36Sopenharmony_ci{
226362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
226462306a36Sopenharmony_ci	.name = "DAC Capture Switch",
226562306a36Sopenharmony_ci	.info = snd_mixer_boolean_info,
226662306a36Sopenharmony_ci	.get = snd_cs46xx_pcm_capture_get,
226762306a36Sopenharmony_ci	.put = snd_cs46xx_pcm_capture_put
226862306a36Sopenharmony_ci},
226962306a36Sopenharmony_ci{
227062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
227162306a36Sopenharmony_ci	.name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
227262306a36Sopenharmony_ci	.info = snd_mixer_boolean_info,
227362306a36Sopenharmony_ci	.get = snd_cs46xx_iec958_get,
227462306a36Sopenharmony_ci	.put = snd_cs46xx_iec958_put,
227562306a36Sopenharmony_ci	.private_value = CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT,
227662306a36Sopenharmony_ci},
227762306a36Sopenharmony_ci{
227862306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
227962306a36Sopenharmony_ci	.name = SNDRV_CTL_NAME_IEC958("Input ",NONE,SWITCH),
228062306a36Sopenharmony_ci	.info = snd_mixer_boolean_info,
228162306a36Sopenharmony_ci	.get = snd_cs46xx_iec958_get,
228262306a36Sopenharmony_ci	.put = snd_cs46xx_iec958_put,
228362306a36Sopenharmony_ci	.private_value = CS46XX_MIXER_SPDIF_INPUT_ELEMENT,
228462306a36Sopenharmony_ci},
228562306a36Sopenharmony_ci#if 0
228662306a36Sopenharmony_ci/* Input IEC958 volume does not work for the moment. (Benny) */
228762306a36Sopenharmony_ci{
228862306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
228962306a36Sopenharmony_ci	.name = SNDRV_CTL_NAME_IEC958("Input ",NONE,VOLUME),
229062306a36Sopenharmony_ci	.info = snd_cs46xx_vol_info,
229162306a36Sopenharmony_ci	.get = snd_cs46xx_vol_iec958_get,
229262306a36Sopenharmony_ci	.put = snd_cs46xx_vol_iec958_put,
229362306a36Sopenharmony_ci	.private_value = (ASYNCRX_SCB_ADDR + 0xE) << 2,
229462306a36Sopenharmony_ci},
229562306a36Sopenharmony_ci#endif
229662306a36Sopenharmony_ci{
229762306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
229862306a36Sopenharmony_ci	.name =  SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
229962306a36Sopenharmony_ci	.info =	 snd_cs46xx_spdif_info,
230062306a36Sopenharmony_ci	.get =	 snd_cs46xx_spdif_default_get,
230162306a36Sopenharmony_ci	.put =   snd_cs46xx_spdif_default_put,
230262306a36Sopenharmony_ci},
230362306a36Sopenharmony_ci{
230462306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
230562306a36Sopenharmony_ci	.name =	 SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
230662306a36Sopenharmony_ci	.info =	 snd_cs46xx_spdif_info,
230762306a36Sopenharmony_ci        .get =	 snd_cs46xx_spdif_mask_get,
230862306a36Sopenharmony_ci	.access = SNDRV_CTL_ELEM_ACCESS_READ
230962306a36Sopenharmony_ci},
231062306a36Sopenharmony_ci{
231162306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
231262306a36Sopenharmony_ci	.name =	 SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
231362306a36Sopenharmony_ci	.info =	 snd_cs46xx_spdif_info,
231462306a36Sopenharmony_ci	.get =	 snd_cs46xx_spdif_stream_get,
231562306a36Sopenharmony_ci	.put =	 snd_cs46xx_spdif_stream_put
231662306a36Sopenharmony_ci},
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci#endif
231962306a36Sopenharmony_ci};
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
232262306a36Sopenharmony_ci/* set primary cs4294 codec into Extended Audio Mode */
232362306a36Sopenharmony_cistatic int snd_cs46xx_front_dup_get(struct snd_kcontrol *kcontrol,
232462306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
232562306a36Sopenharmony_ci{
232662306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
232762306a36Sopenharmony_ci	unsigned short val;
232862306a36Sopenharmony_ci	val = snd_ac97_read(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX], AC97_CSR_ACMODE);
232962306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = (val & 0x200) ? 0 : 1;
233062306a36Sopenharmony_ci	return 0;
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic int snd_cs46xx_front_dup_put(struct snd_kcontrol *kcontrol,
233462306a36Sopenharmony_ci				    struct snd_ctl_elem_value *ucontrol)
233562306a36Sopenharmony_ci{
233662306a36Sopenharmony_ci	struct snd_cs46xx *chip = snd_kcontrol_chip(kcontrol);
233762306a36Sopenharmony_ci	return snd_ac97_update_bits(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
233862306a36Sopenharmony_ci				    AC97_CSR_ACMODE, 0x200,
233962306a36Sopenharmony_ci				    ucontrol->value.integer.value[0] ? 0 : 0x200);
234062306a36Sopenharmony_ci}
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs46xx_front_dup_ctl = {
234362306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
234462306a36Sopenharmony_ci	.name = "Duplicate Front",
234562306a36Sopenharmony_ci	.info = snd_mixer_boolean_info,
234662306a36Sopenharmony_ci	.get = snd_cs46xx_front_dup_get,
234762306a36Sopenharmony_ci	.put = snd_cs46xx_front_dup_put,
234862306a36Sopenharmony_ci};
234962306a36Sopenharmony_ci#endif
235062306a36Sopenharmony_ci
235162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
235262306a36Sopenharmony_ci/* Only available on the Hercules Game Theater XP soundcard */
235362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_hercules_controls[] = {
235462306a36Sopenharmony_ci{
235562306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
235662306a36Sopenharmony_ci	.name = "Optical/Coaxial SPDIF Input Switch",
235762306a36Sopenharmony_ci	.info = snd_mixer_boolean_info,
235862306a36Sopenharmony_ci	.get = snd_herc_spdif_select_get,
235962306a36Sopenharmony_ci	.put = snd_herc_spdif_select_put,
236062306a36Sopenharmony_ci},
236162306a36Sopenharmony_ci};
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_cistatic void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)
236562306a36Sopenharmony_ci{
236662306a36Sopenharmony_ci	unsigned long end_time;
236762306a36Sopenharmony_ci	int err;
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	/* reset to defaults */
237062306a36Sopenharmony_ci	snd_ac97_write(ac97, AC97_RESET, 0);
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	/* set the desired CODEC mode */
237362306a36Sopenharmony_ci	if (ac97->num == CS46XX_PRIMARY_CODEC_INDEX) {
237462306a36Sopenharmony_ci		dev_dbg(ac97->bus->card->dev, "CODEC1 mode %04x\n", 0x0);
237562306a36Sopenharmony_ci		snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x0);
237662306a36Sopenharmony_ci	} else if (ac97->num == CS46XX_SECONDARY_CODEC_INDEX) {
237762306a36Sopenharmony_ci		dev_dbg(ac97->bus->card->dev, "CODEC2 mode %04x\n", 0x3);
237862306a36Sopenharmony_ci		snd_cs46xx_ac97_write(ac97, AC97_CSR_ACMODE, 0x3);
237962306a36Sopenharmony_ci	} else {
238062306a36Sopenharmony_ci		snd_BUG(); /* should never happen ... */
238162306a36Sopenharmony_ci	}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	udelay(50);
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	/* it's necessary to wait awhile until registers are accessible after RESET */
238662306a36Sopenharmony_ci	/* because the PCM or MASTER volume registers can be modified, */
238762306a36Sopenharmony_ci	/* the REC_GAIN register is used for tests */
238862306a36Sopenharmony_ci	end_time = jiffies + HZ;
238962306a36Sopenharmony_ci	do {
239062306a36Sopenharmony_ci		unsigned short ext_mid;
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci		/* use preliminary reads to settle the communication */
239362306a36Sopenharmony_ci		snd_ac97_read(ac97, AC97_RESET);
239462306a36Sopenharmony_ci		snd_ac97_read(ac97, AC97_VENDOR_ID1);
239562306a36Sopenharmony_ci		snd_ac97_read(ac97, AC97_VENDOR_ID2);
239662306a36Sopenharmony_ci		/* modem? */
239762306a36Sopenharmony_ci		ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
239862306a36Sopenharmony_ci		if (ext_mid != 0xffff && (ext_mid & 1) != 0)
239962306a36Sopenharmony_ci			return;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci		/* test if we can write to the record gain volume register */
240262306a36Sopenharmony_ci		snd_ac97_write(ac97, AC97_REC_GAIN, 0x8a05);
240362306a36Sopenharmony_ci		err = snd_ac97_read(ac97, AC97_REC_GAIN);
240462306a36Sopenharmony_ci		if (err == 0x8a05)
240562306a36Sopenharmony_ci			return;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci		msleep(10);
240862306a36Sopenharmony_ci	} while (time_after_eq(end_time, jiffies));
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	dev_err(ac97->bus->card->dev,
241162306a36Sopenharmony_ci		"CS46xx secondary codec doesn't respond!\n");
241262306a36Sopenharmony_ci}
241362306a36Sopenharmony_ci#endif
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_cistatic int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
241662306a36Sopenharmony_ci{
241762306a36Sopenharmony_ci	int idx, err;
241862306a36Sopenharmony_ci	struct snd_ac97_template ac97;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	memset(&ac97, 0, sizeof(ac97));
242162306a36Sopenharmony_ci	ac97.private_data = chip;
242262306a36Sopenharmony_ci	ac97.private_free = snd_cs46xx_mixer_free_ac97;
242362306a36Sopenharmony_ci	ac97.num = codec;
242462306a36Sopenharmony_ci	if (chip->amplifier_ctrl == amp_voyetra)
242562306a36Sopenharmony_ci		ac97.scaps = AC97_SCAP_INV_EAPD;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	if (codec == CS46XX_SECONDARY_CODEC_INDEX) {
242862306a36Sopenharmony_ci		snd_cs46xx_codec_write(chip, AC97_RESET, 0, codec);
242962306a36Sopenharmony_ci		udelay(10);
243062306a36Sopenharmony_ci		if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) {
243162306a36Sopenharmony_ci			dev_dbg(chip->card->dev,
243262306a36Sopenharmony_ci				"secondary codec not present\n");
243362306a36Sopenharmony_ci			return -ENXIO;
243462306a36Sopenharmony_ci		}
243562306a36Sopenharmony_ci	}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	snd_cs46xx_codec_write(chip, AC97_MASTER, 0x8000, codec);
243862306a36Sopenharmony_ci	for (idx = 0; idx < 100; ++idx) {
243962306a36Sopenharmony_ci		if (snd_cs46xx_codec_read(chip, AC97_MASTER, codec) == 0x8000) {
244062306a36Sopenharmony_ci			err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[codec]);
244162306a36Sopenharmony_ci			return err;
244262306a36Sopenharmony_ci		}
244362306a36Sopenharmony_ci		msleep(10);
244462306a36Sopenharmony_ci	}
244562306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "codec %d detection timeout\n", codec);
244662306a36Sopenharmony_ci	return -ENXIO;
244762306a36Sopenharmony_ci}
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ciint snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
245062306a36Sopenharmony_ci{
245162306a36Sopenharmony_ci	struct snd_card *card = chip->card;
245262306a36Sopenharmony_ci	int err;
245362306a36Sopenharmony_ci	unsigned int idx;
245462306a36Sopenharmony_ci	static const struct snd_ac97_bus_ops ops = {
245562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
245662306a36Sopenharmony_ci		.reset = snd_cs46xx_codec_reset,
245762306a36Sopenharmony_ci#endif
245862306a36Sopenharmony_ci		.write = snd_cs46xx_ac97_write,
245962306a36Sopenharmony_ci		.read = snd_cs46xx_ac97_read,
246062306a36Sopenharmony_ci	};
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci	/* detect primary codec */
246362306a36Sopenharmony_ci	chip->nr_ac97_codecs = 0;
246462306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "detecting primary codec\n");
246562306a36Sopenharmony_ci	err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
246662306a36Sopenharmony_ci	if (err < 0)
246762306a36Sopenharmony_ci		return err;
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	if (cs46xx_detect_codec(chip, CS46XX_PRIMARY_CODEC_INDEX) < 0)
247062306a36Sopenharmony_ci		return -ENXIO;
247162306a36Sopenharmony_ci	chip->nr_ac97_codecs = 1;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
247462306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "detecting secondary codec\n");
247562306a36Sopenharmony_ci	/* try detect a secondary codec */
247662306a36Sopenharmony_ci	if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX))
247762306a36Sopenharmony_ci		chip->nr_ac97_codecs = 2;
247862306a36Sopenharmony_ci#endif /* CONFIG_SND_CS46XX_NEW_DSP */
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci	/* add cs4630 mixer controls */
248162306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_cs46xx_controls); idx++) {
248262306a36Sopenharmony_ci		struct snd_kcontrol *kctl;
248362306a36Sopenharmony_ci		kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip);
248462306a36Sopenharmony_ci		if (kctl && kctl->id.iface == SNDRV_CTL_ELEM_IFACE_PCM)
248562306a36Sopenharmony_ci			kctl->id.device = spdif_device;
248662306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl);
248762306a36Sopenharmony_ci		if (err < 0)
248862306a36Sopenharmony_ci			return err;
248962306a36Sopenharmony_ci	}
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci	/* get EAPD mixer switch (for voyetra hack) */
249262306a36Sopenharmony_ci	chip->eapd_switch = snd_ctl_find_id_mixer(chip->card,
249362306a36Sopenharmony_ci						  "External Amplifier");
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
249662306a36Sopenharmony_ci	if (chip->nr_ac97_codecs == 1) {
249762306a36Sopenharmony_ci		unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
249862306a36Sopenharmony_ci		if ((id2 & 0xfff0) == 0x5920) {	/* CS4294 and CS4298 */
249962306a36Sopenharmony_ci			err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
250062306a36Sopenharmony_ci			if (err < 0)
250162306a36Sopenharmony_ci				return err;
250262306a36Sopenharmony_ci			snd_ac97_write_cache(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX],
250362306a36Sopenharmony_ci					     AC97_CSR_ACMODE, 0x200);
250462306a36Sopenharmony_ci		}
250562306a36Sopenharmony_ci	}
250662306a36Sopenharmony_ci	/* do soundcard specific mixer setup */
250762306a36Sopenharmony_ci	if (chip->mixer_init) {
250862306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "calling chip->mixer_init(chip);\n");
250962306a36Sopenharmony_ci		chip->mixer_init(chip);
251062306a36Sopenharmony_ci	}
251162306a36Sopenharmony_ci#endif
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci 	/* turn on amplifier */
251462306a36Sopenharmony_ci	chip->amplifier_ctrl(chip, 1);
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	return 0;
251762306a36Sopenharmony_ci}
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci/*
252062306a36Sopenharmony_ci *  RawMIDI interface
252162306a36Sopenharmony_ci */
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_cistatic void snd_cs46xx_midi_reset(struct snd_cs46xx *chip)
252462306a36Sopenharmony_ci{
252562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_MIDCR, MIDCR_MRST);
252662306a36Sopenharmony_ci	udelay(100);
252762306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
252862306a36Sopenharmony_ci}
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_cistatic int snd_cs46xx_midi_input_open(struct snd_rawmidi_substream *substream)
253162306a36Sopenharmony_ci{
253262306a36Sopenharmony_ci	struct snd_cs46xx *chip = substream->rmidi->private_data;
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	chip->active_ctrl(chip, 1);
253562306a36Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
253662306a36Sopenharmony_ci	chip->uartm |= CS46XX_MODE_INPUT;
253762306a36Sopenharmony_ci	chip->midcr |= MIDCR_RXE;
253862306a36Sopenharmony_ci	chip->midi_input = substream;
253962306a36Sopenharmony_ci	if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
254062306a36Sopenharmony_ci		snd_cs46xx_midi_reset(chip);
254162306a36Sopenharmony_ci	} else {
254262306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
254362306a36Sopenharmony_ci	}
254462306a36Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
254562306a36Sopenharmony_ci	return 0;
254662306a36Sopenharmony_ci}
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_cistatic int snd_cs46xx_midi_input_close(struct snd_rawmidi_substream *substream)
254962306a36Sopenharmony_ci{
255062306a36Sopenharmony_ci	struct snd_cs46xx *chip = substream->rmidi->private_data;
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
255362306a36Sopenharmony_ci	chip->midcr &= ~(MIDCR_RXE | MIDCR_RIE);
255462306a36Sopenharmony_ci	chip->midi_input = NULL;
255562306a36Sopenharmony_ci	if (!(chip->uartm & CS46XX_MODE_OUTPUT)) {
255662306a36Sopenharmony_ci		snd_cs46xx_midi_reset(chip);
255762306a36Sopenharmony_ci	} else {
255862306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci	chip->uartm &= ~CS46XX_MODE_INPUT;
256162306a36Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
256262306a36Sopenharmony_ci	chip->active_ctrl(chip, -1);
256362306a36Sopenharmony_ci	return 0;
256462306a36Sopenharmony_ci}
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_cistatic int snd_cs46xx_midi_output_open(struct snd_rawmidi_substream *substream)
256762306a36Sopenharmony_ci{
256862306a36Sopenharmony_ci	struct snd_cs46xx *chip = substream->rmidi->private_data;
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	chip->active_ctrl(chip, 1);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
257362306a36Sopenharmony_ci	chip->uartm |= CS46XX_MODE_OUTPUT;
257462306a36Sopenharmony_ci	chip->midcr |= MIDCR_TXE;
257562306a36Sopenharmony_ci	chip->midi_output = substream;
257662306a36Sopenharmony_ci	if (!(chip->uartm & CS46XX_MODE_INPUT)) {
257762306a36Sopenharmony_ci		snd_cs46xx_midi_reset(chip);
257862306a36Sopenharmony_ci	} else {
257962306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
258062306a36Sopenharmony_ci	}
258162306a36Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
258262306a36Sopenharmony_ci	return 0;
258362306a36Sopenharmony_ci}
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_cistatic int snd_cs46xx_midi_output_close(struct snd_rawmidi_substream *substream)
258662306a36Sopenharmony_ci{
258762306a36Sopenharmony_ci	struct snd_cs46xx *chip = substream->rmidi->private_data;
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	spin_lock_irq(&chip->reg_lock);
259062306a36Sopenharmony_ci	chip->midcr &= ~(MIDCR_TXE | MIDCR_TIE);
259162306a36Sopenharmony_ci	chip->midi_output = NULL;
259262306a36Sopenharmony_ci	if (!(chip->uartm & CS46XX_MODE_INPUT)) {
259362306a36Sopenharmony_ci		snd_cs46xx_midi_reset(chip);
259462306a36Sopenharmony_ci	} else {
259562306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
259662306a36Sopenharmony_ci	}
259762306a36Sopenharmony_ci	chip->uartm &= ~CS46XX_MODE_OUTPUT;
259862306a36Sopenharmony_ci	spin_unlock_irq(&chip->reg_lock);
259962306a36Sopenharmony_ci	chip->active_ctrl(chip, -1);
260062306a36Sopenharmony_ci	return 0;
260162306a36Sopenharmony_ci}
260262306a36Sopenharmony_ci
260362306a36Sopenharmony_cistatic void snd_cs46xx_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
260462306a36Sopenharmony_ci{
260562306a36Sopenharmony_ci	unsigned long flags;
260662306a36Sopenharmony_ci	struct snd_cs46xx *chip = substream->rmidi->private_data;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
260962306a36Sopenharmony_ci	if (up) {
261062306a36Sopenharmony_ci		if ((chip->midcr & MIDCR_RIE) == 0) {
261162306a36Sopenharmony_ci			chip->midcr |= MIDCR_RIE;
261262306a36Sopenharmony_ci			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
261362306a36Sopenharmony_ci		}
261462306a36Sopenharmony_ci	} else {
261562306a36Sopenharmony_ci		if (chip->midcr & MIDCR_RIE) {
261662306a36Sopenharmony_ci			chip->midcr &= ~MIDCR_RIE;
261762306a36Sopenharmony_ci			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
261862306a36Sopenharmony_ci		}
261962306a36Sopenharmony_ci	}
262062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
262162306a36Sopenharmony_ci}
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_cistatic void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
262462306a36Sopenharmony_ci{
262562306a36Sopenharmony_ci	unsigned long flags;
262662306a36Sopenharmony_ci	struct snd_cs46xx *chip = substream->rmidi->private_data;
262762306a36Sopenharmony_ci	unsigned char byte;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	spin_lock_irqsave(&chip->reg_lock, flags);
263062306a36Sopenharmony_ci	if (up) {
263162306a36Sopenharmony_ci		if ((chip->midcr & MIDCR_TIE) == 0) {
263262306a36Sopenharmony_ci			chip->midcr |= MIDCR_TIE;
263362306a36Sopenharmony_ci			/* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
263462306a36Sopenharmony_ci			while ((chip->midcr & MIDCR_TIE) &&
263562306a36Sopenharmony_ci			       (snd_cs46xx_peekBA0(chip, BA0_MIDSR) & MIDSR_TBF) == 0) {
263662306a36Sopenharmony_ci				if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
263762306a36Sopenharmony_ci					chip->midcr &= ~MIDCR_TIE;
263862306a36Sopenharmony_ci				} else {
263962306a36Sopenharmony_ci					snd_cs46xx_pokeBA0(chip, BA0_MIDWP, byte);
264062306a36Sopenharmony_ci				}
264162306a36Sopenharmony_ci			}
264262306a36Sopenharmony_ci			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
264362306a36Sopenharmony_ci		}
264462306a36Sopenharmony_ci	} else {
264562306a36Sopenharmony_ci		if (chip->midcr & MIDCR_TIE) {
264662306a36Sopenharmony_ci			chip->midcr &= ~MIDCR_TIE;
264762306a36Sopenharmony_ci			snd_cs46xx_pokeBA0(chip, BA0_MIDCR, chip->midcr);
264862306a36Sopenharmony_ci		}
264962306a36Sopenharmony_ci	}
265062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->reg_lock, flags);
265162306a36Sopenharmony_ci}
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_cs46xx_midi_output =
265462306a36Sopenharmony_ci{
265562306a36Sopenharmony_ci	.open =		snd_cs46xx_midi_output_open,
265662306a36Sopenharmony_ci	.close =	snd_cs46xx_midi_output_close,
265762306a36Sopenharmony_ci	.trigger =	snd_cs46xx_midi_output_trigger,
265862306a36Sopenharmony_ci};
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_cs46xx_midi_input =
266162306a36Sopenharmony_ci{
266262306a36Sopenharmony_ci	.open =		snd_cs46xx_midi_input_open,
266362306a36Sopenharmony_ci	.close =	snd_cs46xx_midi_input_close,
266462306a36Sopenharmony_ci	.trigger =	snd_cs46xx_midi_input_trigger,
266562306a36Sopenharmony_ci};
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ciint snd_cs46xx_midi(struct snd_cs46xx *chip, int device)
266862306a36Sopenharmony_ci{
266962306a36Sopenharmony_ci	struct snd_rawmidi *rmidi;
267062306a36Sopenharmony_ci	int err;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	err = snd_rawmidi_new(chip->card, "CS46XX", device, 1, 1, &rmidi);
267362306a36Sopenharmony_ci	if (err < 0)
267462306a36Sopenharmony_ci		return err;
267562306a36Sopenharmony_ci	strcpy(rmidi->name, "CS46XX");
267662306a36Sopenharmony_ci	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_cs46xx_midi_output);
267762306a36Sopenharmony_ci	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
267862306a36Sopenharmony_ci	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
267962306a36Sopenharmony_ci	rmidi->private_data = chip;
268062306a36Sopenharmony_ci	chip->rmidi = rmidi;
268162306a36Sopenharmony_ci	return 0;
268262306a36Sopenharmony_ci}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci/*
268662306a36Sopenharmony_ci * gameport interface
268762306a36Sopenharmony_ci */
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_GAMEPORT)
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_cistatic void snd_cs46xx_gameport_trigger(struct gameport *gameport)
269262306a36Sopenharmony_ci{
269362306a36Sopenharmony_ci	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	if (snd_BUG_ON(!chip))
269662306a36Sopenharmony_ci		return;
269762306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF);  //outb(gameport->io, 0xFF);
269862306a36Sopenharmony_ci}
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_cistatic unsigned char snd_cs46xx_gameport_read(struct gameport *gameport)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	if (snd_BUG_ON(!chip))
270562306a36Sopenharmony_ci		return 0;
270662306a36Sopenharmony_ci	return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io);
270762306a36Sopenharmony_ci}
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_cistatic int snd_cs46xx_gameport_cooked_read(struct gameport *gameport, int *axes, int *buttons)
271062306a36Sopenharmony_ci{
271162306a36Sopenharmony_ci	struct snd_cs46xx *chip = gameport_get_port_data(gameport);
271262306a36Sopenharmony_ci	unsigned js1, js2, jst;
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	if (snd_BUG_ON(!chip))
271562306a36Sopenharmony_ci		return 0;
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_ci	js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1);
271862306a36Sopenharmony_ci	js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2);
271962306a36Sopenharmony_ci	jst = snd_cs46xx_peekBA0(chip, BA0_JSPT);
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	*buttons = (~jst >> 4) & 0x0F;
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	axes[0] = ((js1 & JSC1_Y1V_MASK) >> JSC1_Y1V_SHIFT) & 0xFFFF;
272462306a36Sopenharmony_ci	axes[1] = ((js1 & JSC1_X1V_MASK) >> JSC1_X1V_SHIFT) & 0xFFFF;
272562306a36Sopenharmony_ci	axes[2] = ((js2 & JSC2_Y2V_MASK) >> JSC2_Y2V_SHIFT) & 0xFFFF;
272662306a36Sopenharmony_ci	axes[3] = ((js2 & JSC2_X2V_MASK) >> JSC2_X2V_SHIFT) & 0xFFFF;
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	for(jst=0;jst<4;++jst)
272962306a36Sopenharmony_ci		if(axes[jst]==0xFFFF) axes[jst] = -1;
273062306a36Sopenharmony_ci	return 0;
273162306a36Sopenharmony_ci}
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_cistatic int snd_cs46xx_gameport_open(struct gameport *gameport, int mode)
273462306a36Sopenharmony_ci{
273562306a36Sopenharmony_ci	switch (mode) {
273662306a36Sopenharmony_ci	case GAMEPORT_MODE_COOKED:
273762306a36Sopenharmony_ci		return 0;
273862306a36Sopenharmony_ci	case GAMEPORT_MODE_RAW:
273962306a36Sopenharmony_ci		return 0;
274062306a36Sopenharmony_ci	default:
274162306a36Sopenharmony_ci		return -1;
274262306a36Sopenharmony_ci	}
274362306a36Sopenharmony_ci	return 0;
274462306a36Sopenharmony_ci}
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ciint snd_cs46xx_gameport(struct snd_cs46xx *chip)
274762306a36Sopenharmony_ci{
274862306a36Sopenharmony_ci	struct gameport *gp;
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	chip->gameport = gp = gameport_allocate_port();
275162306a36Sopenharmony_ci	if (!gp) {
275262306a36Sopenharmony_ci		dev_err(chip->card->dev,
275362306a36Sopenharmony_ci			"cannot allocate memory for gameport\n");
275462306a36Sopenharmony_ci		return -ENOMEM;
275562306a36Sopenharmony_ci	}
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci	gameport_set_name(gp, "CS46xx Gameport");
275862306a36Sopenharmony_ci	gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci));
275962306a36Sopenharmony_ci	gameport_set_dev_parent(gp, &chip->pci->dev);
276062306a36Sopenharmony_ci	gameport_set_port_data(gp, chip);
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	gp->open = snd_cs46xx_gameport_open;
276362306a36Sopenharmony_ci	gp->read = snd_cs46xx_gameport_read;
276462306a36Sopenharmony_ci	gp->trigger = snd_cs46xx_gameport_trigger;
276562306a36Sopenharmony_ci	gp->cooked_read = snd_cs46xx_gameport_cooked_read;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_JSIO, 0xFF); // ?
276862306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_JSCTL, JSCTL_SP_MEDIUM_SLOW);
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	gameport_register_port(gp);
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	return 0;
277362306a36Sopenharmony_ci}
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_cistatic inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip)
277662306a36Sopenharmony_ci{
277762306a36Sopenharmony_ci	if (chip->gameport) {
277862306a36Sopenharmony_ci		gameport_unregister_port(chip->gameport);
277962306a36Sopenharmony_ci		chip->gameport = NULL;
278062306a36Sopenharmony_ci	}
278162306a36Sopenharmony_ci}
278262306a36Sopenharmony_ci#else
278362306a36Sopenharmony_ciint snd_cs46xx_gameport(struct snd_cs46xx *chip) { return -ENOSYS; }
278462306a36Sopenharmony_cistatic inline void snd_cs46xx_remove_gameport(struct snd_cs46xx *chip) { }
278562306a36Sopenharmony_ci#endif /* CONFIG_GAMEPORT */
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci#ifdef CONFIG_SND_PROC_FS
278862306a36Sopenharmony_ci/*
278962306a36Sopenharmony_ci *  proc interface
279062306a36Sopenharmony_ci */
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_cistatic ssize_t snd_cs46xx_io_read(struct snd_info_entry *entry,
279362306a36Sopenharmony_ci				  void *file_private_data,
279462306a36Sopenharmony_ci				  struct file *file, char __user *buf,
279562306a36Sopenharmony_ci				  size_t count, loff_t pos)
279662306a36Sopenharmony_ci{
279762306a36Sopenharmony_ci	struct snd_cs46xx_region *region = entry->private_data;
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	if (copy_to_user_fromio(buf, region->remap_addr + pos, count))
280062306a36Sopenharmony_ci		return -EFAULT;
280162306a36Sopenharmony_ci	return count;
280262306a36Sopenharmony_ci}
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_cistatic const struct snd_info_entry_ops snd_cs46xx_proc_io_ops = {
280562306a36Sopenharmony_ci	.read = snd_cs46xx_io_read,
280662306a36Sopenharmony_ci};
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_cistatic int snd_cs46xx_proc_init(struct snd_card *card, struct snd_cs46xx *chip)
280962306a36Sopenharmony_ci{
281062306a36Sopenharmony_ci	struct snd_info_entry *entry;
281162306a36Sopenharmony_ci	int idx;
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	for (idx = 0; idx < 5; idx++) {
281462306a36Sopenharmony_ci		struct snd_cs46xx_region *region = &chip->region.idx[idx];
281562306a36Sopenharmony_ci		if (! snd_card_proc_new(card, region->name, &entry)) {
281662306a36Sopenharmony_ci			entry->content = SNDRV_INFO_CONTENT_DATA;
281762306a36Sopenharmony_ci			entry->private_data = chip;
281862306a36Sopenharmony_ci			entry->c.ops = &snd_cs46xx_proc_io_ops;
281962306a36Sopenharmony_ci			entry->size = region->size;
282062306a36Sopenharmony_ci			entry->mode = S_IFREG | 0400;
282162306a36Sopenharmony_ci		}
282262306a36Sopenharmony_ci	}
282362306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
282462306a36Sopenharmony_ci	cs46xx_dsp_proc_init(card, chip);
282562306a36Sopenharmony_ci#endif
282662306a36Sopenharmony_ci	return 0;
282762306a36Sopenharmony_ci}
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_cistatic int snd_cs46xx_proc_done(struct snd_cs46xx *chip)
283062306a36Sopenharmony_ci{
283162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
283262306a36Sopenharmony_ci	cs46xx_dsp_proc_done(chip);
283362306a36Sopenharmony_ci#endif
283462306a36Sopenharmony_ci	return 0;
283562306a36Sopenharmony_ci}
283662306a36Sopenharmony_ci#else /* !CONFIG_SND_PROC_FS */
283762306a36Sopenharmony_ci#define snd_cs46xx_proc_init(card, chip)
283862306a36Sopenharmony_ci#define snd_cs46xx_proc_done(chip)
283962306a36Sopenharmony_ci#endif
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_ci/*
284262306a36Sopenharmony_ci * stop the h/w
284362306a36Sopenharmony_ci */
284462306a36Sopenharmony_cistatic void snd_cs46xx_hw_stop(struct snd_cs46xx *chip)
284562306a36Sopenharmony_ci{
284662306a36Sopenharmony_ci	unsigned int tmp;
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
284962306a36Sopenharmony_ci	tmp &= ~0x0000f03f;
285062306a36Sopenharmony_ci	tmp |=  0x00000010;
285162306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt disable */
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_CIE);
285462306a36Sopenharmony_ci	tmp &= ~0x0000003f;
285562306a36Sopenharmony_ci	tmp |=  0x00000011;
285662306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt disable */
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci	/*
285962306a36Sopenharmony_ci         *  Stop playback DMA.
286062306a36Sopenharmony_ci	 */
286162306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_PCTL);
286262306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci	/*
286562306a36Sopenharmony_ci         *  Stop capture DMA.
286662306a36Sopenharmony_ci	 */
286762306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_CCTL);
286862306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	/*
287162306a36Sopenharmony_ci         *  Reset the processor.
287262306a36Sopenharmony_ci         */
287362306a36Sopenharmony_ci	snd_cs46xx_reset(chip);
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	snd_cs46xx_proc_stop(chip);
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci	/*
287862306a36Sopenharmony_ci	 *  Power down the PLL.
287962306a36Sopenharmony_ci	 */
288062306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_ci	/*
288362306a36Sopenharmony_ci	 *  Turn off the Processor by turning off the software clock enable flag in
288462306a36Sopenharmony_ci	 *  the clock control register.
288562306a36Sopenharmony_ci	 */
288662306a36Sopenharmony_ci	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE;
288762306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
288862306a36Sopenharmony_ci}
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_cistatic void snd_cs46xx_free(struct snd_card *card)
289262306a36Sopenharmony_ci{
289362306a36Sopenharmony_ci	struct snd_cs46xx *chip = card->private_data;
289462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
289562306a36Sopenharmony_ci	int idx;
289662306a36Sopenharmony_ci#endif
289762306a36Sopenharmony_ci
289862306a36Sopenharmony_ci	if (chip->active_ctrl)
289962306a36Sopenharmony_ci		chip->active_ctrl(chip, 1);
290062306a36Sopenharmony_ci
290162306a36Sopenharmony_ci	snd_cs46xx_remove_gameport(chip);
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci	if (chip->amplifier_ctrl)
290462306a36Sopenharmony_ci		chip->amplifier_ctrl(chip, -chip->amplifier); /* force to off */
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	snd_cs46xx_proc_done(chip);
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	snd_cs46xx_hw_stop(chip);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	if (chip->active_ctrl)
291162306a36Sopenharmony_ci		chip->active_ctrl(chip, -chip->amplifier);
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
291462306a36Sopenharmony_ci	if (chip->dsp_spos_instance) {
291562306a36Sopenharmony_ci		cs46xx_dsp_spos_destroy(chip);
291662306a36Sopenharmony_ci		chip->dsp_spos_instance = NULL;
291762306a36Sopenharmony_ci	}
291862306a36Sopenharmony_ci	for (idx = 0; idx < CS46XX_DSP_MODULES; idx++)
291962306a36Sopenharmony_ci		free_module_desc(chip->modules[idx]);
292062306a36Sopenharmony_ci#else
292162306a36Sopenharmony_ci	vfree(chip->ba1);
292262306a36Sopenharmony_ci#endif
292362306a36Sopenharmony_ci}
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci/*
292662306a36Sopenharmony_ci *  initialize chip
292762306a36Sopenharmony_ci */
292862306a36Sopenharmony_cistatic int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
292962306a36Sopenharmony_ci{
293062306a36Sopenharmony_ci	int timeout;
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci	/*
293362306a36Sopenharmony_ci	 *  First, blast the clock control register to zero so that the PLL starts
293462306a36Sopenharmony_ci         *  out in a known state, and blast the master serial port control register
293562306a36Sopenharmony_ci         *  to zero so that the serial ports also start out in a known state.
293662306a36Sopenharmony_ci         */
293762306a36Sopenharmony_ci        snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, 0);
293862306a36Sopenharmony_ci        snd_cs46xx_pokeBA0(chip, BA0_SERMC1, 0);
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	/*
294162306a36Sopenharmony_ci	 *  If we are in AC97 mode, then we must set the part to a host controlled
294262306a36Sopenharmony_ci         *  AC-link.  Otherwise, we won't be able to bring up the link.
294362306a36Sopenharmony_ci         */
294462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
294562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0 |
294662306a36Sopenharmony_ci			   SERACC_TWO_CODECS);	/* 2.00 dual codecs */
294762306a36Sopenharmony_ci	/* snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_2_0); */ /* 2.00 codec */
294862306a36Sopenharmony_ci#else
294962306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERACC, SERACC_HSP | SERACC_CHIP_TYPE_1_03); /* 1.03 codec */
295062306a36Sopenharmony_ci#endif
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci        /*
295362306a36Sopenharmony_ci         *  Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97
295462306a36Sopenharmony_ci         *  spec) and then drive it high.  This is done for non AC97 modes since
295562306a36Sopenharmony_ci         *  there might be logic external to the CS461x that uses the ARST# line
295662306a36Sopenharmony_ci         *  for a reset.
295762306a36Sopenharmony_ci         */
295862306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, 0);
295962306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
296062306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, 0);
296162306a36Sopenharmony_ci#endif
296262306a36Sopenharmony_ci	udelay(50);
296362306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_RSTN);
296462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
296562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_RSTN);
296662306a36Sopenharmony_ci#endif
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	/*
296962306a36Sopenharmony_ci	 *  The first thing we do here is to enable sync generation.  As soon
297062306a36Sopenharmony_ci	 *  as we start receiving bit clock, we'll start producing the SYNC
297162306a36Sopenharmony_ci	 *  signal.
297262306a36Sopenharmony_ci	 */
297362306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN);
297462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
297562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_ESYN | ACCTL_RSTN);
297662306a36Sopenharmony_ci#endif
297762306a36Sopenharmony_ci
297862306a36Sopenharmony_ci	/*
297962306a36Sopenharmony_ci	 *  Now wait for a short while to allow the AC97 part to start
298062306a36Sopenharmony_ci	 *  generating bit clock (so we don't try to start the PLL without an
298162306a36Sopenharmony_ci	 *  input clock).
298262306a36Sopenharmony_ci	 */
298362306a36Sopenharmony_ci	mdelay(10);
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	/*
298662306a36Sopenharmony_ci	 *  Set the serial port timing configuration, so that
298762306a36Sopenharmony_ci	 *  the clock control circuit gets its clock from the correct place.
298862306a36Sopenharmony_ci	 */
298962306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97);
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci	/*
299262306a36Sopenharmony_ci	 *  Write the selected clock control setup to the hardware.  Do not turn on
299362306a36Sopenharmony_ci	 *  SWCE yet (if requested), so that the devices clocked by the output of
299462306a36Sopenharmony_ci	 *  PLL are not clocked until the PLL is stable.
299562306a36Sopenharmony_ci	 */
299662306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ);
299762306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_PLLM, 0x3a);
299862306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_CLKCR2, CLKCR2_PDIVS_8);
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci	/*
300162306a36Sopenharmony_ci	 *  Power up the PLL.
300262306a36Sopenharmony_ci	 */
300362306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP);
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	/*
300662306a36Sopenharmony_ci         *  Wait until the PLL has stabilized.
300762306a36Sopenharmony_ci	 */
300862306a36Sopenharmony_ci	msleep(100);
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	/*
301162306a36Sopenharmony_ci	 *  Turn on clocking of the core so that we can setup the serial ports.
301262306a36Sopenharmony_ci	 */
301362306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, CLKCR1_PLLP | CLKCR1_SWCE);
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	/*
301662306a36Sopenharmony_ci	 * Enable FIFO  Host Bypass
301762306a36Sopenharmony_ci	 */
301862306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERBCF, SERBCF_HBP);
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci	/*
302162306a36Sopenharmony_ci	 *  Fill the serial port FIFOs with silence.
302262306a36Sopenharmony_ci	 */
302362306a36Sopenharmony_ci	snd_cs46xx_clear_serial_FIFOs(chip);
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	/*
302662306a36Sopenharmony_ci	 *  Set the serial port FIFO pointer to the first sample in the FIFO.
302762306a36Sopenharmony_ci	 */
302862306a36Sopenharmony_ci	/* snd_cs46xx_pokeBA0(chip, BA0_SERBSP, 0); */
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci	/*
303162306a36Sopenharmony_ci	 *  Write the serial port configuration to the part.  The master
303262306a36Sopenharmony_ci	 *  enable bit is not set until all other values have been written.
303362306a36Sopenharmony_ci	 */
303462306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN);
303562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN);
303662306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE);
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
304062306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERC7, SERC7_ASDI2EN);
304162306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERC3, 0);
304262306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERC4, 0);
304362306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERC5, 0);
304462306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_SERC6, 1);
304562306a36Sopenharmony_ci#endif
304662306a36Sopenharmony_ci
304762306a36Sopenharmony_ci	mdelay(5);
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci	/*
305162306a36Sopenharmony_ci	 * Wait for the codec ready signal from the AC97 codec.
305262306a36Sopenharmony_ci	 */
305362306a36Sopenharmony_ci	timeout = 150;
305462306a36Sopenharmony_ci	while (timeout-- > 0) {
305562306a36Sopenharmony_ci		/*
305662306a36Sopenharmony_ci		 *  Read the AC97 status register to see if we've seen a CODEC READY
305762306a36Sopenharmony_ci		 *  signal from the AC97 codec.
305862306a36Sopenharmony_ci		 */
305962306a36Sopenharmony_ci		if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY)
306062306a36Sopenharmony_ci			goto ok1;
306162306a36Sopenharmony_ci		msleep(10);
306262306a36Sopenharmony_ci	}
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci	dev_err(chip->card->dev,
306662306a36Sopenharmony_ci		"create - never read codec ready from AC'97\n");
306762306a36Sopenharmony_ci	dev_err(chip->card->dev,
306862306a36Sopenharmony_ci		"it is not probably bug, try to use CS4236 driver\n");
306962306a36Sopenharmony_ci	return -EIO;
307062306a36Sopenharmony_ci ok1:
307162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
307262306a36Sopenharmony_ci	{
307362306a36Sopenharmony_ci		int count;
307462306a36Sopenharmony_ci		for (count = 0; count < 150; count++) {
307562306a36Sopenharmony_ci			/* First, we want to wait for a short time. */
307662306a36Sopenharmony_ci			udelay(25);
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci			if (snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY)
307962306a36Sopenharmony_ci				break;
308062306a36Sopenharmony_ci		}
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci		/*
308362306a36Sopenharmony_ci		 *  Make sure CODEC is READY.
308462306a36Sopenharmony_ci		 */
308562306a36Sopenharmony_ci		if (!(snd_cs46xx_peekBA0(chip, BA0_ACSTS2) & ACSTS_CRDY))
308662306a36Sopenharmony_ci			dev_dbg(chip->card->dev,
308762306a36Sopenharmony_ci				"never read card ready from secondary AC'97\n");
308862306a36Sopenharmony_ci	}
308962306a36Sopenharmony_ci#endif
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	/*
309262306a36Sopenharmony_ci	 *  Assert the vaid frame signal so that we can start sending commands
309362306a36Sopenharmony_ci	 *  to the AC97 codec.
309462306a36Sopenharmony_ci	 */
309562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
309662306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
309762306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACCTL2, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN);
309862306a36Sopenharmony_ci#endif
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci	/*
310262306a36Sopenharmony_ci	 *  Wait until we've sampled input slots 3 and 4 as valid, meaning that
310362306a36Sopenharmony_ci	 *  the codec is pumping ADC data across the AC-link.
310462306a36Sopenharmony_ci	 */
310562306a36Sopenharmony_ci	timeout = 150;
310662306a36Sopenharmony_ci	while (timeout-- > 0) {
310762306a36Sopenharmony_ci		/*
310862306a36Sopenharmony_ci		 *  Read the input slot valid register and see if input slots 3 and
310962306a36Sopenharmony_ci		 *  4 are valid yet.
311062306a36Sopenharmony_ci		 */
311162306a36Sopenharmony_ci		if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4))
311262306a36Sopenharmony_ci			goto ok2;
311362306a36Sopenharmony_ci		msleep(10);
311462306a36Sopenharmony_ci	}
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci#ifndef CONFIG_SND_CS46XX_NEW_DSP
311762306a36Sopenharmony_ci	dev_err(chip->card->dev,
311862306a36Sopenharmony_ci		"create - never read ISV3 & ISV4 from AC'97\n");
311962306a36Sopenharmony_ci	return -EIO;
312062306a36Sopenharmony_ci#else
312162306a36Sopenharmony_ci	/* This may happen on a cold boot with a Terratec SiXPack 5.1.
312262306a36Sopenharmony_ci	   Reloading the driver may help, if there's other soundcards
312362306a36Sopenharmony_ci	   with the same problem I would like to know. (Benny) */
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	dev_err(chip->card->dev, "never read ISV3 & ISV4 from AC'97\n");
312662306a36Sopenharmony_ci	dev_err(chip->card->dev,
312762306a36Sopenharmony_ci		"Try reloading the ALSA driver, if you find something\n");
312862306a36Sopenharmony_ci	dev_err(chip->card->dev,
312962306a36Sopenharmony_ci		"broken or not working on your soundcard upon\n");
313062306a36Sopenharmony_ci	dev_err(chip->card->dev,
313162306a36Sopenharmony_ci		"this message please report to alsa-devel@alsa-project.org\n");
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	return -EIO;
313462306a36Sopenharmony_ci#endif
313562306a36Sopenharmony_ci ok2:
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci	/*
313862306a36Sopenharmony_ci	 *  Now, assert valid frame and the slot 3 and 4 valid bits.  This will
313962306a36Sopenharmony_ci	 *  commense the transfer of digital audio data to the AC97 codec.
314062306a36Sopenharmony_ci	 */
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4);
314362306a36Sopenharmony_ci
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci	/*
314662306a36Sopenharmony_ci	 *  Power down the DAC and ADC.  We will power them up (if) when we need
314762306a36Sopenharmony_ci	 *  them.
314862306a36Sopenharmony_ci	 */
314962306a36Sopenharmony_ci	/* snd_cs46xx_pokeBA0(chip, BA0_AC97_POWERDOWN, 0x300); */
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_ci	/*
315262306a36Sopenharmony_ci	 *  Turn off the Processor by turning off the software clock enable flag in
315362306a36Sopenharmony_ci	 *  the clock control register.
315462306a36Sopenharmony_ci	 */
315562306a36Sopenharmony_ci	/* tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1) & ~CLKCR1_SWCE; */
315662306a36Sopenharmony_ci	/* snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp); */
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	return 0;
315962306a36Sopenharmony_ci}
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci/*
316262306a36Sopenharmony_ci *  start and load DSP
316362306a36Sopenharmony_ci */
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_cistatic void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
316662306a36Sopenharmony_ci{
316762306a36Sopenharmony_ci	unsigned int tmp;
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
317262306a36Sopenharmony_ci	tmp &= ~0x0000f03f;
317362306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt enable */
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_CIE);
317662306a36Sopenharmony_ci	tmp &= ~0x0000003f;
317762306a36Sopenharmony_ci	tmp |=  0x00000001;
317862306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt enable */
317962306a36Sopenharmony_ci}
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ciint snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
318262306a36Sopenharmony_ci{
318362306a36Sopenharmony_ci	unsigned int tmp;
318462306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
318562306a36Sopenharmony_ci	int i;
318662306a36Sopenharmony_ci#endif
318762306a36Sopenharmony_ci	int err;
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci	/*
319062306a36Sopenharmony_ci	 *  Reset the processor.
319162306a36Sopenharmony_ci	 */
319262306a36Sopenharmony_ci	snd_cs46xx_reset(chip);
319362306a36Sopenharmony_ci	/*
319462306a36Sopenharmony_ci	 *  Download the image to the processor.
319562306a36Sopenharmony_ci	 */
319662306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
319762306a36Sopenharmony_ci	for (i = 0; i < CS46XX_DSP_MODULES; i++) {
319862306a36Sopenharmony_ci		err = load_firmware(chip, &chip->modules[i], module_names[i]);
319962306a36Sopenharmony_ci		if (err < 0) {
320062306a36Sopenharmony_ci			dev_err(chip->card->dev, "firmware load error [%s]\n",
320162306a36Sopenharmony_ci				   module_names[i]);
320262306a36Sopenharmony_ci			return err;
320362306a36Sopenharmony_ci		}
320462306a36Sopenharmony_ci		err = cs46xx_dsp_load_module(chip, chip->modules[i]);
320562306a36Sopenharmony_ci		if (err < 0) {
320662306a36Sopenharmony_ci			dev_err(chip->card->dev, "image download error [%s]\n",
320762306a36Sopenharmony_ci				   module_names[i]);
320862306a36Sopenharmony_ci			return err;
320962306a36Sopenharmony_ci		}
321062306a36Sopenharmony_ci	}
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	if (cs46xx_dsp_scb_and_task_init(chip) < 0)
321362306a36Sopenharmony_ci		return -EIO;
321462306a36Sopenharmony_ci#else
321562306a36Sopenharmony_ci	err = load_firmware(chip);
321662306a36Sopenharmony_ci	if (err < 0)
321762306a36Sopenharmony_ci		return err;
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci	/* old image */
322062306a36Sopenharmony_ci	err = snd_cs46xx_download_image(chip);
322162306a36Sopenharmony_ci	if (err < 0) {
322262306a36Sopenharmony_ci		dev_err(chip->card->dev, "image download error\n");
322362306a36Sopenharmony_ci		return err;
322462306a36Sopenharmony_ci	}
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci	/*
322762306a36Sopenharmony_ci         *  Stop playback DMA.
322862306a36Sopenharmony_ci	 */
322962306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_PCTL);
323062306a36Sopenharmony_ci	chip->play_ctl = tmp & 0xffff0000;
323162306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PCTL, tmp & 0x0000ffff);
323262306a36Sopenharmony_ci#endif
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ci	/*
323562306a36Sopenharmony_ci         *  Stop capture DMA.
323662306a36Sopenharmony_ci	 */
323762306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_CCTL);
323862306a36Sopenharmony_ci	chip->capt.ctl = tmp & 0x0000ffff;
323962306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci	mdelay(5);
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	snd_cs46xx_set_play_sample_rate(chip, 8000);
324462306a36Sopenharmony_ci	snd_cs46xx_set_capture_sample_rate(chip, 8000);
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci	snd_cs46xx_proc_start(chip);
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci	cs46xx_enable_stream_irqs(chip);
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_ci#ifndef CONFIG_SND_CS46XX_NEW_DSP
325162306a36Sopenharmony_ci	/* set the attenuation to 0dB */
325262306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_PVOL, 0x80008000);
325362306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000);
325462306a36Sopenharmony_ci#endif
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	return 0;
325762306a36Sopenharmony_ci}
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci/*
326162306a36Sopenharmony_ci *	AMP control - null AMP
326262306a36Sopenharmony_ci */
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_cistatic void amp_none(struct snd_cs46xx *chip, int change)
326562306a36Sopenharmony_ci{
326662306a36Sopenharmony_ci}
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
326962306a36Sopenharmony_cistatic int voyetra_setup_eapd_slot(struct snd_cs46xx *chip)
327062306a36Sopenharmony_ci{
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	u32 idx, valid_slots,tmp,powerdown = 0;
327362306a36Sopenharmony_ci	u16 modem_power,pin_config,logic_type;
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "cs46xx_setup_eapd_slot()+\n");
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_ci	/*
327862306a36Sopenharmony_ci	 *  See if the devices are powered down.  If so, we must power them up first
327962306a36Sopenharmony_ci	 *  or they will not respond.
328062306a36Sopenharmony_ci	 */
328162306a36Sopenharmony_ci	tmp = snd_cs46xx_peekBA0(chip, BA0_CLKCR1);
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	if (!(tmp & CLKCR1_SWCE)) {
328462306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp | CLKCR1_SWCE);
328562306a36Sopenharmony_ci		powerdown = 1;
328662306a36Sopenharmony_ci	}
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_ci	/*
328962306a36Sopenharmony_ci	 * Clear PRA.  The Bonzo chip will be used for GPIO not for modem
329062306a36Sopenharmony_ci	 * stuff.
329162306a36Sopenharmony_ci	 */
329262306a36Sopenharmony_ci	if(chip->nr_ac97_codecs != 2) {
329362306a36Sopenharmony_ci		dev_err(chip->card->dev,
329462306a36Sopenharmony_ci			"cs46xx_setup_eapd_slot() - no secondary codec configured\n");
329562306a36Sopenharmony_ci		return -EINVAL;
329662306a36Sopenharmony_ci	}
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	modem_power = snd_cs46xx_codec_read (chip,
329962306a36Sopenharmony_ci					     AC97_EXTENDED_MSTATUS,
330062306a36Sopenharmony_ci					     CS46XX_SECONDARY_CODEC_INDEX);
330162306a36Sopenharmony_ci	modem_power &=0xFEFF;
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci	snd_cs46xx_codec_write(chip,
330462306a36Sopenharmony_ci			       AC97_EXTENDED_MSTATUS, modem_power,
330562306a36Sopenharmony_ci			       CS46XX_SECONDARY_CODEC_INDEX);
330662306a36Sopenharmony_ci
330762306a36Sopenharmony_ci	/*
330862306a36Sopenharmony_ci	 * Set GPIO pin's 7 and 8 so that they are configured for output.
330962306a36Sopenharmony_ci	 */
331062306a36Sopenharmony_ci	pin_config = snd_cs46xx_codec_read (chip,
331162306a36Sopenharmony_ci					    AC97_GPIO_CFG,
331262306a36Sopenharmony_ci					    CS46XX_SECONDARY_CODEC_INDEX);
331362306a36Sopenharmony_ci	pin_config &=0x27F;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	snd_cs46xx_codec_write(chip,
331662306a36Sopenharmony_ci			       AC97_GPIO_CFG, pin_config,
331762306a36Sopenharmony_ci			       CS46XX_SECONDARY_CODEC_INDEX);
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci	/*
332062306a36Sopenharmony_ci	 * Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic.
332162306a36Sopenharmony_ci	 */
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	logic_type = snd_cs46xx_codec_read(chip, AC97_GPIO_POLARITY,
332462306a36Sopenharmony_ci					   CS46XX_SECONDARY_CODEC_INDEX);
332562306a36Sopenharmony_ci	logic_type &=0x27F;
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci	snd_cs46xx_codec_write (chip, AC97_GPIO_POLARITY, logic_type,
332862306a36Sopenharmony_ci				CS46XX_SECONDARY_CODEC_INDEX);
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
333162306a36Sopenharmony_ci	valid_slots |= 0x200;
333262306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_ci	if ( cs46xx_wait_for_fifo(chip,1) ) {
333562306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "FIFO is busy\n");
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci	  return -EINVAL;
333862306a36Sopenharmony_ci	}
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci	/*
334162306a36Sopenharmony_ci	 * Fill slots 12 with the correct value for the GPIO pins.
334262306a36Sopenharmony_ci	 */
334362306a36Sopenharmony_ci	for(idx = 0x90; idx <= 0x9F; idx++) {
334462306a36Sopenharmony_ci		/*
334562306a36Sopenharmony_ci		 * Initialize the fifo so that bits 7 and 8 are on.
334662306a36Sopenharmony_ci		 *
334762306a36Sopenharmony_ci		 * Remember that the GPIO pins in bonzo are shifted by 4 bits to
334862306a36Sopenharmony_ci		 * the left.  0x1800 corresponds to bits 7 and 8.
334962306a36Sopenharmony_ci		 */
335062306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0x1800);
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci		/*
335362306a36Sopenharmony_ci		 * Wait for command to complete
335462306a36Sopenharmony_ci		 */
335562306a36Sopenharmony_ci		if ( cs46xx_wait_for_fifo(chip,200) ) {
335662306a36Sopenharmony_ci			dev_dbg(chip->card->dev,
335762306a36Sopenharmony_ci				"failed waiting for FIFO at addr (%02X)\n",
335862306a36Sopenharmony_ci				idx);
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci			return -EINVAL;
336162306a36Sopenharmony_ci		}
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci		/*
336462306a36Sopenharmony_ci		 * Write the serial port FIFO index.
336562306a36Sopenharmony_ci		 */
336662306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci		/*
336962306a36Sopenharmony_ci		 * Tell the serial port to load the new value into the FIFO location.
337062306a36Sopenharmony_ci		 */
337162306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
337262306a36Sopenharmony_ci	}
337362306a36Sopenharmony_ci
337462306a36Sopenharmony_ci	/* wait for last command to complete */
337562306a36Sopenharmony_ci	cs46xx_wait_for_fifo(chip,200);
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	/*
337862306a36Sopenharmony_ci	 *  Now, if we powered up the devices, then power them back down again.
337962306a36Sopenharmony_ci	 *  This is kinda ugly, but should never happen.
338062306a36Sopenharmony_ci	 */
338162306a36Sopenharmony_ci	if (powerdown)
338262306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	return 0;
338562306a36Sopenharmony_ci}
338662306a36Sopenharmony_ci#endif
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_ci/*
338962306a36Sopenharmony_ci *	Crystal EAPD mode
339062306a36Sopenharmony_ci */
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_cistatic void amp_voyetra(struct snd_cs46xx *chip, int change)
339362306a36Sopenharmony_ci{
339462306a36Sopenharmony_ci	/* Manage the EAPD bit on the Crystal 4297
339562306a36Sopenharmony_ci	   and the Analog AD1885 */
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
339862306a36Sopenharmony_ci	int old = chip->amplifier;
339962306a36Sopenharmony_ci#endif
340062306a36Sopenharmony_ci	int oval, val;
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	chip->amplifier += change;
340362306a36Sopenharmony_ci	oval = snd_cs46xx_codec_read(chip, AC97_POWERDOWN,
340462306a36Sopenharmony_ci				     CS46XX_PRIMARY_CODEC_INDEX);
340562306a36Sopenharmony_ci	val = oval;
340662306a36Sopenharmony_ci	if (chip->amplifier) {
340762306a36Sopenharmony_ci		/* Turn the EAPD amp on */
340862306a36Sopenharmony_ci		val |= 0x8000;
340962306a36Sopenharmony_ci	} else {
341062306a36Sopenharmony_ci		/* Turn the EAPD amp off */
341162306a36Sopenharmony_ci		val &= ~0x8000;
341262306a36Sopenharmony_ci	}
341362306a36Sopenharmony_ci	if (val != oval) {
341462306a36Sopenharmony_ci		snd_cs46xx_codec_write(chip, AC97_POWERDOWN, val,
341562306a36Sopenharmony_ci				       CS46XX_PRIMARY_CODEC_INDEX);
341662306a36Sopenharmony_ci		if (chip->eapd_switch)
341762306a36Sopenharmony_ci			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
341862306a36Sopenharmony_ci				       &chip->eapd_switch->id);
341962306a36Sopenharmony_ci	}
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
342262306a36Sopenharmony_ci	if (chip->amplifier && !old) {
342362306a36Sopenharmony_ci		voyetra_setup_eapd_slot(chip);
342462306a36Sopenharmony_ci	}
342562306a36Sopenharmony_ci#endif
342662306a36Sopenharmony_ci}
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_cistatic void hercules_init(struct snd_cs46xx *chip)
342962306a36Sopenharmony_ci{
343062306a36Sopenharmony_ci	/* default: AMP off, and SPDIF input optical */
343162306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
343262306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
343362306a36Sopenharmony_ci}
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci/*
343762306a36Sopenharmony_ci *	Game Theatre XP card - EGPIO[2] is used to enable the external amp.
343862306a36Sopenharmony_ci */
343962306a36Sopenharmony_cistatic void amp_hercules(struct snd_cs46xx *chip, int change)
344062306a36Sopenharmony_ci{
344162306a36Sopenharmony_ci	int old = chip->amplifier;
344262306a36Sopenharmony_ci	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
344362306a36Sopenharmony_ci	int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci	chip->amplifier += change;
344662306a36Sopenharmony_ci	if (chip->amplifier && !old) {
344762306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "Hercules amplifier ON\n");
344862306a36Sopenharmony_ci
344962306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,
345062306a36Sopenharmony_ci				   EGPIODR_GPOE2 | val1);     /* enable EGPIO2 output */
345162306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR,
345262306a36Sopenharmony_ci				   EGPIOPTR_GPPT2 | val2);   /* open-drain on output */
345362306a36Sopenharmony_ci	} else if (old && !chip->amplifier) {
345462306a36Sopenharmony_ci		dev_dbg(chip->card->dev, "Hercules amplifier OFF\n");
345562306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,  val1 & ~EGPIODR_GPOE2); /* disable */
345662306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT2); /* disable */
345762306a36Sopenharmony_ci	}
345862306a36Sopenharmony_ci}
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_cistatic void voyetra_mixer_init (struct snd_cs46xx *chip)
346162306a36Sopenharmony_ci{
346262306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "initializing Voyetra mixer\n");
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_ci	/* Enable SPDIF out */
346562306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
346662306a36Sopenharmony_ci	snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
346762306a36Sopenharmony_ci}
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_cistatic void hercules_mixer_init (struct snd_cs46xx *chip)
347062306a36Sopenharmony_ci{
347162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
347262306a36Sopenharmony_ci	unsigned int idx;
347362306a36Sopenharmony_ci	int err;
347462306a36Sopenharmony_ci	struct snd_card *card = chip->card;
347562306a36Sopenharmony_ci#endif
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci	/* set EGPIO to default */
347862306a36Sopenharmony_ci	hercules_init(chip);
347962306a36Sopenharmony_ci
348062306a36Sopenharmony_ci	dev_dbg(chip->card->dev, "initializing Hercules mixer\n");
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
348362306a36Sopenharmony_ci	if (chip->in_suspend)
348462306a36Sopenharmony_ci		return;
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	for (idx = 0 ; idx < ARRAY_SIZE(snd_hercules_controls); idx++) {
348762306a36Sopenharmony_ci		struct snd_kcontrol *kctl;
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci		kctl = snd_ctl_new1(&snd_hercules_controls[idx], chip);
349062306a36Sopenharmony_ci		err = snd_ctl_add(card, kctl);
349162306a36Sopenharmony_ci		if (err < 0) {
349262306a36Sopenharmony_ci			dev_err(card->dev,
349362306a36Sopenharmony_ci				"failed to initialize Hercules mixer (%d)\n",
349462306a36Sopenharmony_ci				err);
349562306a36Sopenharmony_ci			break;
349662306a36Sopenharmony_ci		}
349762306a36Sopenharmony_ci	}
349862306a36Sopenharmony_ci#endif
349962306a36Sopenharmony_ci}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci#if 0
350362306a36Sopenharmony_ci/*
350462306a36Sopenharmony_ci *	Untested
350562306a36Sopenharmony_ci */
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_cistatic void amp_voyetra_4294(struct snd_cs46xx *chip, int change)
350862306a36Sopenharmony_ci{
350962306a36Sopenharmony_ci	chip->amplifier += change;
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	if (chip->amplifier) {
351262306a36Sopenharmony_ci		/* Switch the GPIO pins 7 and 8 to open drain */
351362306a36Sopenharmony_ci		snd_cs46xx_codec_write(chip, 0x4C,
351462306a36Sopenharmony_ci				       snd_cs46xx_codec_read(chip, 0x4C) & 0xFE7F);
351562306a36Sopenharmony_ci		snd_cs46xx_codec_write(chip, 0x4E,
351662306a36Sopenharmony_ci				       snd_cs46xx_codec_read(chip, 0x4E) | 0x0180);
351762306a36Sopenharmony_ci		/* Now wake the AMP (this might be backwards) */
351862306a36Sopenharmony_ci		snd_cs46xx_codec_write(chip, 0x54,
351962306a36Sopenharmony_ci				       snd_cs46xx_codec_read(chip, 0x54) & ~0x0180);
352062306a36Sopenharmony_ci	} else {
352162306a36Sopenharmony_ci		snd_cs46xx_codec_write(chip, 0x54,
352262306a36Sopenharmony_ci				       snd_cs46xx_codec_read(chip, 0x54) | 0x0180);
352362306a36Sopenharmony_ci	}
352462306a36Sopenharmony_ci}
352562306a36Sopenharmony_ci#endif
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci/*
352962306a36Sopenharmony_ci *	Handle the CLKRUN on a thinkpad. We must disable CLKRUN support
353062306a36Sopenharmony_ci *	whenever we need to beat on the chip.
353162306a36Sopenharmony_ci *
353262306a36Sopenharmony_ci *	The original idea and code for this hack comes from David Kaiser at
353362306a36Sopenharmony_ci *	Linuxcare. Perhaps one day Crystal will document their chips well
353462306a36Sopenharmony_ci *	enough to make them useful.
353562306a36Sopenharmony_ci */
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_cistatic void clkrun_hack(struct snd_cs46xx *chip, int change)
353862306a36Sopenharmony_ci{
353962306a36Sopenharmony_ci	u16 control, nval;
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_ci	if (!chip->acpi_port)
354262306a36Sopenharmony_ci		return;
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_ci	chip->amplifier += change;
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci	/* Read ACPI port */
354762306a36Sopenharmony_ci	nval = control = inw(chip->acpi_port + 0x10);
354862306a36Sopenharmony_ci
354962306a36Sopenharmony_ci	/* Flip CLKRUN off while running */
355062306a36Sopenharmony_ci	if (! chip->amplifier)
355162306a36Sopenharmony_ci		nval |= 0x2000;
355262306a36Sopenharmony_ci	else
355362306a36Sopenharmony_ci		nval &= ~0x2000;
355462306a36Sopenharmony_ci	if (nval != control)
355562306a36Sopenharmony_ci		outw(nval, chip->acpi_port + 0x10);
355662306a36Sopenharmony_ci}
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci
355962306a36Sopenharmony_ci/*
356062306a36Sopenharmony_ci * detect intel piix4
356162306a36Sopenharmony_ci */
356262306a36Sopenharmony_cistatic void clkrun_init(struct snd_cs46xx *chip)
356362306a36Sopenharmony_ci{
356462306a36Sopenharmony_ci	struct pci_dev *pdev;
356562306a36Sopenharmony_ci	u8 pp;
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci	chip->acpi_port = 0;
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci	pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
357062306a36Sopenharmony_ci		PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
357162306a36Sopenharmony_ci	if (pdev == NULL)
357262306a36Sopenharmony_ci		return;		/* Not a thinkpad thats for sure */
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci	/* Find the control port */
357562306a36Sopenharmony_ci	pci_read_config_byte(pdev, 0x41, &pp);
357662306a36Sopenharmony_ci	chip->acpi_port = pp << 8;
357762306a36Sopenharmony_ci	pci_dev_put(pdev);
357862306a36Sopenharmony_ci}
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ci
358162306a36Sopenharmony_ci/*
358262306a36Sopenharmony_ci * Card subid table
358362306a36Sopenharmony_ci */
358462306a36Sopenharmony_ci
358562306a36Sopenharmony_cistruct cs_card_type
358662306a36Sopenharmony_ci{
358762306a36Sopenharmony_ci	u16 vendor;
358862306a36Sopenharmony_ci	u16 id;
358962306a36Sopenharmony_ci	char *name;
359062306a36Sopenharmony_ci	void (*init)(struct snd_cs46xx *);
359162306a36Sopenharmony_ci	void (*amp)(struct snd_cs46xx *, int);
359262306a36Sopenharmony_ci	void (*active)(struct snd_cs46xx *, int);
359362306a36Sopenharmony_ci	void (*mixer_init)(struct snd_cs46xx *);
359462306a36Sopenharmony_ci};
359562306a36Sopenharmony_ci
359662306a36Sopenharmony_cistatic struct cs_card_type cards[] = {
359762306a36Sopenharmony_ci	{
359862306a36Sopenharmony_ci		.vendor = 0x1489,
359962306a36Sopenharmony_ci		.id = 0x7001,
360062306a36Sopenharmony_ci		.name = "Genius Soundmaker 128 value",
360162306a36Sopenharmony_ci		/* nothing special */
360262306a36Sopenharmony_ci	},
360362306a36Sopenharmony_ci	{
360462306a36Sopenharmony_ci		.vendor = 0x5053,
360562306a36Sopenharmony_ci		.id = 0x3357,
360662306a36Sopenharmony_ci		.name = "Voyetra",
360762306a36Sopenharmony_ci		.amp = amp_voyetra,
360862306a36Sopenharmony_ci		.mixer_init = voyetra_mixer_init,
360962306a36Sopenharmony_ci	},
361062306a36Sopenharmony_ci	{
361162306a36Sopenharmony_ci		.vendor = 0x1071,
361262306a36Sopenharmony_ci		.id = 0x6003,
361362306a36Sopenharmony_ci		.name = "Mitac MI6020/21",
361462306a36Sopenharmony_ci		.amp = amp_voyetra,
361562306a36Sopenharmony_ci	},
361662306a36Sopenharmony_ci	/* Hercules Game Theatre XP */
361762306a36Sopenharmony_ci	{
361862306a36Sopenharmony_ci		.vendor = 0x14af, /* Guillemot Corporation */
361962306a36Sopenharmony_ci		.id = 0x0050,
362062306a36Sopenharmony_ci		.name = "Hercules Game Theatre XP",
362162306a36Sopenharmony_ci		.amp = amp_hercules,
362262306a36Sopenharmony_ci		.mixer_init = hercules_mixer_init,
362362306a36Sopenharmony_ci	},
362462306a36Sopenharmony_ci	{
362562306a36Sopenharmony_ci		.vendor = 0x1681,
362662306a36Sopenharmony_ci		.id = 0x0050,
362762306a36Sopenharmony_ci		.name = "Hercules Game Theatre XP",
362862306a36Sopenharmony_ci		.amp = amp_hercules,
362962306a36Sopenharmony_ci		.mixer_init = hercules_mixer_init,
363062306a36Sopenharmony_ci	},
363162306a36Sopenharmony_ci	{
363262306a36Sopenharmony_ci		.vendor = 0x1681,
363362306a36Sopenharmony_ci		.id = 0x0051,
363462306a36Sopenharmony_ci		.name = "Hercules Game Theatre XP",
363562306a36Sopenharmony_ci		.amp = amp_hercules,
363662306a36Sopenharmony_ci		.mixer_init = hercules_mixer_init,
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_ci	},
363962306a36Sopenharmony_ci	{
364062306a36Sopenharmony_ci		.vendor = 0x1681,
364162306a36Sopenharmony_ci		.id = 0x0052,
364262306a36Sopenharmony_ci		.name = "Hercules Game Theatre XP",
364362306a36Sopenharmony_ci		.amp = amp_hercules,
364462306a36Sopenharmony_ci		.mixer_init = hercules_mixer_init,
364562306a36Sopenharmony_ci	},
364662306a36Sopenharmony_ci	{
364762306a36Sopenharmony_ci		.vendor = 0x1681,
364862306a36Sopenharmony_ci		.id = 0x0053,
364962306a36Sopenharmony_ci		.name = "Hercules Game Theatre XP",
365062306a36Sopenharmony_ci		.amp = amp_hercules,
365162306a36Sopenharmony_ci		.mixer_init = hercules_mixer_init,
365262306a36Sopenharmony_ci	},
365362306a36Sopenharmony_ci	{
365462306a36Sopenharmony_ci		.vendor = 0x1681,
365562306a36Sopenharmony_ci		.id = 0x0054,
365662306a36Sopenharmony_ci		.name = "Hercules Game Theatre XP",
365762306a36Sopenharmony_ci		.amp = amp_hercules,
365862306a36Sopenharmony_ci		.mixer_init = hercules_mixer_init,
365962306a36Sopenharmony_ci	},
366062306a36Sopenharmony_ci	/* Herculess Fortissimo */
366162306a36Sopenharmony_ci	{
366262306a36Sopenharmony_ci		.vendor = 0x1681,
366362306a36Sopenharmony_ci		.id = 0xa010,
366462306a36Sopenharmony_ci		.name = "Hercules Gamesurround Fortissimo II",
366562306a36Sopenharmony_ci	},
366662306a36Sopenharmony_ci	{
366762306a36Sopenharmony_ci		.vendor = 0x1681,
366862306a36Sopenharmony_ci		.id = 0xa011,
366962306a36Sopenharmony_ci		.name = "Hercules Gamesurround Fortissimo III 7.1",
367062306a36Sopenharmony_ci	},
367162306a36Sopenharmony_ci	/* Teratec */
367262306a36Sopenharmony_ci	{
367362306a36Sopenharmony_ci		.vendor = 0x153b,
367462306a36Sopenharmony_ci		.id = 0x112e,
367562306a36Sopenharmony_ci		.name = "Terratec DMX XFire 1024",
367662306a36Sopenharmony_ci	},
367762306a36Sopenharmony_ci	{
367862306a36Sopenharmony_ci		.vendor = 0x153b,
367962306a36Sopenharmony_ci		.id = 0x1136,
368062306a36Sopenharmony_ci		.name = "Terratec SiXPack 5.1",
368162306a36Sopenharmony_ci	},
368262306a36Sopenharmony_ci	/* Not sure if the 570 needs the clkrun hack */
368362306a36Sopenharmony_ci	{
368462306a36Sopenharmony_ci		.vendor = PCI_VENDOR_ID_IBM,
368562306a36Sopenharmony_ci		.id = 0x0132,
368662306a36Sopenharmony_ci		.name = "Thinkpad 570",
368762306a36Sopenharmony_ci		.init = clkrun_init,
368862306a36Sopenharmony_ci		.active = clkrun_hack,
368962306a36Sopenharmony_ci	},
369062306a36Sopenharmony_ci	{
369162306a36Sopenharmony_ci		.vendor = PCI_VENDOR_ID_IBM,
369262306a36Sopenharmony_ci		.id = 0x0153,
369362306a36Sopenharmony_ci		.name = "Thinkpad 600X/A20/T20",
369462306a36Sopenharmony_ci		.init = clkrun_init,
369562306a36Sopenharmony_ci		.active = clkrun_hack,
369662306a36Sopenharmony_ci	},
369762306a36Sopenharmony_ci	{
369862306a36Sopenharmony_ci		.vendor = PCI_VENDOR_ID_IBM,
369962306a36Sopenharmony_ci		.id = 0x1010,
370062306a36Sopenharmony_ci		.name = "Thinkpad 600E (unsupported)",
370162306a36Sopenharmony_ci	},
370262306a36Sopenharmony_ci	{} /* terminator */
370362306a36Sopenharmony_ci};
370462306a36Sopenharmony_ci
370562306a36Sopenharmony_ci
370662306a36Sopenharmony_ci/*
370762306a36Sopenharmony_ci * APM support
370862306a36Sopenharmony_ci */
370962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
371062306a36Sopenharmony_cistatic const unsigned int saved_regs[] = {
371162306a36Sopenharmony_ci	BA0_ACOSV,
371262306a36Sopenharmony_ci	/*BA0_ASER_FADDR,*/
371362306a36Sopenharmony_ci	BA0_ASER_MASTER,
371462306a36Sopenharmony_ci	BA1_PVOL,
371562306a36Sopenharmony_ci	BA1_CVOL,
371662306a36Sopenharmony_ci};
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_cistatic int snd_cs46xx_suspend(struct device *dev)
371962306a36Sopenharmony_ci{
372062306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
372162306a36Sopenharmony_ci	struct snd_cs46xx *chip = card->private_data;
372262306a36Sopenharmony_ci	int i, amp_saved;
372362306a36Sopenharmony_ci
372462306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
372562306a36Sopenharmony_ci	chip->in_suspend = 1;
372662306a36Sopenharmony_ci	// chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
372762306a36Sopenharmony_ci	// chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
373062306a36Sopenharmony_ci	snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	/* save some registers */
373362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
373462306a36Sopenharmony_ci		chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]);
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	amp_saved = chip->amplifier;
373762306a36Sopenharmony_ci	/* turn off amp */
373862306a36Sopenharmony_ci	chip->amplifier_ctrl(chip, -chip->amplifier);
373962306a36Sopenharmony_ci	snd_cs46xx_hw_stop(chip);
374062306a36Sopenharmony_ci	/* disable CLKRUN */
374162306a36Sopenharmony_ci	chip->active_ctrl(chip, -chip->amplifier);
374262306a36Sopenharmony_ci	chip->amplifier = amp_saved; /* restore the status */
374362306a36Sopenharmony_ci	return 0;
374462306a36Sopenharmony_ci}
374562306a36Sopenharmony_ci
374662306a36Sopenharmony_cistatic int snd_cs46xx_resume(struct device *dev)
374762306a36Sopenharmony_ci{
374862306a36Sopenharmony_ci	struct snd_card *card = dev_get_drvdata(dev);
374962306a36Sopenharmony_ci	struct snd_cs46xx *chip = card->private_data;
375062306a36Sopenharmony_ci	int amp_saved;
375162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
375262306a36Sopenharmony_ci	int i;
375362306a36Sopenharmony_ci#endif
375462306a36Sopenharmony_ci	unsigned int tmp;
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci	amp_saved = chip->amplifier;
375762306a36Sopenharmony_ci	chip->amplifier = 0;
375862306a36Sopenharmony_ci	chip->active_ctrl(chip, 1); /* force to on */
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci	snd_cs46xx_chip_init(chip);
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci	snd_cs46xx_reset(chip);
376362306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
376462306a36Sopenharmony_ci	cs46xx_dsp_resume(chip);
376562306a36Sopenharmony_ci	/* restore some registers */
376662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
376762306a36Sopenharmony_ci		snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]);
376862306a36Sopenharmony_ci#else
376962306a36Sopenharmony_ci	snd_cs46xx_download_image(chip);
377062306a36Sopenharmony_ci#endif
377162306a36Sopenharmony_ci
377262306a36Sopenharmony_ci#if 0
377362306a36Sopenharmony_ci	snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE,
377462306a36Sopenharmony_ci			       chip->ac97_general_purpose);
377562306a36Sopenharmony_ci	snd_cs46xx_codec_write(chip, AC97_POWER_CONTROL,
377662306a36Sopenharmony_ci			       chip->ac97_powerdown);
377762306a36Sopenharmony_ci	mdelay(10);
377862306a36Sopenharmony_ci	snd_cs46xx_codec_write(chip, BA0_AC97_POWERDOWN,
377962306a36Sopenharmony_ci			       chip->ac97_powerdown);
378062306a36Sopenharmony_ci	mdelay(5);
378162306a36Sopenharmony_ci#endif
378262306a36Sopenharmony_ci
378362306a36Sopenharmony_ci	snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
378462306a36Sopenharmony_ci	snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	/*
378762306a36Sopenharmony_ci         *  Stop capture DMA.
378862306a36Sopenharmony_ci	 */
378962306a36Sopenharmony_ci	tmp = snd_cs46xx_peek(chip, BA1_CCTL);
379062306a36Sopenharmony_ci	chip->capt.ctl = tmp & 0x0000ffff;
379162306a36Sopenharmony_ci	snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
379262306a36Sopenharmony_ci
379362306a36Sopenharmony_ci	mdelay(5);
379462306a36Sopenharmony_ci
379562306a36Sopenharmony_ci	/* reset playback/capture */
379662306a36Sopenharmony_ci	snd_cs46xx_set_play_sample_rate(chip, 8000);
379762306a36Sopenharmony_ci	snd_cs46xx_set_capture_sample_rate(chip, 8000);
379862306a36Sopenharmony_ci	snd_cs46xx_proc_start(chip);
379962306a36Sopenharmony_ci
380062306a36Sopenharmony_ci	cs46xx_enable_stream_irqs(chip);
380162306a36Sopenharmony_ci
380262306a36Sopenharmony_ci	if (amp_saved)
380362306a36Sopenharmony_ci		chip->amplifier_ctrl(chip, 1); /* turn amp on */
380462306a36Sopenharmony_ci	else
380562306a36Sopenharmony_ci		chip->active_ctrl(chip, -1); /* disable CLKRUN */
380662306a36Sopenharmony_ci	chip->amplifier = amp_saved;
380762306a36Sopenharmony_ci	chip->in_suspend = 0;
380862306a36Sopenharmony_ci	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
380962306a36Sopenharmony_ci	return 0;
381062306a36Sopenharmony_ci}
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ciSIMPLE_DEV_PM_OPS(snd_cs46xx_pm, snd_cs46xx_suspend, snd_cs46xx_resume);
381362306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ci
381662306a36Sopenharmony_ci/*
381762306a36Sopenharmony_ci */
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ciint snd_cs46xx_create(struct snd_card *card,
382062306a36Sopenharmony_ci		      struct pci_dev *pci,
382162306a36Sopenharmony_ci		      int external_amp, int thinkpad)
382262306a36Sopenharmony_ci{
382362306a36Sopenharmony_ci	struct snd_cs46xx *chip = card->private_data;
382462306a36Sopenharmony_ci	int err, idx;
382562306a36Sopenharmony_ci	struct snd_cs46xx_region *region;
382662306a36Sopenharmony_ci	struct cs_card_type *cp;
382762306a36Sopenharmony_ci	u16 ss_card, ss_vendor;
382862306a36Sopenharmony_ci
382962306a36Sopenharmony_ci	/* enable PCI device */
383062306a36Sopenharmony_ci	err = pcim_enable_device(pci);
383162306a36Sopenharmony_ci	if (err < 0)
383262306a36Sopenharmony_ci		return err;
383362306a36Sopenharmony_ci
383462306a36Sopenharmony_ci	spin_lock_init(&chip->reg_lock);
383562306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
383662306a36Sopenharmony_ci	mutex_init(&chip->spos_mutex);
383762306a36Sopenharmony_ci#endif
383862306a36Sopenharmony_ci	chip->card = card;
383962306a36Sopenharmony_ci	chip->pci = pci;
384062306a36Sopenharmony_ci	chip->irq = -1;
384162306a36Sopenharmony_ci
384262306a36Sopenharmony_ci	err = pci_request_regions(pci, "CS46xx");
384362306a36Sopenharmony_ci	if (err < 0)
384462306a36Sopenharmony_ci		return err;
384562306a36Sopenharmony_ci	chip->ba0_addr = pci_resource_start(pci, 0);
384662306a36Sopenharmony_ci	chip->ba1_addr = pci_resource_start(pci, 1);
384762306a36Sopenharmony_ci	if (chip->ba0_addr == 0 || chip->ba0_addr == (unsigned long)~0 ||
384862306a36Sopenharmony_ci	    chip->ba1_addr == 0 || chip->ba1_addr == (unsigned long)~0) {
384962306a36Sopenharmony_ci		dev_err(chip->card->dev,
385062306a36Sopenharmony_ci			"wrong address(es) - ba0 = 0x%lx, ba1 = 0x%lx\n",
385162306a36Sopenharmony_ci			   chip->ba0_addr, chip->ba1_addr);
385262306a36Sopenharmony_ci	    	return -ENOMEM;
385362306a36Sopenharmony_ci	}
385462306a36Sopenharmony_ci
385562306a36Sopenharmony_ci	region = &chip->region.name.ba0;
385662306a36Sopenharmony_ci	strcpy(region->name, "CS46xx_BA0");
385762306a36Sopenharmony_ci	region->base = chip->ba0_addr;
385862306a36Sopenharmony_ci	region->size = CS46XX_BA0_SIZE;
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ci	region = &chip->region.name.data0;
386162306a36Sopenharmony_ci	strcpy(region->name, "CS46xx_BA1_data0");
386262306a36Sopenharmony_ci	region->base = chip->ba1_addr + BA1_SP_DMEM0;
386362306a36Sopenharmony_ci	region->size = CS46XX_BA1_DATA0_SIZE;
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci	region = &chip->region.name.data1;
386662306a36Sopenharmony_ci	strcpy(region->name, "CS46xx_BA1_data1");
386762306a36Sopenharmony_ci	region->base = chip->ba1_addr + BA1_SP_DMEM1;
386862306a36Sopenharmony_ci	region->size = CS46XX_BA1_DATA1_SIZE;
386962306a36Sopenharmony_ci
387062306a36Sopenharmony_ci	region = &chip->region.name.pmem;
387162306a36Sopenharmony_ci	strcpy(region->name, "CS46xx_BA1_pmem");
387262306a36Sopenharmony_ci	region->base = chip->ba1_addr + BA1_SP_PMEM;
387362306a36Sopenharmony_ci	region->size = CS46XX_BA1_PRG_SIZE;
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	region = &chip->region.name.reg;
387662306a36Sopenharmony_ci	strcpy(region->name, "CS46xx_BA1_reg");
387762306a36Sopenharmony_ci	region->base = chip->ba1_addr + BA1_SP_REG;
387862306a36Sopenharmony_ci	region->size = CS46XX_BA1_REG_SIZE;
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	/* set up amp and clkrun hack */
388162306a36Sopenharmony_ci	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &ss_vendor);
388262306a36Sopenharmony_ci	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &ss_card);
388362306a36Sopenharmony_ci
388462306a36Sopenharmony_ci	for (cp = &cards[0]; cp->name; cp++) {
388562306a36Sopenharmony_ci		if (cp->vendor == ss_vendor && cp->id == ss_card) {
388662306a36Sopenharmony_ci			dev_dbg(chip->card->dev, "hack for %s enabled\n",
388762306a36Sopenharmony_ci				cp->name);
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci			chip->amplifier_ctrl = cp->amp;
389062306a36Sopenharmony_ci			chip->active_ctrl = cp->active;
389162306a36Sopenharmony_ci			chip->mixer_init = cp->mixer_init;
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_ci			if (cp->init)
389462306a36Sopenharmony_ci				cp->init(chip);
389562306a36Sopenharmony_ci			break;
389662306a36Sopenharmony_ci		}
389762306a36Sopenharmony_ci	}
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_ci	if (external_amp) {
390062306a36Sopenharmony_ci		dev_info(chip->card->dev,
390162306a36Sopenharmony_ci			 "Crystal EAPD support forced on.\n");
390262306a36Sopenharmony_ci		chip->amplifier_ctrl = amp_voyetra;
390362306a36Sopenharmony_ci	}
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	if (thinkpad) {
390662306a36Sopenharmony_ci		dev_info(chip->card->dev,
390762306a36Sopenharmony_ci			 "Activating CLKRUN hack for Thinkpad.\n");
390862306a36Sopenharmony_ci		chip->active_ctrl = clkrun_hack;
390962306a36Sopenharmony_ci		clkrun_init(chip);
391062306a36Sopenharmony_ci	}
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	if (chip->amplifier_ctrl == NULL)
391362306a36Sopenharmony_ci		chip->amplifier_ctrl = amp_none;
391462306a36Sopenharmony_ci	if (chip->active_ctrl == NULL)
391562306a36Sopenharmony_ci		chip->active_ctrl = amp_none;
391662306a36Sopenharmony_ci
391762306a36Sopenharmony_ci	chip->active_ctrl(chip, 1); /* enable CLKRUN */
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci	pci_set_master(pci);
392062306a36Sopenharmony_ci
392162306a36Sopenharmony_ci	for (idx = 0; idx < 5; idx++) {
392262306a36Sopenharmony_ci		region = &chip->region.idx[idx];
392362306a36Sopenharmony_ci		region->remap_addr = devm_ioremap(&pci->dev, region->base,
392462306a36Sopenharmony_ci						  region->size);
392562306a36Sopenharmony_ci		if (region->remap_addr == NULL) {
392662306a36Sopenharmony_ci			dev_err(chip->card->dev,
392762306a36Sopenharmony_ci				"%s ioremap problem\n", region->name);
392862306a36Sopenharmony_ci			return -ENOMEM;
392962306a36Sopenharmony_ci		}
393062306a36Sopenharmony_ci	}
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_ci	if (devm_request_irq(&pci->dev, pci->irq, snd_cs46xx_interrupt,
393362306a36Sopenharmony_ci			     IRQF_SHARED, KBUILD_MODNAME, chip)) {
393462306a36Sopenharmony_ci		dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq);
393562306a36Sopenharmony_ci		return -EBUSY;
393662306a36Sopenharmony_ci	}
393762306a36Sopenharmony_ci	chip->irq = pci->irq;
393862306a36Sopenharmony_ci	card->sync_irq = chip->irq;
393962306a36Sopenharmony_ci	card->private_free = snd_cs46xx_free;
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci#ifdef CONFIG_SND_CS46XX_NEW_DSP
394262306a36Sopenharmony_ci	chip->dsp_spos_instance = cs46xx_dsp_spos_create(chip);
394362306a36Sopenharmony_ci	if (!chip->dsp_spos_instance)
394462306a36Sopenharmony_ci		return -ENOMEM;
394562306a36Sopenharmony_ci#endif
394662306a36Sopenharmony_ci
394762306a36Sopenharmony_ci	err = snd_cs46xx_chip_init(chip);
394862306a36Sopenharmony_ci	if (err < 0)
394962306a36Sopenharmony_ci		return err;
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci	snd_cs46xx_proc_init(card, chip);
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
395462306a36Sopenharmony_ci	chip->saved_regs = devm_kmalloc_array(&pci->dev,
395562306a36Sopenharmony_ci					      ARRAY_SIZE(saved_regs),
395662306a36Sopenharmony_ci					      sizeof(*chip->saved_regs),
395762306a36Sopenharmony_ci					      GFP_KERNEL);
395862306a36Sopenharmony_ci	if (!chip->saved_regs)
395962306a36Sopenharmony_ci		return -ENOMEM;
396062306a36Sopenharmony_ci#endif
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci	chip->active_ctrl(chip, -1); /* disable CLKRUN */
396362306a36Sopenharmony_ci	return 0;
396462306a36Sopenharmony_ci}
3965