162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for CS4231 sound chips found on Sparcs.
462306a36Sopenharmony_ci * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Based entirely upon drivers/sbus/audio/cs4231.c which is:
762306a36Sopenharmony_ci * Copyright (C) 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
862306a36Sopenharmony_ci * and also sound/isa/cs423x/cs4231_lib.c which is:
962306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/moduleparam.h>
1862306a36Sopenharmony_ci#include <linux/irq.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/platform_device.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <sound/core.h>
2462306a36Sopenharmony_ci#include <sound/pcm.h>
2562306a36Sopenharmony_ci#include <sound/info.h>
2662306a36Sopenharmony_ci#include <sound/control.h>
2762306a36Sopenharmony_ci#include <sound/timer.h>
2862306a36Sopenharmony_ci#include <sound/initval.h>
2962306a36Sopenharmony_ci#include <sound/pcm_params.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifdef CONFIG_SBUS
3262306a36Sopenharmony_ci#define SBUS_SUPPORT
3362306a36Sopenharmony_ci#endif
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64)
3662306a36Sopenharmony_ci#define EBUS_SUPPORT
3762306a36Sopenharmony_ci#include <linux/pci.h>
3862306a36Sopenharmony_ci#include <asm/ebus_dma.h>
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
4262306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
4362306a36Sopenharmony_ci/* Enable this card */
4462306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
4762306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Sun CS4231 soundcard.");
4862306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
4962306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Sun CS4231 soundcard.");
5062306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
5162306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Sun CS4231 soundcard.");
5262306a36Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela, Derrick J. Brashear and David S. Miller");
5362306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun CS4231");
5462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#ifdef SBUS_SUPPORT
5762306a36Sopenharmony_cistruct sbus_dma_info {
5862306a36Sopenharmony_ci       spinlock_t	lock;	/* DMA access lock */
5962306a36Sopenharmony_ci       int		dir;
6062306a36Sopenharmony_ci       void __iomem	*regs;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci#endif
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistruct snd_cs4231;
6562306a36Sopenharmony_cistruct cs4231_dma_control {
6662306a36Sopenharmony_ci	void		(*prepare)(struct cs4231_dma_control *dma_cont,
6762306a36Sopenharmony_ci				   int dir);
6862306a36Sopenharmony_ci	void		(*enable)(struct cs4231_dma_control *dma_cont, int on);
6962306a36Sopenharmony_ci	int		(*request)(struct cs4231_dma_control *dma_cont,
7062306a36Sopenharmony_ci				   dma_addr_t bus_addr, size_t len);
7162306a36Sopenharmony_ci	unsigned int	(*address)(struct cs4231_dma_control *dma_cont);
7262306a36Sopenharmony_ci#ifdef EBUS_SUPPORT
7362306a36Sopenharmony_ci	struct		ebus_dma_info	ebus_info;
7462306a36Sopenharmony_ci#endif
7562306a36Sopenharmony_ci#ifdef SBUS_SUPPORT
7662306a36Sopenharmony_ci	struct		sbus_dma_info	sbus_info;
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistruct snd_cs4231 {
8162306a36Sopenharmony_ci	spinlock_t		lock;	/* registers access lock */
8262306a36Sopenharmony_ci	void __iomem		*port;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	struct cs4231_dma_control	p_dma;
8562306a36Sopenharmony_ci	struct cs4231_dma_control	c_dma;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	u32			flags;
8862306a36Sopenharmony_ci#define CS4231_FLAG_EBUS	0x00000001
8962306a36Sopenharmony_ci#define CS4231_FLAG_PLAYBACK	0x00000002
9062306a36Sopenharmony_ci#define CS4231_FLAG_CAPTURE	0x00000004
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	struct snd_card		*card;
9362306a36Sopenharmony_ci	struct snd_pcm		*pcm;
9462306a36Sopenharmony_ci	struct snd_pcm_substream	*playback_substream;
9562306a36Sopenharmony_ci	unsigned int		p_periods_sent;
9662306a36Sopenharmony_ci	struct snd_pcm_substream	*capture_substream;
9762306a36Sopenharmony_ci	unsigned int		c_periods_sent;
9862306a36Sopenharmony_ci	struct snd_timer	*timer;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	unsigned short mode;
10162306a36Sopenharmony_ci#define CS4231_MODE_NONE	0x0000
10262306a36Sopenharmony_ci#define CS4231_MODE_PLAY	0x0001
10362306a36Sopenharmony_ci#define CS4231_MODE_RECORD	0x0002
10462306a36Sopenharmony_ci#define CS4231_MODE_TIMER	0x0004
10562306a36Sopenharmony_ci#define CS4231_MODE_OPEN	(CS4231_MODE_PLAY | CS4231_MODE_RECORD | \
10662306a36Sopenharmony_ci				 CS4231_MODE_TIMER)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	unsigned char		image[32];	/* registers image */
10962306a36Sopenharmony_ci	int			mce_bit;
11062306a36Sopenharmony_ci	int			calibrate_mute;
11162306a36Sopenharmony_ci	struct mutex		mce_mutex;	/* mutex for mce register */
11262306a36Sopenharmony_ci	struct mutex		open_mutex;	/* mutex for ALSA open/close */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	struct platform_device	*op;
11562306a36Sopenharmony_ci	unsigned int		irq[2];
11662306a36Sopenharmony_ci	unsigned int		regs_size;
11762306a36Sopenharmony_ci	struct snd_cs4231	*next;
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for
12162306a36Sopenharmony_ci * now....  -DaveM
12262306a36Sopenharmony_ci */
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* IO ports */
12562306a36Sopenharmony_ci#include <sound/cs4231-regs.h>
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* XXX offsets are different than PC ISA chips... */
12862306a36Sopenharmony_ci#define CS4231U(chip, x)	((chip)->port + ((c_d_c_CS4231##x) << 2))
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* SBUS DMA register defines.  */
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define APCCSR	0x10UL	/* APC DMA CSR */
13362306a36Sopenharmony_ci#define APCCVA	0x20UL	/* APC Capture DMA Address */
13462306a36Sopenharmony_ci#define APCCC	0x24UL	/* APC Capture Count */
13562306a36Sopenharmony_ci#define APCCNVA	0x28UL	/* APC Capture DMA Next Address */
13662306a36Sopenharmony_ci#define APCCNC	0x2cUL	/* APC Capture Next Count */
13762306a36Sopenharmony_ci#define APCPVA	0x30UL	/* APC Play DMA Address */
13862306a36Sopenharmony_ci#define APCPC	0x34UL	/* APC Play Count */
13962306a36Sopenharmony_ci#define APCPNVA	0x38UL	/* APC Play DMA Next Address */
14062306a36Sopenharmony_ci#define APCPNC	0x3cUL	/* APC Play Next Count */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/* Defines for SBUS DMA-routines */
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#define APCVA  0x0UL	/* APC DMA Address */
14562306a36Sopenharmony_ci#define APCC   0x4UL	/* APC Count */
14662306a36Sopenharmony_ci#define APCNVA 0x8UL	/* APC DMA Next Address */
14762306a36Sopenharmony_ci#define APCNC  0xcUL	/* APC Next Count */
14862306a36Sopenharmony_ci#define APC_PLAY 0x30UL	/* Play registers start at 0x30 */
14962306a36Sopenharmony_ci#define APC_RECORD 0x20UL /* Record registers start at 0x20 */
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* APCCSR bits */
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci#define APC_INT_PENDING 0x800000 /* Interrupt Pending */
15462306a36Sopenharmony_ci#define APC_PLAY_INT    0x400000 /* Playback interrupt */
15562306a36Sopenharmony_ci#define APC_CAPT_INT    0x200000 /* Capture interrupt */
15662306a36Sopenharmony_ci#define APC_GENL_INT    0x100000 /* General interrupt */
15762306a36Sopenharmony_ci#define APC_XINT_ENA    0x80000  /* General ext int. enable */
15862306a36Sopenharmony_ci#define APC_XINT_PLAY   0x40000  /* Playback ext intr */
15962306a36Sopenharmony_ci#define APC_XINT_CAPT   0x20000  /* Capture ext intr */
16062306a36Sopenharmony_ci#define APC_XINT_GENL   0x10000  /* Error ext intr */
16162306a36Sopenharmony_ci#define APC_XINT_EMPT   0x8000   /* Pipe empty interrupt (0 write to pva) */
16262306a36Sopenharmony_ci#define APC_XINT_PEMP   0x4000   /* Play pipe empty (pva and pnva not set) */
16362306a36Sopenharmony_ci#define APC_XINT_PNVA   0x2000   /* Playback NVA dirty */
16462306a36Sopenharmony_ci#define APC_XINT_PENA   0x1000   /* play pipe empty Int enable */
16562306a36Sopenharmony_ci#define APC_XINT_COVF   0x800    /* Cap data dropped on floor */
16662306a36Sopenharmony_ci#define APC_XINT_CNVA   0x400    /* Capture NVA dirty */
16762306a36Sopenharmony_ci#define APC_XINT_CEMP   0x200    /* Capture pipe empty (cva and cnva not set) */
16862306a36Sopenharmony_ci#define APC_XINT_CENA   0x100    /* Cap. pipe empty int enable */
16962306a36Sopenharmony_ci#define APC_PPAUSE      0x80     /* Pause the play DMA */
17062306a36Sopenharmony_ci#define APC_CPAUSE      0x40     /* Pause the capture DMA */
17162306a36Sopenharmony_ci#define APC_CDC_RESET   0x20     /* CODEC RESET */
17262306a36Sopenharmony_ci#define APC_PDMA_READY  0x08     /* Play DMA Go */
17362306a36Sopenharmony_ci#define APC_CDMA_READY  0x04     /* Capture DMA Go */
17462306a36Sopenharmony_ci#define APC_CHIP_RESET  0x01     /* Reset the chip */
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/* EBUS DMA register offsets  */
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci#define EBDMA_CSR	0x00UL	/* Control/Status */
17962306a36Sopenharmony_ci#define EBDMA_ADDR	0x04UL	/* DMA Address */
18062306a36Sopenharmony_ci#define EBDMA_COUNT	0x08UL	/* DMA Count */
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/*
18362306a36Sopenharmony_ci *  Some variables
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic const unsigned char freq_bits[14] = {
18762306a36Sopenharmony_ci	/* 5510 */	0x00 | CS4231_XTAL2,
18862306a36Sopenharmony_ci	/* 6620 */	0x0E | CS4231_XTAL2,
18962306a36Sopenharmony_ci	/* 8000 */	0x00 | CS4231_XTAL1,
19062306a36Sopenharmony_ci	/* 9600 */	0x0E | CS4231_XTAL1,
19162306a36Sopenharmony_ci	/* 11025 */	0x02 | CS4231_XTAL2,
19262306a36Sopenharmony_ci	/* 16000 */	0x02 | CS4231_XTAL1,
19362306a36Sopenharmony_ci	/* 18900 */	0x04 | CS4231_XTAL2,
19462306a36Sopenharmony_ci	/* 22050 */	0x06 | CS4231_XTAL2,
19562306a36Sopenharmony_ci	/* 27042 */	0x04 | CS4231_XTAL1,
19662306a36Sopenharmony_ci	/* 32000 */	0x06 | CS4231_XTAL1,
19762306a36Sopenharmony_ci	/* 33075 */	0x0C | CS4231_XTAL2,
19862306a36Sopenharmony_ci	/* 37800 */	0x08 | CS4231_XTAL2,
19962306a36Sopenharmony_ci	/* 44100 */	0x0A | CS4231_XTAL2,
20062306a36Sopenharmony_ci	/* 48000 */	0x0C | CS4231_XTAL1
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic const unsigned int rates[14] = {
20462306a36Sopenharmony_ci	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
20562306a36Sopenharmony_ci	27042, 32000, 33075, 37800, 44100, 48000
20662306a36Sopenharmony_ci};
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
20962306a36Sopenharmony_ci	.count	= ARRAY_SIZE(rates),
21062306a36Sopenharmony_ci	.list	= rates,
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int snd_cs4231_xrate(struct snd_pcm_runtime *runtime)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	return snd_pcm_hw_constraint_list(runtime, 0,
21662306a36Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_RATE,
21762306a36Sopenharmony_ci					  &hw_constraints_rates);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic const unsigned char snd_cs4231_original_image[32] =
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	0x00,			/* 00/00 - lic */
22362306a36Sopenharmony_ci	0x00,			/* 01/01 - ric */
22462306a36Sopenharmony_ci	0x9f,			/* 02/02 - la1ic */
22562306a36Sopenharmony_ci	0x9f,			/* 03/03 - ra1ic */
22662306a36Sopenharmony_ci	0x9f,			/* 04/04 - la2ic */
22762306a36Sopenharmony_ci	0x9f,			/* 05/05 - ra2ic */
22862306a36Sopenharmony_ci	0xbf,			/* 06/06 - loc */
22962306a36Sopenharmony_ci	0xbf,			/* 07/07 - roc */
23062306a36Sopenharmony_ci	0x20,			/* 08/08 - pdfr */
23162306a36Sopenharmony_ci	CS4231_AUTOCALIB,	/* 09/09 - ic */
23262306a36Sopenharmony_ci	0x00,			/* 0a/10 - pc */
23362306a36Sopenharmony_ci	0x00,			/* 0b/11 - ti */
23462306a36Sopenharmony_ci	CS4231_MODE2,		/* 0c/12 - mi */
23562306a36Sopenharmony_ci	0x00,			/* 0d/13 - lbc */
23662306a36Sopenharmony_ci	0x00,			/* 0e/14 - pbru */
23762306a36Sopenharmony_ci	0x00,			/* 0f/15 - pbrl */
23862306a36Sopenharmony_ci	0x80,			/* 10/16 - afei */
23962306a36Sopenharmony_ci	0x01,			/* 11/17 - afeii */
24062306a36Sopenharmony_ci	0x9f,			/* 12/18 - llic */
24162306a36Sopenharmony_ci	0x9f,			/* 13/19 - rlic */
24262306a36Sopenharmony_ci	0x00,			/* 14/20 - tlb */
24362306a36Sopenharmony_ci	0x00,			/* 15/21 - thb */
24462306a36Sopenharmony_ci	0x00,			/* 16/22 - la3mic/reserved */
24562306a36Sopenharmony_ci	0x00,			/* 17/23 - ra3mic/reserved */
24662306a36Sopenharmony_ci	0x00,			/* 18/24 - afs */
24762306a36Sopenharmony_ci	0x00,			/* 19/25 - lamoc/version */
24862306a36Sopenharmony_ci	0x00,			/* 1a/26 - mioc */
24962306a36Sopenharmony_ci	0x00,			/* 1b/27 - ramoc/reserved */
25062306a36Sopenharmony_ci	0x20,			/* 1c/28 - cdfr */
25162306a36Sopenharmony_ci	0x00,			/* 1d/29 - res4 */
25262306a36Sopenharmony_ci	0x00,			/* 1e/30 - cbru */
25362306a36Sopenharmony_ci	0x00,			/* 1f/31 - cbrl */
25462306a36Sopenharmony_ci};
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic u8 __cs4231_readb(struct snd_cs4231 *cp, void __iomem *reg_addr)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	if (cp->flags & CS4231_FLAG_EBUS)
25962306a36Sopenharmony_ci		return readb(reg_addr);
26062306a36Sopenharmony_ci	else
26162306a36Sopenharmony_ci		return sbus_readb(reg_addr);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic void __cs4231_writeb(struct snd_cs4231 *cp, u8 val,
26562306a36Sopenharmony_ci			    void __iomem *reg_addr)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	if (cp->flags & CS4231_FLAG_EBUS)
26862306a36Sopenharmony_ci		return writeb(val, reg_addr);
26962306a36Sopenharmony_ci	else
27062306a36Sopenharmony_ci		return sbus_writeb(val, reg_addr);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci/*
27462306a36Sopenharmony_ci *  Basic I/O functions
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void snd_cs4231_ready(struct snd_cs4231 *chip)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	int timeout;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	for (timeout = 250; timeout > 0; timeout--) {
28262306a36Sopenharmony_ci		int val = __cs4231_readb(chip, CS4231U(chip, REGSEL));
28362306a36Sopenharmony_ci		if ((val & CS4231_INIT) == 0)
28462306a36Sopenharmony_ci			break;
28562306a36Sopenharmony_ci		udelay(100);
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic void snd_cs4231_dout(struct snd_cs4231 *chip, unsigned char reg,
29062306a36Sopenharmony_ci			    unsigned char value)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	snd_cs4231_ready(chip);
29362306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
29462306a36Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
29562306a36Sopenharmony_ci		snd_printdd("out: auto calibration time out - reg = 0x%x, "
29662306a36Sopenharmony_ci			    "value = 0x%x\n",
29762306a36Sopenharmony_ci			    reg, value);
29862306a36Sopenharmony_ci#endif
29962306a36Sopenharmony_ci	__cs4231_writeb(chip, chip->mce_bit | reg, CS4231U(chip, REGSEL));
30062306a36Sopenharmony_ci	wmb();
30162306a36Sopenharmony_ci	__cs4231_writeb(chip, value, CS4231U(chip, REG));
30262306a36Sopenharmony_ci	mb();
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic inline void snd_cs4231_outm(struct snd_cs4231 *chip, unsigned char reg,
30662306a36Sopenharmony_ci		     unsigned char mask, unsigned char value)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	unsigned char tmp = (chip->image[reg] & mask) | value;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	chip->image[reg] = tmp;
31162306a36Sopenharmony_ci	if (!chip->calibrate_mute)
31262306a36Sopenharmony_ci		snd_cs4231_dout(chip, reg, tmp);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg,
31662306a36Sopenharmony_ci			   unsigned char value)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	snd_cs4231_dout(chip, reg, value);
31962306a36Sopenharmony_ci	chip->image[reg] = value;
32062306a36Sopenharmony_ci	mb();
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	snd_cs4231_ready(chip);
32662306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
32762306a36Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
32862306a36Sopenharmony_ci		snd_printdd("in: auto calibration time out - reg = 0x%x\n",
32962306a36Sopenharmony_ci			    reg);
33062306a36Sopenharmony_ci#endif
33162306a36Sopenharmony_ci	__cs4231_writeb(chip, chip->mce_bit | reg, CS4231U(chip, REGSEL));
33262306a36Sopenharmony_ci	mb();
33362306a36Sopenharmony_ci	return __cs4231_readb(chip, CS4231U(chip, REG));
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci/*
33762306a36Sopenharmony_ci *  CS4231 detection / MCE routines
33862306a36Sopenharmony_ci */
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void snd_cs4231_busy_wait(struct snd_cs4231 *chip)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	int timeout;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/* looks like this sequence is proper for CS4231A chip (GUS MAX) */
34562306a36Sopenharmony_ci	for (timeout = 5; timeout > 0; timeout--)
34662306a36Sopenharmony_ci		__cs4231_readb(chip, CS4231U(chip, REGSEL));
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	/* end of cleanup sequence */
34962306a36Sopenharmony_ci	for (timeout = 500; timeout > 0; timeout--) {
35062306a36Sopenharmony_ci		int val = __cs4231_readb(chip, CS4231U(chip, REGSEL));
35162306a36Sopenharmony_ci		if ((val & CS4231_INIT) == 0)
35262306a36Sopenharmony_ci			break;
35362306a36Sopenharmony_ci		msleep(1);
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void snd_cs4231_mce_up(struct snd_cs4231 *chip)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	unsigned long flags;
36062306a36Sopenharmony_ci	int timeout;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
36362306a36Sopenharmony_ci	snd_cs4231_ready(chip);
36462306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
36562306a36Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
36662306a36Sopenharmony_ci		snd_printdd("mce_up - auto calibration time out (0)\n");
36762306a36Sopenharmony_ci#endif
36862306a36Sopenharmony_ci	chip->mce_bit |= CS4231_MCE;
36962306a36Sopenharmony_ci	timeout = __cs4231_readb(chip, CS4231U(chip, REGSEL));
37062306a36Sopenharmony_ci	if (timeout == 0x80)
37162306a36Sopenharmony_ci		snd_printdd("mce_up [%p]: serious init problem - "
37262306a36Sopenharmony_ci			    "codec still busy\n",
37362306a36Sopenharmony_ci			    chip->port);
37462306a36Sopenharmony_ci	if (!(timeout & CS4231_MCE))
37562306a36Sopenharmony_ci		__cs4231_writeb(chip, chip->mce_bit | (timeout & 0x1f),
37662306a36Sopenharmony_ci				CS4231U(chip, REGSEL));
37762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void snd_cs4231_mce_down(struct snd_cs4231 *chip)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	unsigned long flags, timeout;
38362306a36Sopenharmony_ci	int reg;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	snd_cs4231_busy_wait(chip);
38662306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
38762306a36Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
38862306a36Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
38962306a36Sopenharmony_ci		snd_printdd("mce_down [%p] - auto calibration time out (0)\n",
39062306a36Sopenharmony_ci			    CS4231U(chip, REGSEL));
39162306a36Sopenharmony_ci#endif
39262306a36Sopenharmony_ci	chip->mce_bit &= ~CS4231_MCE;
39362306a36Sopenharmony_ci	reg = __cs4231_readb(chip, CS4231U(chip, REGSEL));
39462306a36Sopenharmony_ci	__cs4231_writeb(chip, chip->mce_bit | (reg & 0x1f),
39562306a36Sopenharmony_ci			CS4231U(chip, REGSEL));
39662306a36Sopenharmony_ci	if (reg == 0x80)
39762306a36Sopenharmony_ci		snd_printdd("mce_down [%p]: serious init problem "
39862306a36Sopenharmony_ci			    "- codec still busy\n", chip->port);
39962306a36Sopenharmony_ci	if ((reg & CS4231_MCE) == 0) {
40062306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
40162306a36Sopenharmony_ci		return;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/*
40562306a36Sopenharmony_ci	 * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low.
40662306a36Sopenharmony_ci	 */
40762306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
40862306a36Sopenharmony_ci	do {
40962306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
41062306a36Sopenharmony_ci		msleep(1);
41162306a36Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
41262306a36Sopenharmony_ci		reg = snd_cs4231_in(chip, CS4231_TEST_INIT);
41362306a36Sopenharmony_ci		reg &= CS4231_CALIB_IN_PROGRESS;
41462306a36Sopenharmony_ci	} while (reg && time_before(jiffies, timeout));
41562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (reg)
41862306a36Sopenharmony_ci		snd_printk(KERN_ERR
41962306a36Sopenharmony_ci			   "mce_down - auto calibration time out (2)\n");
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void snd_cs4231_advance_dma(struct cs4231_dma_control *dma_cont,
42362306a36Sopenharmony_ci				   struct snd_pcm_substream *substream,
42462306a36Sopenharmony_ci				   unsigned int *periods_sent)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	while (1) {
42962306a36Sopenharmony_ci		unsigned int period_size = snd_pcm_lib_period_bytes(substream);
43062306a36Sopenharmony_ci		unsigned int offset = period_size * (*periods_sent);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		if (WARN_ON(period_size >= (1 << 24)))
43362306a36Sopenharmony_ci			return;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		if (dma_cont->request(dma_cont,
43662306a36Sopenharmony_ci				      runtime->dma_addr + offset, period_size))
43762306a36Sopenharmony_ci			return;
43862306a36Sopenharmony_ci		(*periods_sent) = ((*periods_sent) + 1) % runtime->periods;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic void cs4231_dma_trigger(struct snd_pcm_substream *substream,
44362306a36Sopenharmony_ci			       unsigned int what, int on)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
44662306a36Sopenharmony_ci	struct cs4231_dma_control *dma_cont;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (what & CS4231_PLAYBACK_ENABLE) {
44962306a36Sopenharmony_ci		dma_cont = &chip->p_dma;
45062306a36Sopenharmony_ci		if (on) {
45162306a36Sopenharmony_ci			dma_cont->prepare(dma_cont, 0);
45262306a36Sopenharmony_ci			dma_cont->enable(dma_cont, 1);
45362306a36Sopenharmony_ci			snd_cs4231_advance_dma(dma_cont,
45462306a36Sopenharmony_ci				chip->playback_substream,
45562306a36Sopenharmony_ci				&chip->p_periods_sent);
45662306a36Sopenharmony_ci		} else {
45762306a36Sopenharmony_ci			dma_cont->enable(dma_cont, 0);
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci	if (what & CS4231_RECORD_ENABLE) {
46162306a36Sopenharmony_ci		dma_cont = &chip->c_dma;
46262306a36Sopenharmony_ci		if (on) {
46362306a36Sopenharmony_ci			dma_cont->prepare(dma_cont, 1);
46462306a36Sopenharmony_ci			dma_cont->enable(dma_cont, 1);
46562306a36Sopenharmony_ci			snd_cs4231_advance_dma(dma_cont,
46662306a36Sopenharmony_ci				chip->capture_substream,
46762306a36Sopenharmony_ci				&chip->c_periods_sent);
46862306a36Sopenharmony_ci		} else {
46962306a36Sopenharmony_ci			dma_cont->enable(dma_cont, 0);
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int snd_cs4231_trigger(struct snd_pcm_substream *substream, int cmd)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
47762306a36Sopenharmony_ci	int result = 0;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	switch (cmd) {
48062306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
48162306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
48262306a36Sopenharmony_ci	{
48362306a36Sopenharmony_ci		unsigned int what = 0;
48462306a36Sopenharmony_ci		struct snd_pcm_substream *s;
48562306a36Sopenharmony_ci		unsigned long flags;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
48862306a36Sopenharmony_ci			if (s == chip->playback_substream) {
48962306a36Sopenharmony_ci				what |= CS4231_PLAYBACK_ENABLE;
49062306a36Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
49162306a36Sopenharmony_ci			} else if (s == chip->capture_substream) {
49262306a36Sopenharmony_ci				what |= CS4231_RECORD_ENABLE;
49362306a36Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
49462306a36Sopenharmony_ci			}
49562306a36Sopenharmony_ci		}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
49862306a36Sopenharmony_ci		if (cmd == SNDRV_PCM_TRIGGER_START) {
49962306a36Sopenharmony_ci			cs4231_dma_trigger(substream, what, 1);
50062306a36Sopenharmony_ci			chip->image[CS4231_IFACE_CTRL] |= what;
50162306a36Sopenharmony_ci		} else {
50262306a36Sopenharmony_ci			cs4231_dma_trigger(substream, what, 0);
50362306a36Sopenharmony_ci			chip->image[CS4231_IFACE_CTRL] &= ~what;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci		snd_cs4231_out(chip, CS4231_IFACE_CTRL,
50662306a36Sopenharmony_ci			       chip->image[CS4231_IFACE_CTRL]);
50762306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
50862306a36Sopenharmony_ci		break;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci	default:
51162306a36Sopenharmony_ci		result = -EINVAL;
51262306a36Sopenharmony_ci		break;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return result;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci/*
51962306a36Sopenharmony_ci *  CODEC I/O
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic unsigned char snd_cs4231_get_rate(unsigned int rate)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	int i;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	for (i = 0; i < 14; i++)
52762306a36Sopenharmony_ci		if (rate == rates[i])
52862306a36Sopenharmony_ci			return freq_bits[i];
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return freq_bits[13];
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic unsigned char snd_cs4231_get_format(struct snd_cs4231 *chip, int format,
53462306a36Sopenharmony_ci					   int channels)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	unsigned char rformat;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	rformat = CS4231_LINEAR_8;
53962306a36Sopenharmony_ci	switch (format) {
54062306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_MU_LAW:
54162306a36Sopenharmony_ci		rformat = CS4231_ULAW_8;
54262306a36Sopenharmony_ci		break;
54362306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_A_LAW:
54462306a36Sopenharmony_ci		rformat = CS4231_ALAW_8;
54562306a36Sopenharmony_ci		break;
54662306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
54762306a36Sopenharmony_ci		rformat = CS4231_LINEAR_16;
54862306a36Sopenharmony_ci		break;
54962306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
55062306a36Sopenharmony_ci		rformat = CS4231_LINEAR_16_BIG;
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	case SNDRV_PCM_FORMAT_IMA_ADPCM:
55362306a36Sopenharmony_ci		rformat = CS4231_ADPCM_16;
55462306a36Sopenharmony_ci		break;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	if (channels > 1)
55762306a36Sopenharmony_ci		rformat |= CS4231_STEREO;
55862306a36Sopenharmony_ci	return rformat;
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic void snd_cs4231_calibrate_mute(struct snd_cs4231 *chip, int mute)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	unsigned long flags;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	mute = mute ? 1 : 0;
56662306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
56762306a36Sopenharmony_ci	if (chip->calibrate_mute == mute) {
56862306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
56962306a36Sopenharmony_ci		return;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci	if (!mute) {
57262306a36Sopenharmony_ci		snd_cs4231_dout(chip, CS4231_LEFT_INPUT,
57362306a36Sopenharmony_ci				chip->image[CS4231_LEFT_INPUT]);
57462306a36Sopenharmony_ci		snd_cs4231_dout(chip, CS4231_RIGHT_INPUT,
57562306a36Sopenharmony_ci				chip->image[CS4231_RIGHT_INPUT]);
57662306a36Sopenharmony_ci		snd_cs4231_dout(chip, CS4231_LOOPBACK,
57762306a36Sopenharmony_ci				chip->image[CS4231_LOOPBACK]);
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT,
58062306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]);
58162306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT,
58262306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]);
58362306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT,
58462306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]);
58562306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT,
58662306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]);
58762306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT,
58862306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]);
58962306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT,
59062306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]);
59162306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN,
59262306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]);
59362306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN,
59462306a36Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]);
59562306a36Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_MONO_CTRL,
59662306a36Sopenharmony_ci			mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
59762306a36Sopenharmony_ci	chip->calibrate_mute = mute;
59862306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void snd_cs4231_playback_format(struct snd_cs4231 *chip,
60262306a36Sopenharmony_ci				       struct snd_pcm_hw_params *params,
60362306a36Sopenharmony_ci				       unsigned char pdfr)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	unsigned long flags;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	mutex_lock(&chip->mce_mutex);
60862306a36Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 1);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	snd_cs4231_mce_up(chip);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
61362306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
61462306a36Sopenharmony_ci		       (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ?
61562306a36Sopenharmony_ci		       (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) :
61662306a36Sopenharmony_ci		       pdfr);
61762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 0);
62262306a36Sopenharmony_ci	mutex_unlock(&chip->mce_mutex);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void snd_cs4231_capture_format(struct snd_cs4231 *chip,
62662306a36Sopenharmony_ci				      struct snd_pcm_hw_params *params,
62762306a36Sopenharmony_ci				      unsigned char cdfr)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	unsigned long flags;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	mutex_lock(&chip->mce_mutex);
63262306a36Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 1);
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	snd_cs4231_mce_up(chip);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
63762306a36Sopenharmony_ci	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
63862306a36Sopenharmony_ci		snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
63962306a36Sopenharmony_ci			       ((chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) |
64062306a36Sopenharmony_ci			       (cdfr & 0x0f));
64162306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
64262306a36Sopenharmony_ci		snd_cs4231_mce_down(chip);
64362306a36Sopenharmony_ci		snd_cs4231_mce_up(chip);
64462306a36Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr);
64762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 0);
65262306a36Sopenharmony_ci	mutex_unlock(&chip->mce_mutex);
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci/*
65662306a36Sopenharmony_ci *  Timer interface
65762306a36Sopenharmony_ci */
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic unsigned long snd_cs4231_timer_resolution(struct snd_timer *timer)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int snd_cs4231_timer_start(struct snd_timer *timer)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	unsigned long flags;
66962306a36Sopenharmony_ci	unsigned int ticks;
67062306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
67362306a36Sopenharmony_ci	ticks = timer->sticks;
67462306a36Sopenharmony_ci	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
67562306a36Sopenharmony_ci	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
67662306a36Sopenharmony_ci	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
67762306a36Sopenharmony_ci		snd_cs4231_out(chip, CS4231_TIMER_HIGH,
67862306a36Sopenharmony_ci			       chip->image[CS4231_TIMER_HIGH] =
67962306a36Sopenharmony_ci			       (unsigned char) (ticks >> 8));
68062306a36Sopenharmony_ci		snd_cs4231_out(chip, CS4231_TIMER_LOW,
68162306a36Sopenharmony_ci			       chip->image[CS4231_TIMER_LOW] =
68262306a36Sopenharmony_ci			       (unsigned char) ticks);
68362306a36Sopenharmony_ci		snd_cs4231_out(chip, CS4231_ALT_FEATURE_1,
68462306a36Sopenharmony_ci			       chip->image[CS4231_ALT_FEATURE_1] |
68562306a36Sopenharmony_ci					CS4231_TIMER_ENABLE);
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return 0;
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic int snd_cs4231_timer_stop(struct snd_timer *timer)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	unsigned long flags;
69562306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
69862306a36Sopenharmony_ci	chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE;
69962306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1,
70062306a36Sopenharmony_ci		       chip->image[CS4231_ALT_FEATURE_1]);
70162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic void snd_cs4231_init(struct snd_cs4231 *chip)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	unsigned long flags;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
71362306a36Sopenharmony_ci	snd_printdd("init: (1)\n");
71462306a36Sopenharmony_ci#endif
71562306a36Sopenharmony_ci	snd_cs4231_mce_up(chip);
71662306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
71762306a36Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
71862306a36Sopenharmony_ci					    CS4231_PLAYBACK_PIO |
71962306a36Sopenharmony_ci					    CS4231_RECORD_ENABLE |
72062306a36Sopenharmony_ci					    CS4231_RECORD_PIO |
72162306a36Sopenharmony_ci					    CS4231_CALIB_MODE);
72262306a36Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
72362306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
72462306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
72562306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
72862306a36Sopenharmony_ci	snd_printdd("init: (2)\n");
72962306a36Sopenharmony_ci#endif
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	snd_cs4231_mce_up(chip);
73262306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
73362306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1,
73462306a36Sopenharmony_ci			chip->image[CS4231_ALT_FEATURE_1]);
73562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
73662306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
73962306a36Sopenharmony_ci	snd_printdd("init: (3) - afei = 0x%x\n",
74062306a36Sopenharmony_ci		    chip->image[CS4231_ALT_FEATURE_1]);
74162306a36Sopenharmony_ci#endif
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
74462306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_ALT_FEATURE_2,
74562306a36Sopenharmony_ci			chip->image[CS4231_ALT_FEATURE_2]);
74662306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	snd_cs4231_mce_up(chip);
74962306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
75062306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
75162306a36Sopenharmony_ci			chip->image[CS4231_PLAYBK_FORMAT]);
75262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
75362306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
75662306a36Sopenharmony_ci	snd_printdd("init: (4)\n");
75762306a36Sopenharmony_ci#endif
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	snd_cs4231_mce_up(chip);
76062306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
76162306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]);
76262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
76362306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
76662306a36Sopenharmony_ci	snd_printdd("init: (5)\n");
76762306a36Sopenharmony_ci#endif
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int snd_cs4231_open(struct snd_cs4231 *chip, unsigned int mode)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	unsigned long flags;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	mutex_lock(&chip->open_mutex);
77562306a36Sopenharmony_ci	if ((chip->mode & mode)) {
77662306a36Sopenharmony_ci		mutex_unlock(&chip->open_mutex);
77762306a36Sopenharmony_ci		return -EAGAIN;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci	if (chip->mode & CS4231_MODE_OPEN) {
78062306a36Sopenharmony_ci		chip->mode |= mode;
78162306a36Sopenharmony_ci		mutex_unlock(&chip->open_mutex);
78262306a36Sopenharmony_ci		return 0;
78362306a36Sopenharmony_ci	}
78462306a36Sopenharmony_ci	/* ok. now enable and ack CODEC IRQ */
78562306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
78662306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
78762306a36Sopenharmony_ci		       CS4231_RECORD_IRQ |
78862306a36Sopenharmony_ci		       CS4231_TIMER_IRQ);
78962306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
79062306a36Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
79162306a36Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
79462306a36Sopenharmony_ci		       CS4231_RECORD_IRQ |
79562306a36Sopenharmony_ci		       CS4231_TIMER_IRQ);
79662306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	chip->mode = mode;
80162306a36Sopenharmony_ci	mutex_unlock(&chip->open_mutex);
80262306a36Sopenharmony_ci	return 0;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic void snd_cs4231_close(struct snd_cs4231 *chip, unsigned int mode)
80662306a36Sopenharmony_ci{
80762306a36Sopenharmony_ci	unsigned long flags;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	mutex_lock(&chip->open_mutex);
81062306a36Sopenharmony_ci	chip->mode &= ~mode;
81162306a36Sopenharmony_ci	if (chip->mode & CS4231_MODE_OPEN) {
81262306a36Sopenharmony_ci		mutex_unlock(&chip->open_mutex);
81362306a36Sopenharmony_ci		return;
81462306a36Sopenharmony_ci	}
81562306a36Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 1);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* disable IRQ */
81862306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
81962306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
82062306a36Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
82162306a36Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	/* now disable record & playback */
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (chip->image[CS4231_IFACE_CTRL] &
82662306a36Sopenharmony_ci	    (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
82762306a36Sopenharmony_ci	     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
82862306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
82962306a36Sopenharmony_ci		snd_cs4231_mce_up(chip);
83062306a36Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
83162306a36Sopenharmony_ci		chip->image[CS4231_IFACE_CTRL] &=
83262306a36Sopenharmony_ci			~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
83362306a36Sopenharmony_ci			  CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
83462306a36Sopenharmony_ci		snd_cs4231_out(chip, CS4231_IFACE_CTRL,
83562306a36Sopenharmony_ci				chip->image[CS4231_IFACE_CTRL]);
83662306a36Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
83762306a36Sopenharmony_ci		snd_cs4231_mce_down(chip);
83862306a36Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* clear IRQ again */
84262306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
84362306a36Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
84462306a36Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
84562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 0);
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	chip->mode = 0;
85062306a36Sopenharmony_ci	mutex_unlock(&chip->open_mutex);
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci/*
85462306a36Sopenharmony_ci *  timer open/close
85562306a36Sopenharmony_ci */
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic int snd_cs4231_timer_open(struct snd_timer *timer)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
86062306a36Sopenharmony_ci	snd_cs4231_open(chip, CS4231_MODE_TIMER);
86162306a36Sopenharmony_ci	return 0;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int snd_cs4231_timer_close(struct snd_timer *timer)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
86762306a36Sopenharmony_ci	snd_cs4231_close(chip, CS4231_MODE_TIMER);
86862306a36Sopenharmony_ci	return 0;
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic const struct snd_timer_hardware snd_cs4231_timer_table = {
87262306a36Sopenharmony_ci	.flags		=	SNDRV_TIMER_HW_AUTO,
87362306a36Sopenharmony_ci	.resolution	=	9945,
87462306a36Sopenharmony_ci	.ticks		=	65535,
87562306a36Sopenharmony_ci	.open		=	snd_cs4231_timer_open,
87662306a36Sopenharmony_ci	.close		=	snd_cs4231_timer_close,
87762306a36Sopenharmony_ci	.c_resolution	=	snd_cs4231_timer_resolution,
87862306a36Sopenharmony_ci	.start		=	snd_cs4231_timer_start,
87962306a36Sopenharmony_ci	.stop		=	snd_cs4231_timer_stop,
88062306a36Sopenharmony_ci};
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci/*
88362306a36Sopenharmony_ci *  ok.. exported functions..
88462306a36Sopenharmony_ci */
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic int snd_cs4231_playback_hw_params(struct snd_pcm_substream *substream,
88762306a36Sopenharmony_ci					 struct snd_pcm_hw_params *hw_params)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
89062306a36Sopenharmony_ci	unsigned char new_pdfr;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params),
89362306a36Sopenharmony_ci					 params_channels(hw_params)) |
89462306a36Sopenharmony_ci		snd_cs4231_get_rate(params_rate(hw_params));
89562306a36Sopenharmony_ci	snd_cs4231_playback_format(chip, hw_params, new_pdfr);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return 0;
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
90362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
90462306a36Sopenharmony_ci	unsigned long flags;
90562306a36Sopenharmony_ci	int ret = 0;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
91062306a36Sopenharmony_ci					    CS4231_PLAYBACK_PIO);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	if (WARN_ON(runtime->period_size > 0xffff + 1)) {
91362306a36Sopenharmony_ci		ret = -EINVAL;
91462306a36Sopenharmony_ci		goto out;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	chip->p_periods_sent = 0;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ciout:
92062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	return ret;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic int snd_cs4231_capture_hw_params(struct snd_pcm_substream *substream,
92662306a36Sopenharmony_ci					struct snd_pcm_hw_params *hw_params)
92762306a36Sopenharmony_ci{
92862306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
92962306a36Sopenharmony_ci	unsigned char new_cdfr;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params),
93262306a36Sopenharmony_ci					 params_channels(hw_params)) |
93362306a36Sopenharmony_ci		snd_cs4231_get_rate(params_rate(hw_params));
93462306a36Sopenharmony_ci	snd_cs4231_capture_format(chip, hw_params, new_cdfr);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	return 0;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
94262306a36Sopenharmony_ci	unsigned long flags;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
94562306a36Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE |
94662306a36Sopenharmony_ci					    CS4231_RECORD_PIO);
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	chip->c_periods_sent = 0;
95062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	return 0;
95362306a36Sopenharmony_ci}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_cistatic void snd_cs4231_overrange(struct snd_cs4231 *chip)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	unsigned long flags;
95862306a36Sopenharmony_ci	unsigned char res;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
96162306a36Sopenharmony_ci	res = snd_cs4231_in(chip, CS4231_TEST_INIT);
96262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* detect overrange only above 0dB; may be user selectable? */
96562306a36Sopenharmony_ci	if (res & (0x08 | 0x02))
96662306a36Sopenharmony_ci		chip->capture_substream->runtime->overrange++;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic void snd_cs4231_play_callback(struct snd_cs4231 *chip)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	if (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE) {
97262306a36Sopenharmony_ci		snd_pcm_period_elapsed(chip->playback_substream);
97362306a36Sopenharmony_ci		snd_cs4231_advance_dma(&chip->p_dma, chip->playback_substream,
97462306a36Sopenharmony_ci					    &chip->p_periods_sent);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void snd_cs4231_capture_callback(struct snd_cs4231 *chip)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) {
98162306a36Sopenharmony_ci		snd_pcm_period_elapsed(chip->capture_substream);
98262306a36Sopenharmony_ci		snd_cs4231_advance_dma(&chip->c_dma, chip->capture_substream,
98362306a36Sopenharmony_ci					    &chip->c_periods_sent);
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cs4231_playback_pointer(
98862306a36Sopenharmony_ci					struct snd_pcm_substream *substream)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
99162306a36Sopenharmony_ci	struct cs4231_dma_control *dma_cont = &chip->p_dma;
99262306a36Sopenharmony_ci	size_t ptr;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
99562306a36Sopenharmony_ci		return 0;
99662306a36Sopenharmony_ci	ptr = dma_cont->address(dma_cont);
99762306a36Sopenharmony_ci	if (ptr != 0)
99862306a36Sopenharmony_ci		ptr -= substream->runtime->dma_addr;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_cs4231_capture_pointer(
100462306a36Sopenharmony_ci					struct snd_pcm_substream *substream)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
100762306a36Sopenharmony_ci	struct cs4231_dma_control *dma_cont = &chip->c_dma;
100862306a36Sopenharmony_ci	size_t ptr;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
101162306a36Sopenharmony_ci		return 0;
101262306a36Sopenharmony_ci	ptr = dma_cont->address(dma_cont);
101362306a36Sopenharmony_ci	if (ptr != 0)
101462306a36Sopenharmony_ci		ptr -= substream->runtime->dma_addr;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic int snd_cs4231_probe(struct snd_cs4231 *chip)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	unsigned long flags;
102262306a36Sopenharmony_ci	int i;
102362306a36Sopenharmony_ci	int id = 0;
102462306a36Sopenharmony_ci	int vers = 0;
102562306a36Sopenharmony_ci	unsigned char *ptr;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	for (i = 0; i < 50; i++) {
102862306a36Sopenharmony_ci		mb();
102962306a36Sopenharmony_ci		if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
103062306a36Sopenharmony_ci			msleep(2);
103162306a36Sopenharmony_ci		else {
103262306a36Sopenharmony_ci			spin_lock_irqsave(&chip->lock, flags);
103362306a36Sopenharmony_ci			snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
103462306a36Sopenharmony_ci			id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f;
103562306a36Sopenharmony_ci			vers = snd_cs4231_in(chip, CS4231_VERSION);
103662306a36Sopenharmony_ci			spin_unlock_irqrestore(&chip->lock, flags);
103762306a36Sopenharmony_ci			if (id == 0x0a)
103862306a36Sopenharmony_ci				break;	/* this is valid value */
103962306a36Sopenharmony_ci		}
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci	snd_printdd("cs4231: port = %p, id = 0x%x\n", chip->port, id);
104262306a36Sopenharmony_ci	if (id != 0x0a)
104362306a36Sopenharmony_ci		return -ENODEV;	/* no valid device found */
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* clear any pendings IRQ */
104862306a36Sopenharmony_ci	__cs4231_readb(chip, CS4231U(chip, STATUS));
104962306a36Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));
105062306a36Sopenharmony_ci	mb();
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
105562306a36Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] =
105662306a36Sopenharmony_ci		chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA;
105762306a36Sopenharmony_ci	chip->image[CS4231_ALT_FEATURE_1] = 0x80;
105862306a36Sopenharmony_ci	chip->image[CS4231_ALT_FEATURE_2] = 0x01;
105962306a36Sopenharmony_ci	if (vers & 0x20)
106062306a36Sopenharmony_ci		chip->image[CS4231_ALT_FEATURE_2] |= 0x02;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	ptr = (unsigned char *) &chip->image;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	for (i = 0; i < 32; i++)	/* ok.. fill all CS4231 registers */
106962306a36Sopenharmony_ci		snd_cs4231_out(chip, i, *ptr++);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	snd_cs4231_mce_up(chip);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	snd_cs4231_mce_down(chip);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	mdelay(2);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	return 0;		/* all things are ok.. */
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs4231_playback = {
108362306a36Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP |
108462306a36Sopenharmony_ci				  SNDRV_PCM_INFO_INTERLEAVED |
108562306a36Sopenharmony_ci				  SNDRV_PCM_INFO_MMAP_VALID |
108662306a36Sopenharmony_ci				  SNDRV_PCM_INFO_SYNC_START,
108762306a36Sopenharmony_ci	.formats		= SNDRV_PCM_FMTBIT_MU_LAW |
108862306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_A_LAW |
108962306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_IMA_ADPCM |
109062306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_U8 |
109162306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_LE |
109262306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_BE,
109362306a36Sopenharmony_ci	.rates			= SNDRV_PCM_RATE_KNOT |
109462306a36Sopenharmony_ci				  SNDRV_PCM_RATE_8000_48000,
109562306a36Sopenharmony_ci	.rate_min		= 5510,
109662306a36Sopenharmony_ci	.rate_max		= 48000,
109762306a36Sopenharmony_ci	.channels_min		= 1,
109862306a36Sopenharmony_ci	.channels_max		= 2,
109962306a36Sopenharmony_ci	.buffer_bytes_max	= 32 * 1024,
110062306a36Sopenharmony_ci	.period_bytes_min	= 64,
110162306a36Sopenharmony_ci	.period_bytes_max	= 32 * 1024,
110262306a36Sopenharmony_ci	.periods_min		= 1,
110362306a36Sopenharmony_ci	.periods_max		= 1024,
110462306a36Sopenharmony_ci};
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs4231_capture = {
110762306a36Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP |
110862306a36Sopenharmony_ci				  SNDRV_PCM_INFO_INTERLEAVED |
110962306a36Sopenharmony_ci				  SNDRV_PCM_INFO_MMAP_VALID |
111062306a36Sopenharmony_ci				  SNDRV_PCM_INFO_SYNC_START,
111162306a36Sopenharmony_ci	.formats		= SNDRV_PCM_FMTBIT_MU_LAW |
111262306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_A_LAW |
111362306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_IMA_ADPCM |
111462306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_U8 |
111562306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_LE |
111662306a36Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_BE,
111762306a36Sopenharmony_ci	.rates			= SNDRV_PCM_RATE_KNOT |
111862306a36Sopenharmony_ci				  SNDRV_PCM_RATE_8000_48000,
111962306a36Sopenharmony_ci	.rate_min		= 5510,
112062306a36Sopenharmony_ci	.rate_max		= 48000,
112162306a36Sopenharmony_ci	.channels_min		= 1,
112262306a36Sopenharmony_ci	.channels_max		= 2,
112362306a36Sopenharmony_ci	.buffer_bytes_max	= 32 * 1024,
112462306a36Sopenharmony_ci	.period_bytes_min	= 64,
112562306a36Sopenharmony_ci	.period_bytes_max	= 32 * 1024,
112662306a36Sopenharmony_ci	.periods_min		= 1,
112762306a36Sopenharmony_ci	.periods_max		= 1024,
112862306a36Sopenharmony_ci};
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic int snd_cs4231_playback_open(struct snd_pcm_substream *substream)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
113362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
113462306a36Sopenharmony_ci	int err;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	runtime->hw = snd_cs4231_playback;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	err = snd_cs4231_open(chip, CS4231_MODE_PLAY);
113962306a36Sopenharmony_ci	if (err < 0)
114062306a36Sopenharmony_ci		return err;
114162306a36Sopenharmony_ci	chip->playback_substream = substream;
114262306a36Sopenharmony_ci	chip->p_periods_sent = 0;
114362306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
114462306a36Sopenharmony_ci	snd_cs4231_xrate(runtime);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	return 0;
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic int snd_cs4231_capture_open(struct snd_pcm_substream *substream)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
115262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
115362306a36Sopenharmony_ci	int err;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	runtime->hw = snd_cs4231_capture;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	err = snd_cs4231_open(chip, CS4231_MODE_RECORD);
115862306a36Sopenharmony_ci	if (err < 0)
115962306a36Sopenharmony_ci		return err;
116062306a36Sopenharmony_ci	chip->capture_substream = substream;
116162306a36Sopenharmony_ci	chip->c_periods_sent = 0;
116262306a36Sopenharmony_ci	snd_pcm_set_sync(substream);
116362306a36Sopenharmony_ci	snd_cs4231_xrate(runtime);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return 0;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic int snd_cs4231_playback_close(struct snd_pcm_substream *substream)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	snd_cs4231_close(chip, CS4231_MODE_PLAY);
117362306a36Sopenharmony_ci	chip->playback_substream = NULL;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	return 0;
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic int snd_cs4231_capture_close(struct snd_pcm_substream *substream)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	snd_cs4231_close(chip, CS4231_MODE_RECORD);
118362306a36Sopenharmony_ci	chip->capture_substream = NULL;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	return 0;
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci/* XXX We can do some power-management, in particular on EBUS using
118962306a36Sopenharmony_ci * XXX the audio AUXIO register...
119062306a36Sopenharmony_ci */
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs4231_playback_ops = {
119362306a36Sopenharmony_ci	.open		=	snd_cs4231_playback_open,
119462306a36Sopenharmony_ci	.close		=	snd_cs4231_playback_close,
119562306a36Sopenharmony_ci	.hw_params	=	snd_cs4231_playback_hw_params,
119662306a36Sopenharmony_ci	.prepare	=	snd_cs4231_playback_prepare,
119762306a36Sopenharmony_ci	.trigger	=	snd_cs4231_trigger,
119862306a36Sopenharmony_ci	.pointer	=	snd_cs4231_playback_pointer,
119962306a36Sopenharmony_ci};
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cs4231_capture_ops = {
120262306a36Sopenharmony_ci	.open		=	snd_cs4231_capture_open,
120362306a36Sopenharmony_ci	.close		=	snd_cs4231_capture_close,
120462306a36Sopenharmony_ci	.hw_params	=	snd_cs4231_capture_hw_params,
120562306a36Sopenharmony_ci	.prepare	=	snd_cs4231_capture_prepare,
120662306a36Sopenharmony_ci	.trigger	=	snd_cs4231_trigger,
120762306a36Sopenharmony_ci	.pointer	=	snd_cs4231_capture_pointer,
120862306a36Sopenharmony_ci};
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic int snd_cs4231_pcm(struct snd_card *card)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
121362306a36Sopenharmony_ci	struct snd_pcm *pcm;
121462306a36Sopenharmony_ci	int err;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	err = snd_pcm_new(card, "CS4231", 0, 1, 1, &pcm);
121762306a36Sopenharmony_ci	if (err < 0)
121862306a36Sopenharmony_ci		return err;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
122162306a36Sopenharmony_ci			&snd_cs4231_playback_ops);
122262306a36Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
122362306a36Sopenharmony_ci			&snd_cs4231_capture_ops);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* global setup */
122662306a36Sopenharmony_ci	pcm->private_data = chip;
122762306a36Sopenharmony_ci	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
122862306a36Sopenharmony_ci	strcpy(pcm->name, "CS4231");
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
123162306a36Sopenharmony_ci				       &chip->op->dev, 64 * 1024, 128 * 1024);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	chip->pcm = pcm;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return 0;
123662306a36Sopenharmony_ci}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_cistatic int snd_cs4231_timer(struct snd_card *card)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
124162306a36Sopenharmony_ci	struct snd_timer *timer;
124262306a36Sopenharmony_ci	struct snd_timer_id tid;
124362306a36Sopenharmony_ci	int err;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	/* Timer initialization */
124662306a36Sopenharmony_ci	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
124762306a36Sopenharmony_ci	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
124862306a36Sopenharmony_ci	tid.card = card->number;
124962306a36Sopenharmony_ci	tid.device = 0;
125062306a36Sopenharmony_ci	tid.subdevice = 0;
125162306a36Sopenharmony_ci	err = snd_timer_new(card, "CS4231", &tid, &timer);
125262306a36Sopenharmony_ci	if (err < 0)
125362306a36Sopenharmony_ci		return err;
125462306a36Sopenharmony_ci	strcpy(timer->name, "CS4231");
125562306a36Sopenharmony_ci	timer->private_data = chip;
125662306a36Sopenharmony_ci	timer->hw = snd_cs4231_timer_table;
125762306a36Sopenharmony_ci	chip->timer = timer;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	return 0;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci/*
126362306a36Sopenharmony_ci *  MIXER part
126462306a36Sopenharmony_ci */
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_cistatic int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol,
126762306a36Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	static const char * const texts[4] = {
127062306a36Sopenharmony_ci		"Line", "CD", "Mic", "Mix"
127162306a36Sopenharmony_ci	};
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 2, 4, texts);
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol,
127762306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
128062306a36Sopenharmony_ci	unsigned long flags;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
128362306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] =
128462306a36Sopenharmony_ci		(chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
128562306a36Sopenharmony_ci	ucontrol->value.enumerated.item[1] =
128662306a36Sopenharmony_ci		(chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
128762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	return 0;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cistatic int snd_cs4231_put_mux(struct snd_kcontrol *kcontrol,
129362306a36Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
129662306a36Sopenharmony_ci	unsigned long flags;
129762306a36Sopenharmony_ci	unsigned short left, right;
129862306a36Sopenharmony_ci	int change;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 3 ||
130162306a36Sopenharmony_ci	    ucontrol->value.enumerated.item[1] > 3)
130262306a36Sopenharmony_ci		return -EINVAL;
130362306a36Sopenharmony_ci	left = ucontrol->value.enumerated.item[0] << 6;
130462306a36Sopenharmony_ci	right = ucontrol->value.enumerated.item[1] << 6;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
130962306a36Sopenharmony_ci	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
131062306a36Sopenharmony_ci	change = left != chip->image[CS4231_LEFT_INPUT] ||
131162306a36Sopenharmony_ci		 right != chip->image[CS4231_RIGHT_INPUT];
131262306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_LEFT_INPUT, left);
131362306a36Sopenharmony_ci	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	return change;
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic int snd_cs4231_info_single(struct snd_kcontrol *kcontrol,
132162306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	uinfo->type = (mask == 1) ?
132662306a36Sopenharmony_ci		SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
132762306a36Sopenharmony_ci	uinfo->count = 1;
132862306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
132962306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	return 0;
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic int snd_cs4231_get_single(struct snd_kcontrol *kcontrol,
133562306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
133862306a36Sopenharmony_ci	unsigned long flags;
133962306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
134062306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
134162306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
134262306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	if (invert)
135162306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
135262306a36Sopenharmony_ci			(mask - ucontrol->value.integer.value[0]);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	return 0;
135562306a36Sopenharmony_ci}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_cistatic int snd_cs4231_put_single(struct snd_kcontrol *kcontrol,
135862306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
135962306a36Sopenharmony_ci{
136062306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
136162306a36Sopenharmony_ci	unsigned long flags;
136262306a36Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
136362306a36Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
136462306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
136562306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
136662306a36Sopenharmony_ci	int change;
136762306a36Sopenharmony_ci	unsigned short val;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
137062306a36Sopenharmony_ci	if (invert)
137162306a36Sopenharmony_ci		val = mask - val;
137262306a36Sopenharmony_ci	val <<= shift;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	val = (chip->image[reg] & ~(mask << shift)) | val;
137762306a36Sopenharmony_ci	change = val != chip->image[reg];
137862306a36Sopenharmony_ci	snd_cs4231_out(chip, reg, val);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	return change;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic int snd_cs4231_info_double(struct snd_kcontrol *kcontrol,
138662306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	uinfo->type = mask == 1 ?
139162306a36Sopenharmony_ci		SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
139262306a36Sopenharmony_ci	uinfo->count = 2;
139362306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
139462306a36Sopenharmony_ci	uinfo->value.integer.max = mask;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	return 0;
139762306a36Sopenharmony_ci}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_cistatic int snd_cs4231_get_double(struct snd_kcontrol *kcontrol,
140062306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
140162306a36Sopenharmony_ci{
140262306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
140362306a36Sopenharmony_ci	unsigned long flags;
140462306a36Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
140562306a36Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
140662306a36Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
140762306a36Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
140862306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
140962306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	ucontrol->value.integer.value[0] =
141462306a36Sopenharmony_ci		(chip->image[left_reg] >> shift_left) & mask;
141562306a36Sopenharmony_ci	ucontrol->value.integer.value[1] =
141662306a36Sopenharmony_ci		(chip->image[right_reg] >> shift_right) & mask;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	if (invert) {
142162306a36Sopenharmony_ci		ucontrol->value.integer.value[0] =
142262306a36Sopenharmony_ci			(mask - ucontrol->value.integer.value[0]);
142362306a36Sopenharmony_ci		ucontrol->value.integer.value[1] =
142462306a36Sopenharmony_ci			(mask - ucontrol->value.integer.value[1]);
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	return 0;
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_cistatic int snd_cs4231_put_double(struct snd_kcontrol *kcontrol,
143162306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
143262306a36Sopenharmony_ci{
143362306a36Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
143462306a36Sopenharmony_ci	unsigned long flags;
143562306a36Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
143662306a36Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
143762306a36Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
143862306a36Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
143962306a36Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
144062306a36Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
144162306a36Sopenharmony_ci	int change;
144262306a36Sopenharmony_ci	unsigned short val1, val2;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	val1 = ucontrol->value.integer.value[0] & mask;
144562306a36Sopenharmony_ci	val2 = ucontrol->value.integer.value[1] & mask;
144662306a36Sopenharmony_ci	if (invert) {
144762306a36Sopenharmony_ci		val1 = mask - val1;
144862306a36Sopenharmony_ci		val2 = mask - val2;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci	val1 <<= shift_left;
145162306a36Sopenharmony_ci	val2 <<= shift_right;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
145662306a36Sopenharmony_ci	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
145762306a36Sopenharmony_ci	change = val1 != chip->image[left_reg];
145862306a36Sopenharmony_ci	change |= val2 != chip->image[right_reg];
145962306a36Sopenharmony_ci	snd_cs4231_out(chip, left_reg, val1);
146062306a36Sopenharmony_ci	snd_cs4231_out(chip, right_reg, val2);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	return change;
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \
146862306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .index = (xindex), \
146962306a36Sopenharmony_ci  .info = snd_cs4231_info_single,	\
147062306a36Sopenharmony_ci  .get = snd_cs4231_get_single, .put = snd_cs4231_put_single,	\
147162306a36Sopenharmony_ci  .private_value = (reg) | ((shift) << 8) | ((mask) << 16) | ((invert) << 24) }
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, \
147462306a36Sopenharmony_ci			shift_right, mask, invert) \
147562306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .index = (xindex), \
147662306a36Sopenharmony_ci  .info = snd_cs4231_info_double,	\
147762306a36Sopenharmony_ci  .get = snd_cs4231_get_double, .put = snd_cs4231_put_double,	\
147862306a36Sopenharmony_ci  .private_value = (left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | \
147962306a36Sopenharmony_ci		   ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22) }
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4231_controls[] = {
148262306a36Sopenharmony_ciCS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT,
148362306a36Sopenharmony_ci		CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
148462306a36Sopenharmony_ciCS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT,
148562306a36Sopenharmony_ci		CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
148662306a36Sopenharmony_ciCS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN,
148762306a36Sopenharmony_ci		CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
148862306a36Sopenharmony_ciCS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN,
148962306a36Sopenharmony_ci		CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
149062306a36Sopenharmony_ciCS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT,
149162306a36Sopenharmony_ci		CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
149262306a36Sopenharmony_ciCS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT,
149362306a36Sopenharmony_ci		CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
149462306a36Sopenharmony_ciCS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT,
149562306a36Sopenharmony_ci		CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
149662306a36Sopenharmony_ciCS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT,
149762306a36Sopenharmony_ci		CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
149862306a36Sopenharmony_ciCS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1),
149962306a36Sopenharmony_ciCS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
150062306a36Sopenharmony_ciCS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1),
150162306a36Sopenharmony_ciCS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
150262306a36Sopenharmony_ciCS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0,
150362306a36Sopenharmony_ci		15, 0),
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
150662306a36Sopenharmony_ci	.name	= "Capture Source",
150762306a36Sopenharmony_ci	.info	= snd_cs4231_info_mux,
150862306a36Sopenharmony_ci	.get	= snd_cs4231_get_mux,
150962306a36Sopenharmony_ci	.put	= snd_cs4231_put_mux,
151062306a36Sopenharmony_ci},
151162306a36Sopenharmony_ciCS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5,
151262306a36Sopenharmony_ci		1, 0),
151362306a36Sopenharmony_ciCS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
151462306a36Sopenharmony_ciCS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1),
151562306a36Sopenharmony_ci/* SPARC specific uses of XCTL{0,1} general purpose outputs.  */
151662306a36Sopenharmony_ciCS4231_SINGLE("Line Out Switch", 0, CS4231_PIN_CTRL, 6, 1, 1),
151762306a36Sopenharmony_ciCS4231_SINGLE("Headphone Out Switch", 0, CS4231_PIN_CTRL, 7, 1, 1)
151862306a36Sopenharmony_ci};
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic int snd_cs4231_mixer(struct snd_card *card)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
152362306a36Sopenharmony_ci	int err, idx;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	if (snd_BUG_ON(!chip || !chip->pcm))
152662306a36Sopenharmony_ci		return -EINVAL;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	strcpy(card->mixername, chip->pcm->name);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
153162306a36Sopenharmony_ci		err = snd_ctl_add(card,
153262306a36Sopenharmony_ci				 snd_ctl_new1(&snd_cs4231_controls[idx], chip));
153362306a36Sopenharmony_ci		if (err < 0)
153462306a36Sopenharmony_ci			return err;
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci	return 0;
153762306a36Sopenharmony_ci}
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_cistatic int dev;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_cistatic int cs4231_attach_begin(struct platform_device *op,
154262306a36Sopenharmony_ci			       struct snd_card **rcard)
154362306a36Sopenharmony_ci{
154462306a36Sopenharmony_ci	struct snd_card *card;
154562306a36Sopenharmony_ci	struct snd_cs4231 *chip;
154662306a36Sopenharmony_ci	int err;
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	*rcard = NULL;
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	if (dev >= SNDRV_CARDS)
155162306a36Sopenharmony_ci		return -ENODEV;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	if (!enable[dev]) {
155462306a36Sopenharmony_ci		dev++;
155562306a36Sopenharmony_ci		return -ENOENT;
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	err = snd_card_new(&op->dev, index[dev], id[dev], THIS_MODULE,
155962306a36Sopenharmony_ci			   sizeof(struct snd_cs4231), &card);
156062306a36Sopenharmony_ci	if (err < 0)
156162306a36Sopenharmony_ci		return err;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	strcpy(card->driver, "CS4231");
156462306a36Sopenharmony_ci	strcpy(card->shortname, "Sun CS4231");
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	chip = card->private_data;
156762306a36Sopenharmony_ci	chip->card = card;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	*rcard = card;
157062306a36Sopenharmony_ci	return 0;
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic int cs4231_attach_finish(struct snd_card *card)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
157662306a36Sopenharmony_ci	int err;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	err = snd_cs4231_pcm(card);
157962306a36Sopenharmony_ci	if (err < 0)
158062306a36Sopenharmony_ci		goto out_err;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	err = snd_cs4231_mixer(card);
158362306a36Sopenharmony_ci	if (err < 0)
158462306a36Sopenharmony_ci		goto out_err;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	err = snd_cs4231_timer(card);
158762306a36Sopenharmony_ci	if (err < 0)
158862306a36Sopenharmony_ci		goto out_err;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	err = snd_card_register(card);
159162306a36Sopenharmony_ci	if (err < 0)
159262306a36Sopenharmony_ci		goto out_err;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	dev_set_drvdata(&chip->op->dev, chip);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	dev++;
159762306a36Sopenharmony_ci	return 0;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ciout_err:
160062306a36Sopenharmony_ci	snd_card_free(card);
160162306a36Sopenharmony_ci	return err;
160262306a36Sopenharmony_ci}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci#ifdef SBUS_SUPPORT
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_cistatic irqreturn_t snd_cs4231_sbus_interrupt(int irq, void *dev_id)
160762306a36Sopenharmony_ci{
160862306a36Sopenharmony_ci	unsigned long flags;
160962306a36Sopenharmony_ci	unsigned char status;
161062306a36Sopenharmony_ci	u32 csr;
161162306a36Sopenharmony_ci	struct snd_cs4231 *chip = dev_id;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	/*This is IRQ is not raised by the cs4231*/
161462306a36Sopenharmony_ci	if (!(__cs4231_readb(chip, CS4231U(chip, STATUS)) & CS4231_GLOBALIRQ))
161562306a36Sopenharmony_ci		return IRQ_NONE;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	/* ACK the APC interrupt. */
161862306a36Sopenharmony_ci	csr = sbus_readl(chip->port + APCCSR);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	sbus_writel(csr, chip->port + APCCSR);
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	if ((csr & APC_PDMA_READY) &&
162362306a36Sopenharmony_ci	    (csr & APC_PLAY_INT) &&
162462306a36Sopenharmony_ci	    (csr & APC_XINT_PNVA) &&
162562306a36Sopenharmony_ci	    !(csr & APC_XINT_EMPT))
162662306a36Sopenharmony_ci			snd_cs4231_play_callback(chip);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	if ((csr & APC_CDMA_READY) &&
162962306a36Sopenharmony_ci	    (csr & APC_CAPT_INT) &&
163062306a36Sopenharmony_ci	    (csr & APC_XINT_CNVA) &&
163162306a36Sopenharmony_ci	    !(csr & APC_XINT_EMPT))
163262306a36Sopenharmony_ci			snd_cs4231_capture_callback(chip);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	if (status & CS4231_TIMER_IRQ) {
163762306a36Sopenharmony_ci		if (chip->timer)
163862306a36Sopenharmony_ci			snd_timer_interrupt(chip->timer, chip->timer->sticks);
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	if ((status & CS4231_RECORD_IRQ) && (csr & APC_CDMA_READY))
164262306a36Sopenharmony_ci		snd_cs4231_overrange(chip);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	/* ACK the CS4231 interrupt. */
164562306a36Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
164662306a36Sopenharmony_ci	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
164762306a36Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	return IRQ_HANDLED;
165062306a36Sopenharmony_ci}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci/*
165362306a36Sopenharmony_ci * SBUS DMA routines
165462306a36Sopenharmony_ci */
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic int sbus_dma_request(struct cs4231_dma_control *dma_cont,
165762306a36Sopenharmony_ci			    dma_addr_t bus_addr, size_t len)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	unsigned long flags;
166062306a36Sopenharmony_ci	u32 test, csr;
166162306a36Sopenharmony_ci	int err;
166262306a36Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	if (len >= (1 << 24))
166562306a36Sopenharmony_ci		return -EINVAL;
166662306a36Sopenharmony_ci	spin_lock_irqsave(&base->lock, flags);
166762306a36Sopenharmony_ci	csr = sbus_readl(base->regs + APCCSR);
166862306a36Sopenharmony_ci	err = -EINVAL;
166962306a36Sopenharmony_ci	test = APC_CDMA_READY;
167062306a36Sopenharmony_ci	if (base->dir == APC_PLAY)
167162306a36Sopenharmony_ci		test = APC_PDMA_READY;
167262306a36Sopenharmony_ci	if (!(csr & test))
167362306a36Sopenharmony_ci		goto out;
167462306a36Sopenharmony_ci	err = -EBUSY;
167562306a36Sopenharmony_ci	test = APC_XINT_CNVA;
167662306a36Sopenharmony_ci	if (base->dir == APC_PLAY)
167762306a36Sopenharmony_ci		test = APC_XINT_PNVA;
167862306a36Sopenharmony_ci	if (!(csr & test))
167962306a36Sopenharmony_ci		goto out;
168062306a36Sopenharmony_ci	err = 0;
168162306a36Sopenharmony_ci	sbus_writel(bus_addr, base->regs + base->dir + APCNVA);
168262306a36Sopenharmony_ci	sbus_writel(len, base->regs + base->dir + APCNC);
168362306a36Sopenharmony_ciout:
168462306a36Sopenharmony_ci	spin_unlock_irqrestore(&base->lock, flags);
168562306a36Sopenharmony_ci	return err;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_cistatic void sbus_dma_prepare(struct cs4231_dma_control *dma_cont, int d)
168962306a36Sopenharmony_ci{
169062306a36Sopenharmony_ci	unsigned long flags;
169162306a36Sopenharmony_ci	u32 csr, test;
169262306a36Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	spin_lock_irqsave(&base->lock, flags);
169562306a36Sopenharmony_ci	csr = sbus_readl(base->regs + APCCSR);
169662306a36Sopenharmony_ci	test =  APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA |
169762306a36Sopenharmony_ci		APC_XINT_PLAY | APC_XINT_PEMP | APC_XINT_GENL |
169862306a36Sopenharmony_ci		 APC_XINT_PENA;
169962306a36Sopenharmony_ci	if (base->dir == APC_RECORD)
170062306a36Sopenharmony_ci		test = APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA |
170162306a36Sopenharmony_ci			APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL;
170262306a36Sopenharmony_ci	csr |= test;
170362306a36Sopenharmony_ci	sbus_writel(csr, base->regs + APCCSR);
170462306a36Sopenharmony_ci	spin_unlock_irqrestore(&base->lock, flags);
170562306a36Sopenharmony_ci}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_cistatic void sbus_dma_enable(struct cs4231_dma_control *dma_cont, int on)
170862306a36Sopenharmony_ci{
170962306a36Sopenharmony_ci	unsigned long flags;
171062306a36Sopenharmony_ci	u32 csr, shift;
171162306a36Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	spin_lock_irqsave(&base->lock, flags);
171462306a36Sopenharmony_ci	if (!on) {
171562306a36Sopenharmony_ci		sbus_writel(0, base->regs + base->dir + APCNC);
171662306a36Sopenharmony_ci		sbus_writel(0, base->regs + base->dir + APCNVA);
171762306a36Sopenharmony_ci		if (base->dir == APC_PLAY) {
171862306a36Sopenharmony_ci			sbus_writel(0, base->regs + base->dir + APCC);
171962306a36Sopenharmony_ci			sbus_writel(0, base->regs + base->dir + APCVA);
172062306a36Sopenharmony_ci		}
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci		udelay(1200);
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci	csr = sbus_readl(base->regs + APCCSR);
172562306a36Sopenharmony_ci	shift = 0;
172662306a36Sopenharmony_ci	if (base->dir == APC_PLAY)
172762306a36Sopenharmony_ci		shift = 1;
172862306a36Sopenharmony_ci	if (on)
172962306a36Sopenharmony_ci		csr &= ~(APC_CPAUSE << shift);
173062306a36Sopenharmony_ci	else
173162306a36Sopenharmony_ci		csr |= (APC_CPAUSE << shift);
173262306a36Sopenharmony_ci	sbus_writel(csr, base->regs + APCCSR);
173362306a36Sopenharmony_ci	if (on)
173462306a36Sopenharmony_ci		csr |= (APC_CDMA_READY << shift);
173562306a36Sopenharmony_ci	else
173662306a36Sopenharmony_ci		csr &= ~(APC_CDMA_READY << shift);
173762306a36Sopenharmony_ci	sbus_writel(csr, base->regs + APCCSR);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	spin_unlock_irqrestore(&base->lock, flags);
174062306a36Sopenharmony_ci}
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_cistatic unsigned int sbus_dma_addr(struct cs4231_dma_control *dma_cont)
174362306a36Sopenharmony_ci{
174462306a36Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	return sbus_readl(base->regs + base->dir + APCVA);
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci/*
175062306a36Sopenharmony_ci * Init and exit routines
175162306a36Sopenharmony_ci */
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_cistatic int snd_cs4231_sbus_free(struct snd_cs4231 *chip)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	struct platform_device *op = chip->op;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (chip->irq[0])
175862306a36Sopenharmony_ci		free_irq(chip->irq[0], chip);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	if (chip->port)
176162306a36Sopenharmony_ci		of_iounmap(&op->resource[0], chip->port, chip->regs_size);
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	return 0;
176462306a36Sopenharmony_ci}
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_cistatic int snd_cs4231_sbus_dev_free(struct snd_device *device)
176762306a36Sopenharmony_ci{
176862306a36Sopenharmony_ci	struct snd_cs4231 *cp = device->device_data;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	return snd_cs4231_sbus_free(cp);
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cistatic const struct snd_device_ops snd_cs4231_sbus_dev_ops = {
177462306a36Sopenharmony_ci	.dev_free	=	snd_cs4231_sbus_dev_free,
177562306a36Sopenharmony_ci};
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_cistatic int snd_cs4231_sbus_create(struct snd_card *card,
177862306a36Sopenharmony_ci				  struct platform_device *op,
177962306a36Sopenharmony_ci				  int dev)
178062306a36Sopenharmony_ci{
178162306a36Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
178262306a36Sopenharmony_ci	int err;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
178562306a36Sopenharmony_ci	spin_lock_init(&chip->c_dma.sbus_info.lock);
178662306a36Sopenharmony_ci	spin_lock_init(&chip->p_dma.sbus_info.lock);
178762306a36Sopenharmony_ci	mutex_init(&chip->mce_mutex);
178862306a36Sopenharmony_ci	mutex_init(&chip->open_mutex);
178962306a36Sopenharmony_ci	chip->op = op;
179062306a36Sopenharmony_ci	chip->regs_size = resource_size(&op->resource[0]);
179162306a36Sopenharmony_ci	memcpy(&chip->image, &snd_cs4231_original_image,
179262306a36Sopenharmony_ci	       sizeof(snd_cs4231_original_image));
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	chip->port = of_ioremap(&op->resource[0], 0,
179562306a36Sopenharmony_ci				chip->regs_size, "cs4231");
179662306a36Sopenharmony_ci	if (!chip->port) {
179762306a36Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev);
179862306a36Sopenharmony_ci		return -EIO;
179962306a36Sopenharmony_ci	}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	chip->c_dma.sbus_info.regs = chip->port;
180262306a36Sopenharmony_ci	chip->p_dma.sbus_info.regs = chip->port;
180362306a36Sopenharmony_ci	chip->c_dma.sbus_info.dir = APC_RECORD;
180462306a36Sopenharmony_ci	chip->p_dma.sbus_info.dir = APC_PLAY;
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	chip->p_dma.prepare = sbus_dma_prepare;
180762306a36Sopenharmony_ci	chip->p_dma.enable = sbus_dma_enable;
180862306a36Sopenharmony_ci	chip->p_dma.request = sbus_dma_request;
180962306a36Sopenharmony_ci	chip->p_dma.address = sbus_dma_addr;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	chip->c_dma.prepare = sbus_dma_prepare;
181262306a36Sopenharmony_ci	chip->c_dma.enable = sbus_dma_enable;
181362306a36Sopenharmony_ci	chip->c_dma.request = sbus_dma_request;
181462306a36Sopenharmony_ci	chip->c_dma.address = sbus_dma_addr;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	if (request_irq(op->archdata.irqs[0], snd_cs4231_sbus_interrupt,
181762306a36Sopenharmony_ci			IRQF_SHARED, "cs4231", chip)) {
181862306a36Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n",
181962306a36Sopenharmony_ci			    dev, op->archdata.irqs[0]);
182062306a36Sopenharmony_ci		snd_cs4231_sbus_free(chip);
182162306a36Sopenharmony_ci		return -EBUSY;
182262306a36Sopenharmony_ci	}
182362306a36Sopenharmony_ci	chip->irq[0] = op->archdata.irqs[0];
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	if (snd_cs4231_probe(chip) < 0) {
182662306a36Sopenharmony_ci		snd_cs4231_sbus_free(chip);
182762306a36Sopenharmony_ci		return -ENODEV;
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci	snd_cs4231_init(chip);
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
183262306a36Sopenharmony_ci			     chip, &snd_cs4231_sbus_dev_ops);
183362306a36Sopenharmony_ci	if (err < 0) {
183462306a36Sopenharmony_ci		snd_cs4231_sbus_free(chip);
183562306a36Sopenharmony_ci		return err;
183662306a36Sopenharmony_ci	}
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	return 0;
183962306a36Sopenharmony_ci}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_cistatic int cs4231_sbus_probe(struct platform_device *op)
184262306a36Sopenharmony_ci{
184362306a36Sopenharmony_ci	struct resource *rp = &op->resource[0];
184462306a36Sopenharmony_ci	struct snd_card *card;
184562306a36Sopenharmony_ci	int err;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	err = cs4231_attach_begin(op, &card);
184862306a36Sopenharmony_ci	if (err)
184962306a36Sopenharmony_ci		return err;
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
185262306a36Sopenharmony_ci		card->shortname,
185362306a36Sopenharmony_ci		rp->flags & 0xffL,
185462306a36Sopenharmony_ci		(unsigned long long)rp->start,
185562306a36Sopenharmony_ci		op->archdata.irqs[0]);
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	err = snd_cs4231_sbus_create(card, op, dev);
185862306a36Sopenharmony_ci	if (err < 0) {
185962306a36Sopenharmony_ci		snd_card_free(card);
186062306a36Sopenharmony_ci		return err;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	return cs4231_attach_finish(card);
186462306a36Sopenharmony_ci}
186562306a36Sopenharmony_ci#endif
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci#ifdef EBUS_SUPPORT
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_cistatic void snd_cs4231_ebus_play_callback(struct ebus_dma_info *p, int event,
187062306a36Sopenharmony_ci					  void *cookie)
187162306a36Sopenharmony_ci{
187262306a36Sopenharmony_ci	struct snd_cs4231 *chip = cookie;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	snd_cs4231_play_callback(chip);
187562306a36Sopenharmony_ci}
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_cistatic void snd_cs4231_ebus_capture_callback(struct ebus_dma_info *p,
187862306a36Sopenharmony_ci					     int event, void *cookie)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	struct snd_cs4231 *chip = cookie;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	snd_cs4231_capture_callback(chip);
188362306a36Sopenharmony_ci}
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci/*
188662306a36Sopenharmony_ci * EBUS DMA wrappers
188762306a36Sopenharmony_ci */
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_cistatic int _ebus_dma_request(struct cs4231_dma_control *dma_cont,
189062306a36Sopenharmony_ci			     dma_addr_t bus_addr, size_t len)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	return ebus_dma_request(&dma_cont->ebus_info, bus_addr, len);
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic void _ebus_dma_enable(struct cs4231_dma_control *dma_cont, int on)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	ebus_dma_enable(&dma_cont->ebus_info, on);
189862306a36Sopenharmony_ci}
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_cistatic void _ebus_dma_prepare(struct cs4231_dma_control *dma_cont, int dir)
190162306a36Sopenharmony_ci{
190262306a36Sopenharmony_ci	ebus_dma_prepare(&dma_cont->ebus_info, dir);
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_cistatic unsigned int _ebus_dma_addr(struct cs4231_dma_control *dma_cont)
190662306a36Sopenharmony_ci{
190762306a36Sopenharmony_ci	return ebus_dma_addr(&dma_cont->ebus_info);
190862306a36Sopenharmony_ci}
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci/*
191162306a36Sopenharmony_ci * Init and exit routines
191262306a36Sopenharmony_ci */
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_cistatic int snd_cs4231_ebus_free(struct snd_cs4231 *chip)
191562306a36Sopenharmony_ci{
191662306a36Sopenharmony_ci	struct platform_device *op = chip->op;
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	if (chip->c_dma.ebus_info.regs) {
191962306a36Sopenharmony_ci		ebus_dma_unregister(&chip->c_dma.ebus_info);
192062306a36Sopenharmony_ci		of_iounmap(&op->resource[2], chip->c_dma.ebus_info.regs, 0x10);
192162306a36Sopenharmony_ci	}
192262306a36Sopenharmony_ci	if (chip->p_dma.ebus_info.regs) {
192362306a36Sopenharmony_ci		ebus_dma_unregister(&chip->p_dma.ebus_info);
192462306a36Sopenharmony_ci		of_iounmap(&op->resource[1], chip->p_dma.ebus_info.regs, 0x10);
192562306a36Sopenharmony_ci	}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (chip->port)
192862306a36Sopenharmony_ci		of_iounmap(&op->resource[0], chip->port, 0x10);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	return 0;
193162306a36Sopenharmony_ci}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_cistatic int snd_cs4231_ebus_dev_free(struct snd_device *device)
193462306a36Sopenharmony_ci{
193562306a36Sopenharmony_ci	struct snd_cs4231 *cp = device->device_data;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	return snd_cs4231_ebus_free(cp);
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_cistatic const struct snd_device_ops snd_cs4231_ebus_dev_ops = {
194162306a36Sopenharmony_ci	.dev_free	=	snd_cs4231_ebus_dev_free,
194262306a36Sopenharmony_ci};
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_cistatic int snd_cs4231_ebus_create(struct snd_card *card,
194562306a36Sopenharmony_ci				  struct platform_device *op,
194662306a36Sopenharmony_ci				  int dev)
194762306a36Sopenharmony_ci{
194862306a36Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
194962306a36Sopenharmony_ci	int err;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	spin_lock_init(&chip->lock);
195262306a36Sopenharmony_ci	spin_lock_init(&chip->c_dma.ebus_info.lock);
195362306a36Sopenharmony_ci	spin_lock_init(&chip->p_dma.ebus_info.lock);
195462306a36Sopenharmony_ci	mutex_init(&chip->mce_mutex);
195562306a36Sopenharmony_ci	mutex_init(&chip->open_mutex);
195662306a36Sopenharmony_ci	chip->flags |= CS4231_FLAG_EBUS;
195762306a36Sopenharmony_ci	chip->op = op;
195862306a36Sopenharmony_ci	memcpy(&chip->image, &snd_cs4231_original_image,
195962306a36Sopenharmony_ci	       sizeof(snd_cs4231_original_image));
196062306a36Sopenharmony_ci	strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)");
196162306a36Sopenharmony_ci	chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
196262306a36Sopenharmony_ci	chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback;
196362306a36Sopenharmony_ci	chip->c_dma.ebus_info.client_cookie = chip;
196462306a36Sopenharmony_ci	chip->c_dma.ebus_info.irq = op->archdata.irqs[0];
196562306a36Sopenharmony_ci	strcpy(chip->p_dma.ebus_info.name, "cs4231(play)");
196662306a36Sopenharmony_ci	chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
196762306a36Sopenharmony_ci	chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback;
196862306a36Sopenharmony_ci	chip->p_dma.ebus_info.client_cookie = chip;
196962306a36Sopenharmony_ci	chip->p_dma.ebus_info.irq = op->archdata.irqs[1];
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	chip->p_dma.prepare = _ebus_dma_prepare;
197262306a36Sopenharmony_ci	chip->p_dma.enable = _ebus_dma_enable;
197362306a36Sopenharmony_ci	chip->p_dma.request = _ebus_dma_request;
197462306a36Sopenharmony_ci	chip->p_dma.address = _ebus_dma_addr;
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	chip->c_dma.prepare = _ebus_dma_prepare;
197762306a36Sopenharmony_ci	chip->c_dma.enable = _ebus_dma_enable;
197862306a36Sopenharmony_ci	chip->c_dma.request = _ebus_dma_request;
197962306a36Sopenharmony_ci	chip->c_dma.address = _ebus_dma_addr;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	chip->port = of_ioremap(&op->resource[0], 0, 0x10, "cs4231");
198262306a36Sopenharmony_ci	chip->p_dma.ebus_info.regs =
198362306a36Sopenharmony_ci		of_ioremap(&op->resource[1], 0, 0x10, "cs4231_pdma");
198462306a36Sopenharmony_ci	chip->c_dma.ebus_info.regs =
198562306a36Sopenharmony_ci		of_ioremap(&op->resource[2], 0, 0x10, "cs4231_cdma");
198662306a36Sopenharmony_ci	if (!chip->port || !chip->p_dma.ebus_info.regs ||
198762306a36Sopenharmony_ci	    !chip->c_dma.ebus_info.regs) {
198862306a36Sopenharmony_ci		snd_cs4231_ebus_free(chip);
198962306a36Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev);
199062306a36Sopenharmony_ci		return -EIO;
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	if (ebus_dma_register(&chip->c_dma.ebus_info)) {
199462306a36Sopenharmony_ci		snd_cs4231_ebus_free(chip);
199562306a36Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to register EBUS capture DMA\n",
199662306a36Sopenharmony_ci			    dev);
199762306a36Sopenharmony_ci		return -EBUSY;
199862306a36Sopenharmony_ci	}
199962306a36Sopenharmony_ci	if (ebus_dma_irq_enable(&chip->c_dma.ebus_info, 1)) {
200062306a36Sopenharmony_ci		snd_cs4231_ebus_free(chip);
200162306a36Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to enable EBUS capture IRQ\n",
200262306a36Sopenharmony_ci			    dev);
200362306a36Sopenharmony_ci		return -EBUSY;
200462306a36Sopenharmony_ci	}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	if (ebus_dma_register(&chip->p_dma.ebus_info)) {
200762306a36Sopenharmony_ci		snd_cs4231_ebus_free(chip);
200862306a36Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to register EBUS play DMA\n",
200962306a36Sopenharmony_ci			    dev);
201062306a36Sopenharmony_ci		return -EBUSY;
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci	if (ebus_dma_irq_enable(&chip->p_dma.ebus_info, 1)) {
201362306a36Sopenharmony_ci		snd_cs4231_ebus_free(chip);
201462306a36Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to enable EBUS play IRQ\n", dev);
201562306a36Sopenharmony_ci		return -EBUSY;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	if (snd_cs4231_probe(chip) < 0) {
201962306a36Sopenharmony_ci		snd_cs4231_ebus_free(chip);
202062306a36Sopenharmony_ci		return -ENODEV;
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci	snd_cs4231_init(chip);
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
202562306a36Sopenharmony_ci			     chip, &snd_cs4231_ebus_dev_ops);
202662306a36Sopenharmony_ci	if (err < 0) {
202762306a36Sopenharmony_ci		snd_cs4231_ebus_free(chip);
202862306a36Sopenharmony_ci		return err;
202962306a36Sopenharmony_ci	}
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	return 0;
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_cistatic int cs4231_ebus_probe(struct platform_device *op)
203562306a36Sopenharmony_ci{
203662306a36Sopenharmony_ci	struct snd_card *card;
203762306a36Sopenharmony_ci	int err;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	err = cs4231_attach_begin(op, &card);
204062306a36Sopenharmony_ci	if (err)
204162306a36Sopenharmony_ci		return err;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	sprintf(card->longname, "%s at 0x%llx, irq %d",
204462306a36Sopenharmony_ci		card->shortname,
204562306a36Sopenharmony_ci		op->resource[0].start,
204662306a36Sopenharmony_ci		op->archdata.irqs[0]);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	err = snd_cs4231_ebus_create(card, op, dev);
204962306a36Sopenharmony_ci	if (err < 0) {
205062306a36Sopenharmony_ci		snd_card_free(card);
205162306a36Sopenharmony_ci		return err;
205262306a36Sopenharmony_ci	}
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	return cs4231_attach_finish(card);
205562306a36Sopenharmony_ci}
205662306a36Sopenharmony_ci#endif
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_cistatic int cs4231_probe(struct platform_device *op)
205962306a36Sopenharmony_ci{
206062306a36Sopenharmony_ci#ifdef EBUS_SUPPORT
206162306a36Sopenharmony_ci	if (of_node_name_eq(op->dev.of_node->parent, "ebus"))
206262306a36Sopenharmony_ci		return cs4231_ebus_probe(op);
206362306a36Sopenharmony_ci#endif
206462306a36Sopenharmony_ci#ifdef SBUS_SUPPORT
206562306a36Sopenharmony_ci	if (of_node_name_eq(op->dev.of_node->parent, "sbus") ||
206662306a36Sopenharmony_ci	    of_node_name_eq(op->dev.of_node->parent, "sbi"))
206762306a36Sopenharmony_ci		return cs4231_sbus_probe(op);
206862306a36Sopenharmony_ci#endif
206962306a36Sopenharmony_ci	return -ENODEV;
207062306a36Sopenharmony_ci}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_cistatic void cs4231_remove(struct platform_device *op)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	struct snd_cs4231 *chip = dev_get_drvdata(&op->dev);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	snd_card_free(chip->card);
207762306a36Sopenharmony_ci}
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_cistatic const struct of_device_id cs4231_match[] = {
208062306a36Sopenharmony_ci	{
208162306a36Sopenharmony_ci		.name = "SUNW,CS4231",
208262306a36Sopenharmony_ci	},
208362306a36Sopenharmony_ci	{
208462306a36Sopenharmony_ci		.name = "audio",
208562306a36Sopenharmony_ci		.compatible = "SUNW,CS4231",
208662306a36Sopenharmony_ci	},
208762306a36Sopenharmony_ci	{},
208862306a36Sopenharmony_ci};
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cs4231_match);
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_cistatic struct platform_driver cs4231_driver = {
209362306a36Sopenharmony_ci	.driver = {
209462306a36Sopenharmony_ci		.name = "audio",
209562306a36Sopenharmony_ci		.of_match_table = cs4231_match,
209662306a36Sopenharmony_ci	},
209762306a36Sopenharmony_ci	.probe		= cs4231_probe,
209862306a36Sopenharmony_ci	.remove_new	= cs4231_remove,
209962306a36Sopenharmony_ci};
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_cimodule_platform_driver(cs4231_driver);
2102