18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for CS4231 sound chips found on Sparcs.
48c2ecf20Sopenharmony_ci * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Based entirely upon drivers/sbus/audio/cs4231.c which is:
78c2ecf20Sopenharmony_ci * Copyright (C) 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu)
88c2ecf20Sopenharmony_ci * and also sound/isa/cs423x/cs4231_lib.c which is:
98c2ecf20Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
188c2ecf20Sopenharmony_ci#include <linux/irq.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/of.h>
218c2ecf20Sopenharmony_ci#include <linux/of_device.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <sound/core.h>
248c2ecf20Sopenharmony_ci#include <sound/pcm.h>
258c2ecf20Sopenharmony_ci#include <sound/info.h>
268c2ecf20Sopenharmony_ci#include <sound/control.h>
278c2ecf20Sopenharmony_ci#include <sound/timer.h>
288c2ecf20Sopenharmony_ci#include <sound/initval.h>
298c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#ifdef CONFIG_SBUS
328c2ecf20Sopenharmony_ci#define SBUS_SUPPORT
338c2ecf20Sopenharmony_ci#endif
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#if defined(CONFIG_PCI) && defined(CONFIG_SPARC64)
368c2ecf20Sopenharmony_ci#define EBUS_SUPPORT
378c2ecf20Sopenharmony_ci#include <linux/pci.h>
388c2ecf20Sopenharmony_ci#include <asm/ebus_dma.h>
398c2ecf20Sopenharmony_ci#endif
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
428c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
438c2ecf20Sopenharmony_ci/* Enable this card */
448c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for Sun CS4231 soundcard.");
488c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444);
498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for Sun CS4231 soundcard.");
508c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444);
518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable Sun CS4231 soundcard.");
528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaroslav Kysela, Derrick J. Brashear and David S. Miller");
538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sun CS4231");
548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
558c2ecf20Sopenharmony_ciMODULE_SUPPORTED_DEVICE("{{Sun,CS4231}}");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#ifdef SBUS_SUPPORT
588c2ecf20Sopenharmony_cistruct sbus_dma_info {
598c2ecf20Sopenharmony_ci       spinlock_t	lock;	/* DMA access lock */
608c2ecf20Sopenharmony_ci       int		dir;
618c2ecf20Sopenharmony_ci       void __iomem	*regs;
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct snd_cs4231;
668c2ecf20Sopenharmony_cistruct cs4231_dma_control {
678c2ecf20Sopenharmony_ci	void		(*prepare)(struct cs4231_dma_control *dma_cont,
688c2ecf20Sopenharmony_ci				   int dir);
698c2ecf20Sopenharmony_ci	void		(*enable)(struct cs4231_dma_control *dma_cont, int on);
708c2ecf20Sopenharmony_ci	int		(*request)(struct cs4231_dma_control *dma_cont,
718c2ecf20Sopenharmony_ci				   dma_addr_t bus_addr, size_t len);
728c2ecf20Sopenharmony_ci	unsigned int	(*address)(struct cs4231_dma_control *dma_cont);
738c2ecf20Sopenharmony_ci#ifdef EBUS_SUPPORT
748c2ecf20Sopenharmony_ci	struct		ebus_dma_info	ebus_info;
758c2ecf20Sopenharmony_ci#endif
768c2ecf20Sopenharmony_ci#ifdef SBUS_SUPPORT
778c2ecf20Sopenharmony_ci	struct		sbus_dma_info	sbus_info;
788c2ecf20Sopenharmony_ci#endif
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistruct snd_cs4231 {
828c2ecf20Sopenharmony_ci	spinlock_t		lock;	/* registers access lock */
838c2ecf20Sopenharmony_ci	void __iomem		*port;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	struct cs4231_dma_control	p_dma;
868c2ecf20Sopenharmony_ci	struct cs4231_dma_control	c_dma;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	u32			flags;
898c2ecf20Sopenharmony_ci#define CS4231_FLAG_EBUS	0x00000001
908c2ecf20Sopenharmony_ci#define CS4231_FLAG_PLAYBACK	0x00000002
918c2ecf20Sopenharmony_ci#define CS4231_FLAG_CAPTURE	0x00000004
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	struct snd_card		*card;
948c2ecf20Sopenharmony_ci	struct snd_pcm		*pcm;
958c2ecf20Sopenharmony_ci	struct snd_pcm_substream	*playback_substream;
968c2ecf20Sopenharmony_ci	unsigned int		p_periods_sent;
978c2ecf20Sopenharmony_ci	struct snd_pcm_substream	*capture_substream;
988c2ecf20Sopenharmony_ci	unsigned int		c_periods_sent;
998c2ecf20Sopenharmony_ci	struct snd_timer	*timer;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	unsigned short mode;
1028c2ecf20Sopenharmony_ci#define CS4231_MODE_NONE	0x0000
1038c2ecf20Sopenharmony_ci#define CS4231_MODE_PLAY	0x0001
1048c2ecf20Sopenharmony_ci#define CS4231_MODE_RECORD	0x0002
1058c2ecf20Sopenharmony_ci#define CS4231_MODE_TIMER	0x0004
1068c2ecf20Sopenharmony_ci#define CS4231_MODE_OPEN	(CS4231_MODE_PLAY | CS4231_MODE_RECORD | \
1078c2ecf20Sopenharmony_ci				 CS4231_MODE_TIMER)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	unsigned char		image[32];	/* registers image */
1108c2ecf20Sopenharmony_ci	int			mce_bit;
1118c2ecf20Sopenharmony_ci	int			calibrate_mute;
1128c2ecf20Sopenharmony_ci	struct mutex		mce_mutex;	/* mutex for mce register */
1138c2ecf20Sopenharmony_ci	struct mutex		open_mutex;	/* mutex for ALSA open/close */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	struct platform_device	*op;
1168c2ecf20Sopenharmony_ci	unsigned int		irq[2];
1178c2ecf20Sopenharmony_ci	unsigned int		regs_size;
1188c2ecf20Sopenharmony_ci	struct snd_cs4231	*next;
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for
1228c2ecf20Sopenharmony_ci * now....  -DaveM
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/* IO ports */
1268c2ecf20Sopenharmony_ci#include <sound/cs4231-regs.h>
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/* XXX offsets are different than PC ISA chips... */
1298c2ecf20Sopenharmony_ci#define CS4231U(chip, x)	((chip)->port + ((c_d_c_CS4231##x) << 2))
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/* SBUS DMA register defines.  */
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#define APCCSR	0x10UL	/* APC DMA CSR */
1348c2ecf20Sopenharmony_ci#define APCCVA	0x20UL	/* APC Capture DMA Address */
1358c2ecf20Sopenharmony_ci#define APCCC	0x24UL	/* APC Capture Count */
1368c2ecf20Sopenharmony_ci#define APCCNVA	0x28UL	/* APC Capture DMA Next Address */
1378c2ecf20Sopenharmony_ci#define APCCNC	0x2cUL	/* APC Capture Next Count */
1388c2ecf20Sopenharmony_ci#define APCPVA	0x30UL	/* APC Play DMA Address */
1398c2ecf20Sopenharmony_ci#define APCPC	0x34UL	/* APC Play Count */
1408c2ecf20Sopenharmony_ci#define APCPNVA	0x38UL	/* APC Play DMA Next Address */
1418c2ecf20Sopenharmony_ci#define APCPNC	0x3cUL	/* APC Play Next Count */
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* Defines for SBUS DMA-routines */
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci#define APCVA  0x0UL	/* APC DMA Address */
1468c2ecf20Sopenharmony_ci#define APCC   0x4UL	/* APC Count */
1478c2ecf20Sopenharmony_ci#define APCNVA 0x8UL	/* APC DMA Next Address */
1488c2ecf20Sopenharmony_ci#define APCNC  0xcUL	/* APC Next Count */
1498c2ecf20Sopenharmony_ci#define APC_PLAY 0x30UL	/* Play registers start at 0x30 */
1508c2ecf20Sopenharmony_ci#define APC_RECORD 0x20UL /* Record registers start at 0x20 */
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* APCCSR bits */
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#define APC_INT_PENDING 0x800000 /* Interrupt Pending */
1558c2ecf20Sopenharmony_ci#define APC_PLAY_INT    0x400000 /* Playback interrupt */
1568c2ecf20Sopenharmony_ci#define APC_CAPT_INT    0x200000 /* Capture interrupt */
1578c2ecf20Sopenharmony_ci#define APC_GENL_INT    0x100000 /* General interrupt */
1588c2ecf20Sopenharmony_ci#define APC_XINT_ENA    0x80000  /* General ext int. enable */
1598c2ecf20Sopenharmony_ci#define APC_XINT_PLAY   0x40000  /* Playback ext intr */
1608c2ecf20Sopenharmony_ci#define APC_XINT_CAPT   0x20000  /* Capture ext intr */
1618c2ecf20Sopenharmony_ci#define APC_XINT_GENL   0x10000  /* Error ext intr */
1628c2ecf20Sopenharmony_ci#define APC_XINT_EMPT   0x8000   /* Pipe empty interrupt (0 write to pva) */
1638c2ecf20Sopenharmony_ci#define APC_XINT_PEMP   0x4000   /* Play pipe empty (pva and pnva not set) */
1648c2ecf20Sopenharmony_ci#define APC_XINT_PNVA   0x2000   /* Playback NVA dirty */
1658c2ecf20Sopenharmony_ci#define APC_XINT_PENA   0x1000   /* play pipe empty Int enable */
1668c2ecf20Sopenharmony_ci#define APC_XINT_COVF   0x800    /* Cap data dropped on floor */
1678c2ecf20Sopenharmony_ci#define APC_XINT_CNVA   0x400    /* Capture NVA dirty */
1688c2ecf20Sopenharmony_ci#define APC_XINT_CEMP   0x200    /* Capture pipe empty (cva and cnva not set) */
1698c2ecf20Sopenharmony_ci#define APC_XINT_CENA   0x100    /* Cap. pipe empty int enable */
1708c2ecf20Sopenharmony_ci#define APC_PPAUSE      0x80     /* Pause the play DMA */
1718c2ecf20Sopenharmony_ci#define APC_CPAUSE      0x40     /* Pause the capture DMA */
1728c2ecf20Sopenharmony_ci#define APC_CDC_RESET   0x20     /* CODEC RESET */
1738c2ecf20Sopenharmony_ci#define APC_PDMA_READY  0x08     /* Play DMA Go */
1748c2ecf20Sopenharmony_ci#define APC_CDMA_READY  0x04     /* Capture DMA Go */
1758c2ecf20Sopenharmony_ci#define APC_CHIP_RESET  0x01     /* Reset the chip */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/* EBUS DMA register offsets  */
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci#define EBDMA_CSR	0x00UL	/* Control/Status */
1808c2ecf20Sopenharmony_ci#define EBDMA_ADDR	0x04UL	/* DMA Address */
1818c2ecf20Sopenharmony_ci#define EBDMA_COUNT	0x08UL	/* DMA Count */
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/*
1848c2ecf20Sopenharmony_ci *  Some variables
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic const unsigned char freq_bits[14] = {
1888c2ecf20Sopenharmony_ci	/* 5510 */	0x00 | CS4231_XTAL2,
1898c2ecf20Sopenharmony_ci	/* 6620 */	0x0E | CS4231_XTAL2,
1908c2ecf20Sopenharmony_ci	/* 8000 */	0x00 | CS4231_XTAL1,
1918c2ecf20Sopenharmony_ci	/* 9600 */	0x0E | CS4231_XTAL1,
1928c2ecf20Sopenharmony_ci	/* 11025 */	0x02 | CS4231_XTAL2,
1938c2ecf20Sopenharmony_ci	/* 16000 */	0x02 | CS4231_XTAL1,
1948c2ecf20Sopenharmony_ci	/* 18900 */	0x04 | CS4231_XTAL2,
1958c2ecf20Sopenharmony_ci	/* 22050 */	0x06 | CS4231_XTAL2,
1968c2ecf20Sopenharmony_ci	/* 27042 */	0x04 | CS4231_XTAL1,
1978c2ecf20Sopenharmony_ci	/* 32000 */	0x06 | CS4231_XTAL1,
1988c2ecf20Sopenharmony_ci	/* 33075 */	0x0C | CS4231_XTAL2,
1998c2ecf20Sopenharmony_ci	/* 37800 */	0x08 | CS4231_XTAL2,
2008c2ecf20Sopenharmony_ci	/* 44100 */	0x0A | CS4231_XTAL2,
2018c2ecf20Sopenharmony_ci	/* 48000 */	0x0C | CS4231_XTAL1
2028c2ecf20Sopenharmony_ci};
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic const unsigned int rates[14] = {
2058c2ecf20Sopenharmony_ci	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
2068c2ecf20Sopenharmony_ci	27042, 32000, 33075, 37800, 44100, 48000
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
2108c2ecf20Sopenharmony_ci	.count	= ARRAY_SIZE(rates),
2118c2ecf20Sopenharmony_ci	.list	= rates,
2128c2ecf20Sopenharmony_ci};
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int snd_cs4231_xrate(struct snd_pcm_runtime *runtime)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	return snd_pcm_hw_constraint_list(runtime, 0,
2178c2ecf20Sopenharmony_ci					  SNDRV_PCM_HW_PARAM_RATE,
2188c2ecf20Sopenharmony_ci					  &hw_constraints_rates);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic const unsigned char snd_cs4231_original_image[32] =
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	0x00,			/* 00/00 - lic */
2248c2ecf20Sopenharmony_ci	0x00,			/* 01/01 - ric */
2258c2ecf20Sopenharmony_ci	0x9f,			/* 02/02 - la1ic */
2268c2ecf20Sopenharmony_ci	0x9f,			/* 03/03 - ra1ic */
2278c2ecf20Sopenharmony_ci	0x9f,			/* 04/04 - la2ic */
2288c2ecf20Sopenharmony_ci	0x9f,			/* 05/05 - ra2ic */
2298c2ecf20Sopenharmony_ci	0xbf,			/* 06/06 - loc */
2308c2ecf20Sopenharmony_ci	0xbf,			/* 07/07 - roc */
2318c2ecf20Sopenharmony_ci	0x20,			/* 08/08 - pdfr */
2328c2ecf20Sopenharmony_ci	CS4231_AUTOCALIB,	/* 09/09 - ic */
2338c2ecf20Sopenharmony_ci	0x00,			/* 0a/10 - pc */
2348c2ecf20Sopenharmony_ci	0x00,			/* 0b/11 - ti */
2358c2ecf20Sopenharmony_ci	CS4231_MODE2,		/* 0c/12 - mi */
2368c2ecf20Sopenharmony_ci	0x00,			/* 0d/13 - lbc */
2378c2ecf20Sopenharmony_ci	0x00,			/* 0e/14 - pbru */
2388c2ecf20Sopenharmony_ci	0x00,			/* 0f/15 - pbrl */
2398c2ecf20Sopenharmony_ci	0x80,			/* 10/16 - afei */
2408c2ecf20Sopenharmony_ci	0x01,			/* 11/17 - afeii */
2418c2ecf20Sopenharmony_ci	0x9f,			/* 12/18 - llic */
2428c2ecf20Sopenharmony_ci	0x9f,			/* 13/19 - rlic */
2438c2ecf20Sopenharmony_ci	0x00,			/* 14/20 - tlb */
2448c2ecf20Sopenharmony_ci	0x00,			/* 15/21 - thb */
2458c2ecf20Sopenharmony_ci	0x00,			/* 16/22 - la3mic/reserved */
2468c2ecf20Sopenharmony_ci	0x00,			/* 17/23 - ra3mic/reserved */
2478c2ecf20Sopenharmony_ci	0x00,			/* 18/24 - afs */
2488c2ecf20Sopenharmony_ci	0x00,			/* 19/25 - lamoc/version */
2498c2ecf20Sopenharmony_ci	0x00,			/* 1a/26 - mioc */
2508c2ecf20Sopenharmony_ci	0x00,			/* 1b/27 - ramoc/reserved */
2518c2ecf20Sopenharmony_ci	0x20,			/* 1c/28 - cdfr */
2528c2ecf20Sopenharmony_ci	0x00,			/* 1d/29 - res4 */
2538c2ecf20Sopenharmony_ci	0x00,			/* 1e/30 - cbru */
2548c2ecf20Sopenharmony_ci	0x00,			/* 1f/31 - cbrl */
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic u8 __cs4231_readb(struct snd_cs4231 *cp, void __iomem *reg_addr)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	if (cp->flags & CS4231_FLAG_EBUS)
2608c2ecf20Sopenharmony_ci		return readb(reg_addr);
2618c2ecf20Sopenharmony_ci	else
2628c2ecf20Sopenharmony_ci		return sbus_readb(reg_addr);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void __cs4231_writeb(struct snd_cs4231 *cp, u8 val,
2668c2ecf20Sopenharmony_ci			    void __iomem *reg_addr)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	if (cp->flags & CS4231_FLAG_EBUS)
2698c2ecf20Sopenharmony_ci		return writeb(val, reg_addr);
2708c2ecf20Sopenharmony_ci	else
2718c2ecf20Sopenharmony_ci		return sbus_writeb(val, reg_addr);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/*
2758c2ecf20Sopenharmony_ci *  Basic I/O functions
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic void snd_cs4231_ready(struct snd_cs4231 *chip)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	int timeout;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	for (timeout = 250; timeout > 0; timeout--) {
2838c2ecf20Sopenharmony_ci		int val = __cs4231_readb(chip, CS4231U(chip, REGSEL));
2848c2ecf20Sopenharmony_ci		if ((val & CS4231_INIT) == 0)
2858c2ecf20Sopenharmony_ci			break;
2868c2ecf20Sopenharmony_ci		udelay(100);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void snd_cs4231_dout(struct snd_cs4231 *chip, unsigned char reg,
2918c2ecf20Sopenharmony_ci			    unsigned char value)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	snd_cs4231_ready(chip);
2948c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
2958c2ecf20Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
2968c2ecf20Sopenharmony_ci		snd_printdd("out: auto calibration time out - reg = 0x%x, "
2978c2ecf20Sopenharmony_ci			    "value = 0x%x\n",
2988c2ecf20Sopenharmony_ci			    reg, value);
2998c2ecf20Sopenharmony_ci#endif
3008c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, chip->mce_bit | reg, CS4231U(chip, REGSEL));
3018c2ecf20Sopenharmony_ci	wmb();
3028c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, value, CS4231U(chip, REG));
3038c2ecf20Sopenharmony_ci	mb();
3048c2ecf20Sopenharmony_ci}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic inline void snd_cs4231_outm(struct snd_cs4231 *chip, unsigned char reg,
3078c2ecf20Sopenharmony_ci		     unsigned char mask, unsigned char value)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	unsigned char tmp = (chip->image[reg] & mask) | value;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	chip->image[reg] = tmp;
3128c2ecf20Sopenharmony_ci	if (!chip->calibrate_mute)
3138c2ecf20Sopenharmony_ci		snd_cs4231_dout(chip, reg, tmp);
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg,
3178c2ecf20Sopenharmony_ci			   unsigned char value)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, reg, value);
3208c2ecf20Sopenharmony_ci	chip->image[reg] = value;
3218c2ecf20Sopenharmony_ci	mb();
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	snd_cs4231_ready(chip);
3278c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
3288c2ecf20Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
3298c2ecf20Sopenharmony_ci		snd_printdd("in: auto calibration time out - reg = 0x%x\n",
3308c2ecf20Sopenharmony_ci			    reg);
3318c2ecf20Sopenharmony_ci#endif
3328c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, chip->mce_bit | reg, CS4231U(chip, REGSEL));
3338c2ecf20Sopenharmony_ci	mb();
3348c2ecf20Sopenharmony_ci	return __cs4231_readb(chip, CS4231U(chip, REG));
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/*
3388c2ecf20Sopenharmony_ci *  CS4231 detection / MCE routines
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_cistatic void snd_cs4231_busy_wait(struct snd_cs4231 *chip)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	int timeout;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* looks like this sequence is proper for CS4231A chip (GUS MAX) */
3468c2ecf20Sopenharmony_ci	for (timeout = 5; timeout > 0; timeout--)
3478c2ecf20Sopenharmony_ci		__cs4231_readb(chip, CS4231U(chip, REGSEL));
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/* end of cleanup sequence */
3508c2ecf20Sopenharmony_ci	for (timeout = 500; timeout > 0; timeout--) {
3518c2ecf20Sopenharmony_ci		int val = __cs4231_readb(chip, CS4231U(chip, REGSEL));
3528c2ecf20Sopenharmony_ci		if ((val & CS4231_INIT) == 0)
3538c2ecf20Sopenharmony_ci			break;
3548c2ecf20Sopenharmony_ci		msleep(1);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic void snd_cs4231_mce_up(struct snd_cs4231 *chip)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	unsigned long flags;
3618c2ecf20Sopenharmony_ci	int timeout;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
3648c2ecf20Sopenharmony_ci	snd_cs4231_ready(chip);
3658c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
3668c2ecf20Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
3678c2ecf20Sopenharmony_ci		snd_printdd("mce_up - auto calibration time out (0)\n");
3688c2ecf20Sopenharmony_ci#endif
3698c2ecf20Sopenharmony_ci	chip->mce_bit |= CS4231_MCE;
3708c2ecf20Sopenharmony_ci	timeout = __cs4231_readb(chip, CS4231U(chip, REGSEL));
3718c2ecf20Sopenharmony_ci	if (timeout == 0x80)
3728c2ecf20Sopenharmony_ci		snd_printdd("mce_up [%p]: serious init problem - "
3738c2ecf20Sopenharmony_ci			    "codec still busy\n",
3748c2ecf20Sopenharmony_ci			    chip->port);
3758c2ecf20Sopenharmony_ci	if (!(timeout & CS4231_MCE))
3768c2ecf20Sopenharmony_ci		__cs4231_writeb(chip, chip->mce_bit | (timeout & 0x1f),
3778c2ecf20Sopenharmony_ci				CS4231U(chip, REGSEL));
3788c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void snd_cs4231_mce_down(struct snd_cs4231 *chip)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	unsigned long flags, timeout;
3848c2ecf20Sopenharmony_ci	int reg;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	snd_cs4231_busy_wait(chip);
3878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
3888c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_DEBUG
3898c2ecf20Sopenharmony_ci	if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
3908c2ecf20Sopenharmony_ci		snd_printdd("mce_down [%p] - auto calibration time out (0)\n",
3918c2ecf20Sopenharmony_ci			    CS4231U(chip, REGSEL));
3928c2ecf20Sopenharmony_ci#endif
3938c2ecf20Sopenharmony_ci	chip->mce_bit &= ~CS4231_MCE;
3948c2ecf20Sopenharmony_ci	reg = __cs4231_readb(chip, CS4231U(chip, REGSEL));
3958c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, chip->mce_bit | (reg & 0x1f),
3968c2ecf20Sopenharmony_ci			CS4231U(chip, REGSEL));
3978c2ecf20Sopenharmony_ci	if (reg == 0x80)
3988c2ecf20Sopenharmony_ci		snd_printdd("mce_down [%p]: serious init problem "
3998c2ecf20Sopenharmony_ci			    "- codec still busy\n", chip->port);
4008c2ecf20Sopenharmony_ci	if ((reg & CS4231_MCE) == 0) {
4018c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
4028c2ecf20Sopenharmony_ci		return;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low.
4078c2ecf20Sopenharmony_ci	 */
4088c2ecf20Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(250);
4098c2ecf20Sopenharmony_ci	do {
4108c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
4118c2ecf20Sopenharmony_ci		msleep(1);
4128c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
4138c2ecf20Sopenharmony_ci		reg = snd_cs4231_in(chip, CS4231_TEST_INIT);
4148c2ecf20Sopenharmony_ci		reg &= CS4231_CALIB_IN_PROGRESS;
4158c2ecf20Sopenharmony_ci	} while (reg && time_before(jiffies, timeout));
4168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (reg)
4198c2ecf20Sopenharmony_ci		snd_printk(KERN_ERR
4208c2ecf20Sopenharmony_ci			   "mce_down - auto calibration time out (2)\n");
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic void snd_cs4231_advance_dma(struct cs4231_dma_control *dma_cont,
4248c2ecf20Sopenharmony_ci				   struct snd_pcm_substream *substream,
4258c2ecf20Sopenharmony_ci				   unsigned int *periods_sent)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	while (1) {
4308c2ecf20Sopenharmony_ci		unsigned int period_size = snd_pcm_lib_period_bytes(substream);
4318c2ecf20Sopenharmony_ci		unsigned int offset = period_size * (*periods_sent);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		if (WARN_ON(period_size >= (1 << 24)))
4348c2ecf20Sopenharmony_ci			return;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		if (dma_cont->request(dma_cont,
4378c2ecf20Sopenharmony_ci				      runtime->dma_addr + offset, period_size))
4388c2ecf20Sopenharmony_ci			return;
4398c2ecf20Sopenharmony_ci		(*periods_sent) = ((*periods_sent) + 1) % runtime->periods;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic void cs4231_dma_trigger(struct snd_pcm_substream *substream,
4448c2ecf20Sopenharmony_ci			       unsigned int what, int on)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
4478c2ecf20Sopenharmony_ci	struct cs4231_dma_control *dma_cont;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (what & CS4231_PLAYBACK_ENABLE) {
4508c2ecf20Sopenharmony_ci		dma_cont = &chip->p_dma;
4518c2ecf20Sopenharmony_ci		if (on) {
4528c2ecf20Sopenharmony_ci			dma_cont->prepare(dma_cont, 0);
4538c2ecf20Sopenharmony_ci			dma_cont->enable(dma_cont, 1);
4548c2ecf20Sopenharmony_ci			snd_cs4231_advance_dma(dma_cont,
4558c2ecf20Sopenharmony_ci				chip->playback_substream,
4568c2ecf20Sopenharmony_ci				&chip->p_periods_sent);
4578c2ecf20Sopenharmony_ci		} else {
4588c2ecf20Sopenharmony_ci			dma_cont->enable(dma_cont, 0);
4598c2ecf20Sopenharmony_ci		}
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci	if (what & CS4231_RECORD_ENABLE) {
4628c2ecf20Sopenharmony_ci		dma_cont = &chip->c_dma;
4638c2ecf20Sopenharmony_ci		if (on) {
4648c2ecf20Sopenharmony_ci			dma_cont->prepare(dma_cont, 1);
4658c2ecf20Sopenharmony_ci			dma_cont->enable(dma_cont, 1);
4668c2ecf20Sopenharmony_ci			snd_cs4231_advance_dma(dma_cont,
4678c2ecf20Sopenharmony_ci				chip->capture_substream,
4688c2ecf20Sopenharmony_ci				&chip->c_periods_sent);
4698c2ecf20Sopenharmony_ci		} else {
4708c2ecf20Sopenharmony_ci			dma_cont->enable(dma_cont, 0);
4718c2ecf20Sopenharmony_ci		}
4728c2ecf20Sopenharmony_ci	}
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int snd_cs4231_trigger(struct snd_pcm_substream *substream, int cmd)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
4788c2ecf20Sopenharmony_ci	int result = 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	switch (cmd) {
4818c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4828c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4838c2ecf20Sopenharmony_ci	{
4848c2ecf20Sopenharmony_ci		unsigned int what = 0;
4858c2ecf20Sopenharmony_ci		struct snd_pcm_substream *s;
4868c2ecf20Sopenharmony_ci		unsigned long flags;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		snd_pcm_group_for_each_entry(s, substream) {
4898c2ecf20Sopenharmony_ci			if (s == chip->playback_substream) {
4908c2ecf20Sopenharmony_ci				what |= CS4231_PLAYBACK_ENABLE;
4918c2ecf20Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
4928c2ecf20Sopenharmony_ci			} else if (s == chip->capture_substream) {
4938c2ecf20Sopenharmony_ci				what |= CS4231_RECORD_ENABLE;
4948c2ecf20Sopenharmony_ci				snd_pcm_trigger_done(s, substream);
4958c2ecf20Sopenharmony_ci			}
4968c2ecf20Sopenharmony_ci		}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
4998c2ecf20Sopenharmony_ci		if (cmd == SNDRV_PCM_TRIGGER_START) {
5008c2ecf20Sopenharmony_ci			cs4231_dma_trigger(substream, what, 1);
5018c2ecf20Sopenharmony_ci			chip->image[CS4231_IFACE_CTRL] |= what;
5028c2ecf20Sopenharmony_ci		} else {
5038c2ecf20Sopenharmony_ci			cs4231_dma_trigger(substream, what, 0);
5048c2ecf20Sopenharmony_ci			chip->image[CS4231_IFACE_CTRL] &= ~what;
5058c2ecf20Sopenharmony_ci		}
5068c2ecf20Sopenharmony_ci		snd_cs4231_out(chip, CS4231_IFACE_CTRL,
5078c2ecf20Sopenharmony_ci			       chip->image[CS4231_IFACE_CTRL]);
5088c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
5098c2ecf20Sopenharmony_ci		break;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci	default:
5128c2ecf20Sopenharmony_ci		result = -EINVAL;
5138c2ecf20Sopenharmony_ci		break;
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return result;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/*
5208c2ecf20Sopenharmony_ci *  CODEC I/O
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic unsigned char snd_cs4231_get_rate(unsigned int rate)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	int i;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	for (i = 0; i < 14; i++)
5288c2ecf20Sopenharmony_ci		if (rate == rates[i])
5298c2ecf20Sopenharmony_ci			return freq_bits[i];
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	return freq_bits[13];
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic unsigned char snd_cs4231_get_format(struct snd_cs4231 *chip, int format,
5358c2ecf20Sopenharmony_ci					   int channels)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	unsigned char rformat;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	rformat = CS4231_LINEAR_8;
5408c2ecf20Sopenharmony_ci	switch (format) {
5418c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_MU_LAW:
5428c2ecf20Sopenharmony_ci		rformat = CS4231_ULAW_8;
5438c2ecf20Sopenharmony_ci		break;
5448c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_A_LAW:
5458c2ecf20Sopenharmony_ci		rformat = CS4231_ALAW_8;
5468c2ecf20Sopenharmony_ci		break;
5478c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_LE:
5488c2ecf20Sopenharmony_ci		rformat = CS4231_LINEAR_16;
5498c2ecf20Sopenharmony_ci		break;
5508c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_S16_BE:
5518c2ecf20Sopenharmony_ci		rformat = CS4231_LINEAR_16_BIG;
5528c2ecf20Sopenharmony_ci		break;
5538c2ecf20Sopenharmony_ci	case SNDRV_PCM_FORMAT_IMA_ADPCM:
5548c2ecf20Sopenharmony_ci		rformat = CS4231_ADPCM_16;
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci	if (channels > 1)
5588c2ecf20Sopenharmony_ci		rformat |= CS4231_STEREO;
5598c2ecf20Sopenharmony_ci	return rformat;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic void snd_cs4231_calibrate_mute(struct snd_cs4231 *chip, int mute)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	unsigned long flags;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	mute = mute ? 1 : 0;
5678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
5688c2ecf20Sopenharmony_ci	if (chip->calibrate_mute == mute) {
5698c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
5708c2ecf20Sopenharmony_ci		return;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci	if (!mute) {
5738c2ecf20Sopenharmony_ci		snd_cs4231_dout(chip, CS4231_LEFT_INPUT,
5748c2ecf20Sopenharmony_ci				chip->image[CS4231_LEFT_INPUT]);
5758c2ecf20Sopenharmony_ci		snd_cs4231_dout(chip, CS4231_RIGHT_INPUT,
5768c2ecf20Sopenharmony_ci				chip->image[CS4231_RIGHT_INPUT]);
5778c2ecf20Sopenharmony_ci		snd_cs4231_dout(chip, CS4231_LOOPBACK,
5788c2ecf20Sopenharmony_ci				chip->image[CS4231_LOOPBACK]);
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT,
5818c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]);
5828c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT,
5838c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]);
5848c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT,
5858c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]);
5868c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT,
5878c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]);
5888c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT,
5898c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]);
5908c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT,
5918c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]);
5928c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN,
5938c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]);
5948c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN,
5958c2ecf20Sopenharmony_ci			mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]);
5968c2ecf20Sopenharmony_ci	snd_cs4231_dout(chip, CS4231_MONO_CTRL,
5978c2ecf20Sopenharmony_ci			mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
5988c2ecf20Sopenharmony_ci	chip->calibrate_mute = mute;
5998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_cistatic void snd_cs4231_playback_format(struct snd_cs4231 *chip,
6038c2ecf20Sopenharmony_ci				       struct snd_pcm_hw_params *params,
6048c2ecf20Sopenharmony_ci				       unsigned char pdfr)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	unsigned long flags;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	mutex_lock(&chip->mce_mutex);
6098c2ecf20Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 1);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	snd_cs4231_mce_up(chip);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
6148c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
6158c2ecf20Sopenharmony_ci		       (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ?
6168c2ecf20Sopenharmony_ci		       (pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) :
6178c2ecf20Sopenharmony_ci		       pdfr);
6188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 0);
6238c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mce_mutex);
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic void snd_cs4231_capture_format(struct snd_cs4231 *chip,
6278c2ecf20Sopenharmony_ci				      struct snd_pcm_hw_params *params,
6288c2ecf20Sopenharmony_ci				      unsigned char cdfr)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	unsigned long flags;
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	mutex_lock(&chip->mce_mutex);
6338c2ecf20Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 1);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	snd_cs4231_mce_up(chip);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
6388c2ecf20Sopenharmony_ci	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
6398c2ecf20Sopenharmony_ci		snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
6408c2ecf20Sopenharmony_ci			       ((chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) |
6418c2ecf20Sopenharmony_ci			       (cdfr & 0x0f));
6428c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
6438c2ecf20Sopenharmony_ci		snd_cs4231_mce_down(chip);
6448c2ecf20Sopenharmony_ci		snd_cs4231_mce_up(chip);
6458c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr);
6488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 0);
6538c2ecf20Sopenharmony_ci	mutex_unlock(&chip->mce_mutex);
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci/*
6578c2ecf20Sopenharmony_ci *  Timer interface
6588c2ecf20Sopenharmony_ci */
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistatic unsigned long snd_cs4231_timer_resolution(struct snd_timer *timer)
6618c2ecf20Sopenharmony_ci{
6628c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic int snd_cs4231_timer_start(struct snd_timer *timer)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	unsigned long flags;
6708c2ecf20Sopenharmony_ci	unsigned int ticks;
6718c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
6748c2ecf20Sopenharmony_ci	ticks = timer->sticks;
6758c2ecf20Sopenharmony_ci	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
6768c2ecf20Sopenharmony_ci	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
6778c2ecf20Sopenharmony_ci	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
6788c2ecf20Sopenharmony_ci		snd_cs4231_out(chip, CS4231_TIMER_HIGH,
6798c2ecf20Sopenharmony_ci			       chip->image[CS4231_TIMER_HIGH] =
6808c2ecf20Sopenharmony_ci			       (unsigned char) (ticks >> 8));
6818c2ecf20Sopenharmony_ci		snd_cs4231_out(chip, CS4231_TIMER_LOW,
6828c2ecf20Sopenharmony_ci			       chip->image[CS4231_TIMER_LOW] =
6838c2ecf20Sopenharmony_ci			       (unsigned char) ticks);
6848c2ecf20Sopenharmony_ci		snd_cs4231_out(chip, CS4231_ALT_FEATURE_1,
6858c2ecf20Sopenharmony_ci			       chip->image[CS4231_ALT_FEATURE_1] |
6868c2ecf20Sopenharmony_ci					CS4231_TIMER_ENABLE);
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return 0;
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic int snd_cs4231_timer_stop(struct snd_timer *timer)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	unsigned long flags;
6968c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
6998c2ecf20Sopenharmony_ci	chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE;
7008c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1,
7018c2ecf20Sopenharmony_ci		       chip->image[CS4231_ALT_FEATURE_1]);
7028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	return 0;
7058c2ecf20Sopenharmony_ci}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic void snd_cs4231_init(struct snd_cs4231 *chip)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	unsigned long flags;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
7148c2ecf20Sopenharmony_ci	snd_printdd("init: (1)\n");
7158c2ecf20Sopenharmony_ci#endif
7168c2ecf20Sopenharmony_ci	snd_cs4231_mce_up(chip);
7178c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
7188c2ecf20Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
7198c2ecf20Sopenharmony_ci					    CS4231_PLAYBACK_PIO |
7208c2ecf20Sopenharmony_ci					    CS4231_RECORD_ENABLE |
7218c2ecf20Sopenharmony_ci					    CS4231_RECORD_PIO |
7228c2ecf20Sopenharmony_ci					    CS4231_CALIB_MODE);
7238c2ecf20Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
7248c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
7258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
7268c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
7298c2ecf20Sopenharmony_ci	snd_printdd("init: (2)\n");
7308c2ecf20Sopenharmony_ci#endif
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	snd_cs4231_mce_up(chip);
7338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
7348c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1,
7358c2ecf20Sopenharmony_ci			chip->image[CS4231_ALT_FEATURE_1]);
7368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
7378c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
7408c2ecf20Sopenharmony_ci	snd_printdd("init: (3) - afei = 0x%x\n",
7418c2ecf20Sopenharmony_ci		    chip->image[CS4231_ALT_FEATURE_1]);
7428c2ecf20Sopenharmony_ci#endif
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
7458c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_ALT_FEATURE_2,
7468c2ecf20Sopenharmony_ci			chip->image[CS4231_ALT_FEATURE_2]);
7478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	snd_cs4231_mce_up(chip);
7508c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
7518c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT,
7528c2ecf20Sopenharmony_ci			chip->image[CS4231_PLAYBK_FORMAT]);
7538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
7548c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
7578c2ecf20Sopenharmony_ci	snd_printdd("init: (4)\n");
7588c2ecf20Sopenharmony_ci#endif
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	snd_cs4231_mce_up(chip);
7618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
7628c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]);
7638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
7648c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci#ifdef SNDRV_DEBUG_MCE
7678c2ecf20Sopenharmony_ci	snd_printdd("init: (5)\n");
7688c2ecf20Sopenharmony_ci#endif
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic int snd_cs4231_open(struct snd_cs4231 *chip, unsigned int mode)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	unsigned long flags;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	mutex_lock(&chip->open_mutex);
7768c2ecf20Sopenharmony_ci	if ((chip->mode & mode)) {
7778c2ecf20Sopenharmony_ci		mutex_unlock(&chip->open_mutex);
7788c2ecf20Sopenharmony_ci		return -EAGAIN;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci	if (chip->mode & CS4231_MODE_OPEN) {
7818c2ecf20Sopenharmony_ci		chip->mode |= mode;
7828c2ecf20Sopenharmony_ci		mutex_unlock(&chip->open_mutex);
7838c2ecf20Sopenharmony_ci		return 0;
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci	/* ok. now enable and ack CODEC IRQ */
7868c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
7878c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
7888c2ecf20Sopenharmony_ci		       CS4231_RECORD_IRQ |
7898c2ecf20Sopenharmony_ci		       CS4231_TIMER_IRQ);
7908c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
7918c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
7928c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ |
7958c2ecf20Sopenharmony_ci		       CS4231_RECORD_IRQ |
7968c2ecf20Sopenharmony_ci		       CS4231_TIMER_IRQ);
7978c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	chip->mode = mode;
8028c2ecf20Sopenharmony_ci	mutex_unlock(&chip->open_mutex);
8038c2ecf20Sopenharmony_ci	return 0;
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_cistatic void snd_cs4231_close(struct snd_cs4231 *chip, unsigned int mode)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	unsigned long flags;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	mutex_lock(&chip->open_mutex);
8118c2ecf20Sopenharmony_ci	chip->mode &= ~mode;
8128c2ecf20Sopenharmony_ci	if (chip->mode & CS4231_MODE_OPEN) {
8138c2ecf20Sopenharmony_ci		mutex_unlock(&chip->open_mutex);
8148c2ecf20Sopenharmony_ci		return;
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 1);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* disable IRQ */
8198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
8208c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
8218c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
8228c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	/* now disable record & playback */
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (chip->image[CS4231_IFACE_CTRL] &
8278c2ecf20Sopenharmony_ci	    (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
8288c2ecf20Sopenharmony_ci	     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
8298c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
8308c2ecf20Sopenharmony_ci		snd_cs4231_mce_up(chip);
8318c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
8328c2ecf20Sopenharmony_ci		chip->image[CS4231_IFACE_CTRL] &=
8338c2ecf20Sopenharmony_ci			~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
8348c2ecf20Sopenharmony_ci			  CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
8358c2ecf20Sopenharmony_ci		snd_cs4231_out(chip, CS4231_IFACE_CTRL,
8368c2ecf20Sopenharmony_ci				chip->image[CS4231_IFACE_CTRL]);
8378c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&chip->lock, flags);
8388c2ecf20Sopenharmony_ci		snd_cs4231_mce_down(chip);
8398c2ecf20Sopenharmony_ci		spin_lock_irqsave(&chip->lock, flags);
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	/* clear IRQ again */
8438c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0);
8448c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
8458c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));	/* clear IRQ */
8468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	snd_cs4231_calibrate_mute(chip, 0);
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	chip->mode = 0;
8518c2ecf20Sopenharmony_ci	mutex_unlock(&chip->open_mutex);
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci/*
8558c2ecf20Sopenharmony_ci *  timer open/close
8568c2ecf20Sopenharmony_ci */
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_cistatic int snd_cs4231_timer_open(struct snd_timer *timer)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
8618c2ecf20Sopenharmony_ci	snd_cs4231_open(chip, CS4231_MODE_TIMER);
8628c2ecf20Sopenharmony_ci	return 0;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic int snd_cs4231_timer_close(struct snd_timer *timer)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_timer_chip(timer);
8688c2ecf20Sopenharmony_ci	snd_cs4231_close(chip, CS4231_MODE_TIMER);
8698c2ecf20Sopenharmony_ci	return 0;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_cistatic const struct snd_timer_hardware snd_cs4231_timer_table = {
8738c2ecf20Sopenharmony_ci	.flags		=	SNDRV_TIMER_HW_AUTO,
8748c2ecf20Sopenharmony_ci	.resolution	=	9945,
8758c2ecf20Sopenharmony_ci	.ticks		=	65535,
8768c2ecf20Sopenharmony_ci	.open		=	snd_cs4231_timer_open,
8778c2ecf20Sopenharmony_ci	.close		=	snd_cs4231_timer_close,
8788c2ecf20Sopenharmony_ci	.c_resolution	=	snd_cs4231_timer_resolution,
8798c2ecf20Sopenharmony_ci	.start		=	snd_cs4231_timer_start,
8808c2ecf20Sopenharmony_ci	.stop		=	snd_cs4231_timer_stop,
8818c2ecf20Sopenharmony_ci};
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci/*
8848c2ecf20Sopenharmony_ci *  ok.. exported functions..
8858c2ecf20Sopenharmony_ci */
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_cistatic int snd_cs4231_playback_hw_params(struct snd_pcm_substream *substream,
8888c2ecf20Sopenharmony_ci					 struct snd_pcm_hw_params *hw_params)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
8918c2ecf20Sopenharmony_ci	unsigned char new_pdfr;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params),
8948c2ecf20Sopenharmony_ci					 params_channels(hw_params)) |
8958c2ecf20Sopenharmony_ci		snd_cs4231_get_rate(params_rate(hw_params));
8968c2ecf20Sopenharmony_ci	snd_cs4231_playback_format(chip, hw_params, new_pdfr);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	return 0;
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cistatic int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
9048c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
9058c2ecf20Sopenharmony_ci	unsigned long flags;
9068c2ecf20Sopenharmony_ci	int ret = 0;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
9118c2ecf20Sopenharmony_ci					    CS4231_PLAYBACK_PIO);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	if (WARN_ON(runtime->period_size > 0xffff + 1)) {
9148c2ecf20Sopenharmony_ci		ret = -EINVAL;
9158c2ecf20Sopenharmony_ci		goto out;
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	chip->p_periods_sent = 0;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ciout:
9218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	return ret;
9248c2ecf20Sopenharmony_ci}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_cistatic int snd_cs4231_capture_hw_params(struct snd_pcm_substream *substream,
9278c2ecf20Sopenharmony_ci					struct snd_pcm_hw_params *hw_params)
9288c2ecf20Sopenharmony_ci{
9298c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
9308c2ecf20Sopenharmony_ci	unsigned char new_cdfr;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params),
9338c2ecf20Sopenharmony_ci					 params_channels(hw_params)) |
9348c2ecf20Sopenharmony_ci		snd_cs4231_get_rate(params_rate(hw_params));
9358c2ecf20Sopenharmony_ci	snd_cs4231_capture_format(chip, hw_params, new_cdfr);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	return 0;
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_cistatic int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
9438c2ecf20Sopenharmony_ci	unsigned long flags;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
9468c2ecf20Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE |
9478c2ecf20Sopenharmony_ci					    CS4231_RECORD_PIO);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	chip->c_periods_sent = 0;
9518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	return 0;
9548c2ecf20Sopenharmony_ci}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_cistatic void snd_cs4231_overrange(struct snd_cs4231 *chip)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	unsigned long flags;
9598c2ecf20Sopenharmony_ci	unsigned char res;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
9628c2ecf20Sopenharmony_ci	res = snd_cs4231_in(chip, CS4231_TEST_INIT);
9638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	/* detect overrange only above 0dB; may be user selectable? */
9668c2ecf20Sopenharmony_ci	if (res & (0x08 | 0x02))
9678c2ecf20Sopenharmony_ci		chip->capture_substream->runtime->overrange++;
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic void snd_cs4231_play_callback(struct snd_cs4231 *chip)
9718c2ecf20Sopenharmony_ci{
9728c2ecf20Sopenharmony_ci	if (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE) {
9738c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(chip->playback_substream);
9748c2ecf20Sopenharmony_ci		snd_cs4231_advance_dma(&chip->p_dma, chip->playback_substream,
9758c2ecf20Sopenharmony_ci					    &chip->p_periods_sent);
9768c2ecf20Sopenharmony_ci	}
9778c2ecf20Sopenharmony_ci}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_cistatic void snd_cs4231_capture_callback(struct snd_cs4231 *chip)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) {
9828c2ecf20Sopenharmony_ci		snd_pcm_period_elapsed(chip->capture_substream);
9838c2ecf20Sopenharmony_ci		snd_cs4231_advance_dma(&chip->c_dma, chip->capture_substream,
9848c2ecf20Sopenharmony_ci					    &chip->c_periods_sent);
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci}
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cs4231_playback_pointer(
9898c2ecf20Sopenharmony_ci					struct snd_pcm_substream *substream)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
9928c2ecf20Sopenharmony_ci	struct cs4231_dma_control *dma_cont = &chip->p_dma;
9938c2ecf20Sopenharmony_ci	size_t ptr;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
9968c2ecf20Sopenharmony_ci		return 0;
9978c2ecf20Sopenharmony_ci	ptr = dma_cont->address(dma_cont);
9988c2ecf20Sopenharmony_ci	if (ptr != 0)
9998c2ecf20Sopenharmony_ci		ptr -= substream->runtime->dma_addr;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t snd_cs4231_capture_pointer(
10058c2ecf20Sopenharmony_ci					struct snd_pcm_substream *substream)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
10088c2ecf20Sopenharmony_ci	struct cs4231_dma_control *dma_cont = &chip->c_dma;
10098c2ecf20Sopenharmony_ci	size_t ptr;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
10128c2ecf20Sopenharmony_ci		return 0;
10138c2ecf20Sopenharmony_ci	ptr = dma_cont->address(dma_cont);
10148c2ecf20Sopenharmony_ci	if (ptr != 0)
10158c2ecf20Sopenharmony_ci		ptr -= substream->runtime->dma_addr;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr);
10188c2ecf20Sopenharmony_ci}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_cistatic int snd_cs4231_probe(struct snd_cs4231 *chip)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	unsigned long flags;
10238c2ecf20Sopenharmony_ci	int i;
10248c2ecf20Sopenharmony_ci	int id = 0;
10258c2ecf20Sopenharmony_ci	int vers = 0;
10268c2ecf20Sopenharmony_ci	unsigned char *ptr;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	for (i = 0; i < 50; i++) {
10298c2ecf20Sopenharmony_ci		mb();
10308c2ecf20Sopenharmony_ci		if (__cs4231_readb(chip, CS4231U(chip, REGSEL)) & CS4231_INIT)
10318c2ecf20Sopenharmony_ci			msleep(2);
10328c2ecf20Sopenharmony_ci		else {
10338c2ecf20Sopenharmony_ci			spin_lock_irqsave(&chip->lock, flags);
10348c2ecf20Sopenharmony_ci			snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2);
10358c2ecf20Sopenharmony_ci			id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f;
10368c2ecf20Sopenharmony_ci			vers = snd_cs4231_in(chip, CS4231_VERSION);
10378c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&chip->lock, flags);
10388c2ecf20Sopenharmony_ci			if (id == 0x0a)
10398c2ecf20Sopenharmony_ci				break;	/* this is valid value */
10408c2ecf20Sopenharmony_ci		}
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci	snd_printdd("cs4231: port = %p, id = 0x%x\n", chip->port, id);
10438c2ecf20Sopenharmony_ci	if (id != 0x0a)
10448c2ecf20Sopenharmony_ci		return -ENODEV;	/* no valid device found */
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/* clear any pendings IRQ */
10498c2ecf20Sopenharmony_ci	__cs4231_readb(chip, CS4231U(chip, STATUS));
10508c2ecf20Sopenharmony_ci	__cs4231_writeb(chip, 0, CS4231U(chip, STATUS));
10518c2ecf20Sopenharmony_ci	mb();
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
10568c2ecf20Sopenharmony_ci	chip->image[CS4231_IFACE_CTRL] =
10578c2ecf20Sopenharmony_ci		chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA;
10588c2ecf20Sopenharmony_ci	chip->image[CS4231_ALT_FEATURE_1] = 0x80;
10598c2ecf20Sopenharmony_ci	chip->image[CS4231_ALT_FEATURE_2] = 0x01;
10608c2ecf20Sopenharmony_ci	if (vers & 0x20)
10618c2ecf20Sopenharmony_ci		chip->image[CS4231_ALT_FEATURE_2] |= 0x02;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	ptr = (unsigned char *) &chip->image;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++)	/* ok.. fill all CS4231 registers */
10708c2ecf20Sopenharmony_ci		snd_cs4231_out(chip, i, *ptr++);
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	snd_cs4231_mce_up(chip);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	snd_cs4231_mce_down(chip);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	mdelay(2);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	return 0;		/* all things are ok.. */
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs4231_playback = {
10848c2ecf20Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP |
10858c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_INTERLEAVED |
10868c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_MMAP_VALID |
10878c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_SYNC_START,
10888c2ecf20Sopenharmony_ci	.formats		= SNDRV_PCM_FMTBIT_MU_LAW |
10898c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_A_LAW |
10908c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_IMA_ADPCM |
10918c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_U8 |
10928c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_LE |
10938c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_BE,
10948c2ecf20Sopenharmony_ci	.rates			= SNDRV_PCM_RATE_KNOT |
10958c2ecf20Sopenharmony_ci				  SNDRV_PCM_RATE_8000_48000,
10968c2ecf20Sopenharmony_ci	.rate_min		= 5510,
10978c2ecf20Sopenharmony_ci	.rate_max		= 48000,
10988c2ecf20Sopenharmony_ci	.channels_min		= 1,
10998c2ecf20Sopenharmony_ci	.channels_max		= 2,
11008c2ecf20Sopenharmony_ci	.buffer_bytes_max	= 32 * 1024,
11018c2ecf20Sopenharmony_ci	.period_bytes_min	= 64,
11028c2ecf20Sopenharmony_ci	.period_bytes_max	= 32 * 1024,
11038c2ecf20Sopenharmony_ci	.periods_min		= 1,
11048c2ecf20Sopenharmony_ci	.periods_max		= 1024,
11058c2ecf20Sopenharmony_ci};
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cs4231_capture = {
11088c2ecf20Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP |
11098c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_INTERLEAVED |
11108c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_MMAP_VALID |
11118c2ecf20Sopenharmony_ci				  SNDRV_PCM_INFO_SYNC_START,
11128c2ecf20Sopenharmony_ci	.formats		= SNDRV_PCM_FMTBIT_MU_LAW |
11138c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_A_LAW |
11148c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_IMA_ADPCM |
11158c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_U8 |
11168c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_LE |
11178c2ecf20Sopenharmony_ci				  SNDRV_PCM_FMTBIT_S16_BE,
11188c2ecf20Sopenharmony_ci	.rates			= SNDRV_PCM_RATE_KNOT |
11198c2ecf20Sopenharmony_ci				  SNDRV_PCM_RATE_8000_48000,
11208c2ecf20Sopenharmony_ci	.rate_min		= 5510,
11218c2ecf20Sopenharmony_ci	.rate_max		= 48000,
11228c2ecf20Sopenharmony_ci	.channels_min		= 1,
11238c2ecf20Sopenharmony_ci	.channels_max		= 2,
11248c2ecf20Sopenharmony_ci	.buffer_bytes_max	= 32 * 1024,
11258c2ecf20Sopenharmony_ci	.period_bytes_min	= 64,
11268c2ecf20Sopenharmony_ci	.period_bytes_max	= 32 * 1024,
11278c2ecf20Sopenharmony_ci	.periods_min		= 1,
11288c2ecf20Sopenharmony_ci	.periods_max		= 1024,
11298c2ecf20Sopenharmony_ci};
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic int snd_cs4231_playback_open(struct snd_pcm_substream *substream)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
11348c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11358c2ecf20Sopenharmony_ci	int err;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	runtime->hw = snd_cs4231_playback;
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	err = snd_cs4231_open(chip, CS4231_MODE_PLAY);
11408c2ecf20Sopenharmony_ci	if (err < 0)
11418c2ecf20Sopenharmony_ci		return err;
11428c2ecf20Sopenharmony_ci	chip->playback_substream = substream;
11438c2ecf20Sopenharmony_ci	chip->p_periods_sent = 0;
11448c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
11458c2ecf20Sopenharmony_ci	snd_cs4231_xrate(runtime);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	return 0;
11488c2ecf20Sopenharmony_ci}
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_cistatic int snd_cs4231_capture_open(struct snd_pcm_substream *substream)
11518c2ecf20Sopenharmony_ci{
11528c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
11538c2ecf20Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
11548c2ecf20Sopenharmony_ci	int err;
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	runtime->hw = snd_cs4231_capture;
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	err = snd_cs4231_open(chip, CS4231_MODE_RECORD);
11598c2ecf20Sopenharmony_ci	if (err < 0)
11608c2ecf20Sopenharmony_ci		return err;
11618c2ecf20Sopenharmony_ci	chip->capture_substream = substream;
11628c2ecf20Sopenharmony_ci	chip->c_periods_sent = 0;
11638c2ecf20Sopenharmony_ci	snd_pcm_set_sync(substream);
11648c2ecf20Sopenharmony_ci	snd_cs4231_xrate(runtime);
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	return 0;
11678c2ecf20Sopenharmony_ci}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_cistatic int snd_cs4231_playback_close(struct snd_pcm_substream *substream)
11708c2ecf20Sopenharmony_ci{
11718c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	snd_cs4231_close(chip, CS4231_MODE_PLAY);
11748c2ecf20Sopenharmony_ci	chip->playback_substream = NULL;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	return 0;
11778c2ecf20Sopenharmony_ci}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_cistatic int snd_cs4231_capture_close(struct snd_pcm_substream *substream)
11808c2ecf20Sopenharmony_ci{
11818c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	snd_cs4231_close(chip, CS4231_MODE_RECORD);
11848c2ecf20Sopenharmony_ci	chip->capture_substream = NULL;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	return 0;
11878c2ecf20Sopenharmony_ci}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci/* XXX We can do some power-management, in particular on EBUS using
11908c2ecf20Sopenharmony_ci * XXX the audio AUXIO register...
11918c2ecf20Sopenharmony_ci */
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cs4231_playback_ops = {
11948c2ecf20Sopenharmony_ci	.open		=	snd_cs4231_playback_open,
11958c2ecf20Sopenharmony_ci	.close		=	snd_cs4231_playback_close,
11968c2ecf20Sopenharmony_ci	.hw_params	=	snd_cs4231_playback_hw_params,
11978c2ecf20Sopenharmony_ci	.prepare	=	snd_cs4231_playback_prepare,
11988c2ecf20Sopenharmony_ci	.trigger	=	snd_cs4231_trigger,
11998c2ecf20Sopenharmony_ci	.pointer	=	snd_cs4231_playback_pointer,
12008c2ecf20Sopenharmony_ci};
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cs4231_capture_ops = {
12038c2ecf20Sopenharmony_ci	.open		=	snd_cs4231_capture_open,
12048c2ecf20Sopenharmony_ci	.close		=	snd_cs4231_capture_close,
12058c2ecf20Sopenharmony_ci	.hw_params	=	snd_cs4231_capture_hw_params,
12068c2ecf20Sopenharmony_ci	.prepare	=	snd_cs4231_capture_prepare,
12078c2ecf20Sopenharmony_ci	.trigger	=	snd_cs4231_trigger,
12088c2ecf20Sopenharmony_ci	.pointer	=	snd_cs4231_capture_pointer,
12098c2ecf20Sopenharmony_ci};
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic int snd_cs4231_pcm(struct snd_card *card)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
12148c2ecf20Sopenharmony_ci	struct snd_pcm *pcm;
12158c2ecf20Sopenharmony_ci	int err;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	err = snd_pcm_new(card, "CS4231", 0, 1, 1, &pcm);
12188c2ecf20Sopenharmony_ci	if (err < 0)
12198c2ecf20Sopenharmony_ci		return err;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
12228c2ecf20Sopenharmony_ci			&snd_cs4231_playback_ops);
12238c2ecf20Sopenharmony_ci	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
12248c2ecf20Sopenharmony_ci			&snd_cs4231_capture_ops);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	/* global setup */
12278c2ecf20Sopenharmony_ci	pcm->private_data = chip;
12288c2ecf20Sopenharmony_ci	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
12298c2ecf20Sopenharmony_ci	strcpy(pcm->name, "CS4231");
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
12328c2ecf20Sopenharmony_ci				       &chip->op->dev, 64 * 1024, 128 * 1024);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	chip->pcm = pcm;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	return 0;
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_cistatic int snd_cs4231_timer(struct snd_card *card)
12408c2ecf20Sopenharmony_ci{
12418c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
12428c2ecf20Sopenharmony_ci	struct snd_timer *timer;
12438c2ecf20Sopenharmony_ci	struct snd_timer_id tid;
12448c2ecf20Sopenharmony_ci	int err;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	/* Timer initialization */
12478c2ecf20Sopenharmony_ci	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
12488c2ecf20Sopenharmony_ci	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
12498c2ecf20Sopenharmony_ci	tid.card = card->number;
12508c2ecf20Sopenharmony_ci	tid.device = 0;
12518c2ecf20Sopenharmony_ci	tid.subdevice = 0;
12528c2ecf20Sopenharmony_ci	err = snd_timer_new(card, "CS4231", &tid, &timer);
12538c2ecf20Sopenharmony_ci	if (err < 0)
12548c2ecf20Sopenharmony_ci		return err;
12558c2ecf20Sopenharmony_ci	strcpy(timer->name, "CS4231");
12568c2ecf20Sopenharmony_ci	timer->private_data = chip;
12578c2ecf20Sopenharmony_ci	timer->hw = snd_cs4231_timer_table;
12588c2ecf20Sopenharmony_ci	chip->timer = timer;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	return 0;
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci/*
12648c2ecf20Sopenharmony_ci *  MIXER part
12658c2ecf20Sopenharmony_ci */
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_cistatic int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol,
12688c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_info *uinfo)
12698c2ecf20Sopenharmony_ci{
12708c2ecf20Sopenharmony_ci	static const char * const texts[4] = {
12718c2ecf20Sopenharmony_ci		"Line", "CD", "Mic", "Mix"
12728c2ecf20Sopenharmony_ci	};
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(uinfo, 2, 4, texts);
12758c2ecf20Sopenharmony_ci}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_cistatic int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol,
12788c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
12798c2ecf20Sopenharmony_ci{
12808c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
12818c2ecf20Sopenharmony_ci	unsigned long flags;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
12848c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[0] =
12858c2ecf20Sopenharmony_ci		(chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
12868c2ecf20Sopenharmony_ci	ucontrol->value.enumerated.item[1] =
12878c2ecf20Sopenharmony_ci		(chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
12888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	return 0;
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cistatic int snd_cs4231_put_mux(struct snd_kcontrol *kcontrol,
12948c2ecf20Sopenharmony_ci			      struct snd_ctl_elem_value *ucontrol)
12958c2ecf20Sopenharmony_ci{
12968c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
12978c2ecf20Sopenharmony_ci	unsigned long flags;
12988c2ecf20Sopenharmony_ci	unsigned short left, right;
12998c2ecf20Sopenharmony_ci	int change;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if (ucontrol->value.enumerated.item[0] > 3 ||
13028c2ecf20Sopenharmony_ci	    ucontrol->value.enumerated.item[1] > 3)
13038c2ecf20Sopenharmony_ci		return -EINVAL;
13048c2ecf20Sopenharmony_ci	left = ucontrol->value.enumerated.item[0] << 6;
13058c2ecf20Sopenharmony_ci	right = ucontrol->value.enumerated.item[1] << 6;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
13108c2ecf20Sopenharmony_ci	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
13118c2ecf20Sopenharmony_ci	change = left != chip->image[CS4231_LEFT_INPUT] ||
13128c2ecf20Sopenharmony_ci		 right != chip->image[CS4231_RIGHT_INPUT];
13138c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_LEFT_INPUT, left);
13148c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	return change;
13198c2ecf20Sopenharmony_ci}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_cistatic int snd_cs4231_info_single(struct snd_kcontrol *kcontrol,
13228c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	uinfo->type = (mask == 1) ?
13278c2ecf20Sopenharmony_ci		SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
13288c2ecf20Sopenharmony_ci	uinfo->count = 1;
13298c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
13308c2ecf20Sopenharmony_ci	uinfo->value.integer.max = mask;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	return 0;
13338c2ecf20Sopenharmony_ci}
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_cistatic int snd_cs4231_get_single(struct snd_kcontrol *kcontrol,
13368c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
13378c2ecf20Sopenharmony_ci{
13388c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
13398c2ecf20Sopenharmony_ci	unsigned long flags;
13408c2ecf20Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
13418c2ecf20Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
13428c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
13438c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	if (invert)
13528c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] =
13538c2ecf20Sopenharmony_ci			(mask - ucontrol->value.integer.value[0]);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	return 0;
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic int snd_cs4231_put_single(struct snd_kcontrol *kcontrol,
13598c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
13608c2ecf20Sopenharmony_ci{
13618c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
13628c2ecf20Sopenharmony_ci	unsigned long flags;
13638c2ecf20Sopenharmony_ci	int reg = kcontrol->private_value & 0xff;
13648c2ecf20Sopenharmony_ci	int shift = (kcontrol->private_value >> 8) & 0xff;
13658c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 16) & 0xff;
13668c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 24) & 0xff;
13678c2ecf20Sopenharmony_ci	int change;
13688c2ecf20Sopenharmony_ci	unsigned short val;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	val = (ucontrol->value.integer.value[0] & mask);
13718c2ecf20Sopenharmony_ci	if (invert)
13728c2ecf20Sopenharmony_ci		val = mask - val;
13738c2ecf20Sopenharmony_ci	val <<= shift;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	val = (chip->image[reg] & ~(mask << shift)) | val;
13788c2ecf20Sopenharmony_ci	change = val != chip->image[reg];
13798c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, reg, val);
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	return change;
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic int snd_cs4231_info_double(struct snd_kcontrol *kcontrol,
13878c2ecf20Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
13888c2ecf20Sopenharmony_ci{
13898c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	uinfo->type = mask == 1 ?
13928c2ecf20Sopenharmony_ci		SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
13938c2ecf20Sopenharmony_ci	uinfo->count = 2;
13948c2ecf20Sopenharmony_ci	uinfo->value.integer.min = 0;
13958c2ecf20Sopenharmony_ci	uinfo->value.integer.max = mask;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	return 0;
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_cistatic int snd_cs4231_get_double(struct snd_kcontrol *kcontrol,
14018c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
14028c2ecf20Sopenharmony_ci{
14038c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
14048c2ecf20Sopenharmony_ci	unsigned long flags;
14058c2ecf20Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
14068c2ecf20Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
14078c2ecf20Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
14088c2ecf20Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
14098c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
14108c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[0] =
14158c2ecf20Sopenharmony_ci		(chip->image[left_reg] >> shift_left) & mask;
14168c2ecf20Sopenharmony_ci	ucontrol->value.integer.value[1] =
14178c2ecf20Sopenharmony_ci		(chip->image[right_reg] >> shift_right) & mask;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	if (invert) {
14228c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[0] =
14238c2ecf20Sopenharmony_ci			(mask - ucontrol->value.integer.value[0]);
14248c2ecf20Sopenharmony_ci		ucontrol->value.integer.value[1] =
14258c2ecf20Sopenharmony_ci			(mask - ucontrol->value.integer.value[1]);
14268c2ecf20Sopenharmony_ci	}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	return 0;
14298c2ecf20Sopenharmony_ci}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_cistatic int snd_cs4231_put_double(struct snd_kcontrol *kcontrol,
14328c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
14338c2ecf20Sopenharmony_ci{
14348c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol);
14358c2ecf20Sopenharmony_ci	unsigned long flags;
14368c2ecf20Sopenharmony_ci	int left_reg = kcontrol->private_value & 0xff;
14378c2ecf20Sopenharmony_ci	int right_reg = (kcontrol->private_value >> 8) & 0xff;
14388c2ecf20Sopenharmony_ci	int shift_left = (kcontrol->private_value >> 16) & 0x07;
14398c2ecf20Sopenharmony_ci	int shift_right = (kcontrol->private_value >> 19) & 0x07;
14408c2ecf20Sopenharmony_ci	int mask = (kcontrol->private_value >> 24) & 0xff;
14418c2ecf20Sopenharmony_ci	int invert = (kcontrol->private_value >> 22) & 1;
14428c2ecf20Sopenharmony_ci	int change;
14438c2ecf20Sopenharmony_ci	unsigned short val1, val2;
14448c2ecf20Sopenharmony_ci
14458c2ecf20Sopenharmony_ci	val1 = ucontrol->value.integer.value[0] & mask;
14468c2ecf20Sopenharmony_ci	val2 = ucontrol->value.integer.value[1] & mask;
14478c2ecf20Sopenharmony_ci	if (invert) {
14488c2ecf20Sopenharmony_ci		val1 = mask - val1;
14498c2ecf20Sopenharmony_ci		val2 = mask - val2;
14508c2ecf20Sopenharmony_ci	}
14518c2ecf20Sopenharmony_ci	val1 <<= shift_left;
14528c2ecf20Sopenharmony_ci	val2 <<= shift_right;
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
14578c2ecf20Sopenharmony_ci	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
14588c2ecf20Sopenharmony_ci	change = val1 != chip->image[left_reg];
14598c2ecf20Sopenharmony_ci	change |= val2 != chip->image[right_reg];
14608c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, left_reg, val1);
14618c2ecf20Sopenharmony_ci	snd_cs4231_out(chip, right_reg, val2);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	return change;
14668c2ecf20Sopenharmony_ci}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci#define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \
14698c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .index = (xindex), \
14708c2ecf20Sopenharmony_ci  .info = snd_cs4231_info_single,	\
14718c2ecf20Sopenharmony_ci  .get = snd_cs4231_get_single, .put = snd_cs4231_put_single,	\
14728c2ecf20Sopenharmony_ci  .private_value = (reg) | ((shift) << 8) | ((mask) << 16) | ((invert) << 24) }
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci#define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, \
14758c2ecf20Sopenharmony_ci			shift_right, mask, invert) \
14768c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .index = (xindex), \
14778c2ecf20Sopenharmony_ci  .info = snd_cs4231_info_double,	\
14788c2ecf20Sopenharmony_ci  .get = snd_cs4231_get_double, .put = snd_cs4231_put_double,	\
14798c2ecf20Sopenharmony_ci  .private_value = (left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | \
14808c2ecf20Sopenharmony_ci		   ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22) }
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new snd_cs4231_controls[] = {
14838c2ecf20Sopenharmony_ciCS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT,
14848c2ecf20Sopenharmony_ci		CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
14858c2ecf20Sopenharmony_ciCS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT,
14868c2ecf20Sopenharmony_ci		CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),
14878c2ecf20Sopenharmony_ciCS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN,
14888c2ecf20Sopenharmony_ci		CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
14898c2ecf20Sopenharmony_ciCS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN,
14908c2ecf20Sopenharmony_ci		CS4231_RIGHT_LINE_IN, 0, 0, 31, 1),
14918c2ecf20Sopenharmony_ciCS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT,
14928c2ecf20Sopenharmony_ci		CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
14938c2ecf20Sopenharmony_ciCS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT,
14948c2ecf20Sopenharmony_ci		CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
14958c2ecf20Sopenharmony_ciCS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT,
14968c2ecf20Sopenharmony_ci		CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
14978c2ecf20Sopenharmony_ciCS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT,
14988c2ecf20Sopenharmony_ci		CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
14998c2ecf20Sopenharmony_ciCS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1),
15008c2ecf20Sopenharmony_ciCS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1),
15018c2ecf20Sopenharmony_ciCS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1),
15028c2ecf20Sopenharmony_ciCS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
15038c2ecf20Sopenharmony_ciCS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0,
15048c2ecf20Sopenharmony_ci		15, 0),
15058c2ecf20Sopenharmony_ci{
15068c2ecf20Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
15078c2ecf20Sopenharmony_ci	.name	= "Capture Source",
15088c2ecf20Sopenharmony_ci	.info	= snd_cs4231_info_mux,
15098c2ecf20Sopenharmony_ci	.get	= snd_cs4231_get_mux,
15108c2ecf20Sopenharmony_ci	.put	= snd_cs4231_put_mux,
15118c2ecf20Sopenharmony_ci},
15128c2ecf20Sopenharmony_ciCS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5,
15138c2ecf20Sopenharmony_ci		1, 0),
15148c2ecf20Sopenharmony_ciCS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
15158c2ecf20Sopenharmony_ciCS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1),
15168c2ecf20Sopenharmony_ci/* SPARC specific uses of XCTL{0,1} general purpose outputs.  */
15178c2ecf20Sopenharmony_ciCS4231_SINGLE("Line Out Switch", 0, CS4231_PIN_CTRL, 6, 1, 1),
15188c2ecf20Sopenharmony_ciCS4231_SINGLE("Headphone Out Switch", 0, CS4231_PIN_CTRL, 7, 1, 1)
15198c2ecf20Sopenharmony_ci};
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cistatic int snd_cs4231_mixer(struct snd_card *card)
15228c2ecf20Sopenharmony_ci{
15238c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
15248c2ecf20Sopenharmony_ci	int err, idx;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	if (snd_BUG_ON(!chip || !chip->pcm))
15278c2ecf20Sopenharmony_ci		return -EINVAL;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	strcpy(card->mixername, chip->pcm->name);
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) {
15328c2ecf20Sopenharmony_ci		err = snd_ctl_add(card,
15338c2ecf20Sopenharmony_ci				 snd_ctl_new1(&snd_cs4231_controls[idx], chip));
15348c2ecf20Sopenharmony_ci		if (err < 0)
15358c2ecf20Sopenharmony_ci			return err;
15368c2ecf20Sopenharmony_ci	}
15378c2ecf20Sopenharmony_ci	return 0;
15388c2ecf20Sopenharmony_ci}
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_cistatic int dev;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_cistatic int cs4231_attach_begin(struct platform_device *op,
15438c2ecf20Sopenharmony_ci			       struct snd_card **rcard)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	struct snd_card *card;
15468c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip;
15478c2ecf20Sopenharmony_ci	int err;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	*rcard = NULL;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	if (dev >= SNDRV_CARDS)
15528c2ecf20Sopenharmony_ci		return -ENODEV;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	if (!enable[dev]) {
15558c2ecf20Sopenharmony_ci		dev++;
15568c2ecf20Sopenharmony_ci		return -ENOENT;
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	err = snd_card_new(&op->dev, index[dev], id[dev], THIS_MODULE,
15608c2ecf20Sopenharmony_ci			   sizeof(struct snd_cs4231), &card);
15618c2ecf20Sopenharmony_ci	if (err < 0)
15628c2ecf20Sopenharmony_ci		return err;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	strcpy(card->driver, "CS4231");
15658c2ecf20Sopenharmony_ci	strcpy(card->shortname, "Sun CS4231");
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	chip = card->private_data;
15688c2ecf20Sopenharmony_ci	chip->card = card;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	*rcard = card;
15718c2ecf20Sopenharmony_ci	return 0;
15728c2ecf20Sopenharmony_ci}
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_cistatic int cs4231_attach_finish(struct snd_card *card)
15758c2ecf20Sopenharmony_ci{
15768c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
15778c2ecf20Sopenharmony_ci	int err;
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	err = snd_cs4231_pcm(card);
15808c2ecf20Sopenharmony_ci	if (err < 0)
15818c2ecf20Sopenharmony_ci		goto out_err;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	err = snd_cs4231_mixer(card);
15848c2ecf20Sopenharmony_ci	if (err < 0)
15858c2ecf20Sopenharmony_ci		goto out_err;
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	err = snd_cs4231_timer(card);
15888c2ecf20Sopenharmony_ci	if (err < 0)
15898c2ecf20Sopenharmony_ci		goto out_err;
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	err = snd_card_register(card);
15928c2ecf20Sopenharmony_ci	if (err < 0)
15938c2ecf20Sopenharmony_ci		goto out_err;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	dev_set_drvdata(&chip->op->dev, chip);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	dev++;
15988c2ecf20Sopenharmony_ci	return 0;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ciout_err:
16018c2ecf20Sopenharmony_ci	snd_card_free(card);
16028c2ecf20Sopenharmony_ci	return err;
16038c2ecf20Sopenharmony_ci}
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci#ifdef SBUS_SUPPORT
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_cistatic irqreturn_t snd_cs4231_sbus_interrupt(int irq, void *dev_id)
16088c2ecf20Sopenharmony_ci{
16098c2ecf20Sopenharmony_ci	unsigned long flags;
16108c2ecf20Sopenharmony_ci	unsigned char status;
16118c2ecf20Sopenharmony_ci	u32 csr;
16128c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = dev_id;
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	/*This is IRQ is not raised by the cs4231*/
16158c2ecf20Sopenharmony_ci	if (!(__cs4231_readb(chip, CS4231U(chip, STATUS)) & CS4231_GLOBALIRQ))
16168c2ecf20Sopenharmony_ci		return IRQ_NONE;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci	/* ACK the APC interrupt. */
16198c2ecf20Sopenharmony_ci	csr = sbus_readl(chip->port + APCCSR);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci	sbus_writel(csr, chip->port + APCCSR);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	if ((csr & APC_PDMA_READY) &&
16248c2ecf20Sopenharmony_ci	    (csr & APC_PLAY_INT) &&
16258c2ecf20Sopenharmony_ci	    (csr & APC_XINT_PNVA) &&
16268c2ecf20Sopenharmony_ci	    !(csr & APC_XINT_EMPT))
16278c2ecf20Sopenharmony_ci			snd_cs4231_play_callback(chip);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	if ((csr & APC_CDMA_READY) &&
16308c2ecf20Sopenharmony_ci	    (csr & APC_CAPT_INT) &&
16318c2ecf20Sopenharmony_ci	    (csr & APC_XINT_CNVA) &&
16328c2ecf20Sopenharmony_ci	    !(csr & APC_XINT_EMPT))
16338c2ecf20Sopenharmony_ci			snd_cs4231_capture_callback(chip);
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS);
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	if (status & CS4231_TIMER_IRQ) {
16388c2ecf20Sopenharmony_ci		if (chip->timer)
16398c2ecf20Sopenharmony_ci			snd_timer_interrupt(chip->timer, chip->timer->sticks);
16408c2ecf20Sopenharmony_ci	}
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	if ((status & CS4231_RECORD_IRQ) && (csr & APC_CDMA_READY))
16438c2ecf20Sopenharmony_ci		snd_cs4231_overrange(chip);
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	/* ACK the CS4231 interrupt. */
16468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&chip->lock, flags);
16478c2ecf20Sopenharmony_ci	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0);
16488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&chip->lock, flags);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci/*
16548c2ecf20Sopenharmony_ci * SBUS DMA routines
16558c2ecf20Sopenharmony_ci */
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_cistatic int sbus_dma_request(struct cs4231_dma_control *dma_cont,
16588c2ecf20Sopenharmony_ci			    dma_addr_t bus_addr, size_t len)
16598c2ecf20Sopenharmony_ci{
16608c2ecf20Sopenharmony_ci	unsigned long flags;
16618c2ecf20Sopenharmony_ci	u32 test, csr;
16628c2ecf20Sopenharmony_ci	int err;
16638c2ecf20Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	if (len >= (1 << 24))
16668c2ecf20Sopenharmony_ci		return -EINVAL;
16678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&base->lock, flags);
16688c2ecf20Sopenharmony_ci	csr = sbus_readl(base->regs + APCCSR);
16698c2ecf20Sopenharmony_ci	err = -EINVAL;
16708c2ecf20Sopenharmony_ci	test = APC_CDMA_READY;
16718c2ecf20Sopenharmony_ci	if (base->dir == APC_PLAY)
16728c2ecf20Sopenharmony_ci		test = APC_PDMA_READY;
16738c2ecf20Sopenharmony_ci	if (!(csr & test))
16748c2ecf20Sopenharmony_ci		goto out;
16758c2ecf20Sopenharmony_ci	err = -EBUSY;
16768c2ecf20Sopenharmony_ci	test = APC_XINT_CNVA;
16778c2ecf20Sopenharmony_ci	if (base->dir == APC_PLAY)
16788c2ecf20Sopenharmony_ci		test = APC_XINT_PNVA;
16798c2ecf20Sopenharmony_ci	if (!(csr & test))
16808c2ecf20Sopenharmony_ci		goto out;
16818c2ecf20Sopenharmony_ci	err = 0;
16828c2ecf20Sopenharmony_ci	sbus_writel(bus_addr, base->regs + base->dir + APCNVA);
16838c2ecf20Sopenharmony_ci	sbus_writel(len, base->regs + base->dir + APCNC);
16848c2ecf20Sopenharmony_ciout:
16858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&base->lock, flags);
16868c2ecf20Sopenharmony_ci	return err;
16878c2ecf20Sopenharmony_ci}
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_cistatic void sbus_dma_prepare(struct cs4231_dma_control *dma_cont, int d)
16908c2ecf20Sopenharmony_ci{
16918c2ecf20Sopenharmony_ci	unsigned long flags;
16928c2ecf20Sopenharmony_ci	u32 csr, test;
16938c2ecf20Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&base->lock, flags);
16968c2ecf20Sopenharmony_ci	csr = sbus_readl(base->regs + APCCSR);
16978c2ecf20Sopenharmony_ci	test =  APC_GENL_INT | APC_PLAY_INT | APC_XINT_ENA |
16988c2ecf20Sopenharmony_ci		APC_XINT_PLAY | APC_XINT_PEMP | APC_XINT_GENL |
16998c2ecf20Sopenharmony_ci		 APC_XINT_PENA;
17008c2ecf20Sopenharmony_ci	if (base->dir == APC_RECORD)
17018c2ecf20Sopenharmony_ci		test = APC_GENL_INT | APC_CAPT_INT | APC_XINT_ENA |
17028c2ecf20Sopenharmony_ci			APC_XINT_CAPT | APC_XINT_CEMP | APC_XINT_GENL;
17038c2ecf20Sopenharmony_ci	csr |= test;
17048c2ecf20Sopenharmony_ci	sbus_writel(csr, base->regs + APCCSR);
17058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&base->lock, flags);
17068c2ecf20Sopenharmony_ci}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_cistatic void sbus_dma_enable(struct cs4231_dma_control *dma_cont, int on)
17098c2ecf20Sopenharmony_ci{
17108c2ecf20Sopenharmony_ci	unsigned long flags;
17118c2ecf20Sopenharmony_ci	u32 csr, shift;
17128c2ecf20Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&base->lock, flags);
17158c2ecf20Sopenharmony_ci	if (!on) {
17168c2ecf20Sopenharmony_ci		sbus_writel(0, base->regs + base->dir + APCNC);
17178c2ecf20Sopenharmony_ci		sbus_writel(0, base->regs + base->dir + APCNVA);
17188c2ecf20Sopenharmony_ci		if (base->dir == APC_PLAY) {
17198c2ecf20Sopenharmony_ci			sbus_writel(0, base->regs + base->dir + APCC);
17208c2ecf20Sopenharmony_ci			sbus_writel(0, base->regs + base->dir + APCVA);
17218c2ecf20Sopenharmony_ci		}
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci		udelay(1200);
17248c2ecf20Sopenharmony_ci	}
17258c2ecf20Sopenharmony_ci	csr = sbus_readl(base->regs + APCCSR);
17268c2ecf20Sopenharmony_ci	shift = 0;
17278c2ecf20Sopenharmony_ci	if (base->dir == APC_PLAY)
17288c2ecf20Sopenharmony_ci		shift = 1;
17298c2ecf20Sopenharmony_ci	if (on)
17308c2ecf20Sopenharmony_ci		csr &= ~(APC_CPAUSE << shift);
17318c2ecf20Sopenharmony_ci	else
17328c2ecf20Sopenharmony_ci		csr |= (APC_CPAUSE << shift);
17338c2ecf20Sopenharmony_ci	sbus_writel(csr, base->regs + APCCSR);
17348c2ecf20Sopenharmony_ci	if (on)
17358c2ecf20Sopenharmony_ci		csr |= (APC_CDMA_READY << shift);
17368c2ecf20Sopenharmony_ci	else
17378c2ecf20Sopenharmony_ci		csr &= ~(APC_CDMA_READY << shift);
17388c2ecf20Sopenharmony_ci	sbus_writel(csr, base->regs + APCCSR);
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&base->lock, flags);
17418c2ecf20Sopenharmony_ci}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_cistatic unsigned int sbus_dma_addr(struct cs4231_dma_control *dma_cont)
17448c2ecf20Sopenharmony_ci{
17458c2ecf20Sopenharmony_ci	struct sbus_dma_info *base = &dma_cont->sbus_info;
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	return sbus_readl(base->regs + base->dir + APCVA);
17488c2ecf20Sopenharmony_ci}
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci/*
17518c2ecf20Sopenharmony_ci * Init and exit routines
17528c2ecf20Sopenharmony_ci */
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_cistatic int snd_cs4231_sbus_free(struct snd_cs4231 *chip)
17558c2ecf20Sopenharmony_ci{
17568c2ecf20Sopenharmony_ci	struct platform_device *op = chip->op;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	if (chip->irq[0])
17598c2ecf20Sopenharmony_ci		free_irq(chip->irq[0], chip);
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	if (chip->port)
17628c2ecf20Sopenharmony_ci		of_iounmap(&op->resource[0], chip->port, chip->regs_size);
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_ci	return 0;
17658c2ecf20Sopenharmony_ci}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_cistatic int snd_cs4231_sbus_dev_free(struct snd_device *device)
17688c2ecf20Sopenharmony_ci{
17698c2ecf20Sopenharmony_ci	struct snd_cs4231 *cp = device->device_data;
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_ci	return snd_cs4231_sbus_free(cp);
17728c2ecf20Sopenharmony_ci}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_cistatic const struct snd_device_ops snd_cs4231_sbus_dev_ops = {
17758c2ecf20Sopenharmony_ci	.dev_free	=	snd_cs4231_sbus_dev_free,
17768c2ecf20Sopenharmony_ci};
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_cistatic int snd_cs4231_sbus_create(struct snd_card *card,
17798c2ecf20Sopenharmony_ci				  struct platform_device *op,
17808c2ecf20Sopenharmony_ci				  int dev)
17818c2ecf20Sopenharmony_ci{
17828c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
17838c2ecf20Sopenharmony_ci	int err;
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	spin_lock_init(&chip->lock);
17868c2ecf20Sopenharmony_ci	spin_lock_init(&chip->c_dma.sbus_info.lock);
17878c2ecf20Sopenharmony_ci	spin_lock_init(&chip->p_dma.sbus_info.lock);
17888c2ecf20Sopenharmony_ci	mutex_init(&chip->mce_mutex);
17898c2ecf20Sopenharmony_ci	mutex_init(&chip->open_mutex);
17908c2ecf20Sopenharmony_ci	chip->op = op;
17918c2ecf20Sopenharmony_ci	chip->regs_size = resource_size(&op->resource[0]);
17928c2ecf20Sopenharmony_ci	memcpy(&chip->image, &snd_cs4231_original_image,
17938c2ecf20Sopenharmony_ci	       sizeof(snd_cs4231_original_image));
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	chip->port = of_ioremap(&op->resource[0], 0,
17968c2ecf20Sopenharmony_ci				chip->regs_size, "cs4231");
17978c2ecf20Sopenharmony_ci	if (!chip->port) {
17988c2ecf20Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev);
17998c2ecf20Sopenharmony_ci		return -EIO;
18008c2ecf20Sopenharmony_ci	}
18018c2ecf20Sopenharmony_ci
18028c2ecf20Sopenharmony_ci	chip->c_dma.sbus_info.regs = chip->port;
18038c2ecf20Sopenharmony_ci	chip->p_dma.sbus_info.regs = chip->port;
18048c2ecf20Sopenharmony_ci	chip->c_dma.sbus_info.dir = APC_RECORD;
18058c2ecf20Sopenharmony_ci	chip->p_dma.sbus_info.dir = APC_PLAY;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	chip->p_dma.prepare = sbus_dma_prepare;
18088c2ecf20Sopenharmony_ci	chip->p_dma.enable = sbus_dma_enable;
18098c2ecf20Sopenharmony_ci	chip->p_dma.request = sbus_dma_request;
18108c2ecf20Sopenharmony_ci	chip->p_dma.address = sbus_dma_addr;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	chip->c_dma.prepare = sbus_dma_prepare;
18138c2ecf20Sopenharmony_ci	chip->c_dma.enable = sbus_dma_enable;
18148c2ecf20Sopenharmony_ci	chip->c_dma.request = sbus_dma_request;
18158c2ecf20Sopenharmony_ci	chip->c_dma.address = sbus_dma_addr;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	if (request_irq(op->archdata.irqs[0], snd_cs4231_sbus_interrupt,
18188c2ecf20Sopenharmony_ci			IRQF_SHARED, "cs4231", chip)) {
18198c2ecf20Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n",
18208c2ecf20Sopenharmony_ci			    dev, op->archdata.irqs[0]);
18218c2ecf20Sopenharmony_ci		snd_cs4231_sbus_free(chip);
18228c2ecf20Sopenharmony_ci		return -EBUSY;
18238c2ecf20Sopenharmony_ci	}
18248c2ecf20Sopenharmony_ci	chip->irq[0] = op->archdata.irqs[0];
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	if (snd_cs4231_probe(chip) < 0) {
18278c2ecf20Sopenharmony_ci		snd_cs4231_sbus_free(chip);
18288c2ecf20Sopenharmony_ci		return -ENODEV;
18298c2ecf20Sopenharmony_ci	}
18308c2ecf20Sopenharmony_ci	snd_cs4231_init(chip);
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
18338c2ecf20Sopenharmony_ci				  chip, &snd_cs4231_sbus_dev_ops)) < 0) {
18348c2ecf20Sopenharmony_ci		snd_cs4231_sbus_free(chip);
18358c2ecf20Sopenharmony_ci		return err;
18368c2ecf20Sopenharmony_ci	}
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	return 0;
18398c2ecf20Sopenharmony_ci}
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_cistatic int cs4231_sbus_probe(struct platform_device *op)
18428c2ecf20Sopenharmony_ci{
18438c2ecf20Sopenharmony_ci	struct resource *rp = &op->resource[0];
18448c2ecf20Sopenharmony_ci	struct snd_card *card;
18458c2ecf20Sopenharmony_ci	int err;
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_ci	err = cs4231_attach_begin(op, &card);
18488c2ecf20Sopenharmony_ci	if (err)
18498c2ecf20Sopenharmony_ci		return err;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d",
18528c2ecf20Sopenharmony_ci		card->shortname,
18538c2ecf20Sopenharmony_ci		rp->flags & 0xffL,
18548c2ecf20Sopenharmony_ci		(unsigned long long)rp->start,
18558c2ecf20Sopenharmony_ci		op->archdata.irqs[0]);
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	err = snd_cs4231_sbus_create(card, op, dev);
18588c2ecf20Sopenharmony_ci	if (err < 0) {
18598c2ecf20Sopenharmony_ci		snd_card_free(card);
18608c2ecf20Sopenharmony_ci		return err;
18618c2ecf20Sopenharmony_ci	}
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	return cs4231_attach_finish(card);
18648c2ecf20Sopenharmony_ci}
18658c2ecf20Sopenharmony_ci#endif
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci#ifdef EBUS_SUPPORT
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_cistatic void snd_cs4231_ebus_play_callback(struct ebus_dma_info *p, int event,
18708c2ecf20Sopenharmony_ci					  void *cookie)
18718c2ecf20Sopenharmony_ci{
18728c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = cookie;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	snd_cs4231_play_callback(chip);
18758c2ecf20Sopenharmony_ci}
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_cistatic void snd_cs4231_ebus_capture_callback(struct ebus_dma_info *p,
18788c2ecf20Sopenharmony_ci					     int event, void *cookie)
18798c2ecf20Sopenharmony_ci{
18808c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = cookie;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	snd_cs4231_capture_callback(chip);
18838c2ecf20Sopenharmony_ci}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_ci/*
18868c2ecf20Sopenharmony_ci * EBUS DMA wrappers
18878c2ecf20Sopenharmony_ci */
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_cistatic int _ebus_dma_request(struct cs4231_dma_control *dma_cont,
18908c2ecf20Sopenharmony_ci			     dma_addr_t bus_addr, size_t len)
18918c2ecf20Sopenharmony_ci{
18928c2ecf20Sopenharmony_ci	return ebus_dma_request(&dma_cont->ebus_info, bus_addr, len);
18938c2ecf20Sopenharmony_ci}
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_cistatic void _ebus_dma_enable(struct cs4231_dma_control *dma_cont, int on)
18968c2ecf20Sopenharmony_ci{
18978c2ecf20Sopenharmony_ci	ebus_dma_enable(&dma_cont->ebus_info, on);
18988c2ecf20Sopenharmony_ci}
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_cistatic void _ebus_dma_prepare(struct cs4231_dma_control *dma_cont, int dir)
19018c2ecf20Sopenharmony_ci{
19028c2ecf20Sopenharmony_ci	ebus_dma_prepare(&dma_cont->ebus_info, dir);
19038c2ecf20Sopenharmony_ci}
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_cistatic unsigned int _ebus_dma_addr(struct cs4231_dma_control *dma_cont)
19068c2ecf20Sopenharmony_ci{
19078c2ecf20Sopenharmony_ci	return ebus_dma_addr(&dma_cont->ebus_info);
19088c2ecf20Sopenharmony_ci}
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci/*
19118c2ecf20Sopenharmony_ci * Init and exit routines
19128c2ecf20Sopenharmony_ci */
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_cistatic int snd_cs4231_ebus_free(struct snd_cs4231 *chip)
19158c2ecf20Sopenharmony_ci{
19168c2ecf20Sopenharmony_ci	struct platform_device *op = chip->op;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	if (chip->c_dma.ebus_info.regs) {
19198c2ecf20Sopenharmony_ci		ebus_dma_unregister(&chip->c_dma.ebus_info);
19208c2ecf20Sopenharmony_ci		of_iounmap(&op->resource[2], chip->c_dma.ebus_info.regs, 0x10);
19218c2ecf20Sopenharmony_ci	}
19228c2ecf20Sopenharmony_ci	if (chip->p_dma.ebus_info.regs) {
19238c2ecf20Sopenharmony_ci		ebus_dma_unregister(&chip->p_dma.ebus_info);
19248c2ecf20Sopenharmony_ci		of_iounmap(&op->resource[1], chip->p_dma.ebus_info.regs, 0x10);
19258c2ecf20Sopenharmony_ci	}
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	if (chip->port)
19288c2ecf20Sopenharmony_ci		of_iounmap(&op->resource[0], chip->port, 0x10);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci	return 0;
19318c2ecf20Sopenharmony_ci}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_cistatic int snd_cs4231_ebus_dev_free(struct snd_device *device)
19348c2ecf20Sopenharmony_ci{
19358c2ecf20Sopenharmony_ci	struct snd_cs4231 *cp = device->device_data;
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	return snd_cs4231_ebus_free(cp);
19388c2ecf20Sopenharmony_ci}
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_cistatic const struct snd_device_ops snd_cs4231_ebus_dev_ops = {
19418c2ecf20Sopenharmony_ci	.dev_free	=	snd_cs4231_ebus_dev_free,
19428c2ecf20Sopenharmony_ci};
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_cistatic int snd_cs4231_ebus_create(struct snd_card *card,
19458c2ecf20Sopenharmony_ci				  struct platform_device *op,
19468c2ecf20Sopenharmony_ci				  int dev)
19478c2ecf20Sopenharmony_ci{
19488c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = card->private_data;
19498c2ecf20Sopenharmony_ci	int err;
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	spin_lock_init(&chip->lock);
19528c2ecf20Sopenharmony_ci	spin_lock_init(&chip->c_dma.ebus_info.lock);
19538c2ecf20Sopenharmony_ci	spin_lock_init(&chip->p_dma.ebus_info.lock);
19548c2ecf20Sopenharmony_ci	mutex_init(&chip->mce_mutex);
19558c2ecf20Sopenharmony_ci	mutex_init(&chip->open_mutex);
19568c2ecf20Sopenharmony_ci	chip->flags |= CS4231_FLAG_EBUS;
19578c2ecf20Sopenharmony_ci	chip->op = op;
19588c2ecf20Sopenharmony_ci	memcpy(&chip->image, &snd_cs4231_original_image,
19598c2ecf20Sopenharmony_ci	       sizeof(snd_cs4231_original_image));
19608c2ecf20Sopenharmony_ci	strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)");
19618c2ecf20Sopenharmony_ci	chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
19628c2ecf20Sopenharmony_ci	chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback;
19638c2ecf20Sopenharmony_ci	chip->c_dma.ebus_info.client_cookie = chip;
19648c2ecf20Sopenharmony_ci	chip->c_dma.ebus_info.irq = op->archdata.irqs[0];
19658c2ecf20Sopenharmony_ci	strcpy(chip->p_dma.ebus_info.name, "cs4231(play)");
19668c2ecf20Sopenharmony_ci	chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER;
19678c2ecf20Sopenharmony_ci	chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback;
19688c2ecf20Sopenharmony_ci	chip->p_dma.ebus_info.client_cookie = chip;
19698c2ecf20Sopenharmony_ci	chip->p_dma.ebus_info.irq = op->archdata.irqs[1];
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	chip->p_dma.prepare = _ebus_dma_prepare;
19728c2ecf20Sopenharmony_ci	chip->p_dma.enable = _ebus_dma_enable;
19738c2ecf20Sopenharmony_ci	chip->p_dma.request = _ebus_dma_request;
19748c2ecf20Sopenharmony_ci	chip->p_dma.address = _ebus_dma_addr;
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	chip->c_dma.prepare = _ebus_dma_prepare;
19778c2ecf20Sopenharmony_ci	chip->c_dma.enable = _ebus_dma_enable;
19788c2ecf20Sopenharmony_ci	chip->c_dma.request = _ebus_dma_request;
19798c2ecf20Sopenharmony_ci	chip->c_dma.address = _ebus_dma_addr;
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci	chip->port = of_ioremap(&op->resource[0], 0, 0x10, "cs4231");
19828c2ecf20Sopenharmony_ci	chip->p_dma.ebus_info.regs =
19838c2ecf20Sopenharmony_ci		of_ioremap(&op->resource[1], 0, 0x10, "cs4231_pdma");
19848c2ecf20Sopenharmony_ci	chip->c_dma.ebus_info.regs =
19858c2ecf20Sopenharmony_ci		of_ioremap(&op->resource[2], 0, 0x10, "cs4231_cdma");
19868c2ecf20Sopenharmony_ci	if (!chip->port || !chip->p_dma.ebus_info.regs ||
19878c2ecf20Sopenharmony_ci	    !chip->c_dma.ebus_info.regs) {
19888c2ecf20Sopenharmony_ci		snd_cs4231_ebus_free(chip);
19898c2ecf20Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev);
19908c2ecf20Sopenharmony_ci		return -EIO;
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ci	if (ebus_dma_register(&chip->c_dma.ebus_info)) {
19948c2ecf20Sopenharmony_ci		snd_cs4231_ebus_free(chip);
19958c2ecf20Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to register EBUS capture DMA\n",
19968c2ecf20Sopenharmony_ci			    dev);
19978c2ecf20Sopenharmony_ci		return -EBUSY;
19988c2ecf20Sopenharmony_ci	}
19998c2ecf20Sopenharmony_ci	if (ebus_dma_irq_enable(&chip->c_dma.ebus_info, 1)) {
20008c2ecf20Sopenharmony_ci		snd_cs4231_ebus_free(chip);
20018c2ecf20Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to enable EBUS capture IRQ\n",
20028c2ecf20Sopenharmony_ci			    dev);
20038c2ecf20Sopenharmony_ci		return -EBUSY;
20048c2ecf20Sopenharmony_ci	}
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	if (ebus_dma_register(&chip->p_dma.ebus_info)) {
20078c2ecf20Sopenharmony_ci		snd_cs4231_ebus_free(chip);
20088c2ecf20Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to register EBUS play DMA\n",
20098c2ecf20Sopenharmony_ci			    dev);
20108c2ecf20Sopenharmony_ci		return -EBUSY;
20118c2ecf20Sopenharmony_ci	}
20128c2ecf20Sopenharmony_ci	if (ebus_dma_irq_enable(&chip->p_dma.ebus_info, 1)) {
20138c2ecf20Sopenharmony_ci		snd_cs4231_ebus_free(chip);
20148c2ecf20Sopenharmony_ci		snd_printdd("cs4231-%d: Unable to enable EBUS play IRQ\n", dev);
20158c2ecf20Sopenharmony_ci		return -EBUSY;
20168c2ecf20Sopenharmony_ci	}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	if (snd_cs4231_probe(chip) < 0) {
20198c2ecf20Sopenharmony_ci		snd_cs4231_ebus_free(chip);
20208c2ecf20Sopenharmony_ci		return -ENODEV;
20218c2ecf20Sopenharmony_ci	}
20228c2ecf20Sopenharmony_ci	snd_cs4231_init(chip);
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
20258c2ecf20Sopenharmony_ci				  chip, &snd_cs4231_ebus_dev_ops)) < 0) {
20268c2ecf20Sopenharmony_ci		snd_cs4231_ebus_free(chip);
20278c2ecf20Sopenharmony_ci		return err;
20288c2ecf20Sopenharmony_ci	}
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	return 0;
20318c2ecf20Sopenharmony_ci}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_cistatic int cs4231_ebus_probe(struct platform_device *op)
20348c2ecf20Sopenharmony_ci{
20358c2ecf20Sopenharmony_ci	struct snd_card *card;
20368c2ecf20Sopenharmony_ci	int err;
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	err = cs4231_attach_begin(op, &card);
20398c2ecf20Sopenharmony_ci	if (err)
20408c2ecf20Sopenharmony_ci		return err;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	sprintf(card->longname, "%s at 0x%llx, irq %d",
20438c2ecf20Sopenharmony_ci		card->shortname,
20448c2ecf20Sopenharmony_ci		op->resource[0].start,
20458c2ecf20Sopenharmony_ci		op->archdata.irqs[0]);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	err = snd_cs4231_ebus_create(card, op, dev);
20488c2ecf20Sopenharmony_ci	if (err < 0) {
20498c2ecf20Sopenharmony_ci		snd_card_free(card);
20508c2ecf20Sopenharmony_ci		return err;
20518c2ecf20Sopenharmony_ci	}
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci	return cs4231_attach_finish(card);
20548c2ecf20Sopenharmony_ci}
20558c2ecf20Sopenharmony_ci#endif
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_cistatic int cs4231_probe(struct platform_device *op)
20588c2ecf20Sopenharmony_ci{
20598c2ecf20Sopenharmony_ci#ifdef EBUS_SUPPORT
20608c2ecf20Sopenharmony_ci	if (of_node_name_eq(op->dev.of_node->parent, "ebus"))
20618c2ecf20Sopenharmony_ci		return cs4231_ebus_probe(op);
20628c2ecf20Sopenharmony_ci#endif
20638c2ecf20Sopenharmony_ci#ifdef SBUS_SUPPORT
20648c2ecf20Sopenharmony_ci	if (of_node_name_eq(op->dev.of_node->parent, "sbus") ||
20658c2ecf20Sopenharmony_ci	    of_node_name_eq(op->dev.of_node->parent, "sbi"))
20668c2ecf20Sopenharmony_ci		return cs4231_sbus_probe(op);
20678c2ecf20Sopenharmony_ci#endif
20688c2ecf20Sopenharmony_ci	return -ENODEV;
20698c2ecf20Sopenharmony_ci}
20708c2ecf20Sopenharmony_ci
20718c2ecf20Sopenharmony_cistatic int cs4231_remove(struct platform_device *op)
20728c2ecf20Sopenharmony_ci{
20738c2ecf20Sopenharmony_ci	struct snd_cs4231 *chip = dev_get_drvdata(&op->dev);
20748c2ecf20Sopenharmony_ci
20758c2ecf20Sopenharmony_ci	snd_card_free(chip->card);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	return 0;
20788c2ecf20Sopenharmony_ci}
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_cistatic const struct of_device_id cs4231_match[] = {
20818c2ecf20Sopenharmony_ci	{
20828c2ecf20Sopenharmony_ci		.name = "SUNW,CS4231",
20838c2ecf20Sopenharmony_ci	},
20848c2ecf20Sopenharmony_ci	{
20858c2ecf20Sopenharmony_ci		.name = "audio",
20868c2ecf20Sopenharmony_ci		.compatible = "SUNW,CS4231",
20878c2ecf20Sopenharmony_ci	},
20888c2ecf20Sopenharmony_ci	{},
20898c2ecf20Sopenharmony_ci};
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cs4231_match);
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_cistatic struct platform_driver cs4231_driver = {
20948c2ecf20Sopenharmony_ci	.driver = {
20958c2ecf20Sopenharmony_ci		.name = "audio",
20968c2ecf20Sopenharmony_ci		.of_match_table = cs4231_match,
20978c2ecf20Sopenharmony_ci	},
20988c2ecf20Sopenharmony_ci	.probe		= cs4231_probe,
20998c2ecf20Sopenharmony_ci	.remove		= cs4231_remove,
21008c2ecf20Sopenharmony_ci};
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_cimodule_platform_driver(cs4231_driver);
2103