xref: /openbmc/linux/sound/isa/wss/wss_lib.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29295aea1SKrzysztof Helt /*
39295aea1SKrzysztof Helt  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
49295aea1SKrzysztof Helt  *  Routines for control of CS4231(A)/CS4232/InterWave & compatible chips
59295aea1SKrzysztof Helt  *
69295aea1SKrzysztof Helt  *  Bugs:
79295aea1SKrzysztof Helt  *     - sometimes record brokes playback with WSS portion of
89295aea1SKrzysztof Helt  *       Yamaha OPL3-SA3 chip
99295aea1SKrzysztof Helt  *     - CS4231 (GUS MAX) - still trouble with occasional noises
109295aea1SKrzysztof Helt  *			  - broken initialization?
119295aea1SKrzysztof Helt  */
129295aea1SKrzysztof Helt 
139295aea1SKrzysztof Helt #include <linux/delay.h>
149295aea1SKrzysztof Helt #include <linux/pm.h>
159295aea1SKrzysztof Helt #include <linux/init.h>
169295aea1SKrzysztof Helt #include <linux/interrupt.h>
179295aea1SKrzysztof Helt #include <linux/slab.h>
189295aea1SKrzysztof Helt #include <linux/ioport.h>
19da155d5bSPaul Gortmaker #include <linux/module.h>
206cbbfe1cSTakashi Iwai #include <linux/io.h>
219295aea1SKrzysztof Helt #include <sound/core.h>
2261ef19d7SKrzysztof Helt #include <sound/wss.h>
239295aea1SKrzysztof Helt #include <sound/pcm_params.h>
245664daa1SKrzysztof Helt #include <sound/tlv.h>
259295aea1SKrzysztof Helt 
269295aea1SKrzysztof Helt #include <asm/dma.h>
279295aea1SKrzysztof Helt #include <asm/irq.h>
289295aea1SKrzysztof Helt 
299295aea1SKrzysztof Helt MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
309295aea1SKrzysztof Helt MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips");
319295aea1SKrzysztof Helt MODULE_LICENSE("GPL");
329295aea1SKrzysztof Helt 
339295aea1SKrzysztof Helt #if 0
349295aea1SKrzysztof Helt #define SNDRV_DEBUG_MCE
359295aea1SKrzysztof Helt #endif
369295aea1SKrzysztof Helt 
379295aea1SKrzysztof Helt /*
389295aea1SKrzysztof Helt  *  Some variables
399295aea1SKrzysztof Helt  */
409295aea1SKrzysztof Helt 
41429bca4dSTakashi Iwai static const unsigned char freq_bits[14] = {
429295aea1SKrzysztof Helt 	/* 5510 */	0x00 | CS4231_XTAL2,
439295aea1SKrzysztof Helt 	/* 6620 */	0x0E | CS4231_XTAL2,
449295aea1SKrzysztof Helt 	/* 8000 */	0x00 | CS4231_XTAL1,
459295aea1SKrzysztof Helt 	/* 9600 */	0x0E | CS4231_XTAL1,
469295aea1SKrzysztof Helt 	/* 11025 */	0x02 | CS4231_XTAL2,
479295aea1SKrzysztof Helt 	/* 16000 */	0x02 | CS4231_XTAL1,
489295aea1SKrzysztof Helt 	/* 18900 */	0x04 | CS4231_XTAL2,
499295aea1SKrzysztof Helt 	/* 22050 */	0x06 | CS4231_XTAL2,
509295aea1SKrzysztof Helt 	/* 27042 */	0x04 | CS4231_XTAL1,
519295aea1SKrzysztof Helt 	/* 32000 */	0x06 | CS4231_XTAL1,
529295aea1SKrzysztof Helt 	/* 33075 */	0x0C | CS4231_XTAL2,
539295aea1SKrzysztof Helt 	/* 37800 */	0x08 | CS4231_XTAL2,
549295aea1SKrzysztof Helt 	/* 44100 */	0x0A | CS4231_XTAL2,
559295aea1SKrzysztof Helt 	/* 48000 */	0x0C | CS4231_XTAL1
569295aea1SKrzysztof Helt };
579295aea1SKrzysztof Helt 
587b3f4776STakashi Iwai static const unsigned int rates[14] = {
599295aea1SKrzysztof Helt 	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050,
609295aea1SKrzysztof Helt 	27042, 32000, 33075, 37800, 44100, 48000
619295aea1SKrzysztof Helt };
629295aea1SKrzysztof Helt 
637b3f4776STakashi Iwai static const struct snd_pcm_hw_constraint_list hw_constraints_rates = {
649295aea1SKrzysztof Helt 	.count = ARRAY_SIZE(rates),
659295aea1SKrzysztof Helt 	.list = rates,
669295aea1SKrzysztof Helt 	.mask = 0,
679295aea1SKrzysztof Helt };
689295aea1SKrzysztof Helt 
snd_wss_xrate(struct snd_pcm_runtime * runtime)697779f75fSKrzysztof Helt static int snd_wss_xrate(struct snd_pcm_runtime *runtime)
709295aea1SKrzysztof Helt {
717779f75fSKrzysztof Helt 	return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
727779f75fSKrzysztof Helt 					  &hw_constraints_rates);
739295aea1SKrzysztof Helt }
749295aea1SKrzysztof Helt 
75429bca4dSTakashi Iwai static const unsigned char snd_wss_original_image[32] =
769295aea1SKrzysztof Helt {
779295aea1SKrzysztof Helt 	0x00,			/* 00/00 - lic */
789295aea1SKrzysztof Helt 	0x00,			/* 01/01 - ric */
799295aea1SKrzysztof Helt 	0x9f,			/* 02/02 - la1ic */
809295aea1SKrzysztof Helt 	0x9f,			/* 03/03 - ra1ic */
819295aea1SKrzysztof Helt 	0x9f,			/* 04/04 - la2ic */
829295aea1SKrzysztof Helt 	0x9f,			/* 05/05 - ra2ic */
839295aea1SKrzysztof Helt 	0xbf,			/* 06/06 - loc */
849295aea1SKrzysztof Helt 	0xbf,			/* 07/07 - roc */
859295aea1SKrzysztof Helt 	0x20,			/* 08/08 - pdfr */
869295aea1SKrzysztof Helt 	CS4231_AUTOCALIB,	/* 09/09 - ic */
879295aea1SKrzysztof Helt 	0x00,			/* 0a/10 - pc */
889295aea1SKrzysztof Helt 	0x00,			/* 0b/11 - ti */
899295aea1SKrzysztof Helt 	CS4231_MODE2,		/* 0c/12 - mi */
909295aea1SKrzysztof Helt 	0xfc,			/* 0d/13 - lbc */
919295aea1SKrzysztof Helt 	0x00,			/* 0e/14 - pbru */
929295aea1SKrzysztof Helt 	0x00,			/* 0f/15 - pbrl */
939295aea1SKrzysztof Helt 	0x80,			/* 10/16 - afei */
949295aea1SKrzysztof Helt 	0x01,			/* 11/17 - afeii */
959295aea1SKrzysztof Helt 	0x9f,			/* 12/18 - llic */
969295aea1SKrzysztof Helt 	0x9f,			/* 13/19 - rlic */
979295aea1SKrzysztof Helt 	0x00,			/* 14/20 - tlb */
989295aea1SKrzysztof Helt 	0x00,			/* 15/21 - thb */
999295aea1SKrzysztof Helt 	0x00,			/* 16/22 - la3mic/reserved */
1009295aea1SKrzysztof Helt 	0x00,			/* 17/23 - ra3mic/reserved */
1019295aea1SKrzysztof Helt 	0x00,			/* 18/24 - afs */
1029295aea1SKrzysztof Helt 	0x00,			/* 19/25 - lamoc/version */
1039295aea1SKrzysztof Helt 	0xcf,			/* 1a/26 - mioc */
1049295aea1SKrzysztof Helt 	0x00,			/* 1b/27 - ramoc/reserved */
1059295aea1SKrzysztof Helt 	0x20,			/* 1c/28 - cdfr */
1069295aea1SKrzysztof Helt 	0x00,			/* 1d/29 - res4 */
1079295aea1SKrzysztof Helt 	0x00,			/* 1e/30 - cbru */
1089295aea1SKrzysztof Helt 	0x00,			/* 1f/31 - cbrl */
1099295aea1SKrzysztof Helt };
1109295aea1SKrzysztof Helt 
111429bca4dSTakashi Iwai static const unsigned char snd_opti93x_original_image[32] =
1129295aea1SKrzysztof Helt {
1139295aea1SKrzysztof Helt 	0x00,		/* 00/00 - l_mixout_outctrl */
1149295aea1SKrzysztof Helt 	0x00,		/* 01/01 - r_mixout_outctrl */
1159295aea1SKrzysztof Helt 	0x88,		/* 02/02 - l_cd_inctrl */
1169295aea1SKrzysztof Helt 	0x88,		/* 03/03 - r_cd_inctrl */
1179295aea1SKrzysztof Helt 	0x88,		/* 04/04 - l_a1/fm_inctrl */
1189295aea1SKrzysztof Helt 	0x88,		/* 05/05 - r_a1/fm_inctrl */
1199295aea1SKrzysztof Helt 	0x80,		/* 06/06 - l_dac_inctrl */
1209295aea1SKrzysztof Helt 	0x80,		/* 07/07 - r_dac_inctrl */
1219295aea1SKrzysztof Helt 	0x00,		/* 08/08 - ply_dataform_reg */
1229295aea1SKrzysztof Helt 	0x00,		/* 09/09 - if_conf */
1239295aea1SKrzysztof Helt 	0x00,		/* 0a/10 - pin_ctrl */
1249295aea1SKrzysztof Helt 	0x00,		/* 0b/11 - err_init_reg */
1259295aea1SKrzysztof Helt 	0x0a,		/* 0c/12 - id_reg */
1269295aea1SKrzysztof Helt 	0x00,		/* 0d/13 - reserved */
1279295aea1SKrzysztof Helt 	0x00,		/* 0e/14 - ply_upcount_reg */
1289295aea1SKrzysztof Helt 	0x00,		/* 0f/15 - ply_lowcount_reg */
1299295aea1SKrzysztof Helt 	0x88,		/* 10/16 - reserved/l_a1_inctrl */
1309295aea1SKrzysztof Helt 	0x88,		/* 11/17 - reserved/r_a1_inctrl */
1319295aea1SKrzysztof Helt 	0x88,		/* 12/18 - l_line_inctrl */
1329295aea1SKrzysztof Helt 	0x88,		/* 13/19 - r_line_inctrl */
1339295aea1SKrzysztof Helt 	0x88,		/* 14/20 - l_mic_inctrl */
1349295aea1SKrzysztof Helt 	0x88,		/* 15/21 - r_mic_inctrl */
1359295aea1SKrzysztof Helt 	0x80,		/* 16/22 - l_out_outctrl */
1369295aea1SKrzysztof Helt 	0x80,		/* 17/23 - r_out_outctrl */
1379295aea1SKrzysztof Helt 	0x00,		/* 18/24 - reserved */
1389295aea1SKrzysztof Helt 	0x00,		/* 19/25 - reserved */
1399295aea1SKrzysztof Helt 	0x00,		/* 1a/26 - reserved */
1409295aea1SKrzysztof Helt 	0x00,		/* 1b/27 - reserved */
1419295aea1SKrzysztof Helt 	0x00,		/* 1c/28 - cap_dataform_reg */
1429295aea1SKrzysztof Helt 	0x00,		/* 1d/29 - reserved */
1439295aea1SKrzysztof Helt 	0x00,		/* 1e/30 - cap_upcount_reg */
1449295aea1SKrzysztof Helt 	0x00		/* 1f/31 - cap_lowcount_reg */
1459295aea1SKrzysztof Helt };
1469295aea1SKrzysztof Helt 
1479295aea1SKrzysztof Helt /*
1489295aea1SKrzysztof Helt  *  Basic I/O functions
1499295aea1SKrzysztof Helt  */
1509295aea1SKrzysztof Helt 
wss_outb(struct snd_wss * chip,u8 offset,u8 val)1517779f75fSKrzysztof Helt static inline void wss_outb(struct snd_wss *chip, u8 offset, u8 val)
1529295aea1SKrzysztof Helt {
1539295aea1SKrzysztof Helt 	outb(val, chip->port + offset);
1549295aea1SKrzysztof Helt }
1559295aea1SKrzysztof Helt 
wss_inb(struct snd_wss * chip,u8 offset)1567779f75fSKrzysztof Helt static inline u8 wss_inb(struct snd_wss *chip, u8 offset)
1579295aea1SKrzysztof Helt {
1589295aea1SKrzysztof Helt 	return inb(chip->port + offset);
1599295aea1SKrzysztof Helt }
1609295aea1SKrzysztof Helt 
snd_wss_wait(struct snd_wss * chip)1617779f75fSKrzysztof Helt static void snd_wss_wait(struct snd_wss *chip)
1629295aea1SKrzysztof Helt {
1639295aea1SKrzysztof Helt 	int timeout;
1649295aea1SKrzysztof Helt 
1659295aea1SKrzysztof Helt 	for (timeout = 250;
1667779f75fSKrzysztof Helt 	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
1679295aea1SKrzysztof Helt 	     timeout--)
1689295aea1SKrzysztof Helt 		udelay(100);
1699295aea1SKrzysztof Helt }
1709295aea1SKrzysztof Helt 
snd_wss_dout(struct snd_wss * chip,unsigned char reg,unsigned char value)1717779f75fSKrzysztof Helt static void snd_wss_dout(struct snd_wss *chip, unsigned char reg,
1727779f75fSKrzysztof Helt 			 unsigned char value)
1739295aea1SKrzysztof Helt {
1749295aea1SKrzysztof Helt 	int timeout;
1759295aea1SKrzysztof Helt 
1769295aea1SKrzysztof Helt 	for (timeout = 250;
1777779f75fSKrzysztof Helt 	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
1789295aea1SKrzysztof Helt 	     timeout--)
1799295aea1SKrzysztof Helt 		udelay(10);
1807779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
1817779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REG), value);
1829295aea1SKrzysztof Helt 	mb();
1839295aea1SKrzysztof Helt }
1849295aea1SKrzysztof Helt 
snd_wss_out(struct snd_wss * chip,unsigned char reg,unsigned char value)1857779f75fSKrzysztof Helt void snd_wss_out(struct snd_wss *chip, unsigned char reg, unsigned char value)
1869295aea1SKrzysztof Helt {
1877779f75fSKrzysztof Helt 	snd_wss_wait(chip);
1889295aea1SKrzysztof Helt #ifdef CONFIG_SND_DEBUG
1897779f75fSKrzysztof Helt 	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
19076d498e4STakashi Iwai 		snd_printk(KERN_DEBUG "out: auto calibration time out "
19176d498e4STakashi Iwai 			   "- reg = 0x%x, value = 0x%x\n", reg, value);
1929295aea1SKrzysztof Helt #endif
1937779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
1947779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REG), value);
1959295aea1SKrzysztof Helt 	chip->image[reg] = value;
1969295aea1SKrzysztof Helt 	mb();
1979295aea1SKrzysztof Helt 	snd_printdd("codec out - reg 0x%x = 0x%x\n",
1989295aea1SKrzysztof Helt 			chip->mce_bit | reg, value);
1999295aea1SKrzysztof Helt }
2007779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_out);
2019295aea1SKrzysztof Helt 
snd_wss_in(struct snd_wss * chip,unsigned char reg)2027779f75fSKrzysztof Helt unsigned char snd_wss_in(struct snd_wss *chip, unsigned char reg)
2039295aea1SKrzysztof Helt {
2047779f75fSKrzysztof Helt 	snd_wss_wait(chip);
2059295aea1SKrzysztof Helt #ifdef CONFIG_SND_DEBUG
2067779f75fSKrzysztof Helt 	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
20776d498e4STakashi Iwai 		snd_printk(KERN_DEBUG "in: auto calibration time out "
20876d498e4STakashi Iwai 			   "- reg = 0x%x\n", reg);
2099295aea1SKrzysztof Helt #endif
2107779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg);
2119295aea1SKrzysztof Helt 	mb();
2127779f75fSKrzysztof Helt 	return wss_inb(chip, CS4231P(REG));
2139295aea1SKrzysztof Helt }
2147779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_in);
2159295aea1SKrzysztof Helt 
snd_cs4236_ext_out(struct snd_wss * chip,unsigned char reg,unsigned char val)2167779f75fSKrzysztof Helt void snd_cs4236_ext_out(struct snd_wss *chip, unsigned char reg,
2177779f75fSKrzysztof Helt 			unsigned char val)
2189295aea1SKrzysztof Helt {
2197779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
2207779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REG),
2217779f75fSKrzysztof Helt 		 reg | (chip->image[CS4236_EXT_REG] & 0x01));
2227779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REG), val);
2239295aea1SKrzysztof Helt 	chip->eimage[CS4236_REG(reg)] = val;
2249295aea1SKrzysztof Helt #if 0
22576d498e4STakashi Iwai 	printk(KERN_DEBUG "ext out : reg = 0x%x, val = 0x%x\n", reg, val);
2269295aea1SKrzysztof Helt #endif
2279295aea1SKrzysztof Helt }
2287779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_cs4236_ext_out);
2299295aea1SKrzysztof Helt 
snd_cs4236_ext_in(struct snd_wss * chip,unsigned char reg)2307779f75fSKrzysztof Helt unsigned char snd_cs4236_ext_in(struct snd_wss *chip, unsigned char reg)
2319295aea1SKrzysztof Helt {
2327779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17);
2337779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REG),
2347779f75fSKrzysztof Helt 		 reg | (chip->image[CS4236_EXT_REG] & 0x01));
2359295aea1SKrzysztof Helt #if 1
2367779f75fSKrzysztof Helt 	return wss_inb(chip, CS4231P(REG));
2379295aea1SKrzysztof Helt #else
2389295aea1SKrzysztof Helt 	{
2399295aea1SKrzysztof Helt 		unsigned char res;
2407779f75fSKrzysztof Helt 		res = wss_inb(chip, CS4231P(REG));
24176d498e4STakashi Iwai 		printk(KERN_DEBUG "ext in : reg = 0x%x, val = 0x%x\n",
24276d498e4STakashi Iwai 		       reg, res);
2439295aea1SKrzysztof Helt 		return res;
2449295aea1SKrzysztof Helt 	}
2459295aea1SKrzysztof Helt #endif
2469295aea1SKrzysztof Helt }
2477779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_cs4236_ext_in);
2489295aea1SKrzysztof Helt 
2499295aea1SKrzysztof Helt #if 0
2509295aea1SKrzysztof Helt 
2517779f75fSKrzysztof Helt static void snd_wss_debug(struct snd_wss *chip)
2529295aea1SKrzysztof Helt {
2537779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2547779f75fSKrzysztof Helt 		"CS4231 REGS:      INDEX = 0x%02x  "
2557779f75fSKrzysztof Helt 		"                 STATUS = 0x%02x\n",
2563caf8c08SRene Herman 					wss_inb(chip, CS4231P(REGSEL)),
2577779f75fSKrzysztof Helt 					wss_inb(chip, CS4231P(STATUS)));
2587779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2597779f75fSKrzysztof Helt 		"  0x00: left input      = 0x%02x  "
2607779f75fSKrzysztof Helt 		"  0x10: alt 1 (CFIG 2)  = 0x%02x\n",
2617779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x00),
2627779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x10));
2637779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2647779f75fSKrzysztof Helt 		"  0x01: right input     = 0x%02x  "
2657779f75fSKrzysztof Helt 		"  0x11: alt 2 (CFIG 3)  = 0x%02x\n",
2667779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x01),
2677779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x11));
2687779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2697779f75fSKrzysztof Helt 		"  0x02: GF1 left input  = 0x%02x  "
2707779f75fSKrzysztof Helt 		"  0x12: left line in    = 0x%02x\n",
2717779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x02),
2727779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x12));
2737779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2747779f75fSKrzysztof Helt 		"  0x03: GF1 right input = 0x%02x  "
2757779f75fSKrzysztof Helt 		"  0x13: right line in   = 0x%02x\n",
2767779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x03),
2777779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x13));
2787779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2797779f75fSKrzysztof Helt 		"  0x04: CD left input   = 0x%02x  "
2807779f75fSKrzysztof Helt 		"  0x14: timer low       = 0x%02x\n",
2817779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x04),
2827779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x14));
2837779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2847779f75fSKrzysztof Helt 		"  0x05: CD right input  = 0x%02x  "
2857779f75fSKrzysztof Helt 		"  0x15: timer high      = 0x%02x\n",
2867779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x05),
2877779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x15));
2887779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2897779f75fSKrzysztof Helt 		"  0x06: left output     = 0x%02x  "
2907779f75fSKrzysztof Helt 		"  0x16: left MIC (PnP)  = 0x%02x\n",
2917779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x06),
2927779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x16));
2937779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2947779f75fSKrzysztof Helt 		"  0x07: right output    = 0x%02x  "
2957779f75fSKrzysztof Helt 		"  0x17: right MIC (PnP) = 0x%02x\n",
2967779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x07),
2977779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x17));
2987779f75fSKrzysztof Helt 	printk(KERN_DEBUG
2997779f75fSKrzysztof Helt 		"  0x08: playback format = 0x%02x  "
3007779f75fSKrzysztof Helt 		"  0x18: IRQ status      = 0x%02x\n",
3017779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x08),
3027779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x18));
3037779f75fSKrzysztof Helt 	printk(KERN_DEBUG
3047779f75fSKrzysztof Helt 		"  0x09: iface (CFIG 1)  = 0x%02x  "
3057779f75fSKrzysztof Helt 		"  0x19: left line out   = 0x%02x\n",
3067779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x09),
3077779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x19));
3087779f75fSKrzysztof Helt 	printk(KERN_DEBUG
3097779f75fSKrzysztof Helt 		"  0x0a: pin control     = 0x%02x  "
3107779f75fSKrzysztof Helt 		"  0x1a: mono control    = 0x%02x\n",
3117779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x0a),
3127779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x1a));
3137779f75fSKrzysztof Helt 	printk(KERN_DEBUG
3147779f75fSKrzysztof Helt 		"  0x0b: init & status   = 0x%02x  "
3157779f75fSKrzysztof Helt 		"  0x1b: right line out  = 0x%02x\n",
3167779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x0b),
3177779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x1b));
3187779f75fSKrzysztof Helt 	printk(KERN_DEBUG
3197779f75fSKrzysztof Helt 		"  0x0c: revision & mode = 0x%02x  "
3207779f75fSKrzysztof Helt 		"  0x1c: record format   = 0x%02x\n",
3217779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x0c),
3227779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x1c));
3237779f75fSKrzysztof Helt 	printk(KERN_DEBUG
3247779f75fSKrzysztof Helt 		"  0x0d: loopback        = 0x%02x  "
3257779f75fSKrzysztof Helt 		"  0x1d: var freq (PnP)  = 0x%02x\n",
3267779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x0d),
3277779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x1d));
3287779f75fSKrzysztof Helt 	printk(KERN_DEBUG
3297779f75fSKrzysztof Helt 		"  0x0e: ply upr count   = 0x%02x  "
3307779f75fSKrzysztof Helt 		"  0x1e: ply lwr count   = 0x%02x\n",
3317779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x0e),
3327779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x1e));
3337779f75fSKrzysztof Helt 	printk(KERN_DEBUG
3347779f75fSKrzysztof Helt 		"  0x0f: rec upr count   = 0x%02x  "
3357779f75fSKrzysztof Helt 		"  0x1f: rec lwr count   = 0x%02x\n",
3367779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x0f),
3377779f75fSKrzysztof Helt 					snd_wss_in(chip, 0x1f));
3389295aea1SKrzysztof Helt }
3399295aea1SKrzysztof Helt 
3409295aea1SKrzysztof Helt #endif
3419295aea1SKrzysztof Helt 
3429295aea1SKrzysztof Helt /*
3439295aea1SKrzysztof Helt  *  CS4231 detection / MCE routines
3449295aea1SKrzysztof Helt  */
3459295aea1SKrzysztof Helt 
snd_wss_busy_wait(struct snd_wss * chip)3467779f75fSKrzysztof Helt static void snd_wss_busy_wait(struct snd_wss *chip)
3479295aea1SKrzysztof Helt {
3489295aea1SKrzysztof Helt 	int timeout;
3499295aea1SKrzysztof Helt 
3509295aea1SKrzysztof Helt 	/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */
3519295aea1SKrzysztof Helt 	for (timeout = 5; timeout > 0; timeout--)
3527779f75fSKrzysztof Helt 		wss_inb(chip, CS4231P(REGSEL));
3539295aea1SKrzysztof Helt 	/* end of cleanup sequence */
354ead893c0SKrzysztof Helt 	for (timeout = 25000;
3557779f75fSKrzysztof Helt 	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT);
3569295aea1SKrzysztof Helt 	     timeout--)
3579295aea1SKrzysztof Helt 		udelay(10);
3589295aea1SKrzysztof Helt }
3599295aea1SKrzysztof Helt 
snd_wss_mce_up(struct snd_wss * chip)3607779f75fSKrzysztof Helt void snd_wss_mce_up(struct snd_wss *chip)
3619295aea1SKrzysztof Helt {
3629295aea1SKrzysztof Helt 	unsigned long flags;
3639295aea1SKrzysztof Helt 	int timeout;
3649295aea1SKrzysztof Helt 
3657779f75fSKrzysztof Helt 	snd_wss_wait(chip);
3669295aea1SKrzysztof Helt #ifdef CONFIG_SND_DEBUG
3677779f75fSKrzysztof Helt 	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
36876d498e4STakashi Iwai 		snd_printk(KERN_DEBUG
36976d498e4STakashi Iwai 			   "mce_up - auto calibration time out (0)\n");
3709295aea1SKrzysztof Helt #endif
3719295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
3729295aea1SKrzysztof Helt 	chip->mce_bit |= CS4231_MCE;
3737779f75fSKrzysztof Helt 	timeout = wss_inb(chip, CS4231P(REGSEL));
3749295aea1SKrzysztof Helt 	if (timeout == 0x80)
37576d498e4STakashi Iwai 		snd_printk(KERN_DEBUG "mce_up [0x%lx]: "
37676d498e4STakashi Iwai 			   "serious init problem - codec still busy\n",
37776d498e4STakashi Iwai 			   chip->port);
3789295aea1SKrzysztof Helt 	if (!(timeout & CS4231_MCE))
3797779f75fSKrzysztof Helt 		wss_outb(chip, CS4231P(REGSEL),
3807779f75fSKrzysztof Helt 			 chip->mce_bit | (timeout & 0x1f));
3819295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
3829295aea1SKrzysztof Helt }
3837779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_mce_up);
3849295aea1SKrzysztof Helt 
snd_wss_mce_down(struct snd_wss * chip)3857779f75fSKrzysztof Helt void snd_wss_mce_down(struct snd_wss *chip)
3869295aea1SKrzysztof Helt {
3879295aea1SKrzysztof Helt 	unsigned long flags;
3889295aea1SKrzysztof Helt 	unsigned long end_time;
3899295aea1SKrzysztof Helt 	int timeout;
390ead893c0SKrzysztof Helt 	int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848;
3919295aea1SKrzysztof Helt 
3927779f75fSKrzysztof Helt 	snd_wss_busy_wait(chip);
3939295aea1SKrzysztof Helt 
3949295aea1SKrzysztof Helt #ifdef CONFIG_SND_DEBUG
3957779f75fSKrzysztof Helt 	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
39676d498e4STakashi Iwai 		snd_printk(KERN_DEBUG "mce_down [0x%lx] - "
39776d498e4STakashi Iwai 			   "auto calibration time out (0)\n",
39876d498e4STakashi Iwai 			   (long)CS4231P(REGSEL));
3999295aea1SKrzysztof Helt #endif
4009295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
4019295aea1SKrzysztof Helt 	chip->mce_bit &= ~CS4231_MCE;
4027779f75fSKrzysztof Helt 	timeout = wss_inb(chip, CS4231P(REGSEL));
4037779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
4049295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
4059295aea1SKrzysztof Helt 	if (timeout == 0x80)
40676d498e4STakashi Iwai 		snd_printk(KERN_DEBUG "mce_down [0x%lx]: "
40776d498e4STakashi Iwai 			   "serious init problem - codec still busy\n",
40876d498e4STakashi Iwai 			   chip->port);
409ead893c0SKrzysztof Helt 	if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & hw_mask))
4109295aea1SKrzysztof Helt 		return;
4119295aea1SKrzysztof Helt 
4129295aea1SKrzysztof Helt 	/*
4139295aea1SKrzysztof Helt 	 * Wait for (possible -- during init auto-calibration may not be set)
4149295aea1SKrzysztof Helt 	 * calibration process to start. Needs up to 5 sample periods on AD1848
4159295aea1SKrzysztof Helt 	 * which at the slowest possible rate of 5.5125 kHz means 907 us.
4169295aea1SKrzysztof Helt 	 */
4179295aea1SKrzysztof Helt 	msleep(1);
4189295aea1SKrzysztof Helt 
4199295aea1SKrzysztof Helt 	snd_printdd("(1) jiffies = %lu\n", jiffies);
4209295aea1SKrzysztof Helt 
4219295aea1SKrzysztof Helt 	/* check condition up to 250 ms */
4229295aea1SKrzysztof Helt 	end_time = jiffies + msecs_to_jiffies(250);
4237779f75fSKrzysztof Helt 	while (snd_wss_in(chip, CS4231_TEST_INIT) &
4249295aea1SKrzysztof Helt 		CS4231_CALIB_IN_PROGRESS) {
4259295aea1SKrzysztof Helt 
4269295aea1SKrzysztof Helt 		if (time_after(jiffies, end_time)) {
4279295aea1SKrzysztof Helt 			snd_printk(KERN_ERR "mce_down - "
4289295aea1SKrzysztof Helt 					"auto calibration time out (2)\n");
4299295aea1SKrzysztof Helt 			return;
4309295aea1SKrzysztof Helt 		}
4319295aea1SKrzysztof Helt 		msleep(1);
4329295aea1SKrzysztof Helt 	}
4339295aea1SKrzysztof Helt 
4349295aea1SKrzysztof Helt 	snd_printdd("(2) jiffies = %lu\n", jiffies);
4359295aea1SKrzysztof Helt 
4369295aea1SKrzysztof Helt 	/* check condition up to 100 ms */
4379295aea1SKrzysztof Helt 	end_time = jiffies + msecs_to_jiffies(100);
4387779f75fSKrzysztof Helt 	while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
4399295aea1SKrzysztof Helt 		if (time_after(jiffies, end_time)) {
4409295aea1SKrzysztof Helt 			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
4419295aea1SKrzysztof Helt 			return;
4429295aea1SKrzysztof Helt 		}
4439295aea1SKrzysztof Helt 		msleep(1);
4449295aea1SKrzysztof Helt 	}
4459295aea1SKrzysztof Helt 
4469295aea1SKrzysztof Helt 	snd_printdd("(3) jiffies = %lu\n", jiffies);
4477779f75fSKrzysztof Helt 	snd_printd("mce_down - exit = 0x%x\n", wss_inb(chip, CS4231P(REGSEL)));
4489295aea1SKrzysztof Helt }
4497779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_mce_down);
4509295aea1SKrzysztof Helt 
snd_wss_get_count(unsigned char format,unsigned int size)4517779f75fSKrzysztof Helt static unsigned int snd_wss_get_count(unsigned char format, unsigned int size)
4529295aea1SKrzysztof Helt {
4539295aea1SKrzysztof Helt 	switch (format & 0xe0) {
4549295aea1SKrzysztof Helt 	case CS4231_LINEAR_16:
4559295aea1SKrzysztof Helt 	case CS4231_LINEAR_16_BIG:
4569295aea1SKrzysztof Helt 		size >>= 1;
4579295aea1SKrzysztof Helt 		break;
4589295aea1SKrzysztof Helt 	case CS4231_ADPCM_16:
4599295aea1SKrzysztof Helt 		return size >> 2;
4609295aea1SKrzysztof Helt 	}
4619295aea1SKrzysztof Helt 	if (format & CS4231_STEREO)
4629295aea1SKrzysztof Helt 		size >>= 1;
4639295aea1SKrzysztof Helt 	return size;
4649295aea1SKrzysztof Helt }
4659295aea1SKrzysztof Helt 
snd_wss_trigger(struct snd_pcm_substream * substream,int cmd)4667779f75fSKrzysztof Helt static int snd_wss_trigger(struct snd_pcm_substream *substream,
4679295aea1SKrzysztof Helt 			   int cmd)
4689295aea1SKrzysztof Helt {
4697779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
4709295aea1SKrzysztof Helt 	int result = 0;
4719295aea1SKrzysztof Helt 	unsigned int what;
4729295aea1SKrzysztof Helt 	struct snd_pcm_substream *s;
4739295aea1SKrzysztof Helt 	int do_start;
4749295aea1SKrzysztof Helt 
4759295aea1SKrzysztof Helt 	switch (cmd) {
4769295aea1SKrzysztof Helt 	case SNDRV_PCM_TRIGGER_START:
4779295aea1SKrzysztof Helt 	case SNDRV_PCM_TRIGGER_RESUME:
4789295aea1SKrzysztof Helt 		do_start = 1; break;
4799295aea1SKrzysztof Helt 	case SNDRV_PCM_TRIGGER_STOP:
4809295aea1SKrzysztof Helt 	case SNDRV_PCM_TRIGGER_SUSPEND:
4819295aea1SKrzysztof Helt 		do_start = 0; break;
4829295aea1SKrzysztof Helt 	default:
4839295aea1SKrzysztof Helt 		return -EINVAL;
4849295aea1SKrzysztof Helt 	}
4859295aea1SKrzysztof Helt 
4869295aea1SKrzysztof Helt 	what = 0;
4879295aea1SKrzysztof Helt 	snd_pcm_group_for_each_entry(s, substream) {
4889295aea1SKrzysztof Helt 		if (s == chip->playback_substream) {
4899295aea1SKrzysztof Helt 			what |= CS4231_PLAYBACK_ENABLE;
4909295aea1SKrzysztof Helt 			snd_pcm_trigger_done(s, substream);
4919295aea1SKrzysztof Helt 		} else if (s == chip->capture_substream) {
4929295aea1SKrzysztof Helt 			what |= CS4231_RECORD_ENABLE;
4939295aea1SKrzysztof Helt 			snd_pcm_trigger_done(s, substream);
4949295aea1SKrzysztof Helt 		}
4959295aea1SKrzysztof Helt 	}
4969295aea1SKrzysztof Helt 	spin_lock(&chip->reg_lock);
4979295aea1SKrzysztof Helt 	if (do_start) {
4989295aea1SKrzysztof Helt 		chip->image[CS4231_IFACE_CTRL] |= what;
4999295aea1SKrzysztof Helt 		if (chip->trigger)
5009295aea1SKrzysztof Helt 			chip->trigger(chip, what, 1);
5019295aea1SKrzysztof Helt 	} else {
5029295aea1SKrzysztof Helt 		chip->image[CS4231_IFACE_CTRL] &= ~what;
5039295aea1SKrzysztof Helt 		if (chip->trigger)
5049295aea1SKrzysztof Helt 			chip->trigger(chip, what, 0);
5059295aea1SKrzysztof Helt 	}
5067779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
5079295aea1SKrzysztof Helt 	spin_unlock(&chip->reg_lock);
5089295aea1SKrzysztof Helt #if 0
5097779f75fSKrzysztof Helt 	snd_wss_debug(chip);
5109295aea1SKrzysztof Helt #endif
5119295aea1SKrzysztof Helt 	return result;
5129295aea1SKrzysztof Helt }
5139295aea1SKrzysztof Helt 
5149295aea1SKrzysztof Helt /*
5159295aea1SKrzysztof Helt  *  CODEC I/O
5169295aea1SKrzysztof Helt  */
5179295aea1SKrzysztof Helt 
snd_wss_get_rate(unsigned int rate)5187779f75fSKrzysztof Helt static unsigned char snd_wss_get_rate(unsigned int rate)
5199295aea1SKrzysztof Helt {
5209295aea1SKrzysztof Helt 	int i;
5219295aea1SKrzysztof Helt 
5229295aea1SKrzysztof Helt 	for (i = 0; i < ARRAY_SIZE(rates); i++)
5239295aea1SKrzysztof Helt 		if (rate == rates[i])
5249295aea1SKrzysztof Helt 			return freq_bits[i];
5259295aea1SKrzysztof Helt 	// snd_BUG();
5269295aea1SKrzysztof Helt 	return freq_bits[ARRAY_SIZE(rates) - 1];
5279295aea1SKrzysztof Helt }
5289295aea1SKrzysztof Helt 
snd_wss_get_format(struct snd_wss * chip,snd_pcm_format_t format,int channels)5297779f75fSKrzysztof Helt static unsigned char snd_wss_get_format(struct snd_wss *chip,
5306be9a60eSTakashi Iwai 					snd_pcm_format_t format,
5319295aea1SKrzysztof Helt 					int channels)
5329295aea1SKrzysztof Helt {
5339295aea1SKrzysztof Helt 	unsigned char rformat;
5349295aea1SKrzysztof Helt 
5359295aea1SKrzysztof Helt 	rformat = CS4231_LINEAR_8;
5369295aea1SKrzysztof Helt 	switch (format) {
5379295aea1SKrzysztof Helt 	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = CS4231_ULAW_8; break;
5389295aea1SKrzysztof Helt 	case SNDRV_PCM_FORMAT_A_LAW:	rformat = CS4231_ALAW_8; break;
5399295aea1SKrzysztof Helt 	case SNDRV_PCM_FORMAT_S16_LE:	rformat = CS4231_LINEAR_16; break;
5409295aea1SKrzysztof Helt 	case SNDRV_PCM_FORMAT_S16_BE:	rformat = CS4231_LINEAR_16_BIG; break;
5419295aea1SKrzysztof Helt 	case SNDRV_PCM_FORMAT_IMA_ADPCM:	rformat = CS4231_ADPCM_16; break;
5429295aea1SKrzysztof Helt 	}
5439295aea1SKrzysztof Helt 	if (channels > 1)
5449295aea1SKrzysztof Helt 		rformat |= CS4231_STEREO;
5459295aea1SKrzysztof Helt #if 0
54676d498e4STakashi Iwai 	snd_printk(KERN_DEBUG "get_format: 0x%x (mode=0x%x)\n", format, mode);
5479295aea1SKrzysztof Helt #endif
5489295aea1SKrzysztof Helt 	return rformat;
5499295aea1SKrzysztof Helt }
5509295aea1SKrzysztof Helt 
snd_wss_calibrate_mute(struct snd_wss * chip,int mute)5517779f75fSKrzysztof Helt static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute)
5529295aea1SKrzysztof Helt {
5539295aea1SKrzysztof Helt 	unsigned long flags;
5549295aea1SKrzysztof Helt 
555ace457c7SKrzysztof Helt 	mute = mute ? 0x80 : 0;
5569295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
5579295aea1SKrzysztof Helt 	if (chip->calibrate_mute == mute) {
5589295aea1SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
5599295aea1SKrzysztof Helt 		return;
5609295aea1SKrzysztof Helt 	}
5619295aea1SKrzysztof Helt 	if (!mute) {
5627779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_LEFT_INPUT,
5637779f75fSKrzysztof Helt 			     chip->image[CS4231_LEFT_INPUT]);
5647779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_RIGHT_INPUT,
5657779f75fSKrzysztof Helt 			     chip->image[CS4231_RIGHT_INPUT]);
5667779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_LOOPBACK,
5677779f75fSKrzysztof Helt 			     chip->image[CS4231_LOOPBACK]);
5689ef344f8SKrzysztof Helt 	} else {
5699ef344f8SKrzysztof Helt 		snd_wss_dout(chip, CS4231_LEFT_INPUT,
5709ef344f8SKrzysztof Helt 			     0);
5719ef344f8SKrzysztof Helt 		snd_wss_dout(chip, CS4231_RIGHT_INPUT,
5729ef344f8SKrzysztof Helt 			     0);
5739ef344f8SKrzysztof Helt 		snd_wss_dout(chip, CS4231_LOOPBACK,
5749ef344f8SKrzysztof Helt 			     0xfd);
5759295aea1SKrzysztof Helt 	}
5769ef344f8SKrzysztof Helt 
5777779f75fSKrzysztof Helt 	snd_wss_dout(chip, CS4231_AUX1_LEFT_INPUT,
578ace457c7SKrzysztof Helt 		     mute | chip->image[CS4231_AUX1_LEFT_INPUT]);
5797779f75fSKrzysztof Helt 	snd_wss_dout(chip, CS4231_AUX1_RIGHT_INPUT,
580ace457c7SKrzysztof Helt 		     mute | chip->image[CS4231_AUX1_RIGHT_INPUT]);
5817779f75fSKrzysztof Helt 	snd_wss_dout(chip, CS4231_AUX2_LEFT_INPUT,
582ace457c7SKrzysztof Helt 		     mute | chip->image[CS4231_AUX2_LEFT_INPUT]);
5837779f75fSKrzysztof Helt 	snd_wss_dout(chip, CS4231_AUX2_RIGHT_INPUT,
584ace457c7SKrzysztof Helt 		     mute | chip->image[CS4231_AUX2_RIGHT_INPUT]);
5857779f75fSKrzysztof Helt 	snd_wss_dout(chip, CS4231_LEFT_OUTPUT,
586ace457c7SKrzysztof Helt 		     mute | chip->image[CS4231_LEFT_OUTPUT]);
5877779f75fSKrzysztof Helt 	snd_wss_dout(chip, CS4231_RIGHT_OUTPUT,
588ace457c7SKrzysztof Helt 		     mute | chip->image[CS4231_RIGHT_OUTPUT]);
589ead893c0SKrzysztof Helt 	if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
5907779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_LEFT_LINE_IN,
591ace457c7SKrzysztof Helt 			     mute | chip->image[CS4231_LEFT_LINE_IN]);
5927779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_RIGHT_LINE_IN,
593ace457c7SKrzysztof Helt 			     mute | chip->image[CS4231_RIGHT_LINE_IN]);
5947779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_MONO_CTRL,
5957779f75fSKrzysztof Helt 			     mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]);
596ead893c0SKrzysztof Helt 	}
5977779f75fSKrzysztof Helt 	if (chip->hardware == WSS_HW_INTERWAVE) {
5987779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_LEFT_MIC_INPUT,
599ace457c7SKrzysztof Helt 			     mute | chip->image[CS4231_LEFT_MIC_INPUT]);
6007779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_RIGHT_MIC_INPUT,
601ace457c7SKrzysztof Helt 			     mute | chip->image[CS4231_RIGHT_MIC_INPUT]);
6027779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_LINE_LEFT_OUTPUT,
603ace457c7SKrzysztof Helt 			     mute | chip->image[CS4231_LINE_LEFT_OUTPUT]);
6047779f75fSKrzysztof Helt 		snd_wss_dout(chip, CS4231_LINE_RIGHT_OUTPUT,
605ace457c7SKrzysztof Helt 			     mute | chip->image[CS4231_LINE_RIGHT_OUTPUT]);
6069295aea1SKrzysztof Helt 	}
6079295aea1SKrzysztof Helt 	chip->calibrate_mute = mute;
6089295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
6099295aea1SKrzysztof Helt }
6109295aea1SKrzysztof Helt 
snd_wss_playback_format(struct snd_wss * chip,struct snd_pcm_hw_params * params,unsigned char pdfr)6117779f75fSKrzysztof Helt static void snd_wss_playback_format(struct snd_wss *chip,
6129295aea1SKrzysztof Helt 				       struct snd_pcm_hw_params *params,
6139295aea1SKrzysztof Helt 				       unsigned char pdfr)
6149295aea1SKrzysztof Helt {
6159295aea1SKrzysztof Helt 	unsigned long flags;
6169295aea1SKrzysztof Helt 	int full_calib = 1;
6179295aea1SKrzysztof Helt 
6189295aea1SKrzysztof Helt 	mutex_lock(&chip->mce_mutex);
6197779f75fSKrzysztof Helt 	if (chip->hardware == WSS_HW_CS4231A ||
6207779f75fSKrzysztof Helt 	    (chip->hardware & WSS_HW_CS4232_MASK)) {
6219295aea1SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
6229295aea1SKrzysztof Helt 		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) {	/* rate is same? */
6237779f75fSKrzysztof Helt 			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
6247779f75fSKrzysztof Helt 				    chip->image[CS4231_ALT_FEATURE_1] | 0x10);
6257779f75fSKrzysztof Helt 			chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
6267779f75fSKrzysztof Helt 			snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
6277779f75fSKrzysztof Helt 				    chip->image[CS4231_PLAYBK_FORMAT]);
6287779f75fSKrzysztof Helt 			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
6297779f75fSKrzysztof Helt 				    chip->image[CS4231_ALT_FEATURE_1] &= ~0x10);
6309295aea1SKrzysztof Helt 			udelay(100); /* Fixes audible clicks at least on GUS MAX */
6319295aea1SKrzysztof Helt 			full_calib = 0;
6329295aea1SKrzysztof Helt 		}
6339295aea1SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
634199f7978SKrzysztof Helt 	} else if (chip->hardware == WSS_HW_AD1845) {
635199f7978SKrzysztof Helt 		unsigned rate = params_rate(params);
636199f7978SKrzysztof Helt 
637199f7978SKrzysztof Helt 		/*
638199f7978SKrzysztof Helt 		 * Program the AD1845 correctly for the playback stream.
639199f7978SKrzysztof Helt 		 * Note that we do NOT need to toggle the MCE bit because
640199f7978SKrzysztof Helt 		 * the PLAYBACK_ENABLE bit of the Interface Configuration
641199f7978SKrzysztof Helt 		 * register is set.
642199f7978SKrzysztof Helt 		 *
643199f7978SKrzysztof Helt 		 * NOTE: We seem to need to write to the MSB before the LSB
644199f7978SKrzysztof Helt 		 *       to get the correct sample frequency.
645199f7978SKrzysztof Helt 		 */
646199f7978SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
647199f7978SKrzysztof Helt 		snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (pdfr & 0xf0));
648199f7978SKrzysztof Helt 		snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
649199f7978SKrzysztof Helt 		snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
650199f7978SKrzysztof Helt 		full_calib = 0;
651199f7978SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
6529295aea1SKrzysztof Helt 	}
6539295aea1SKrzysztof Helt 	if (full_calib) {
6547779f75fSKrzysztof Helt 		snd_wss_mce_up(chip);
6559295aea1SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
6567779f75fSKrzysztof Helt 		if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) {
6577779f75fSKrzysztof Helt 			if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)
6587779f75fSKrzysztof Helt 				pdfr = (pdfr & 0xf0) |
6597779f75fSKrzysztof Helt 				       (chip->image[CS4231_REC_FORMAT] & 0x0f);
6609295aea1SKrzysztof Helt 		} else {
6617779f75fSKrzysztof Helt 			chip->image[CS4231_PLAYBK_FORMAT] = pdfr;
6629295aea1SKrzysztof Helt 		}
6637779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr);
6649295aea1SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
6657779f75fSKrzysztof Helt 		if (chip->hardware == WSS_HW_OPL3SA2)
6669295aea1SKrzysztof Helt 			udelay(100);	/* this seems to help */
6677779f75fSKrzysztof Helt 		snd_wss_mce_down(chip);
6689295aea1SKrzysztof Helt 	}
6699295aea1SKrzysztof Helt 	mutex_unlock(&chip->mce_mutex);
6709295aea1SKrzysztof Helt }
6719295aea1SKrzysztof Helt 
snd_wss_capture_format(struct snd_wss * chip,struct snd_pcm_hw_params * params,unsigned char cdfr)6727779f75fSKrzysztof Helt static void snd_wss_capture_format(struct snd_wss *chip,
6739295aea1SKrzysztof Helt 				   struct snd_pcm_hw_params *params,
6749295aea1SKrzysztof Helt 				   unsigned char cdfr)
6759295aea1SKrzysztof Helt {
6769295aea1SKrzysztof Helt 	unsigned long flags;
6779295aea1SKrzysztof Helt 	int full_calib = 1;
6789295aea1SKrzysztof Helt 
6799295aea1SKrzysztof Helt 	mutex_lock(&chip->mce_mutex);
6807779f75fSKrzysztof Helt 	if (chip->hardware == WSS_HW_CS4231A ||
6817779f75fSKrzysztof Helt 	    (chip->hardware & WSS_HW_CS4232_MASK)) {
6829295aea1SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
6839295aea1SKrzysztof Helt 		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) ||	/* rate is same? */
6849295aea1SKrzysztof Helt 		    (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
6857779f75fSKrzysztof Helt 			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
6867779f75fSKrzysztof Helt 				chip->image[CS4231_ALT_FEATURE_1] | 0x20);
6877779f75fSKrzysztof Helt 			snd_wss_out(chip, CS4231_REC_FORMAT,
6887779f75fSKrzysztof Helt 				chip->image[CS4231_REC_FORMAT] = cdfr);
6897779f75fSKrzysztof Helt 			snd_wss_out(chip, CS4231_ALT_FEATURE_1,
6907779f75fSKrzysztof Helt 				chip->image[CS4231_ALT_FEATURE_1] &= ~0x20);
6919295aea1SKrzysztof Helt 			full_calib = 0;
6929295aea1SKrzysztof Helt 		}
6939295aea1SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
694199f7978SKrzysztof Helt 	} else if (chip->hardware == WSS_HW_AD1845) {
695199f7978SKrzysztof Helt 		unsigned rate = params_rate(params);
696199f7978SKrzysztof Helt 
697199f7978SKrzysztof Helt 		/*
698199f7978SKrzysztof Helt 		 * Program the AD1845 correctly for the capture stream.
699199f7978SKrzysztof Helt 		 * Note that we do NOT need to toggle the MCE bit because
700199f7978SKrzysztof Helt 		 * the PLAYBACK_ENABLE bit of the Interface Configuration
701199f7978SKrzysztof Helt 		 * register is set.
702199f7978SKrzysztof Helt 		 *
703199f7978SKrzysztof Helt 		 * NOTE: We seem to need to write to the MSB before the LSB
704199f7978SKrzysztof Helt 		 *       to get the correct sample frequency.
705199f7978SKrzysztof Helt 		 */
706199f7978SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
707199f7978SKrzysztof Helt 		snd_wss_out(chip, CS4231_REC_FORMAT, (cdfr & 0xf0));
708199f7978SKrzysztof Helt 		snd_wss_out(chip, AD1845_UPR_FREQ_SEL, (rate >> 8) & 0xff);
709199f7978SKrzysztof Helt 		snd_wss_out(chip, AD1845_LWR_FREQ_SEL, rate & 0xff);
710199f7978SKrzysztof Helt 		full_calib = 0;
711199f7978SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
7129295aea1SKrzysztof Helt 	}
7139295aea1SKrzysztof Helt 	if (full_calib) {
7147779f75fSKrzysztof Helt 		snd_wss_mce_up(chip);
7159295aea1SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
7167779f75fSKrzysztof Helt 		if (chip->hardware != WSS_HW_INTERWAVE &&
7177779f75fSKrzysztof Helt 		    !(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) {
7187779f75fSKrzysztof Helt 			if (chip->single_dma)
7197779f75fSKrzysztof Helt 				snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr);
7207779f75fSKrzysztof Helt 			else
7217779f75fSKrzysztof Helt 				snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
7227779f75fSKrzysztof Helt 				   (chip->image[CS4231_PLAYBK_FORMAT] & 0xf0) |
7239295aea1SKrzysztof Helt 				   (cdfr & 0x0f));
7249295aea1SKrzysztof Helt 			spin_unlock_irqrestore(&chip->reg_lock, flags);
7257779f75fSKrzysztof Helt 			snd_wss_mce_down(chip);
7267779f75fSKrzysztof Helt 			snd_wss_mce_up(chip);
7279295aea1SKrzysztof Helt 			spin_lock_irqsave(&chip->reg_lock, flags);
7289295aea1SKrzysztof Helt 		}
729ead893c0SKrzysztof Helt 		if (chip->hardware & WSS_HW_AD1848_MASK)
730ead893c0SKrzysztof Helt 			snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr);
731ead893c0SKrzysztof Helt 		else
7327779f75fSKrzysztof Helt 			snd_wss_out(chip, CS4231_REC_FORMAT, cdfr);
7339295aea1SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
7347779f75fSKrzysztof Helt 		snd_wss_mce_down(chip);
7359295aea1SKrzysztof Helt 	}
7369295aea1SKrzysztof Helt 	mutex_unlock(&chip->mce_mutex);
7379295aea1SKrzysztof Helt }
7389295aea1SKrzysztof Helt 
7399295aea1SKrzysztof Helt /*
7409295aea1SKrzysztof Helt  *  Timer interface
7419295aea1SKrzysztof Helt  */
7429295aea1SKrzysztof Helt 
snd_wss_timer_resolution(struct snd_timer * timer)7437779f75fSKrzysztof Helt static unsigned long snd_wss_timer_resolution(struct snd_timer *timer)
7449295aea1SKrzysztof Helt {
7457779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_timer_chip(timer);
7467779f75fSKrzysztof Helt 	if (chip->hardware & WSS_HW_CS4236B_MASK)
7479295aea1SKrzysztof Helt 		return 14467;
7489295aea1SKrzysztof Helt 	else
7499295aea1SKrzysztof Helt 		return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920;
7509295aea1SKrzysztof Helt }
7519295aea1SKrzysztof Helt 
snd_wss_timer_start(struct snd_timer * timer)7527779f75fSKrzysztof Helt static int snd_wss_timer_start(struct snd_timer *timer)
7539295aea1SKrzysztof Helt {
7549295aea1SKrzysztof Helt 	unsigned long flags;
7559295aea1SKrzysztof Helt 	unsigned int ticks;
7567779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_timer_chip(timer);
7579295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
7589295aea1SKrzysztof Helt 	ticks = timer->sticks;
7599295aea1SKrzysztof Helt 	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 ||
7609295aea1SKrzysztof Helt 	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] ||
7619295aea1SKrzysztof Helt 	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) {
7627779f75fSKrzysztof Helt 		chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8);
7637779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_TIMER_HIGH,
7647779f75fSKrzysztof Helt 			    chip->image[CS4231_TIMER_HIGH]);
7657779f75fSKrzysztof Helt 		chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks;
7667779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_TIMER_LOW,
7677779f75fSKrzysztof Helt 			    chip->image[CS4231_TIMER_LOW]);
7687779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_ALT_FEATURE_1,
7697779f75fSKrzysztof Helt 			    chip->image[CS4231_ALT_FEATURE_1] |
7707779f75fSKrzysztof Helt 			    CS4231_TIMER_ENABLE);
7719295aea1SKrzysztof Helt 	}
7729295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
7739295aea1SKrzysztof Helt 	return 0;
7749295aea1SKrzysztof Helt }
7759295aea1SKrzysztof Helt 
snd_wss_timer_stop(struct snd_timer * timer)7767779f75fSKrzysztof Helt static int snd_wss_timer_stop(struct snd_timer *timer)
7779295aea1SKrzysztof Helt {
7789295aea1SKrzysztof Helt 	unsigned long flags;
7797779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_timer_chip(timer);
7809295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
7817779f75fSKrzysztof Helt 	chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE;
7827779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_ALT_FEATURE_1,
7837779f75fSKrzysztof Helt 		    chip->image[CS4231_ALT_FEATURE_1]);
7849295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
7859295aea1SKrzysztof Helt 	return 0;
7869295aea1SKrzysztof Helt }
7879295aea1SKrzysztof Helt 
snd_wss_init(struct snd_wss * chip)7887779f75fSKrzysztof Helt static void snd_wss_init(struct snd_wss *chip)
7899295aea1SKrzysztof Helt {
7909295aea1SKrzysztof Helt 	unsigned long flags;
7919295aea1SKrzysztof Helt 
7929ef344f8SKrzysztof Helt 	snd_wss_calibrate_mute(chip, 1);
7937779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
7949295aea1SKrzysztof Helt 
7959295aea1SKrzysztof Helt #ifdef SNDRV_DEBUG_MCE
79676d498e4STakashi Iwai 	snd_printk(KERN_DEBUG "init: (1)\n");
7979295aea1SKrzysztof Helt #endif
7987779f75fSKrzysztof Helt 	snd_wss_mce_up(chip);
7999295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
8007779f75fSKrzysztof Helt 	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE |
8017779f75fSKrzysztof Helt 					    CS4231_PLAYBACK_PIO |
8027779f75fSKrzysztof Helt 					    CS4231_RECORD_ENABLE |
8037779f75fSKrzysztof Helt 					    CS4231_RECORD_PIO |
8049295aea1SKrzysztof Helt 					    CS4231_CALIB_MODE);
8059295aea1SKrzysztof Helt 	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB;
8067779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
8079295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
8087779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
8099295aea1SKrzysztof Helt 
8109295aea1SKrzysztof Helt #ifdef SNDRV_DEBUG_MCE
81176d498e4STakashi Iwai 	snd_printk(KERN_DEBUG "init: (2)\n");
8129295aea1SKrzysztof Helt #endif
8139295aea1SKrzysztof Helt 
8147779f75fSKrzysztof Helt 	snd_wss_mce_up(chip);
8159295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
8169ef344f8SKrzysztof Helt 	chip->image[CS4231_IFACE_CTRL] &= ~CS4231_AUTOCALIB;
8179ef344f8SKrzysztof Helt 	snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]);
8187779f75fSKrzysztof Helt 	snd_wss_out(chip,
8197779f75fSKrzysztof Helt 		    CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]);
8209295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
8217779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
8229295aea1SKrzysztof Helt 
8239295aea1SKrzysztof Helt #ifdef SNDRV_DEBUG_MCE
82476d498e4STakashi Iwai 	snd_printk(KERN_DEBUG "init: (3) - afei = 0x%x\n",
8257779f75fSKrzysztof Helt 		   chip->image[CS4231_ALT_FEATURE_1]);
8269295aea1SKrzysztof Helt #endif
8279295aea1SKrzysztof Helt 
8289295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
8297779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_ALT_FEATURE_2,
8307779f75fSKrzysztof Helt 		    chip->image[CS4231_ALT_FEATURE_2]);
8319295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
8329295aea1SKrzysztof Helt 
8337779f75fSKrzysztof Helt 	snd_wss_mce_up(chip);
8349295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
8357779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
8367779f75fSKrzysztof Helt 		    chip->image[CS4231_PLAYBK_FORMAT]);
8379295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
8387779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
8399295aea1SKrzysztof Helt 
8409295aea1SKrzysztof Helt #ifdef SNDRV_DEBUG_MCE
84176d498e4STakashi Iwai 	snd_printk(KERN_DEBUG "init: (4)\n");
8429295aea1SKrzysztof Helt #endif
8439295aea1SKrzysztof Helt 
8447779f75fSKrzysztof Helt 	snd_wss_mce_up(chip);
8459295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
846ead893c0SKrzysztof Helt 	if (!(chip->hardware & WSS_HW_AD1848_MASK))
847ead893c0SKrzysztof Helt 		snd_wss_out(chip, CS4231_REC_FORMAT,
848ead893c0SKrzysztof Helt 			    chip->image[CS4231_REC_FORMAT]);
8499295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
8507779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
8519ef344f8SKrzysztof Helt 	snd_wss_calibrate_mute(chip, 0);
8529295aea1SKrzysztof Helt 
8539295aea1SKrzysztof Helt #ifdef SNDRV_DEBUG_MCE
85476d498e4STakashi Iwai 	snd_printk(KERN_DEBUG "init: (5)\n");
8559295aea1SKrzysztof Helt #endif
8569295aea1SKrzysztof Helt }
8579295aea1SKrzysztof Helt 
snd_wss_open(struct snd_wss * chip,unsigned int mode)8587779f75fSKrzysztof Helt static int snd_wss_open(struct snd_wss *chip, unsigned int mode)
8599295aea1SKrzysztof Helt {
8609295aea1SKrzysztof Helt 	unsigned long flags;
8619295aea1SKrzysztof Helt 
8629295aea1SKrzysztof Helt 	mutex_lock(&chip->open_mutex);
8639295aea1SKrzysztof Helt 	if ((chip->mode & mode) ||
8647779f75fSKrzysztof Helt 	    ((chip->mode & WSS_MODE_OPEN) && chip->single_dma)) {
8659295aea1SKrzysztof Helt 		mutex_unlock(&chip->open_mutex);
8669295aea1SKrzysztof Helt 		return -EAGAIN;
8679295aea1SKrzysztof Helt 	}
8687779f75fSKrzysztof Helt 	if (chip->mode & WSS_MODE_OPEN) {
8699295aea1SKrzysztof Helt 		chip->mode |= mode;
8709295aea1SKrzysztof Helt 		mutex_unlock(&chip->open_mutex);
8719295aea1SKrzysztof Helt 		return 0;
8729295aea1SKrzysztof Helt 	}
8739295aea1SKrzysztof Helt 	/* ok. now enable and ack CODEC IRQ */
8749295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
875ead893c0SKrzysztof Helt 	if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
8767779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_IRQ_STATUS,
8777779f75fSKrzysztof Helt 			    CS4231_PLAYBACK_IRQ |
8789295aea1SKrzysztof Helt 			    CS4231_RECORD_IRQ |
8799295aea1SKrzysztof Helt 			    CS4231_TIMER_IRQ);
8807779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
881ead893c0SKrzysztof Helt 	}
8827779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
8837779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
8849295aea1SKrzysztof Helt 	chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE;
8857779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
886ead893c0SKrzysztof Helt 	if (!(chip->hardware & WSS_HW_AD1848_MASK)) {
8877779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_IRQ_STATUS,
8887779f75fSKrzysztof Helt 			    CS4231_PLAYBACK_IRQ |
8899295aea1SKrzysztof Helt 			    CS4231_RECORD_IRQ |
8909295aea1SKrzysztof Helt 			    CS4231_TIMER_IRQ);
8917779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
892ead893c0SKrzysztof Helt 	}
8939295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
8949295aea1SKrzysztof Helt 
8959295aea1SKrzysztof Helt 	chip->mode = mode;
8969295aea1SKrzysztof Helt 	mutex_unlock(&chip->open_mutex);
8979295aea1SKrzysztof Helt 	return 0;
8989295aea1SKrzysztof Helt }
8999295aea1SKrzysztof Helt 
snd_wss_close(struct snd_wss * chip,unsigned int mode)9007779f75fSKrzysztof Helt static void snd_wss_close(struct snd_wss *chip, unsigned int mode)
9019295aea1SKrzysztof Helt {
9029295aea1SKrzysztof Helt 	unsigned long flags;
9039295aea1SKrzysztof Helt 
9049295aea1SKrzysztof Helt 	mutex_lock(&chip->open_mutex);
9059295aea1SKrzysztof Helt 	chip->mode &= ~mode;
9067779f75fSKrzysztof Helt 	if (chip->mode & WSS_MODE_OPEN) {
9079295aea1SKrzysztof Helt 		mutex_unlock(&chip->open_mutex);
9089295aea1SKrzysztof Helt 		return;
9099295aea1SKrzysztof Helt 	}
9109295aea1SKrzysztof Helt 	/* disable IRQ */
9119295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
912ead893c0SKrzysztof Helt 	if (!(chip->hardware & WSS_HW_AD1848_MASK))
9137779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
9147779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
9157779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
9169295aea1SKrzysztof Helt 	chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE;
9177779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]);
9189295aea1SKrzysztof Helt 
9199295aea1SKrzysztof Helt 	/* now disable record & playback */
9209295aea1SKrzysztof Helt 
9219295aea1SKrzysztof Helt 	if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
9229295aea1SKrzysztof Helt 					       CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) {
9239295aea1SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
9247779f75fSKrzysztof Helt 		snd_wss_mce_up(chip);
9259295aea1SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
9269295aea1SKrzysztof Helt 		chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO |
9279295aea1SKrzysztof Helt 						     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
9287779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_IFACE_CTRL,
9297779f75fSKrzysztof Helt 			    chip->image[CS4231_IFACE_CTRL]);
9309295aea1SKrzysztof Helt 		spin_unlock_irqrestore(&chip->reg_lock, flags);
9317779f75fSKrzysztof Helt 		snd_wss_mce_down(chip);
9329295aea1SKrzysztof Helt 		spin_lock_irqsave(&chip->reg_lock, flags);
9339295aea1SKrzysztof Helt 	}
9349295aea1SKrzysztof Helt 
9359295aea1SKrzysztof Helt 	/* clear IRQ again */
936ead893c0SKrzysztof Helt 	if (!(chip->hardware & WSS_HW_AD1848_MASK))
9377779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_IRQ_STATUS, 0);
9387779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
9397779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */
9409295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
9419295aea1SKrzysztof Helt 
9429295aea1SKrzysztof Helt 	chip->mode = 0;
9439295aea1SKrzysztof Helt 	mutex_unlock(&chip->open_mutex);
9449295aea1SKrzysztof Helt }
9459295aea1SKrzysztof Helt 
9469295aea1SKrzysztof Helt /*
9479295aea1SKrzysztof Helt  *  timer open/close
9489295aea1SKrzysztof Helt  */
9499295aea1SKrzysztof Helt 
snd_wss_timer_open(struct snd_timer * timer)9507779f75fSKrzysztof Helt static int snd_wss_timer_open(struct snd_timer *timer)
9519295aea1SKrzysztof Helt {
9527779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_timer_chip(timer);
9537779f75fSKrzysztof Helt 	snd_wss_open(chip, WSS_MODE_TIMER);
9549295aea1SKrzysztof Helt 	return 0;
9559295aea1SKrzysztof Helt }
9569295aea1SKrzysztof Helt 
snd_wss_timer_close(struct snd_timer * timer)9577779f75fSKrzysztof Helt static int snd_wss_timer_close(struct snd_timer *timer)
9589295aea1SKrzysztof Helt {
9597779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_timer_chip(timer);
9607779f75fSKrzysztof Helt 	snd_wss_close(chip, WSS_MODE_TIMER);
9619295aea1SKrzysztof Helt 	return 0;
9629295aea1SKrzysztof Helt }
9639295aea1SKrzysztof Helt 
9645ff16a3dSTakashi Iwai static const struct snd_timer_hardware snd_wss_timer_table =
9659295aea1SKrzysztof Helt {
9669295aea1SKrzysztof Helt 	.flags =	SNDRV_TIMER_HW_AUTO,
9679295aea1SKrzysztof Helt 	.resolution =	9945,
9689295aea1SKrzysztof Helt 	.ticks =	65535,
9697779f75fSKrzysztof Helt 	.open =		snd_wss_timer_open,
9707779f75fSKrzysztof Helt 	.close =	snd_wss_timer_close,
9717779f75fSKrzysztof Helt 	.c_resolution = snd_wss_timer_resolution,
9727779f75fSKrzysztof Helt 	.start =	snd_wss_timer_start,
9737779f75fSKrzysztof Helt 	.stop =		snd_wss_timer_stop,
9749295aea1SKrzysztof Helt };
9759295aea1SKrzysztof Helt 
9769295aea1SKrzysztof Helt /*
9779295aea1SKrzysztof Helt  *  ok.. exported functions..
9789295aea1SKrzysztof Helt  */
9799295aea1SKrzysztof Helt 
snd_wss_playback_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)9807779f75fSKrzysztof Helt static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream,
9819295aea1SKrzysztof Helt 					 struct snd_pcm_hw_params *hw_params)
9829295aea1SKrzysztof Helt {
9837779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
9849295aea1SKrzysztof Helt 	unsigned char new_pdfr;
9859295aea1SKrzysztof Helt 
9867779f75fSKrzysztof Helt 	new_pdfr = snd_wss_get_format(chip, params_format(hw_params),
9877779f75fSKrzysztof Helt 				params_channels(hw_params)) |
9887779f75fSKrzysztof Helt 				snd_wss_get_rate(params_rate(hw_params));
9899295aea1SKrzysztof Helt 	chip->set_playback_format(chip, hw_params, new_pdfr);
9909295aea1SKrzysztof Helt 	return 0;
9919295aea1SKrzysztof Helt }
9929295aea1SKrzysztof Helt 
snd_wss_playback_prepare(struct snd_pcm_substream * substream)9937779f75fSKrzysztof Helt static int snd_wss_playback_prepare(struct snd_pcm_substream *substream)
9949295aea1SKrzysztof Helt {
9957779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
9969295aea1SKrzysztof Helt 	struct snd_pcm_runtime *runtime = substream->runtime;
9979295aea1SKrzysztof Helt 	unsigned long flags;
9989295aea1SKrzysztof Helt 	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
9999295aea1SKrzysztof Helt 	unsigned int count = snd_pcm_lib_period_bytes(substream);
10009295aea1SKrzysztof Helt 
10019295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
10029295aea1SKrzysztof Helt 	chip->p_dma_size = size;
10039295aea1SKrzysztof Helt 	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO);
10049295aea1SKrzysztof Helt 	snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT);
10057779f75fSKrzysztof Helt 	count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1;
10067779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
10077779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8));
10089295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
10099295aea1SKrzysztof Helt #if 0
10107779f75fSKrzysztof Helt 	snd_wss_debug(chip);
10119295aea1SKrzysztof Helt #endif
10129295aea1SKrzysztof Helt 	return 0;
10139295aea1SKrzysztof Helt }
10149295aea1SKrzysztof Helt 
snd_wss_capture_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * hw_params)10157779f75fSKrzysztof Helt static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream,
10169295aea1SKrzysztof Helt 					struct snd_pcm_hw_params *hw_params)
10179295aea1SKrzysztof Helt {
10187779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
10199295aea1SKrzysztof Helt 	unsigned char new_cdfr;
10209295aea1SKrzysztof Helt 
10217779f75fSKrzysztof Helt 	new_cdfr = snd_wss_get_format(chip, params_format(hw_params),
10227779f75fSKrzysztof Helt 			   params_channels(hw_params)) |
10237779f75fSKrzysztof Helt 			   snd_wss_get_rate(params_rate(hw_params));
10249295aea1SKrzysztof Helt 	chip->set_capture_format(chip, hw_params, new_cdfr);
10259295aea1SKrzysztof Helt 	return 0;
10269295aea1SKrzysztof Helt }
10279295aea1SKrzysztof Helt 
snd_wss_capture_prepare(struct snd_pcm_substream * substream)10287779f75fSKrzysztof Helt static int snd_wss_capture_prepare(struct snd_pcm_substream *substream)
10299295aea1SKrzysztof Helt {
10307779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
10319295aea1SKrzysztof Helt 	struct snd_pcm_runtime *runtime = substream->runtime;
10329295aea1SKrzysztof Helt 	unsigned long flags;
10339295aea1SKrzysztof Helt 	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
10349295aea1SKrzysztof Helt 	unsigned int count = snd_pcm_lib_period_bytes(substream);
10359295aea1SKrzysztof Helt 
10369295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
10379295aea1SKrzysztof Helt 	chip->c_dma_size = size;
10389295aea1SKrzysztof Helt 	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO);
10399295aea1SKrzysztof Helt 	snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT);
1040ead893c0SKrzysztof Helt 	if (chip->hardware & WSS_HW_AD1848_MASK)
1041ead893c0SKrzysztof Helt 		count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT],
1042ead893c0SKrzysztof Helt 					  count);
1043ead893c0SKrzysztof Helt 	else
1044ead893c0SKrzysztof Helt 		count = snd_wss_get_count(chip->image[CS4231_REC_FORMAT],
1045ead893c0SKrzysztof Helt 					  count);
1046ead893c0SKrzysztof Helt 	count--;
10477779f75fSKrzysztof Helt 	if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) {
10487779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count);
10497779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_PLY_UPR_CNT,
10507779f75fSKrzysztof Helt 			    (unsigned char) (count >> 8));
10519295aea1SKrzysztof Helt 	} else {
10527779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count);
10537779f75fSKrzysztof Helt 		snd_wss_out(chip, CS4231_REC_UPR_CNT,
10547779f75fSKrzysztof Helt 			    (unsigned char) (count >> 8));
10559295aea1SKrzysztof Helt 	}
10569295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
10579295aea1SKrzysztof Helt 	return 0;
10589295aea1SKrzysztof Helt }
10599295aea1SKrzysztof Helt 
snd_wss_overrange(struct snd_wss * chip)10607779f75fSKrzysztof Helt void snd_wss_overrange(struct snd_wss *chip)
10619295aea1SKrzysztof Helt {
10629295aea1SKrzysztof Helt 	unsigned long flags;
10639295aea1SKrzysztof Helt 	unsigned char res;
10649295aea1SKrzysztof Helt 
10659295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
10667779f75fSKrzysztof Helt 	res = snd_wss_in(chip, CS4231_TEST_INIT);
10679295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
10689295aea1SKrzysztof Helt 	if (res & (0x08 | 0x02))	/* detect overrange only above 0dB; may be user selectable? */
10699295aea1SKrzysztof Helt 		chip->capture_substream->runtime->overrange++;
10709295aea1SKrzysztof Helt }
10717779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_overrange);
10729295aea1SKrzysztof Helt 
snd_wss_interrupt(int irq,void * dev_id)10737779f75fSKrzysztof Helt irqreturn_t snd_wss_interrupt(int irq, void *dev_id)
10749295aea1SKrzysztof Helt {
10757779f75fSKrzysztof Helt 	struct snd_wss *chip = dev_id;
10769295aea1SKrzysztof Helt 	unsigned char status;
10779295aea1SKrzysztof Helt 
1078760fc6b8SKrzysztof Helt 	if (chip->hardware & WSS_HW_AD1848_MASK)
1079760fc6b8SKrzysztof Helt 		/* pretend it was the only possible irq for AD1848 */
1080760fc6b8SKrzysztof Helt 		status = CS4231_PLAYBACK_IRQ;
1081760fc6b8SKrzysztof Helt 	else
10827779f75fSKrzysztof Helt 		status = snd_wss_in(chip, CS4231_IRQ_STATUS);
10839295aea1SKrzysztof Helt 	if (status & CS4231_TIMER_IRQ) {
10849295aea1SKrzysztof Helt 		if (chip->timer)
10859295aea1SKrzysztof Helt 			snd_timer_interrupt(chip->timer, chip->timer->sticks);
10869295aea1SKrzysztof Helt 	}
10877779f75fSKrzysztof Helt 	if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) {
10889295aea1SKrzysztof Helt 		if (status & CS4231_PLAYBACK_IRQ) {
10897779f75fSKrzysztof Helt 			if (chip->mode & WSS_MODE_PLAY) {
10909295aea1SKrzysztof Helt 				if (chip->playback_substream)
10919295aea1SKrzysztof Helt 					snd_pcm_period_elapsed(chip->playback_substream);
10929295aea1SKrzysztof Helt 			}
10937779f75fSKrzysztof Helt 			if (chip->mode & WSS_MODE_RECORD) {
10949295aea1SKrzysztof Helt 				if (chip->capture_substream) {
10957779f75fSKrzysztof Helt 					snd_wss_overrange(chip);
10969295aea1SKrzysztof Helt 					snd_pcm_period_elapsed(chip->capture_substream);
10979295aea1SKrzysztof Helt 				}
10989295aea1SKrzysztof Helt 			}
10999295aea1SKrzysztof Helt 		}
11009295aea1SKrzysztof Helt 	} else {
11019295aea1SKrzysztof Helt 		if (status & CS4231_PLAYBACK_IRQ) {
11029295aea1SKrzysztof Helt 			if (chip->playback_substream)
11039295aea1SKrzysztof Helt 				snd_pcm_period_elapsed(chip->playback_substream);
11049295aea1SKrzysztof Helt 		}
11059295aea1SKrzysztof Helt 		if (status & CS4231_RECORD_IRQ) {
11069295aea1SKrzysztof Helt 			if (chip->capture_substream) {
11077779f75fSKrzysztof Helt 				snd_wss_overrange(chip);
11089295aea1SKrzysztof Helt 				snd_pcm_period_elapsed(chip->capture_substream);
11099295aea1SKrzysztof Helt 			}
11109295aea1SKrzysztof Helt 		}
11119295aea1SKrzysztof Helt 	}
11129295aea1SKrzysztof Helt 
11139295aea1SKrzysztof Helt 	spin_lock(&chip->reg_lock);
1114760fc6b8SKrzysztof Helt 	status = ~CS4231_ALL_IRQS | ~status;
1115760fc6b8SKrzysztof Helt 	if (chip->hardware & WSS_HW_AD1848_MASK)
1116760fc6b8SKrzysztof Helt 		wss_outb(chip, CS4231P(STATUS), 0);
1117760fc6b8SKrzysztof Helt 	else
11189ef344f8SKrzysztof Helt 		snd_wss_out(chip, CS4231_IRQ_STATUS, status);
11199295aea1SKrzysztof Helt 	spin_unlock(&chip->reg_lock);
11209295aea1SKrzysztof Helt 	return IRQ_HANDLED;
11219295aea1SKrzysztof Helt }
11227779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_interrupt);
11239295aea1SKrzysztof Helt 
snd_wss_playback_pointer(struct snd_pcm_substream * substream)11247779f75fSKrzysztof Helt static snd_pcm_uframes_t snd_wss_playback_pointer(struct snd_pcm_substream *substream)
11259295aea1SKrzysztof Helt {
11267779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
11279295aea1SKrzysztof Helt 	size_t ptr;
11289295aea1SKrzysztof Helt 
11299295aea1SKrzysztof Helt 	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE))
11309295aea1SKrzysztof Helt 		return 0;
11319295aea1SKrzysztof Helt 	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size);
11329295aea1SKrzysztof Helt 	return bytes_to_frames(substream->runtime, ptr);
11339295aea1SKrzysztof Helt }
11349295aea1SKrzysztof Helt 
snd_wss_capture_pointer(struct snd_pcm_substream * substream)11357779f75fSKrzysztof Helt static snd_pcm_uframes_t snd_wss_capture_pointer(struct snd_pcm_substream *substream)
11369295aea1SKrzysztof Helt {
11377779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
11389295aea1SKrzysztof Helt 	size_t ptr;
11399295aea1SKrzysztof Helt 
11409295aea1SKrzysztof Helt 	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE))
11419295aea1SKrzysztof Helt 		return 0;
11429295aea1SKrzysztof Helt 	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size);
11439295aea1SKrzysztof Helt 	return bytes_to_frames(substream->runtime, ptr);
11449295aea1SKrzysztof Helt }
11459295aea1SKrzysztof Helt 
11469295aea1SKrzysztof Helt /*
11479295aea1SKrzysztof Helt 
11489295aea1SKrzysztof Helt  */
11499295aea1SKrzysztof Helt 
snd_ad1848_probe(struct snd_wss * chip)1150760fc6b8SKrzysztof Helt static int snd_ad1848_probe(struct snd_wss *chip)
1151760fc6b8SKrzysztof Helt {
1152c9a7dc2cSRene Herman 	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
1153760fc6b8SKrzysztof Helt 	unsigned long flags;
1154c9a7dc2cSRene Herman 	unsigned char r;
1155c9a7dc2cSRene Herman 	unsigned short hardware = 0;
1156c9a7dc2cSRene Herman 	int err = 0;
1157c9a7dc2cSRene Herman 	int i;
1158760fc6b8SKrzysztof Helt 
1159c9a7dc2cSRene Herman 	while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) {
1160c9a7dc2cSRene Herman 		if (time_after(jiffies, timeout))
1161c9a7dc2cSRene Herman 			return -ENODEV;
1162c9a7dc2cSRene Herman 		cond_resched();
1163c9a7dc2cSRene Herman 	}
1164760fc6b8SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
1165760fc6b8SKrzysztof Helt 
1166c9a7dc2cSRene Herman 	/* set CS423x MODE 1 */
1167f83a59c3SKrzysztof Helt 	snd_wss_dout(chip, CS4231_MISC_INFO, 0);
1168760fc6b8SKrzysztof Helt 
1169f83a59c3SKrzysztof Helt 	snd_wss_dout(chip, CS4231_RIGHT_INPUT, 0x45); /* 0x55 & ~0x10 */
1170c9a7dc2cSRene Herman 	r = snd_wss_in(chip, CS4231_RIGHT_INPUT);
1171c9a7dc2cSRene Herman 	if (r != 0x45) {
1172c9a7dc2cSRene Herman 		/* RMGE always high on AD1847 */
1173c9a7dc2cSRene Herman 		if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45) {
1174c9a7dc2cSRene Herman 			err = -ENODEV;
1175c9a7dc2cSRene Herman 			goto out;
1176c9a7dc2cSRene Herman 		}
1177c9a7dc2cSRene Herman 		hardware = WSS_HW_AD1847;
1178c9a7dc2cSRene Herman 	} else {
1179f83a59c3SKrzysztof Helt 		snd_wss_dout(chip, CS4231_LEFT_INPUT,  0xaa);
1180c9a7dc2cSRene Herman 		r = snd_wss_in(chip, CS4231_LEFT_INPUT);
1181c9a7dc2cSRene Herman 		/* L/RMGE always low on AT2320 */
1182c9a7dc2cSRene Herman 		if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa) {
1183c9a7dc2cSRene Herman 			err = -ENODEV;
1184c9a7dc2cSRene Herman 			goto out;
1185c9a7dc2cSRene Herman 		}
1186c9a7dc2cSRene Herman 	}
1187c9a7dc2cSRene Herman 
1188c9a7dc2cSRene Herman 	/* clear pending IRQ */
1189c9a7dc2cSRene Herman 	wss_inb(chip, CS4231P(STATUS));
1190c9a7dc2cSRene Herman 	wss_outb(chip, CS4231P(STATUS), 0);
1191c9a7dc2cSRene Herman 	mb();
1192c9a7dc2cSRene Herman 
1193c9a7dc2cSRene Herman 	if ((chip->hardware & WSS_HW_TYPE_MASK) != WSS_HW_DETECT)
1194c9a7dc2cSRene Herman 		goto out;
1195c9a7dc2cSRene Herman 
1196c9a7dc2cSRene Herman 	if (hardware) {
1197c9a7dc2cSRene Herman 		chip->hardware = hardware;
1198c9a7dc2cSRene Herman 		goto out;
1199c9a7dc2cSRene Herman 	}
1200c9a7dc2cSRene Herman 
1201c9a7dc2cSRene Herman 	r = snd_wss_in(chip, CS4231_MISC_INFO);
1202c9a7dc2cSRene Herman 
1203c9a7dc2cSRene Herman 	/* set CS423x MODE 2 */
1204f83a59c3SKrzysztof Helt 	snd_wss_dout(chip, CS4231_MISC_INFO, CS4231_MODE2);
1205c9a7dc2cSRene Herman 	for (i = 0; i < 16; i++) {
1206c9a7dc2cSRene Herman 		if (snd_wss_in(chip, i) != snd_wss_in(chip, 16 + i)) {
1207c9a7dc2cSRene Herman 			/* we have more than 16 registers: check ID */
1208c9a7dc2cSRene Herman 			if ((r & 0xf) != 0xa)
1209c9a7dc2cSRene Herman 				goto out_mode;
1210c9a7dc2cSRene Herman 			/*
1211c9a7dc2cSRene Herman 			 * on CMI8330, CS4231_VERSION is volume control and
1212c9a7dc2cSRene Herman 			 * can be set to 0
1213c9a7dc2cSRene Herman 			 */
1214c9a7dc2cSRene Herman 			snd_wss_dout(chip, CS4231_VERSION, 0);
1215c9a7dc2cSRene Herman 			r = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
1216c9a7dc2cSRene Herman 			if (!r)
1217c9a7dc2cSRene Herman 				chip->hardware = WSS_HW_CMI8330;
1218c9a7dc2cSRene Herman 			goto out_mode;
1219c9a7dc2cSRene Herman 		}
1220c9a7dc2cSRene Herman 	}
1221c9a7dc2cSRene Herman 	if (r & 0x80)
1222c9a7dc2cSRene Herman 		chip->hardware = WSS_HW_CS4248;
1223c9a7dc2cSRene Herman 	else
1224c9a7dc2cSRene Herman 		chip->hardware = WSS_HW_AD1848;
1225c9a7dc2cSRene Herman out_mode:
1226f83a59c3SKrzysztof Helt 	snd_wss_dout(chip, CS4231_MISC_INFO, 0);
1227c9a7dc2cSRene Herman out:
1228760fc6b8SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1229c9a7dc2cSRene Herman 	return err;
1230760fc6b8SKrzysztof Helt }
1231760fc6b8SKrzysztof Helt 
snd_wss_probe(struct snd_wss * chip)12327779f75fSKrzysztof Helt static int snd_wss_probe(struct snd_wss *chip)
12339295aea1SKrzysztof Helt {
12349295aea1SKrzysztof Helt 	unsigned long flags;
1235760fc6b8SKrzysztof Helt 	int i, id, rev, regnum;
12369295aea1SKrzysztof Helt 	unsigned char *ptr;
12379295aea1SKrzysztof Helt 	unsigned int hw;
12389295aea1SKrzysztof Helt 
1239760fc6b8SKrzysztof Helt 	id = snd_ad1848_probe(chip);
1240760fc6b8SKrzysztof Helt 	if (id < 0)
1241760fc6b8SKrzysztof Helt 		return id;
1242760fc6b8SKrzysztof Helt 
1243760fc6b8SKrzysztof Helt 	hw = chip->hardware;
1244760fc6b8SKrzysztof Helt 	if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) {
12459295aea1SKrzysztof Helt 		for (i = 0; i < 50; i++) {
12469295aea1SKrzysztof Helt 			mb();
12477779f75fSKrzysztof Helt 			if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT)
1248760fc6b8SKrzysztof Helt 				msleep(2);
12499295aea1SKrzysztof Helt 			else {
12509295aea1SKrzysztof Helt 				spin_lock_irqsave(&chip->reg_lock, flags);
1251760fc6b8SKrzysztof Helt 				snd_wss_out(chip, CS4231_MISC_INFO,
1252760fc6b8SKrzysztof Helt 					    CS4231_MODE2);
12537779f75fSKrzysztof Helt 				id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f;
12549295aea1SKrzysztof Helt 				spin_unlock_irqrestore(&chip->reg_lock, flags);
12559295aea1SKrzysztof Helt 				if (id == 0x0a)
12569295aea1SKrzysztof Helt 					break;	/* this is valid value */
12579295aea1SKrzysztof Helt 			}
12589295aea1SKrzysztof Helt 		}
12597779f75fSKrzysztof Helt 		snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id);
12609295aea1SKrzysztof Helt 		if (id != 0x0a)
12619295aea1SKrzysztof Helt 			return -ENODEV;	/* no valid device found */
12629295aea1SKrzysztof Helt 
12637779f75fSKrzysztof Helt 		rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7;
12649295aea1SKrzysztof Helt 		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
12659295aea1SKrzysztof Helt 		if (rev == 0x80) {
12667779f75fSKrzysztof Helt 			unsigned char tmp = snd_wss_in(chip, 23);
12677779f75fSKrzysztof Helt 			snd_wss_out(chip, 23, ~tmp);
12687779f75fSKrzysztof Helt 			if (snd_wss_in(chip, 23) != tmp)
12697779f75fSKrzysztof Helt 				chip->hardware = WSS_HW_AD1845;
12709295aea1SKrzysztof Helt 			else
12717779f75fSKrzysztof Helt 				chip->hardware = WSS_HW_CS4231;
12729295aea1SKrzysztof Helt 		} else if (rev == 0xa0) {
12737779f75fSKrzysztof Helt 			chip->hardware = WSS_HW_CS4231A;
12749295aea1SKrzysztof Helt 		} else if (rev == 0xa2) {
12757779f75fSKrzysztof Helt 			chip->hardware = WSS_HW_CS4232;
12769295aea1SKrzysztof Helt 		} else if (rev == 0xb2) {
12777779f75fSKrzysztof Helt 			chip->hardware = WSS_HW_CS4232A;
12789295aea1SKrzysztof Helt 		} else if (rev == 0x83) {
12797779f75fSKrzysztof Helt 			chip->hardware = WSS_HW_CS4236;
12809295aea1SKrzysztof Helt 		} else if (rev == 0x03) {
12817779f75fSKrzysztof Helt 			chip->hardware = WSS_HW_CS4236B;
12829295aea1SKrzysztof Helt 		} else {
128376d498e4STakashi Iwai 			snd_printk(KERN_ERR
128476d498e4STakashi Iwai 				   "unknown CS chip with version 0x%x\n", rev);
12859295aea1SKrzysztof Helt 			return -ENODEV;		/* unknown CS4231 chip? */
12869295aea1SKrzysztof Helt 		}
12879295aea1SKrzysztof Helt 	}
12889295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
12897779f75fSKrzysztof Helt 	wss_inb(chip, CS4231P(STATUS));	/* clear any pendings IRQ */
12907779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(STATUS), 0);
12919295aea1SKrzysztof Helt 	mb();
12929295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
12939295aea1SKrzysztof Helt 
1294760fc6b8SKrzysztof Helt 	if (!(chip->hardware & WSS_HW_AD1848_MASK))
12959295aea1SKrzysztof Helt 		chip->image[CS4231_MISC_INFO] = CS4231_MODE2;
12969295aea1SKrzysztof Helt 	switch (chip->hardware) {
12977779f75fSKrzysztof Helt 	case WSS_HW_INTERWAVE:
12989295aea1SKrzysztof Helt 		chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3;
12999295aea1SKrzysztof Helt 		break;
13007779f75fSKrzysztof Helt 	case WSS_HW_CS4235:
13017779f75fSKrzysztof Helt 	case WSS_HW_CS4236B:
13027779f75fSKrzysztof Helt 	case WSS_HW_CS4237B:
13037779f75fSKrzysztof Helt 	case WSS_HW_CS4238B:
13047779f75fSKrzysztof Helt 	case WSS_HW_CS4239:
13057779f75fSKrzysztof Helt 		if (hw == WSS_HW_DETECT3)
13069295aea1SKrzysztof Helt 			chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3;
13079295aea1SKrzysztof Helt 		else
13087779f75fSKrzysztof Helt 			chip->hardware = WSS_HW_CS4236;
13099295aea1SKrzysztof Helt 		break;
13109295aea1SKrzysztof Helt 	}
13119295aea1SKrzysztof Helt 
13129295aea1SKrzysztof Helt 	chip->image[CS4231_IFACE_CTRL] =
13139295aea1SKrzysztof Helt 	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) |
13149295aea1SKrzysztof Helt 	    (chip->single_dma ? CS4231_SINGLE_DMA : 0);
13157779f75fSKrzysztof Helt 	if (chip->hardware != WSS_HW_OPTI93X) {
13169295aea1SKrzysztof Helt 		chip->image[CS4231_ALT_FEATURE_1] = 0x80;
13179295aea1SKrzysztof Helt 		chip->image[CS4231_ALT_FEATURE_2] =
13187779f75fSKrzysztof Helt 			chip->hardware == WSS_HW_INTERWAVE ? 0xc2 : 0x01;
13199295aea1SKrzysztof Helt 	}
1320199f7978SKrzysztof Helt 	/* enable fine grained frequency selection */
1321199f7978SKrzysztof Helt 	if (chip->hardware == WSS_HW_AD1845)
1322199f7978SKrzysztof Helt 		chip->image[AD1845_PWR_DOWN] = 8;
1323199f7978SKrzysztof Helt 
13249295aea1SKrzysztof Helt 	ptr = (unsigned char *) &chip->image;
1325760fc6b8SKrzysztof Helt 	regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32;
13267779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
13279295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
1328760fc6b8SKrzysztof Helt 	for (i = 0; i < regnum; i++)	/* ok.. fill all registers */
13297779f75fSKrzysztof Helt 		snd_wss_out(chip, i, *ptr++);
13309295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
13317779f75fSKrzysztof Helt 	snd_wss_mce_up(chip);
13327779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
13339295aea1SKrzysztof Helt 
13349295aea1SKrzysztof Helt 	mdelay(2);
13359295aea1SKrzysztof Helt 
13369295aea1SKrzysztof Helt 	/* ok.. try check hardware version for CS4236+ chips */
13377779f75fSKrzysztof Helt 	if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) {
13387779f75fSKrzysztof Helt 		if (chip->hardware == WSS_HW_CS4236B) {
13399295aea1SKrzysztof Helt 			rev = snd_cs4236_ext_in(chip, CS4236_VERSION);
13409295aea1SKrzysztof Helt 			snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff);
13419295aea1SKrzysztof Helt 			id = snd_cs4236_ext_in(chip, CS4236_VERSION);
13429295aea1SKrzysztof Helt 			snd_cs4236_ext_out(chip, CS4236_VERSION, rev);
13439295aea1SKrzysztof Helt 			snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id);
13449295aea1SKrzysztof Helt 			if ((id & 0x1f) == 0x1d) {	/* CS4235 */
13457779f75fSKrzysztof Helt 				chip->hardware = WSS_HW_CS4235;
13469295aea1SKrzysztof Helt 				switch (id >> 5) {
13479295aea1SKrzysztof Helt 				case 4:
13489295aea1SKrzysztof Helt 				case 5:
13499295aea1SKrzysztof Helt 				case 6:
13509295aea1SKrzysztof Helt 					break;
13519295aea1SKrzysztof Helt 				default:
135276d498e4STakashi Iwai 					snd_printk(KERN_WARNING
135376d498e4STakashi Iwai 						"unknown CS4235 chip "
135476d498e4STakashi Iwai 						"(enhanced version = 0x%x)\n",
135576d498e4STakashi Iwai 						id);
13569295aea1SKrzysztof Helt 				}
13579295aea1SKrzysztof Helt 			} else if ((id & 0x1f) == 0x0b) {	/* CS4236/B */
13589295aea1SKrzysztof Helt 				switch (id >> 5) {
13599295aea1SKrzysztof Helt 				case 4:
13609295aea1SKrzysztof Helt 				case 5:
13619295aea1SKrzysztof Helt 				case 6:
13629295aea1SKrzysztof Helt 				case 7:
13637779f75fSKrzysztof Helt 					chip->hardware = WSS_HW_CS4236B;
13649295aea1SKrzysztof Helt 					break;
13659295aea1SKrzysztof Helt 				default:
136676d498e4STakashi Iwai 					snd_printk(KERN_WARNING
136776d498e4STakashi Iwai 						"unknown CS4236 chip "
136876d498e4STakashi Iwai 						"(enhanced version = 0x%x)\n",
136976d498e4STakashi Iwai 						id);
13709295aea1SKrzysztof Helt 				}
13719295aea1SKrzysztof Helt 			} else if ((id & 0x1f) == 0x08) {	/* CS4237B */
13727779f75fSKrzysztof Helt 				chip->hardware = WSS_HW_CS4237B;
13739295aea1SKrzysztof Helt 				switch (id >> 5) {
13749295aea1SKrzysztof Helt 				case 4:
13759295aea1SKrzysztof Helt 				case 5:
13769295aea1SKrzysztof Helt 				case 6:
13779295aea1SKrzysztof Helt 				case 7:
13789295aea1SKrzysztof Helt 					break;
13799295aea1SKrzysztof Helt 				default:
138076d498e4STakashi Iwai 					snd_printk(KERN_WARNING
138176d498e4STakashi Iwai 						"unknown CS4237B chip "
138276d498e4STakashi Iwai 						"(enhanced version = 0x%x)\n",
138376d498e4STakashi Iwai 						id);
13849295aea1SKrzysztof Helt 				}
13859295aea1SKrzysztof Helt 			} else if ((id & 0x1f) == 0x09) {	/* CS4238B */
13867779f75fSKrzysztof Helt 				chip->hardware = WSS_HW_CS4238B;
13879295aea1SKrzysztof Helt 				switch (id >> 5) {
13889295aea1SKrzysztof Helt 				case 5:
13899295aea1SKrzysztof Helt 				case 6:
13909295aea1SKrzysztof Helt 				case 7:
13919295aea1SKrzysztof Helt 					break;
13929295aea1SKrzysztof Helt 				default:
139376d498e4STakashi Iwai 					snd_printk(KERN_WARNING
139476d498e4STakashi Iwai 						"unknown CS4238B chip "
139576d498e4STakashi Iwai 						"(enhanced version = 0x%x)\n",
139676d498e4STakashi Iwai 						id);
13979295aea1SKrzysztof Helt 				}
13989295aea1SKrzysztof Helt 			} else if ((id & 0x1f) == 0x1e) {	/* CS4239 */
13997779f75fSKrzysztof Helt 				chip->hardware = WSS_HW_CS4239;
14009295aea1SKrzysztof Helt 				switch (id >> 5) {
14019295aea1SKrzysztof Helt 				case 4:
14029295aea1SKrzysztof Helt 				case 5:
14039295aea1SKrzysztof Helt 				case 6:
14049295aea1SKrzysztof Helt 					break;
14059295aea1SKrzysztof Helt 				default:
140676d498e4STakashi Iwai 					snd_printk(KERN_WARNING
140776d498e4STakashi Iwai 						"unknown CS4239 chip "
140876d498e4STakashi Iwai 						"(enhanced version = 0x%x)\n",
140976d498e4STakashi Iwai 						id);
14109295aea1SKrzysztof Helt 				}
14119295aea1SKrzysztof Helt 			} else {
141276d498e4STakashi Iwai 				snd_printk(KERN_WARNING
141376d498e4STakashi Iwai 					   "unknown CS4236/CS423xB chip "
141476d498e4STakashi Iwai 					   "(enhanced version = 0x%x)\n", id);
14159295aea1SKrzysztof Helt 			}
14169295aea1SKrzysztof Helt 		}
14179295aea1SKrzysztof Helt 	}
14189295aea1SKrzysztof Helt 	return 0;		/* all things are ok.. */
14199295aea1SKrzysztof Helt }
14209295aea1SKrzysztof Helt 
14219295aea1SKrzysztof Helt /*
14229295aea1SKrzysztof Helt 
14239295aea1SKrzysztof Helt  */
14249295aea1SKrzysztof Helt 
1425aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_wss_playback =
14269295aea1SKrzysztof Helt {
14279295aea1SKrzysztof Helt 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
14289295aea1SKrzysztof Helt 				 SNDRV_PCM_INFO_MMAP_VALID |
14299295aea1SKrzysztof Helt 				 SNDRV_PCM_INFO_SYNC_START),
14309295aea1SKrzysztof Helt 	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
14319295aea1SKrzysztof Helt 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
14329295aea1SKrzysztof Helt 	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
14339295aea1SKrzysztof Helt 	.rate_min =		5510,
14349295aea1SKrzysztof Helt 	.rate_max =		48000,
14359295aea1SKrzysztof Helt 	.channels_min =		1,
14369295aea1SKrzysztof Helt 	.channels_max =		2,
14379295aea1SKrzysztof Helt 	.buffer_bytes_max =	(128*1024),
14389295aea1SKrzysztof Helt 	.period_bytes_min =	64,
14399295aea1SKrzysztof Helt 	.period_bytes_max =	(128*1024),
14409295aea1SKrzysztof Helt 	.periods_min =		1,
14419295aea1SKrzysztof Helt 	.periods_max =		1024,
14429295aea1SKrzysztof Helt 	.fifo_size =		0,
14439295aea1SKrzysztof Helt };
14449295aea1SKrzysztof Helt 
1445aec54654SBhumika Goyal static const struct snd_pcm_hardware snd_wss_capture =
14469295aea1SKrzysztof Helt {
14479295aea1SKrzysztof Helt 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
14489295aea1SKrzysztof Helt 				 SNDRV_PCM_INFO_MMAP_VALID |
14499295aea1SKrzysztof Helt 				 SNDRV_PCM_INFO_RESUME |
14509295aea1SKrzysztof Helt 				 SNDRV_PCM_INFO_SYNC_START),
14519295aea1SKrzysztof Helt 	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
14529295aea1SKrzysztof Helt 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
14539295aea1SKrzysztof Helt 	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
14549295aea1SKrzysztof Helt 	.rate_min =		5510,
14559295aea1SKrzysztof Helt 	.rate_max =		48000,
14569295aea1SKrzysztof Helt 	.channels_min =		1,
14579295aea1SKrzysztof Helt 	.channels_max =		2,
14589295aea1SKrzysztof Helt 	.buffer_bytes_max =	(128*1024),
14599295aea1SKrzysztof Helt 	.period_bytes_min =	64,
14609295aea1SKrzysztof Helt 	.period_bytes_max =	(128*1024),
14619295aea1SKrzysztof Helt 	.periods_min =		1,
14629295aea1SKrzysztof Helt 	.periods_max =		1024,
14639295aea1SKrzysztof Helt 	.fifo_size =		0,
14649295aea1SKrzysztof Helt };
14659295aea1SKrzysztof Helt 
14669295aea1SKrzysztof Helt /*
14679295aea1SKrzysztof Helt 
14689295aea1SKrzysztof Helt  */
14699295aea1SKrzysztof Helt 
snd_wss_playback_open(struct snd_pcm_substream * substream)14707779f75fSKrzysztof Helt static int snd_wss_playback_open(struct snd_pcm_substream *substream)
14719295aea1SKrzysztof Helt {
14727779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
14739295aea1SKrzysztof Helt 	struct snd_pcm_runtime *runtime = substream->runtime;
14749295aea1SKrzysztof Helt 	int err;
14759295aea1SKrzysztof Helt 
14767779f75fSKrzysztof Helt 	runtime->hw = snd_wss_playback;
14779295aea1SKrzysztof Helt 
1478ead893c0SKrzysztof Helt 	/* hardware limitation of older chipsets */
1479ead893c0SKrzysztof Helt 	if (chip->hardware & WSS_HW_AD1848_MASK)
1480ead893c0SKrzysztof Helt 		runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM |
1481ead893c0SKrzysztof Helt 					 SNDRV_PCM_FMTBIT_S16_BE);
1482ead893c0SKrzysztof Helt 
14839295aea1SKrzysztof Helt 	/* hardware bug in InterWave chipset */
14847779f75fSKrzysztof Helt 	if (chip->hardware == WSS_HW_INTERWAVE && chip->dma1 > 3)
14859295aea1SKrzysztof Helt 		runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW;
14869295aea1SKrzysztof Helt 
14879295aea1SKrzysztof Helt 	/* hardware limitation of cheap chips */
14887779f75fSKrzysztof Helt 	if (chip->hardware == WSS_HW_CS4235 ||
14897779f75fSKrzysztof Helt 	    chip->hardware == WSS_HW_CS4239)
14909295aea1SKrzysztof Helt 		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE;
14919295aea1SKrzysztof Helt 
14929295aea1SKrzysztof Helt 	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max);
14939295aea1SKrzysztof Helt 	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max);
14949295aea1SKrzysztof Helt 
14959295aea1SKrzysztof Helt 	if (chip->claim_dma) {
1496eb767949STakashi Iwai 		err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1);
1497eb767949STakashi Iwai 		if (err < 0)
14989295aea1SKrzysztof Helt 			return err;
14999295aea1SKrzysztof Helt 	}
15009295aea1SKrzysztof Helt 
15017779f75fSKrzysztof Helt 	err = snd_wss_open(chip, WSS_MODE_PLAY);
15027779f75fSKrzysztof Helt 	if (err < 0) {
15039295aea1SKrzysztof Helt 		if (chip->release_dma)
15049295aea1SKrzysztof Helt 			chip->release_dma(chip, chip->dma_private_data, chip->dma1);
15059295aea1SKrzysztof Helt 		return err;
15069295aea1SKrzysztof Helt 	}
15079295aea1SKrzysztof Helt 	chip->playback_substream = substream;
15089295aea1SKrzysztof Helt 	snd_pcm_set_sync(substream);
15099295aea1SKrzysztof Helt 	chip->rate_constraint(runtime);
15109295aea1SKrzysztof Helt 	return 0;
15119295aea1SKrzysztof Helt }
15129295aea1SKrzysztof Helt 
snd_wss_capture_open(struct snd_pcm_substream * substream)15137779f75fSKrzysztof Helt static int snd_wss_capture_open(struct snd_pcm_substream *substream)
15149295aea1SKrzysztof Helt {
15157779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
15169295aea1SKrzysztof Helt 	struct snd_pcm_runtime *runtime = substream->runtime;
15179295aea1SKrzysztof Helt 	int err;
15189295aea1SKrzysztof Helt 
15197779f75fSKrzysztof Helt 	runtime->hw = snd_wss_capture;
15209295aea1SKrzysztof Helt 
1521ead893c0SKrzysztof Helt 	/* hardware limitation of older chipsets */
1522ead893c0SKrzysztof Helt 	if (chip->hardware & WSS_HW_AD1848_MASK)
1523ead893c0SKrzysztof Helt 		runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM |
1524ead893c0SKrzysztof Helt 					 SNDRV_PCM_FMTBIT_S16_BE);
1525ead893c0SKrzysztof Helt 
15269295aea1SKrzysztof Helt 	/* hardware limitation of cheap chips */
15277779f75fSKrzysztof Helt 	if (chip->hardware == WSS_HW_CS4235 ||
152831eca307SKrzysztof Helt 	    chip->hardware == WSS_HW_CS4239 ||
152931eca307SKrzysztof Helt 	    chip->hardware == WSS_HW_OPTI93X)
153031eca307SKrzysztof Helt 		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 |
153131eca307SKrzysztof Helt 				      SNDRV_PCM_FMTBIT_S16_LE;
15329295aea1SKrzysztof Helt 
15339295aea1SKrzysztof Helt 	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max);
15349295aea1SKrzysztof Helt 	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max);
15359295aea1SKrzysztof Helt 
15369295aea1SKrzysztof Helt 	if (chip->claim_dma) {
1537eb767949STakashi Iwai 		err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2);
1538eb767949STakashi Iwai 		if (err < 0)
15399295aea1SKrzysztof Helt 			return err;
15409295aea1SKrzysztof Helt 	}
15419295aea1SKrzysztof Helt 
15427779f75fSKrzysztof Helt 	err = snd_wss_open(chip, WSS_MODE_RECORD);
15437779f75fSKrzysztof Helt 	if (err < 0) {
15449295aea1SKrzysztof Helt 		if (chip->release_dma)
15459295aea1SKrzysztof Helt 			chip->release_dma(chip, chip->dma_private_data, chip->dma2);
15469295aea1SKrzysztof Helt 		return err;
15479295aea1SKrzysztof Helt 	}
15489295aea1SKrzysztof Helt 	chip->capture_substream = substream;
15499295aea1SKrzysztof Helt 	snd_pcm_set_sync(substream);
15509295aea1SKrzysztof Helt 	chip->rate_constraint(runtime);
15519295aea1SKrzysztof Helt 	return 0;
15529295aea1SKrzysztof Helt }
15539295aea1SKrzysztof Helt 
snd_wss_playback_close(struct snd_pcm_substream * substream)15547779f75fSKrzysztof Helt static int snd_wss_playback_close(struct snd_pcm_substream *substream)
15559295aea1SKrzysztof Helt {
15567779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
15579295aea1SKrzysztof Helt 
15589295aea1SKrzysztof Helt 	chip->playback_substream = NULL;
15597779f75fSKrzysztof Helt 	snd_wss_close(chip, WSS_MODE_PLAY);
15609295aea1SKrzysztof Helt 	return 0;
15619295aea1SKrzysztof Helt }
15629295aea1SKrzysztof Helt 
snd_wss_capture_close(struct snd_pcm_substream * substream)15637779f75fSKrzysztof Helt static int snd_wss_capture_close(struct snd_pcm_substream *substream)
15649295aea1SKrzysztof Helt {
15657779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_pcm_substream_chip(substream);
15669295aea1SKrzysztof Helt 
15679295aea1SKrzysztof Helt 	chip->capture_substream = NULL;
15687779f75fSKrzysztof Helt 	snd_wss_close(chip, WSS_MODE_RECORD);
15699295aea1SKrzysztof Helt 	return 0;
15709295aea1SKrzysztof Helt }
15719295aea1SKrzysztof Helt 
snd_wss_thinkpad_twiddle(struct snd_wss * chip,int on)1572ead893c0SKrzysztof Helt static void snd_wss_thinkpad_twiddle(struct snd_wss *chip, int on)
1573ead893c0SKrzysztof Helt {
1574ead893c0SKrzysztof Helt 	int tmp;
1575ead893c0SKrzysztof Helt 
1576ead893c0SKrzysztof Helt 	if (!chip->thinkpad_flag)
1577ead893c0SKrzysztof Helt 		return;
1578ead893c0SKrzysztof Helt 
1579ead893c0SKrzysztof Helt 	outb(0x1c, AD1848_THINKPAD_CTL_PORT1);
1580ead893c0SKrzysztof Helt 	tmp = inb(AD1848_THINKPAD_CTL_PORT2);
1581ead893c0SKrzysztof Helt 
1582ead893c0SKrzysztof Helt 	if (on)
1583ead893c0SKrzysztof Helt 		/* turn it on */
1584ead893c0SKrzysztof Helt 		tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT;
1585ead893c0SKrzysztof Helt 	else
1586ead893c0SKrzysztof Helt 		/* turn it off */
1587ead893c0SKrzysztof Helt 		tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT;
1588ead893c0SKrzysztof Helt 
1589ead893c0SKrzysztof Helt 	outb(tmp, AD1848_THINKPAD_CTL_PORT2);
1590ead893c0SKrzysztof Helt }
1591ead893c0SKrzysztof Helt 
15929295aea1SKrzysztof Helt #ifdef CONFIG_PM
15939295aea1SKrzysztof Helt 
15949295aea1SKrzysztof Helt /* lowlevel suspend callback for CS4231 */
snd_wss_suspend(struct snd_wss * chip)15957779f75fSKrzysztof Helt static void snd_wss_suspend(struct snd_wss *chip)
15969295aea1SKrzysztof Helt {
15979295aea1SKrzysztof Helt 	int reg;
15989295aea1SKrzysztof Helt 	unsigned long flags;
15999295aea1SKrzysztof Helt 
16009295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
16019295aea1SKrzysztof Helt 	for (reg = 0; reg < 32; reg++)
16027779f75fSKrzysztof Helt 		chip->image[reg] = snd_wss_in(chip, reg);
16039295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
1604ead893c0SKrzysztof Helt 	if (chip->thinkpad_flag)
1605ead893c0SKrzysztof Helt 		snd_wss_thinkpad_twiddle(chip, 0);
16069295aea1SKrzysztof Helt }
16079295aea1SKrzysztof Helt 
16089295aea1SKrzysztof Helt /* lowlevel resume callback for CS4231 */
snd_wss_resume(struct snd_wss * chip)16097779f75fSKrzysztof Helt static void snd_wss_resume(struct snd_wss *chip)
16109295aea1SKrzysztof Helt {
16119295aea1SKrzysztof Helt 	int reg;
16129295aea1SKrzysztof Helt 	unsigned long flags;
16139295aea1SKrzysztof Helt 	/* int timeout; */
16149295aea1SKrzysztof Helt 
1615ead893c0SKrzysztof Helt 	if (chip->thinkpad_flag)
1616ead893c0SKrzysztof Helt 		snd_wss_thinkpad_twiddle(chip, 1);
16177779f75fSKrzysztof Helt 	snd_wss_mce_up(chip);
16189295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
16199295aea1SKrzysztof Helt 	for (reg = 0; reg < 32; reg++) {
16209295aea1SKrzysztof Helt 		switch (reg) {
16219295aea1SKrzysztof Helt 		case CS4231_VERSION:
16229295aea1SKrzysztof Helt 			break;
16239295aea1SKrzysztof Helt 		default:
16247779f75fSKrzysztof Helt 			snd_wss_out(chip, reg, chip->image[reg]);
16259295aea1SKrzysztof Helt 			break;
16269295aea1SKrzysztof Helt 		}
16279295aea1SKrzysztof Helt 	}
1628b4e2a16fSOndrej Zary 	/* Yamaha needs this to resume properly */
1629b4e2a16fSOndrej Zary 	if (chip->hardware == WSS_HW_OPL3SA2)
1630b4e2a16fSOndrej Zary 		snd_wss_out(chip, CS4231_PLAYBK_FORMAT,
1631b4e2a16fSOndrej Zary 			    chip->image[CS4231_PLAYBK_FORMAT]);
16329295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
16339295aea1SKrzysztof Helt #if 1
16347779f75fSKrzysztof Helt 	snd_wss_mce_down(chip);
16359295aea1SKrzysztof Helt #else
16369295aea1SKrzysztof Helt 	/* The following is a workaround to avoid freeze after resume on TP600E.
16377779f75fSKrzysztof Helt 	   This is the first half of copy of snd_wss_mce_down(), but doesn't
16389295aea1SKrzysztof Helt 	   include rescheduling.  -- iwai
16399295aea1SKrzysztof Helt 	   */
16407779f75fSKrzysztof Helt 	snd_wss_busy_wait(chip);
16419295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
16429295aea1SKrzysztof Helt 	chip->mce_bit &= ~CS4231_MCE;
16437779f75fSKrzysztof Helt 	timeout = wss_inb(chip, CS4231P(REGSEL));
16447779f75fSKrzysztof Helt 	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f));
16459295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
16469295aea1SKrzysztof Helt 	if (timeout == 0x80)
164776d498e4STakashi Iwai 		snd_printk(KERN_ERR "down [0x%lx]: serious init problem "
164876d498e4STakashi Iwai 			   "- codec still busy\n", chip->port);
16499295aea1SKrzysztof Helt 	if ((timeout & CS4231_MCE) == 0 ||
16507779f75fSKrzysztof Helt 	    !(chip->hardware & (WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK))) {
16519295aea1SKrzysztof Helt 		return;
16529295aea1SKrzysztof Helt 	}
16537779f75fSKrzysztof Helt 	snd_wss_busy_wait(chip);
16549295aea1SKrzysztof Helt #endif
16559295aea1SKrzysztof Helt }
16569295aea1SKrzysztof Helt #endif /* CONFIG_PM */
16579295aea1SKrzysztof Helt 
snd_wss_chip_id(struct snd_wss * chip)16587779f75fSKrzysztof Helt const char *snd_wss_chip_id(struct snd_wss *chip)
16599295aea1SKrzysztof Helt {
16609295aea1SKrzysztof Helt 	switch (chip->hardware) {
16617779f75fSKrzysztof Helt 	case WSS_HW_CS4231:
16627779f75fSKrzysztof Helt 		return "CS4231";
16637779f75fSKrzysztof Helt 	case WSS_HW_CS4231A:
16647779f75fSKrzysztof Helt 		return "CS4231A";
16657779f75fSKrzysztof Helt 	case WSS_HW_CS4232:
16667779f75fSKrzysztof Helt 		return "CS4232";
16677779f75fSKrzysztof Helt 	case WSS_HW_CS4232A:
16687779f75fSKrzysztof Helt 		return "CS4232A";
16697779f75fSKrzysztof Helt 	case WSS_HW_CS4235:
16707779f75fSKrzysztof Helt 		return "CS4235";
16717779f75fSKrzysztof Helt 	case WSS_HW_CS4236:
16727779f75fSKrzysztof Helt 		return "CS4236";
16737779f75fSKrzysztof Helt 	case WSS_HW_CS4236B:
16747779f75fSKrzysztof Helt 		return "CS4236B";
16757779f75fSKrzysztof Helt 	case WSS_HW_CS4237B:
16767779f75fSKrzysztof Helt 		return "CS4237B";
16777779f75fSKrzysztof Helt 	case WSS_HW_CS4238B:
16787779f75fSKrzysztof Helt 		return "CS4238B";
16797779f75fSKrzysztof Helt 	case WSS_HW_CS4239:
16807779f75fSKrzysztof Helt 		return "CS4239";
16817779f75fSKrzysztof Helt 	case WSS_HW_INTERWAVE:
16827779f75fSKrzysztof Helt 		return "AMD InterWave";
16837779f75fSKrzysztof Helt 	case WSS_HW_OPL3SA2:
16847779f75fSKrzysztof Helt 		return chip->card->shortname;
16857779f75fSKrzysztof Helt 	case WSS_HW_AD1845:
16867779f75fSKrzysztof Helt 		return "AD1845";
16877779f75fSKrzysztof Helt 	case WSS_HW_OPTI93X:
16887779f75fSKrzysztof Helt 		return "OPTi 93x";
1689ead893c0SKrzysztof Helt 	case WSS_HW_AD1847:
1690ead893c0SKrzysztof Helt 		return "AD1847";
1691ead893c0SKrzysztof Helt 	case WSS_HW_AD1848:
1692ead893c0SKrzysztof Helt 		return "AD1848";
1693ead893c0SKrzysztof Helt 	case WSS_HW_CS4248:
1694ead893c0SKrzysztof Helt 		return "CS4248";
1695ead893c0SKrzysztof Helt 	case WSS_HW_CMI8330:
1696ead893c0SKrzysztof Helt 		return "CMI8330/C3D";
16977779f75fSKrzysztof Helt 	default:
16987779f75fSKrzysztof Helt 		return "???";
16999295aea1SKrzysztof Helt 	}
17009295aea1SKrzysztof Helt }
17017779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_chip_id);
17029295aea1SKrzysztof Helt 
snd_wss_new(struct snd_card * card,unsigned short hardware,unsigned short hwshare,struct snd_wss ** rchip)17037779f75fSKrzysztof Helt static int snd_wss_new(struct snd_card *card,
17049295aea1SKrzysztof Helt 			  unsigned short hardware,
17059295aea1SKrzysztof Helt 			  unsigned short hwshare,
17067779f75fSKrzysztof Helt 			  struct snd_wss **rchip)
17079295aea1SKrzysztof Helt {
17087779f75fSKrzysztof Helt 	struct snd_wss *chip;
17099295aea1SKrzysztof Helt 
17109295aea1SKrzysztof Helt 	*rchip = NULL;
1711*ea2bfa29STakashi Iwai 	chip = devm_kzalloc(card->dev, sizeof(*chip), GFP_KERNEL);
17129295aea1SKrzysztof Helt 	if (chip == NULL)
17139295aea1SKrzysztof Helt 		return -ENOMEM;
17149295aea1SKrzysztof Helt 	chip->hardware = hardware;
17159295aea1SKrzysztof Helt 	chip->hwshare = hwshare;
17169295aea1SKrzysztof Helt 
17179295aea1SKrzysztof Helt 	spin_lock_init(&chip->reg_lock);
17189295aea1SKrzysztof Helt 	mutex_init(&chip->mce_mutex);
17199295aea1SKrzysztof Helt 	mutex_init(&chip->open_mutex);
17209295aea1SKrzysztof Helt 	chip->card = card;
17217779f75fSKrzysztof Helt 	chip->rate_constraint = snd_wss_xrate;
17227779f75fSKrzysztof Helt 	chip->set_playback_format = snd_wss_playback_format;
17237779f75fSKrzysztof Helt 	chip->set_capture_format = snd_wss_capture_format;
17247779f75fSKrzysztof Helt 	if (chip->hardware == WSS_HW_OPTI93X)
17259295aea1SKrzysztof Helt 		memcpy(&chip->image, &snd_opti93x_original_image,
17269295aea1SKrzysztof Helt 		       sizeof(snd_opti93x_original_image));
17279295aea1SKrzysztof Helt 	else
17287779f75fSKrzysztof Helt 		memcpy(&chip->image, &snd_wss_original_image,
17297779f75fSKrzysztof Helt 		       sizeof(snd_wss_original_image));
1730760fc6b8SKrzysztof Helt 	if (chip->hardware & WSS_HW_AD1848_MASK) {
1731760fc6b8SKrzysztof Helt 		chip->image[CS4231_PIN_CTRL] = 0;
1732760fc6b8SKrzysztof Helt 		chip->image[CS4231_TEST_INIT] = 0;
1733760fc6b8SKrzysztof Helt 	}
17349295aea1SKrzysztof Helt 
17359295aea1SKrzysztof Helt 	*rchip = chip;
17369295aea1SKrzysztof Helt 	return 0;
17379295aea1SKrzysztof Helt }
17389295aea1SKrzysztof Helt 
snd_wss_create(struct snd_card * card,unsigned long port,unsigned long cport,int irq,int dma1,int dma2,unsigned short hardware,unsigned short hwshare,struct snd_wss ** rchip)17397779f75fSKrzysztof Helt int snd_wss_create(struct snd_card *card,
17409295aea1SKrzysztof Helt 		      unsigned long port,
17419295aea1SKrzysztof Helt 		      unsigned long cport,
17429295aea1SKrzysztof Helt 		      int irq, int dma1, int dma2,
17439295aea1SKrzysztof Helt 		      unsigned short hardware,
17449295aea1SKrzysztof Helt 		      unsigned short hwshare,
17457779f75fSKrzysztof Helt 		      struct snd_wss **rchip)
17469295aea1SKrzysztof Helt {
17477779f75fSKrzysztof Helt 	struct snd_wss *chip;
17489295aea1SKrzysztof Helt 	int err;
17499295aea1SKrzysztof Helt 
17507779f75fSKrzysztof Helt 	err = snd_wss_new(card, hardware, hwshare, &chip);
17519295aea1SKrzysztof Helt 	if (err < 0)
17529295aea1SKrzysztof Helt 		return err;
17539295aea1SKrzysztof Helt 
17549295aea1SKrzysztof Helt 	chip->irq = -1;
17559295aea1SKrzysztof Helt 	chip->dma1 = -1;
17569295aea1SKrzysztof Helt 	chip->dma2 = -1;
17579295aea1SKrzysztof Helt 
1758*ea2bfa29STakashi Iwai 	chip->res_port = devm_request_region(card->dev, port, 4, "WSS");
17597779f75fSKrzysztof Helt 	if (!chip->res_port) {
17607779f75fSKrzysztof Helt 		snd_printk(KERN_ERR "wss: can't grab port 0x%lx\n", port);
17619295aea1SKrzysztof Helt 		return -EBUSY;
17629295aea1SKrzysztof Helt 	}
17639295aea1SKrzysztof Helt 	chip->port = port;
17647779f75fSKrzysztof Helt 	if ((long)cport >= 0) {
1765*ea2bfa29STakashi Iwai 		chip->res_cport = devm_request_region(card->dev, cport, 8,
1766*ea2bfa29STakashi Iwai 						      "CS4232 Control");
17677779f75fSKrzysztof Helt 		if (!chip->res_cport) {
17687779f75fSKrzysztof Helt 			snd_printk(KERN_ERR
17697779f75fSKrzysztof Helt 				"wss: can't grab control port 0x%lx\n", cport);
17709295aea1SKrzysztof Helt 			return -ENODEV;
17719295aea1SKrzysztof Helt 		}
17727779f75fSKrzysztof Helt 	}
17739295aea1SKrzysztof Helt 	chip->cport = cport;
17747779f75fSKrzysztof Helt 	if (!(hwshare & WSS_HWSHARE_IRQ))
1775*ea2bfa29STakashi Iwai 		if (devm_request_irq(card->dev, irq, snd_wss_interrupt, 0,
1776760fc6b8SKrzysztof Helt 				     "WSS", (void *) chip)) {
17777779f75fSKrzysztof Helt 			snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq);
17789295aea1SKrzysztof Helt 			return -EBUSY;
17799295aea1SKrzysztof Helt 		}
17809295aea1SKrzysztof Helt 	chip->irq = irq;
1781959d4c80STakashi Iwai 	card->sync_irq = chip->irq;
1782*ea2bfa29STakashi Iwai 	if (!(hwshare & WSS_HWSHARE_DMA1) &&
1783*ea2bfa29STakashi Iwai 	    snd_devm_request_dma(card->dev, dma1, "WSS - 1")) {
17847779f75fSKrzysztof Helt 		snd_printk(KERN_ERR "wss: can't grab DMA1 %d\n", dma1);
17859295aea1SKrzysztof Helt 		return -EBUSY;
17869295aea1SKrzysztof Helt 	}
17879295aea1SKrzysztof Helt 	chip->dma1 = dma1;
1788*ea2bfa29STakashi Iwai 	if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 &&
1789*ea2bfa29STakashi Iwai 	    snd_devm_request_dma(card->dev, dma2, "WSS - 2")) {
17907779f75fSKrzysztof Helt 		snd_printk(KERN_ERR "wss: can't grab DMA2 %d\n", dma2);
17919295aea1SKrzysztof Helt 		return -EBUSY;
17929295aea1SKrzysztof Helt 	}
17939295aea1SKrzysztof Helt 	if (dma1 == dma2 || dma2 < 0) {
17949295aea1SKrzysztof Helt 		chip->single_dma = 1;
17959295aea1SKrzysztof Helt 		chip->dma2 = chip->dma1;
17969295aea1SKrzysztof Helt 	} else
17979295aea1SKrzysztof Helt 		chip->dma2 = dma2;
17989295aea1SKrzysztof Helt 
1799760fc6b8SKrzysztof Helt 	if (hardware == WSS_HW_THINKPAD) {
1800760fc6b8SKrzysztof Helt 		chip->thinkpad_flag = 1;
1801760fc6b8SKrzysztof Helt 		chip->hardware = WSS_HW_DETECT; /* reset */
1802760fc6b8SKrzysztof Helt 		snd_wss_thinkpad_twiddle(chip, 1);
1803760fc6b8SKrzysztof Helt 	}
1804760fc6b8SKrzysztof Helt 
18059295aea1SKrzysztof Helt 	/* global setup */
1806*ea2bfa29STakashi Iwai 	if (snd_wss_probe(chip) < 0)
18079295aea1SKrzysztof Helt 		return -ENODEV;
18087779f75fSKrzysztof Helt 	snd_wss_init(chip);
18099295aea1SKrzysztof Helt 
18109295aea1SKrzysztof Helt #if 0
18117779f75fSKrzysztof Helt 	if (chip->hardware & WSS_HW_CS4232_MASK) {
18129295aea1SKrzysztof Helt 		if (chip->res_cport == NULL)
181376d498e4STakashi Iwai 			snd_printk(KERN_ERR "CS4232 control port features are "
181476d498e4STakashi Iwai 				   "not accessible\n");
18159295aea1SKrzysztof Helt 	}
18169295aea1SKrzysztof Helt #endif
18179295aea1SKrzysztof Helt 
18189295aea1SKrzysztof Helt #ifdef CONFIG_PM
18199295aea1SKrzysztof Helt 	/* Power Management */
18207779f75fSKrzysztof Helt 	chip->suspend = snd_wss_suspend;
18217779f75fSKrzysztof Helt 	chip->resume = snd_wss_resume;
18229295aea1SKrzysztof Helt #endif
18239295aea1SKrzysztof Helt 
18249295aea1SKrzysztof Helt 	*rchip = chip;
18259295aea1SKrzysztof Helt 	return 0;
18269295aea1SKrzysztof Helt }
18277779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_create);
18289295aea1SKrzysztof Helt 
1829aa5ebc78SArvind Yadav static const struct snd_pcm_ops snd_wss_playback_ops = {
18307779f75fSKrzysztof Helt 	.open =		snd_wss_playback_open,
18317779f75fSKrzysztof Helt 	.close =	snd_wss_playback_close,
18327779f75fSKrzysztof Helt 	.hw_params =	snd_wss_playback_hw_params,
18337779f75fSKrzysztof Helt 	.prepare =	snd_wss_playback_prepare,
18347779f75fSKrzysztof Helt 	.trigger =	snd_wss_trigger,
18357779f75fSKrzysztof Helt 	.pointer =	snd_wss_playback_pointer,
18369295aea1SKrzysztof Helt };
18379295aea1SKrzysztof Helt 
1838aa5ebc78SArvind Yadav static const struct snd_pcm_ops snd_wss_capture_ops = {
18397779f75fSKrzysztof Helt 	.open =		snd_wss_capture_open,
18407779f75fSKrzysztof Helt 	.close =	snd_wss_capture_close,
18417779f75fSKrzysztof Helt 	.hw_params =	snd_wss_capture_hw_params,
18427779f75fSKrzysztof Helt 	.prepare =	snd_wss_capture_prepare,
18437779f75fSKrzysztof Helt 	.trigger =	snd_wss_trigger,
18447779f75fSKrzysztof Helt 	.pointer =	snd_wss_capture_pointer,
18459295aea1SKrzysztof Helt };
18469295aea1SKrzysztof Helt 
snd_wss_pcm(struct snd_wss * chip,int device)1847fa60c065SLars-Peter Clausen int snd_wss_pcm(struct snd_wss *chip, int device)
18489295aea1SKrzysztof Helt {
18499295aea1SKrzysztof Helt 	struct snd_pcm *pcm;
18509295aea1SKrzysztof Helt 	int err;
18519295aea1SKrzysztof Helt 
1852ead893c0SKrzysztof Helt 	err = snd_pcm_new(chip->card, "WSS", device, 1, 1, &pcm);
1853ead893c0SKrzysztof Helt 	if (err < 0)
18549295aea1SKrzysztof Helt 		return err;
18559295aea1SKrzysztof Helt 
18567779f75fSKrzysztof Helt 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_wss_playback_ops);
18577779f75fSKrzysztof Helt 	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_wss_capture_ops);
18589295aea1SKrzysztof Helt 
18599295aea1SKrzysztof Helt 	/* global setup */
18609295aea1SKrzysztof Helt 	pcm->private_data = chip;
18619295aea1SKrzysztof Helt 	pcm->info_flags = 0;
18629295aea1SKrzysztof Helt 	if (chip->single_dma)
18639295aea1SKrzysztof Helt 		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
18647779f75fSKrzysztof Helt 	if (chip->hardware != WSS_HW_INTERWAVE)
18659295aea1SKrzysztof Helt 		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;
18667779f75fSKrzysztof Helt 	strcpy(pcm->name, snd_wss_chip_id(chip));
18679295aea1SKrzysztof Helt 
186815fbacfeSTakashi Iwai 	snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, chip->card->dev,
18699295aea1SKrzysztof Helt 				       64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);
18709295aea1SKrzysztof Helt 
18719295aea1SKrzysztof Helt 	chip->pcm = pcm;
18729295aea1SKrzysztof Helt 	return 0;
18739295aea1SKrzysztof Helt }
18747779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_pcm);
18759295aea1SKrzysztof Helt 
snd_wss_timer_free(struct snd_timer * timer)18767779f75fSKrzysztof Helt static void snd_wss_timer_free(struct snd_timer *timer)
18779295aea1SKrzysztof Helt {
18787779f75fSKrzysztof Helt 	struct snd_wss *chip = timer->private_data;
18799295aea1SKrzysztof Helt 	chip->timer = NULL;
18809295aea1SKrzysztof Helt }
18819295aea1SKrzysztof Helt 
snd_wss_timer(struct snd_wss * chip,int device)1882fa60c065SLars-Peter Clausen int snd_wss_timer(struct snd_wss *chip, int device)
18839295aea1SKrzysztof Helt {
18849295aea1SKrzysztof Helt 	struct snd_timer *timer;
18859295aea1SKrzysztof Helt 	struct snd_timer_id tid;
18869295aea1SKrzysztof Helt 	int err;
18879295aea1SKrzysztof Helt 
18889295aea1SKrzysztof Helt 	/* Timer initialization */
18899295aea1SKrzysztof Helt 	tid.dev_class = SNDRV_TIMER_CLASS_CARD;
18909295aea1SKrzysztof Helt 	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
18919295aea1SKrzysztof Helt 	tid.card = chip->card->number;
18929295aea1SKrzysztof Helt 	tid.device = device;
18939295aea1SKrzysztof Helt 	tid.subdevice = 0;
1894eb767949STakashi Iwai 	err = snd_timer_new(chip->card, "CS4231", &tid, &timer);
1895eb767949STakashi Iwai 	if (err < 0)
18969295aea1SKrzysztof Helt 		return err;
18977779f75fSKrzysztof Helt 	strcpy(timer->name, snd_wss_chip_id(chip));
18989295aea1SKrzysztof Helt 	timer->private_data = chip;
18997779f75fSKrzysztof Helt 	timer->private_free = snd_wss_timer_free;
19007779f75fSKrzysztof Helt 	timer->hw = snd_wss_timer_table;
19019295aea1SKrzysztof Helt 	chip->timer = timer;
19029295aea1SKrzysztof Helt 	return 0;
19039295aea1SKrzysztof Helt }
19047779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_timer);
19059295aea1SKrzysztof Helt 
19069295aea1SKrzysztof Helt /*
19079295aea1SKrzysztof Helt  *  MIXER part
19089295aea1SKrzysztof Helt  */
19099295aea1SKrzysztof Helt 
snd_wss_info_mux(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)19107779f75fSKrzysztof Helt static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
19117779f75fSKrzysztof Helt 			    struct snd_ctl_elem_info *uinfo)
19129295aea1SKrzysztof Helt {
19130773efa5STakashi Iwai 	static const char * const texts[4] = {
19149295aea1SKrzysztof Helt 		"Line", "Aux", "Mic", "Mix"
19159295aea1SKrzysztof Helt 	};
19160773efa5STakashi Iwai 	static const char * const opl3sa_texts[4] = {
19179295aea1SKrzysztof Helt 		"Line", "CD", "Mic", "Mix"
19189295aea1SKrzysztof Helt 	};
19190773efa5STakashi Iwai 	static const char * const gusmax_texts[4] = {
19209295aea1SKrzysztof Helt 		"Line", "Synth", "Mic", "Mix"
19219295aea1SKrzysztof Helt 	};
19220773efa5STakashi Iwai 	const char * const *ptexts = texts;
19237779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
19249295aea1SKrzysztof Helt 
1925622207dcSTakashi Iwai 	if (snd_BUG_ON(!chip->card))
1926622207dcSTakashi Iwai 		return -EINVAL;
19279295aea1SKrzysztof Helt 	if (!strcmp(chip->card->driver, "GUS MAX"))
19289295aea1SKrzysztof Helt 		ptexts = gusmax_texts;
19299295aea1SKrzysztof Helt 	switch (chip->hardware) {
19307779f75fSKrzysztof Helt 	case WSS_HW_INTERWAVE:
19317779f75fSKrzysztof Helt 		ptexts = gusmax_texts;
19327779f75fSKrzysztof Helt 		break;
1933b2e8d7daSKrzysztof Helt 	case WSS_HW_OPTI93X:
19347779f75fSKrzysztof Helt 	case WSS_HW_OPL3SA2:
19357779f75fSKrzysztof Helt 		ptexts = opl3sa_texts;
19367779f75fSKrzysztof Helt 		break;
19379295aea1SKrzysztof Helt 	}
19380773efa5STakashi Iwai 	return snd_ctl_enum_info(uinfo, 2, 4, ptexts);
19399295aea1SKrzysztof Helt }
19409295aea1SKrzysztof Helt 
snd_wss_get_mux(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)19417779f75fSKrzysztof Helt static int snd_wss_get_mux(struct snd_kcontrol *kcontrol,
19427779f75fSKrzysztof Helt 			   struct snd_ctl_elem_value *ucontrol)
19439295aea1SKrzysztof Helt {
19447779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
19459295aea1SKrzysztof Helt 	unsigned long flags;
19469295aea1SKrzysztof Helt 
19479295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
19489295aea1SKrzysztof Helt 	ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6;
19499295aea1SKrzysztof Helt 	ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6;
19509295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
19519295aea1SKrzysztof Helt 	return 0;
19529295aea1SKrzysztof Helt }
19539295aea1SKrzysztof Helt 
snd_wss_put_mux(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)19547779f75fSKrzysztof Helt static int snd_wss_put_mux(struct snd_kcontrol *kcontrol,
19557779f75fSKrzysztof Helt 			   struct snd_ctl_elem_value *ucontrol)
19569295aea1SKrzysztof Helt {
19577779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
19589295aea1SKrzysztof Helt 	unsigned long flags;
19599295aea1SKrzysztof Helt 	unsigned short left, right;
19609295aea1SKrzysztof Helt 	int change;
19619295aea1SKrzysztof Helt 
19629295aea1SKrzysztof Helt 	if (ucontrol->value.enumerated.item[0] > 3 ||
19639295aea1SKrzysztof Helt 	    ucontrol->value.enumerated.item[1] > 3)
19649295aea1SKrzysztof Helt 		return -EINVAL;
19659295aea1SKrzysztof Helt 	left = ucontrol->value.enumerated.item[0] << 6;
19669295aea1SKrzysztof Helt 	right = ucontrol->value.enumerated.item[1] << 6;
19679295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
19689295aea1SKrzysztof Helt 	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left;
19699295aea1SKrzysztof Helt 	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right;
19709295aea1SKrzysztof Helt 	change = left != chip->image[CS4231_LEFT_INPUT] ||
19719295aea1SKrzysztof Helt 		 right != chip->image[CS4231_RIGHT_INPUT];
19727779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_LEFT_INPUT, left);
19737779f75fSKrzysztof Helt 	snd_wss_out(chip, CS4231_RIGHT_INPUT, right);
19749295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
19759295aea1SKrzysztof Helt 	return change;
19769295aea1SKrzysztof Helt }
19779295aea1SKrzysztof Helt 
snd_wss_info_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)19787779f75fSKrzysztof Helt int snd_wss_info_single(struct snd_kcontrol *kcontrol,
19797779f75fSKrzysztof Helt 			struct snd_ctl_elem_info *uinfo)
19809295aea1SKrzysztof Helt {
19819295aea1SKrzysztof Helt 	int mask = (kcontrol->private_value >> 16) & 0xff;
19829295aea1SKrzysztof Helt 
19839295aea1SKrzysztof Helt 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
19849295aea1SKrzysztof Helt 	uinfo->count = 1;
19859295aea1SKrzysztof Helt 	uinfo->value.integer.min = 0;
19869295aea1SKrzysztof Helt 	uinfo->value.integer.max = mask;
19879295aea1SKrzysztof Helt 	return 0;
19889295aea1SKrzysztof Helt }
19897779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_info_single);
19909295aea1SKrzysztof Helt 
snd_wss_get_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)19917779f75fSKrzysztof Helt int snd_wss_get_single(struct snd_kcontrol *kcontrol,
19927779f75fSKrzysztof Helt 		       struct snd_ctl_elem_value *ucontrol)
19939295aea1SKrzysztof Helt {
19947779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
19959295aea1SKrzysztof Helt 	unsigned long flags;
19969295aea1SKrzysztof Helt 	int reg = kcontrol->private_value & 0xff;
19979295aea1SKrzysztof Helt 	int shift = (kcontrol->private_value >> 8) & 0xff;
19989295aea1SKrzysztof Helt 	int mask = (kcontrol->private_value >> 16) & 0xff;
19999295aea1SKrzysztof Helt 	int invert = (kcontrol->private_value >> 24) & 0xff;
20009295aea1SKrzysztof Helt 
20019295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
20029295aea1SKrzysztof Helt 	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask;
20039295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
20049295aea1SKrzysztof Helt 	if (invert)
20059295aea1SKrzysztof Helt 		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
20069295aea1SKrzysztof Helt 	return 0;
20079295aea1SKrzysztof Helt }
20087779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_get_single);
20099295aea1SKrzysztof Helt 
snd_wss_put_single(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)20107779f75fSKrzysztof Helt int snd_wss_put_single(struct snd_kcontrol *kcontrol,
20117779f75fSKrzysztof Helt 		       struct snd_ctl_elem_value *ucontrol)
20129295aea1SKrzysztof Helt {
20137779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
20149295aea1SKrzysztof Helt 	unsigned long flags;
20159295aea1SKrzysztof Helt 	int reg = kcontrol->private_value & 0xff;
20169295aea1SKrzysztof Helt 	int shift = (kcontrol->private_value >> 8) & 0xff;
20179295aea1SKrzysztof Helt 	int mask = (kcontrol->private_value >> 16) & 0xff;
20189295aea1SKrzysztof Helt 	int invert = (kcontrol->private_value >> 24) & 0xff;
20199295aea1SKrzysztof Helt 	int change;
20209295aea1SKrzysztof Helt 	unsigned short val;
20219295aea1SKrzysztof Helt 
20229295aea1SKrzysztof Helt 	val = (ucontrol->value.integer.value[0] & mask);
20239295aea1SKrzysztof Helt 	if (invert)
20249295aea1SKrzysztof Helt 		val = mask - val;
20259295aea1SKrzysztof Helt 	val <<= shift;
20269295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
20279295aea1SKrzysztof Helt 	val = (chip->image[reg] & ~(mask << shift)) | val;
20289295aea1SKrzysztof Helt 	change = val != chip->image[reg];
20297779f75fSKrzysztof Helt 	snd_wss_out(chip, reg, val);
20309295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
20319295aea1SKrzysztof Helt 	return change;
20329295aea1SKrzysztof Helt }
20337779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_put_single);
20349295aea1SKrzysztof Helt 
snd_wss_info_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)20357779f75fSKrzysztof Helt int snd_wss_info_double(struct snd_kcontrol *kcontrol,
20367779f75fSKrzysztof Helt 			struct snd_ctl_elem_info *uinfo)
20379295aea1SKrzysztof Helt {
20389295aea1SKrzysztof Helt 	int mask = (kcontrol->private_value >> 24) & 0xff;
20399295aea1SKrzysztof Helt 
20409295aea1SKrzysztof Helt 	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
20419295aea1SKrzysztof Helt 	uinfo->count = 2;
20429295aea1SKrzysztof Helt 	uinfo->value.integer.min = 0;
20439295aea1SKrzysztof Helt 	uinfo->value.integer.max = mask;
20449295aea1SKrzysztof Helt 	return 0;
20459295aea1SKrzysztof Helt }
20467779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_info_double);
20479295aea1SKrzysztof Helt 
snd_wss_get_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)20487779f75fSKrzysztof Helt int snd_wss_get_double(struct snd_kcontrol *kcontrol,
20497779f75fSKrzysztof Helt 		       struct snd_ctl_elem_value *ucontrol)
20509295aea1SKrzysztof Helt {
20517779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
20529295aea1SKrzysztof Helt 	unsigned long flags;
20539295aea1SKrzysztof Helt 	int left_reg = kcontrol->private_value & 0xff;
20549295aea1SKrzysztof Helt 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
20559295aea1SKrzysztof Helt 	int shift_left = (kcontrol->private_value >> 16) & 0x07;
20569295aea1SKrzysztof Helt 	int shift_right = (kcontrol->private_value >> 19) & 0x07;
20579295aea1SKrzysztof Helt 	int mask = (kcontrol->private_value >> 24) & 0xff;
20589295aea1SKrzysztof Helt 	int invert = (kcontrol->private_value >> 22) & 1;
20599295aea1SKrzysztof Helt 
20609295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
20619295aea1SKrzysztof Helt 	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask;
20629295aea1SKrzysztof Helt 	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask;
20639295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
20649295aea1SKrzysztof Helt 	if (invert) {
20659295aea1SKrzysztof Helt 		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
20669295aea1SKrzysztof Helt 		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
20679295aea1SKrzysztof Helt 	}
20689295aea1SKrzysztof Helt 	return 0;
20699295aea1SKrzysztof Helt }
20707779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_get_double);
20719295aea1SKrzysztof Helt 
snd_wss_put_double(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)20727779f75fSKrzysztof Helt int snd_wss_put_double(struct snd_kcontrol *kcontrol,
20737779f75fSKrzysztof Helt 		       struct snd_ctl_elem_value *ucontrol)
20749295aea1SKrzysztof Helt {
20757779f75fSKrzysztof Helt 	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);
20769295aea1SKrzysztof Helt 	unsigned long flags;
20779295aea1SKrzysztof Helt 	int left_reg = kcontrol->private_value & 0xff;
20789295aea1SKrzysztof Helt 	int right_reg = (kcontrol->private_value >> 8) & 0xff;
20799295aea1SKrzysztof Helt 	int shift_left = (kcontrol->private_value >> 16) & 0x07;
20809295aea1SKrzysztof Helt 	int shift_right = (kcontrol->private_value >> 19) & 0x07;
20819295aea1SKrzysztof Helt 	int mask = (kcontrol->private_value >> 24) & 0xff;
20829295aea1SKrzysztof Helt 	int invert = (kcontrol->private_value >> 22) & 1;
20839295aea1SKrzysztof Helt 	int change;
20849295aea1SKrzysztof Helt 	unsigned short val1, val2;
20859295aea1SKrzysztof Helt 
20869295aea1SKrzysztof Helt 	val1 = ucontrol->value.integer.value[0] & mask;
20879295aea1SKrzysztof Helt 	val2 = ucontrol->value.integer.value[1] & mask;
20889295aea1SKrzysztof Helt 	if (invert) {
20899295aea1SKrzysztof Helt 		val1 = mask - val1;
20909295aea1SKrzysztof Helt 		val2 = mask - val2;
20919295aea1SKrzysztof Helt 	}
20929295aea1SKrzysztof Helt 	val1 <<= shift_left;
20939295aea1SKrzysztof Helt 	val2 <<= shift_right;
20949295aea1SKrzysztof Helt 	spin_lock_irqsave(&chip->reg_lock, flags);
20955664daa1SKrzysztof Helt 	if (left_reg != right_reg) {
20969295aea1SKrzysztof Helt 		val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;
20979295aea1SKrzysztof Helt 		val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2;
20985664daa1SKrzysztof Helt 		change = val1 != chip->image[left_reg] ||
20995664daa1SKrzysztof Helt 			 val2 != chip->image[right_reg];
21007779f75fSKrzysztof Helt 		snd_wss_out(chip, left_reg, val1);
21017779f75fSKrzysztof Helt 		snd_wss_out(chip, right_reg, val2);
21025664daa1SKrzysztof Helt 	} else {
21035664daa1SKrzysztof Helt 		mask = (mask << shift_left) | (mask << shift_right);
21045664daa1SKrzysztof Helt 		val1 = (chip->image[left_reg] & ~mask) | val1 | val2;
21055664daa1SKrzysztof Helt 		change = val1 != chip->image[left_reg];
21065664daa1SKrzysztof Helt 		snd_wss_out(chip, left_reg, val1);
21075664daa1SKrzysztof Helt 	}
21089295aea1SKrzysztof Helt 	spin_unlock_irqrestore(&chip->reg_lock, flags);
21099295aea1SKrzysztof Helt 	return change;
21109295aea1SKrzysztof Helt }
21117779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_put_double);
21129295aea1SKrzysztof Helt 
21135664daa1SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
21145664daa1SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
21155664daa1SKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
2116abd134dbSKrzysztof Helt static const DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
21175664daa1SKrzysztof Helt 
2118fdd1f6fdSTakashi Iwai static const struct snd_kcontrol_new snd_wss_controls[] = {
21197779f75fSKrzysztof Helt WSS_DOUBLE("PCM Playback Switch", 0,
21209295aea1SKrzysztof Helt 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
2121abd134dbSKrzysztof Helt WSS_DOUBLE_TLV("PCM Playback Volume", 0,
2122abd134dbSKrzysztof Helt 		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1,
2123abd134dbSKrzysztof Helt 		db_scale_6bit),
21247779f75fSKrzysztof Helt WSS_DOUBLE("Aux Playback Switch", 0,
21259295aea1SKrzysztof Helt 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
2126abd134dbSKrzysztof Helt WSS_DOUBLE_TLV("Aux Playback Volume", 0,
2127abd134dbSKrzysztof Helt 		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
2128abd134dbSKrzysztof Helt 		db_scale_5bit_12db_max),
21297779f75fSKrzysztof Helt WSS_DOUBLE("Aux Playback Switch", 1,
21307779f75fSKrzysztof Helt 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
2131abd134dbSKrzysztof Helt WSS_DOUBLE_TLV("Aux Playback Volume", 1,
2132abd134dbSKrzysztof Helt 		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
2133abd134dbSKrzysztof Helt 		db_scale_5bit_12db_max),
2134abd134dbSKrzysztof Helt WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT,
2135abd134dbSKrzysztof Helt 		0, 0, 15, 0, db_scale_rec_gain),
21369295aea1SKrzysztof Helt {
21379295aea1SKrzysztof Helt 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21389295aea1SKrzysztof Helt 	.name = "Capture Source",
21397779f75fSKrzysztof Helt 	.info = snd_wss_info_mux,
21407779f75fSKrzysztof Helt 	.get = snd_wss_get_mux,
21417779f75fSKrzysztof Helt 	.put = snd_wss_put_mux,
21427779f75fSKrzysztof Helt },
2143b753e03eSKrzysztof Helt WSS_DOUBLE("Mic Boost (+20dB)", 0,
21447779f75fSKrzysztof Helt 		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
21457779f75fSKrzysztof Helt WSS_SINGLE("Loopback Capture Switch", 0,
21467779f75fSKrzysztof Helt 		CS4231_LOOPBACK, 0, 1, 0),
2147abd134dbSKrzysztof Helt WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1,
2148abd134dbSKrzysztof Helt 		db_scale_6bit),
2149633c7e92SKrzysztof Helt WSS_DOUBLE("Line Playback Switch", 0,
2150633c7e92SKrzysztof Helt 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
2151633c7e92SKrzysztof Helt WSS_DOUBLE_TLV("Line Playback Volume", 0,
2152633c7e92SKrzysztof Helt 		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1,
2153633c7e92SKrzysztof Helt 		db_scale_5bit_12db_max),
2154b753e03eSKrzysztof Helt WSS_SINGLE("Beep Playback Switch", 0,
2155633c7e92SKrzysztof Helt 		CS4231_MONO_CTRL, 7, 1, 1),
2156b753e03eSKrzysztof Helt WSS_SINGLE_TLV("Beep Playback Volume", 0,
2157633c7e92SKrzysztof Helt 		CS4231_MONO_CTRL, 0, 15, 1,
2158633c7e92SKrzysztof Helt 		db_scale_4bit),
2159633c7e92SKrzysztof Helt WSS_SINGLE("Mono Output Playback Switch", 0,
2160633c7e92SKrzysztof Helt 		CS4231_MONO_CTRL, 6, 1, 1),
2161b753e03eSKrzysztof Helt WSS_SINGLE("Beep Bypass Playback Switch", 0,
2162633c7e92SKrzysztof Helt 		CS4231_MONO_CTRL, 5, 1, 0),
21637779f75fSKrzysztof Helt };
21647779f75fSKrzysztof Helt 
snd_wss_mixer(struct snd_wss * chip)21657779f75fSKrzysztof Helt int snd_wss_mixer(struct snd_wss *chip)
21669295aea1SKrzysztof Helt {
21679295aea1SKrzysztof Helt 	struct snd_card *card;
21689295aea1SKrzysztof Helt 	unsigned int idx;
21699295aea1SKrzysztof Helt 	int err;
2170b2e8d7daSKrzysztof Helt 	int count = ARRAY_SIZE(snd_wss_controls);
21719295aea1SKrzysztof Helt 
2172622207dcSTakashi Iwai 	if (snd_BUG_ON(!chip || !chip->pcm))
2173622207dcSTakashi Iwai 		return -EINVAL;
21749295aea1SKrzysztof Helt 
21759295aea1SKrzysztof Helt 	card = chip->card;
21769295aea1SKrzysztof Helt 
21779295aea1SKrzysztof Helt 	strcpy(card->mixername, chip->pcm->name);
21789295aea1SKrzysztof Helt 
2179633c7e92SKrzysztof Helt 	/* Use only the first 11 entries on AD1848 */
2180633c7e92SKrzysztof Helt 	if (chip->hardware & WSS_HW_AD1848_MASK)
2181633c7e92SKrzysztof Helt 		count = 11;
2182b2e8d7daSKrzysztof Helt 	/* There is no loopback on OPTI93X */
2183b2e8d7daSKrzysztof Helt 	else if (chip->hardware == WSS_HW_OPTI93X)
2184b2e8d7daSKrzysztof Helt 		count = 9;
2185633c7e92SKrzysztof Helt 
2186633c7e92SKrzysztof Helt 	for (idx = 0; idx < count; idx++) {
21879295aea1SKrzysztof Helt 		err = snd_ctl_add(card,
21887779f75fSKrzysztof Helt 				snd_ctl_new1(&snd_wss_controls[idx],
21899295aea1SKrzysztof Helt 					     chip));
21909295aea1SKrzysztof Helt 		if (err < 0)
21919295aea1SKrzysztof Helt 			return err;
21929295aea1SKrzysztof Helt 	}
21939295aea1SKrzysztof Helt 	return 0;
21949295aea1SKrzysztof Helt }
21957779f75fSKrzysztof Helt EXPORT_SYMBOL(snd_wss_mixer);
21969295aea1SKrzysztof Helt 
snd_wss_get_pcm_ops(int direction)2197ead893c0SKrzysztof Helt const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction)
2198ead893c0SKrzysztof Helt {
2199ead893c0SKrzysztof Helt 	return direction == SNDRV_PCM_STREAM_PLAYBACK ?
2200ead893c0SKrzysztof Helt 		&snd_wss_playback_ops : &snd_wss_capture_ops;
2201ead893c0SKrzysztof Helt }
2202ead893c0SKrzysztof Helt EXPORT_SYMBOL(snd_wss_get_pcm_ops);
2203