diff options
| author | David S. Miller <davem@davemloft.net> | 2008-10-11 12:39:35 -0700 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-10-11 12:39:35 -0700 | 
| commit | 56c5d900dbb8e042bfad035d18433476931d8f93 (patch) | |
| tree | 00b793965beeef10db03e0ff021d2d965c410759 /sound | |
| parent | 4dd95b63ae25c5cad6986829b5e8788e9faa0330 (diff) | |
| parent | ead9d23d803ea3a73766c3cb27bf7563ac8d7266 (diff) | |
| download | kernel_samsung_tuna-56c5d900dbb8e042bfad035d18433476931d8f93.zip kernel_samsung_tuna-56c5d900dbb8e042bfad035d18433476931d8f93.tar.gz kernel_samsung_tuna-56c5d900dbb8e042bfad035d18433476931d8f93.tar.bz2  | |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts:
	sound/core/memalloc.c
Diffstat (limited to 'sound')
334 files changed, 28362 insertions, 10269 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index 8ebf512..200aca1 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -28,6 +28,10 @@ menuconfig SOUND  if SOUND +config SOUND_OSS_CORE +	bool +	default n +  source "sound/oss/dmasound/Kconfig"  if !M68K @@ -80,6 +84,7 @@ endif # SND  menuconfig SOUND_PRIME  	tristate "Open Sound System (DEPRECATED)" +	select SOUND_OSS_CORE  	help  	  Say 'Y' or 'M' to enable Open Sound System drivers. diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c index 7a16a33..6c515b2 100644 --- a/sound/aoa/codecs/snd-aoa-codec-tas.c +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c @@ -654,15 +654,13 @@ static struct snd_kcontrol_new bass_control = {  static struct transfer_info tas_transfers[] = {  	{  		/* input */ -		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | -			   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, +		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,  		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,  		.transfer_in = 1,  	},  	{  		/* output */ -		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | -			   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, +		.formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S24_BE,  		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,  		.transfer_in = 0,  	}, diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 351e19e..f8e6de4 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -32,11 +32,20 @@ config SND_PXA2XX_PCM  	tristate  	select SND_PCM +config SND_PXA2XX_LIB +	tristate +	select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 + +config SND_PXA2XX_LIB_AC97 +	bool +  config SND_PXA2XX_AC97  	tristate "AC97 driver for the Intel PXA2xx chip"  	depends on ARCH_PXA  	select SND_PXA2XX_PCM  	select SND_AC97_CODEC +	select SND_PXA2XX_LIB +	select SND_PXA2XX_LIB_AC97  	help  	  Say Y or M if you want to support any AC97 codec attached to  	  the PXA2xx AC97 interface. diff --git a/sound/arm/Makefile b/sound/arm/Makefile index 4ef6dd0..2054de1 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -11,5 +11,9 @@ snd-aaci-objs			:= aaci.o devdma.o  obj-$(CONFIG_SND_PXA2XX_PCM)	+= snd-pxa2xx-pcm.o  snd-pxa2xx-pcm-objs		:= pxa2xx-pcm.o +obj-$(CONFIG_SND_PXA2XX_LIB)	+= snd-pxa2xx-lib.o +snd-pxa2xx-lib-y		:= pxa2xx-pcm-lib.o +snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97)	+= pxa2xx-ac97-lib.o +  obj-$(CONFIG_SND_PXA2XX_AC97)	+= snd-pxa2xx-ac97.o  snd-pxa2xx-ac97-objs		:= pxa2xx-ac97.o diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index b0a4744..89096e8 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -999,7 +999,7 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)  	card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,  			    THIS_MODULE, sizeof(struct aaci));  	if (card == NULL) -		return ERR_PTR(-ENOMEM); +		return NULL;  	card->private_free = aaci_free_card; @@ -1083,8 +1083,8 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)  		return ret;  	aaci = aaci_init_card(dev); -	if (IS_ERR(aaci)) { -		ret = PTR_ERR(aaci); +	if (!aaci) { +		ret = -ENOMEM;  		goto out;  	} diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c new file mode 100644 index 0000000..99026df --- /dev/null +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -0,0 +1,384 @@ +/* + * Based on sound/arm/pxa2xx-ac97.c and sound/soc/pxa/pxa2xx-ac97.c + * which contain: + * + * Author:	Nicolas Pitre + * Created:	Dec 02, 2004 + * Copyright:	MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/delay.h> + +#include <sound/ac97_codec.h> +#include <sound/pxa2xx-lib.h> + +#include <asm/irq.h> +#include <mach/hardware.h> +#include <mach/pxa-regs.h> +#include <mach/pxa2xx-gpio.h> +#include <mach/audio.h> + +static DEFINE_MUTEX(car_mutex); +static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); +static volatile long gsr_bits; +static struct clk *ac97_clk; +static struct clk *ac97conf_clk; + +/* + * Beware PXA27x bugs: + * + *   o Slot 12 read from modem space will hang controller. + *   o CDONE, SDONE interrupt fails after any slot 12 IO. + * + * We therefore have an hybrid approach for waiting on SDONE (interrupt or + * 1 jiffy timeout if interrupt never comes). + */ + +unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +{ +	unsigned short val = -1; +	volatile u32 *reg_addr; + +	mutex_lock(&car_mutex); + +	/* set up primary or secondary codec space */ +	if ((cpu_is_pxa21x() || cpu_is_pxa25x()) && reg == AC97_GPIO_STATUS) +		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; +	else +		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; +	reg_addr += (reg >> 1); + +	/* start read access across the ac97 link */ +	GSR = GSR_CDONE | GSR_SDONE; +	gsr_bits = 0; +	val = *reg_addr; +	if (reg == AC97_GPIO_STATUS) +		goto out; +	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 && +	    !((GSR | gsr_bits) & GSR_SDONE)) { +		printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", +				__func__, reg, GSR | gsr_bits); +		val = -1; +		goto out; +	} + +	/* valid data now */ +	GSR = GSR_CDONE | GSR_SDONE; +	gsr_bits = 0; +	val = *reg_addr; +	/* but we've just started another cycle... */ +	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); + +out:	mutex_unlock(&car_mutex); +	return val; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_read); + +void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, +			unsigned short val) +{ +	volatile u32 *reg_addr; + +	mutex_lock(&car_mutex); + +	/* set up primary or secondary codec space */ +	if ((cpu_is_pxa21x() || cpu_is_pxa25x()) && reg == AC97_GPIO_STATUS) +		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; +	else +		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; +	reg_addr += (reg >> 1); + +	GSR = GSR_CDONE | GSR_SDONE; +	gsr_bits = 0; +	*reg_addr = val; +	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 && +	    !((GSR | gsr_bits) & GSR_CDONE)) +		printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", +				__func__, reg, GSR | gsr_bits); + +	mutex_unlock(&car_mutex); +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_write); + +#ifdef CONFIG_PXA25x +static inline void pxa_ac97_warm_pxa25x(void) +{ +	gsr_bits = 0; + +	GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; +	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); +} + +static inline void pxa_ac97_cold_pxa25x(void) +{ +	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */ +	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */ + +	gsr_bits = 0; + +	GCR = GCR_COLD_RST; +	GCR |= GCR_CDONE_IE|GCR_SDONE_IE; +	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); +} +#endif + +#ifdef CONFIG_PXA27x +static inline void pxa_ac97_warm_pxa27x(void) +{ +	gsr_bits = 0; + +	/* warm reset broken on Bulverde, +	   so manually keep AC97 reset high */ +	pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); +	udelay(10); +	GCR |= GCR_WARM_RST; +	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +	udelay(500); +} + +static inline void pxa_ac97_cold_pxa27x(void) +{ +	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */ +	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */ + +	gsr_bits = 0; + +	/* PXA27x Developers Manual section 13.5.2.2.1 */ +	clk_enable(ac97conf_clk); +	udelay(5); +	clk_disable(ac97conf_clk); +	GCR = GCR_COLD_RST; +	udelay(50); +} +#endif + +#ifdef CONFIG_PXA3xx +static inline void pxa_ac97_warm_pxa3xx(void) +{ +	int timeout = 100; + +	gsr_bits = 0; + +	/* Can't use interrupts */ +	GCR |= GCR_WARM_RST; +	while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) +		mdelay(1); +} + +static inline void pxa_ac97_cold_pxa3xx(void) +{ +	int timeout = 1000; + +	/* Hold CLKBPB for 100us */ +	GCR = 0; +	GCR = GCR_CLKBPB; +	udelay(100); +	GCR = 0; + +	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */ +	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */ + +	gsr_bits = 0; + +	/* Can't use interrupts on PXA3xx */ +	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); + +	GCR = GCR_WARM_RST | GCR_COLD_RST; +	while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--) +		mdelay(10); +} +#endif + +bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) +{ +#ifdef CONFIG_PXA25x +	if (cpu_is_pxa21x() || cpu_is_pxa25x()) +		pxa_ac97_warm_pxa25x(); +	else +#endif +#ifdef CONFIG_PXA27x +	if (cpu_is_pxa27x()) +		pxa_ac97_warm_pxa27x(); +	else +#endif +#ifdef CONFIG_PXA3xx +	if (cpu_is_pxa3xx()) +		pxa_ac97_warm_pxa3xx(); +	else +#endif +		BUG(); + +	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) { +		printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", +				 __func__, gsr_bits); + +		return false; +	} + +	return true; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); + +bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) +{ +#ifdef CONFIG_PXA25x +	if (cpu_is_pxa21x() || cpu_is_pxa25x()) +		pxa_ac97_cold_pxa25x(); +	else +#endif +#ifdef CONFIG_PXA27x +	if (cpu_is_pxa27x()) +		pxa_ac97_cold_pxa27x(); +	else +#endif +#ifdef CONFIG_PXA3xx +	if (cpu_is_pxa3xx()) +		pxa_ac97_cold_pxa3xx(); +	else +#endif +		BUG(); + +	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) { +		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", +				 __func__, gsr_bits); + +		return false; +	} + +	return true; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset); + + +void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97) +{ +	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); +	GCR |= GCR_SDONE_IE|GCR_CDONE_IE; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_finish_reset); + +static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) +{ +	long status; + +	status = GSR; +	if (status) { +		GSR = status; +		gsr_bits |= status; +		wake_up(&gsr_wq); + +		/* Although we don't use those we still need to clear them +		   since they tend to spuriously trigger when MMC is used +		   (hardware bug? go figure)... */ +		if (cpu_is_pxa27x()) { +			MISR = MISR_EOC; +			PISR = PISR_EOC; +			MCSR = MCSR_EOC; +		} + +		return IRQ_HANDLED; +	} + +	return IRQ_NONE; +} + +#ifdef CONFIG_PM +int pxa2xx_ac97_hw_suspend(void) +{ +	GCR |= GCR_ACLINK_OFF; +	clk_disable(ac97_clk); +	return 0; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_suspend); + +int pxa2xx_ac97_hw_resume(void) +{ +	if (cpu_is_pxa21x() || cpu_is_pxa25x() || cpu_is_pxa27x()) { +		pxa_gpio_mode(GPIO31_SYNC_AC97_MD); +		pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); +		pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); +		pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); +	} +	if (cpu_is_pxa27x()) { +		/* Use GPIO 113 as AC97 Reset on Bulverde */ +		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +	} +	clk_enable(ac97_clk); +	return 0; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume); +#endif + +int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev) +{ +	int ret; + +	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL); +	if (ret < 0) +		goto err; + +	if (cpu_is_pxa21x() || cpu_is_pxa25x() || cpu_is_pxa27x()) { +		pxa_gpio_mode(GPIO31_SYNC_AC97_MD); +		pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); +		pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); +		pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); +	} + +	if (cpu_is_pxa27x()) { +		/* Use GPIO 113 as AC97 Reset on Bulverde */ +		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +		ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); +		if (IS_ERR(ac97conf_clk)) { +			ret = PTR_ERR(ac97conf_clk); +			ac97conf_clk = NULL; +			goto err_irq; +		} +	} + +	ac97_clk = clk_get(&dev->dev, "AC97CLK"); +	if (IS_ERR(ac97_clk)) { +		ret = PTR_ERR(ac97_clk); +		ac97_clk = NULL; +		goto err_irq; +	} + +	return clk_enable(ac97_clk); + +err_irq: +	GCR |= GCR_ACLINK_OFF; +	if (ac97conf_clk) { +		clk_put(ac97conf_clk); +		ac97conf_clk = NULL; +	} +	free_irq(IRQ_AC97, NULL); +err: +	return ret; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_probe); + +void pxa2xx_ac97_hw_remove(struct platform_device *dev) +{ +	GCR |= GCR_ACLINK_OFF; +	free_irq(IRQ_AC97, NULL); +	if (ac97conf_clk) { +		clk_put(ac97conf_clk); +		ac97conf_clk = NULL; +	} +	clk_disable(ac97_clk); +	clk_put(ac97_clk); +	ac97_clk = NULL; +} +EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_remove); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Intel/Marvell PXA sound library"); +MODULE_LICENSE("GPL"); + diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index 199cca3..c2635be 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -12,198 +12,27 @@  #include <linux/init.h>  #include <linux/module.h> -#include <linux/kernel.h>  #include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/wait.h> -#include <linux/clk.h> -#include <linux/delay.h>  #include <sound/core.h>  #include <sound/pcm.h>  #include <sound/ac97_codec.h>  #include <sound/initval.h> +#include <sound/pxa2xx-lib.h> -#include <asm/irq.h> -#include <linux/mutex.h>  #include <mach/hardware.h>  #include <mach/pxa-regs.h> -#include <mach/pxa2xx-gpio.h>  #include <mach/audio.h>  #include "pxa2xx-pcm.h" - -static DEFINE_MUTEX(car_mutex); -static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); -static volatile long gsr_bits; -static struct clk *ac97_clk; -#ifdef CONFIG_PXA27x -static struct clk *ac97conf_clk; -#endif - -/* - * Beware PXA27x bugs: - * - *   o Slot 12 read from modem space will hang controller. - *   o CDONE, SDONE interrupt fails after any slot 12 IO. - * - * We therefore have an hybrid approach for waiting on SDONE (interrupt or - * 1 jiffy timeout if interrupt never comes). - */  - -static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) -{ -	unsigned short val = -1; -	volatile u32 *reg_addr; - -	mutex_lock(&car_mutex); - -	/* set up primary or secondary codec space */ -	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE; -	reg_addr += (reg >> 1); - -	/* start read access across the ac97 link */ -	GSR = GSR_CDONE | GSR_SDONE; -	gsr_bits = 0; -	val = *reg_addr; -	if (reg == AC97_GPIO_STATUS) -		goto out; -	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 && -	    !((GSR | gsr_bits) & GSR_SDONE)) { -		printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", -				__func__, reg, GSR | gsr_bits); -		val = -1; -		goto out; -	} - -	/* valid data now */ -	GSR = GSR_CDONE | GSR_SDONE; -	gsr_bits = 0; -	val = *reg_addr;			 -	/* but we've just started another cycle... */ -	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); - -out:	mutex_unlock(&car_mutex); -	return val; -} - -static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) -{ -	volatile u32 *reg_addr; - -	mutex_lock(&car_mutex); - -	/* set up primary or secondary codec space */ -	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE; -	reg_addr += (reg >> 1); - -	GSR = GSR_CDONE | GSR_SDONE; -	gsr_bits = 0; -	*reg_addr = val; -	if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 && -	    !((GSR | gsr_bits) & GSR_CDONE)) -		printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", -				__func__, reg, GSR | gsr_bits); - -	mutex_unlock(&car_mutex); -} -  static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)  { -	/* First, try cold reset */ -#ifdef CONFIG_PXA3xx -	int timeout; - -	/* Hold CLKBPB for 100us */ -	GCR = 0; -	GCR = GCR_CLKBPB; -	udelay(100); -	GCR = 0; -#endif - -	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */ -	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */ - -	gsr_bits = 0; -#ifdef CONFIG_PXA27x -	/* PXA27x Developers Manual section 13.5.2.2.1 */ -	clk_enable(ac97conf_clk); -	udelay(5); -	clk_disable(ac97conf_clk); -	GCR = GCR_COLD_RST; -	udelay(50); -#elif defined(CONFIG_PXA3xx) -	timeout = 1000; -	/* Can't use interrupts on PXA3xx */ -	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); - -	GCR = GCR_WARM_RST | GCR_COLD_RST; -	while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--) -		mdelay(10); -#else -	GCR = GCR_COLD_RST; -	GCR |= GCR_CDONE_IE|GCR_SDONE_IE; -	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); -#endif - -	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) { -		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", -				 __func__, gsr_bits); - -		/* let's try warm reset */ -		gsr_bits = 0; -#ifdef CONFIG_PXA27x -		/* warm reset broken on Bulverde, -		   so manually keep AC97 reset high */ -		pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);  -		udelay(10); -		GCR |= GCR_WARM_RST; -		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); -		udelay(500); -#elif defined(CONFIG_PXA3xx) -		timeout = 100; -		/* Can't use interrupts */ -		GCR |= GCR_WARM_RST; -		while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) -			mdelay(1); -#else -		GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN; -		wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); -#endif			 - -		if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) -			printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", -					 __func__, gsr_bits); -	} - -	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); -	GCR |= GCR_SDONE_IE|GCR_CDONE_IE; -} - -static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) -{ -	long status; - -	status = GSR; -	if (status) { -		GSR = status; -		gsr_bits |= status; -		wake_up(&gsr_wq); - -#ifdef CONFIG_PXA27x -		/* Although we don't use those we still need to clear them -		   since they tend to spuriously trigger when MMC is used -		   (hardware bug? go figure)... */ -		MISR = MISR_EOC; -		PISR = PISR_EOC; -		MCSR = MCSR_EOC; -#endif - -		return IRQ_HANDLED; +	if (!pxa2xx_ac97_try_cold_reset(ac97)) { +		pxa2xx_ac97_try_warm_reset(ac97);  	} -	return IRQ_NONE; +	pxa2xx_ac97_finish_reset(ac97);  }  static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { @@ -215,7 +44,7 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {  static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {  	.name			= "AC97 PCM out",  	.dev_addr		= __PREG(PCDR), -	.drcmr			= &DRCMRTXPCDR, +	.drcmr			= &DRCMR(12),  	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |  				  DCMD_BURST32 | DCMD_WIDTH4,  }; @@ -223,7 +52,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {  static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = {  	.name			= "AC97 PCM in",  	.dev_addr		= __PREG(PCDR), -	.drcmr			= &DRCMRRXPCDR, +	.drcmr			= &DRCMR(11),  	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |  				  DCMD_BURST32 | DCMD_WIDTH4,  }; @@ -288,17 +117,19 @@ static int pxa2xx_ac97_do_suspend(struct snd_card *card, pm_message_t state)  	snd_ac97_suspend(pxa2xx_ac97_ac97);  	if (platform_ops && platform_ops->suspend)  		platform_ops->suspend(platform_ops->priv); -	GCR |= GCR_ACLINK_OFF; -	clk_disable(ac97_clk); -	return 0; +	return pxa2xx_ac97_hw_suspend();  }  static int pxa2xx_ac97_do_resume(struct snd_card *card)  {  	pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; +	int rc; + +	rc = pxa2xx_ac97_hw_resume(); +	if (rc) +		return rc; -	clk_enable(ac97_clk);  	if (platform_ops && platform_ops->resume)  		platform_ops->resume(platform_ops->priv);  	snd_ac97_resume(pxa2xx_ac97_ac97); @@ -354,40 +185,17 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)  	if (ret)  		goto err; -	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL); -	if (ret < 0) -		goto err; - -	pxa_gpio_mode(GPIO31_SYNC_AC97_MD); -	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); -	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); -	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); -#ifdef CONFIG_PXA27x -	/* Use GPIO 113 as AC97 Reset on Bulverde */ -	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); -	ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); -	if (IS_ERR(ac97conf_clk)) { -		ret = PTR_ERR(ac97conf_clk); -		ac97conf_clk = NULL; -		goto err; -	} -#endif - -	ac97_clk = clk_get(&dev->dev, "AC97CLK"); -	if (IS_ERR(ac97_clk)) { -		ret = PTR_ERR(ac97_clk); -		ac97_clk = NULL; +	ret = pxa2xx_ac97_hw_probe(dev); +	if (ret)  		goto err; -	} -	clk_enable(ac97_clk);  	ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);  	if (ret) -		goto err; +		goto err_remove;  	memset(&ac97_template, 0, sizeof(ac97_template));  	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97);  	if (ret) -		goto err; +		goto err_remove;  	snprintf(card->shortname, sizeof(card->shortname),  		 "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97)); @@ -401,22 +209,11 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)  		return 0;  	} - err: +err_remove: +	pxa2xx_ac97_hw_remove(dev); +err:  	if (card)  		snd_card_free(card); -	if (ac97_clk) { -		GCR |= GCR_ACLINK_OFF; -		free_irq(IRQ_AC97, NULL); -		clk_disable(ac97_clk); -		clk_put(ac97_clk); -		ac97_clk = NULL; -	} -#ifdef CONFIG_PXA27x -	if (ac97conf_clk) { -		clk_put(ac97conf_clk); -		ac97conf_clk = NULL; -	} -#endif  	return ret;  } @@ -427,15 +224,7 @@ static int __devexit pxa2xx_ac97_remove(struct platform_device *dev)  	if (card) {  		snd_card_free(card);  		platform_set_drvdata(dev, NULL); -		GCR |= GCR_ACLINK_OFF; -		free_irq(IRQ_AC97, NULL); -		clk_disable(ac97_clk); -		clk_put(ac97_clk); -		ac97_clk = NULL; -#ifdef CONFIG_PXA27x -		clk_put(ac97conf_clk); -		ac97conf_clk = NULL; -#endif +		pxa2xx_ac97_hw_remove(dev);  	}  	return 0; diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c new file mode 100644 index 0000000..1c93eb7 --- /dev/null +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -0,0 +1,278 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/pxa2xx-lib.h> + +#include <asm/dma.h> +#include <mach/pxa-regs.h> + +#include "pxa2xx-pcm.h" + +static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { +	.info			= SNDRV_PCM_INFO_MMAP | +				  SNDRV_PCM_INFO_MMAP_VALID | +				  SNDRV_PCM_INFO_INTERLEAVED | +				  SNDRV_PCM_INFO_PAUSE | +				  SNDRV_PCM_INFO_RESUME, +	.formats		= SNDRV_PCM_FMTBIT_S16_LE | +					SNDRV_PCM_FMTBIT_S24_LE | +					SNDRV_PCM_FMTBIT_S32_LE, +	.period_bytes_min	= 32, +	.period_bytes_max	= 8192 - 32, +	.periods_min		= 1, +	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc), +	.buffer_bytes_max	= 128 * 1024, +	.fifo_size		= 32, +}; + +int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, +				struct snd_pcm_hw_params *params) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct pxa2xx_runtime_data *rtd = runtime->private_data; +	size_t totsize = params_buffer_bytes(params); +	size_t period = params_period_bytes(params); +	pxa_dma_desc *dma_desc; +	dma_addr_t dma_buff_phys, next_desc_phys; + +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); +	runtime->dma_bytes = totsize; + +	dma_desc = rtd->dma_desc_array; +	next_desc_phys = rtd->dma_desc_array_phys; +	dma_buff_phys = runtime->dma_addr; +	do { +		next_desc_phys += sizeof(pxa_dma_desc); +		dma_desc->ddadr = next_desc_phys; +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +			dma_desc->dsadr = dma_buff_phys; +			dma_desc->dtadr = rtd->params->dev_addr; +		} else { +			dma_desc->dsadr = rtd->params->dev_addr; +			dma_desc->dtadr = dma_buff_phys; +		} +		if (period > totsize) +			period = totsize; +		dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; +		dma_desc++; +		dma_buff_phys += period; +	} while (totsize -= period); +	dma_desc[-1].ddadr = rtd->dma_desc_array_phys; + +	return 0; +} +EXPORT_SYMBOL(__pxa2xx_pcm_hw_params); + +int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) +{ +	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; + +	if (rtd && rtd->params) +		*rtd->params->drcmr = 0; + +	snd_pcm_set_runtime_buffer(substream, NULL); +	return 0; +} +EXPORT_SYMBOL(__pxa2xx_pcm_hw_free); + +int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ +	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; +	int ret = 0; + +	switch (cmd) { +	case SNDRV_PCM_TRIGGER_START: +		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; +		DCSR(prtd->dma_ch) = DCSR_RUN; +		break; + +	case SNDRV_PCM_TRIGGER_STOP: +	case SNDRV_PCM_TRIGGER_SUSPEND: +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +		DCSR(prtd->dma_ch) &= ~DCSR_RUN; +		break; + +	case SNDRV_PCM_TRIGGER_RESUME: +		DCSR(prtd->dma_ch) |= DCSR_RUN; +		break; +	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; +		DCSR(prtd->dma_ch) |= DCSR_RUN; +		break; + +	default: +		ret = -EINVAL; +	} + +	return ret; +} +EXPORT_SYMBOL(pxa2xx_pcm_trigger); + +snd_pcm_uframes_t +pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct pxa2xx_runtime_data *prtd = runtime->private_data; + +	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? +			 DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); +	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); + +	if (x == runtime->buffer_size) +		x = 0; +	return x; +} +EXPORT_SYMBOL(pxa2xx_pcm_pointer); + +int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) +{ +	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; + +	DCSR(prtd->dma_ch) &= ~DCSR_RUN; +	DCSR(prtd->dma_ch) = 0; +	DCMD(prtd->dma_ch) = 0; +	*prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; + +	return 0; +} +EXPORT_SYMBOL(__pxa2xx_pcm_prepare); + +void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) +{ +	struct snd_pcm_substream *substream = dev_id; +	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; +	int dcsr; + +	dcsr = DCSR(dma_ch); +	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; + +	if (dcsr & DCSR_ENDINTR) { +		snd_pcm_period_elapsed(substream); +	} else { +		printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", +			rtd->params->name, dma_ch, dcsr); +		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); +	} +} +EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); + +int __pxa2xx_pcm_open(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct pxa2xx_runtime_data *rtd; +	int ret; + +	runtime->hw = pxa2xx_pcm_hardware; + +	/* +	 * For mysterious reasons (and despite what the manual says) +	 * playback samples are lost if the DMA count is not a multiple +	 * of the DMA burst size.  Let's add a rule to enforce that. +	 */ +	ret = snd_pcm_hw_constraint_step(runtime, 0, +		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); +	if (ret) +		goto out; + +	ret = snd_pcm_hw_constraint_step(runtime, 0, +		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); +	if (ret) +		goto out; + +	ret = snd_pcm_hw_constraint_integer(runtime, +					    SNDRV_PCM_HW_PARAM_PERIODS); +	if (ret < 0) +		goto out; + +	ret = -ENOMEM; +	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); +	if (!rtd) +		goto out; +	rtd->dma_desc_array = +		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, +				       &rtd->dma_desc_array_phys, GFP_KERNEL); +	if (!rtd->dma_desc_array) +		goto err1; + +	runtime->private_data = rtd; +	return 0; + + err1: +	kfree(rtd); + out: +	return ret; +} +EXPORT_SYMBOL(__pxa2xx_pcm_open); + +int __pxa2xx_pcm_close(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct pxa2xx_runtime_data *rtd = runtime->private_data; + +	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, +			      rtd->dma_desc_array, rtd->dma_desc_array_phys); +	kfree(rtd); +	return 0; +} +EXPORT_SYMBOL(__pxa2xx_pcm_close); + +int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, +	struct vm_area_struct *vma) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	return dma_mmap_writecombine(substream->pcm->card->dev, vma, +				     runtime->dma_area, +				     runtime->dma_addr, +				     runtime->dma_bytes); +} +EXPORT_SYMBOL(pxa2xx_pcm_mmap); + +int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ +	struct snd_pcm_substream *substream = pcm->streams[stream].substream; +	struct snd_dma_buffer *buf = &substream->dma_buffer; +	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; +	buf->dev.type = SNDRV_DMA_TYPE_DEV; +	buf->dev.dev = pcm->card->dev; +	buf->private_data = NULL; +	buf->area = dma_alloc_writecombine(pcm->card->dev, size, +					   &buf->addr, GFP_KERNEL); +	if (!buf->area) +		return -ENOMEM; +	buf->bytes = size; +	return 0; +} +EXPORT_SYMBOL(pxa2xx_pcm_preallocate_dma_buffer); + +void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ +	struct snd_pcm_substream *substream; +	struct snd_dma_buffer *buf; +	int stream; + +	for (stream = 0; stream < 2; stream++) { +		substream = pcm->streams[stream].substream; +		if (!substream) +			continue; +		buf = &substream->dma_buffer; +		if (!buf->area) +			continue; +		dma_free_writecombine(pcm->card->dev, buf->bytes, +				      buf->area, buf->addr); +		buf->area = NULL; +	} +} +EXPORT_SYMBOL(pxa2xx_pcm_free_dma_buffers); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Intel PXA2xx sound library"); +MODULE_LICENSE("GPL"); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c index 381094a..535704f 100644 --- a/sound/arm/pxa2xx-pcm.c +++ b/sound/arm/pxa2xx-pcm.c @@ -10,183 +10,20 @@   * published by the Free Software Foundation.   */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/slab.h> -#include <linux/dma-mapping.h> -  #include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> - -#include <asm/dma.h> -#include <mach/hardware.h> -#include <mach/pxa-regs.h> +#include <sound/pxa2xx-lib.h>  #include "pxa2xx-pcm.h" - -static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { -	.info			= SNDRV_PCM_INFO_MMAP | -				  SNDRV_PCM_INFO_MMAP_VALID | -				  SNDRV_PCM_INFO_INTERLEAVED | -				  SNDRV_PCM_INFO_PAUSE, -	.formats		= SNDRV_PCM_FMTBIT_S16_LE, -	.period_bytes_min	= 32, -	.period_bytes_max	= 8192 - 32, -	.periods_min		= 1, -	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc), -	.buffer_bytes_max	= 128 * 1024, -	.fifo_size		= 32, -}; - -struct pxa2xx_runtime_data { -	int dma_ch; -	struct pxa2xx_pcm_dma_params *params; -	pxa_dma_desc *dma_desc_array; -	dma_addr_t dma_desc_array_phys; -}; - -static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, -				struct snd_pcm_hw_params *params) -{ -	struct snd_pcm_runtime *runtime = substream->runtime; -	struct pxa2xx_runtime_data *rtd = runtime->private_data; -	size_t totsize = params_buffer_bytes(params); -	size_t period = params_period_bytes(params); -	pxa_dma_desc *dma_desc; -	dma_addr_t dma_buff_phys, next_desc_phys; - -	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); -	runtime->dma_bytes = totsize; - -	dma_desc = rtd->dma_desc_array; -	next_desc_phys = rtd->dma_desc_array_phys; -	dma_buff_phys = runtime->dma_addr; -	do { -		next_desc_phys += sizeof(pxa_dma_desc); -		dma_desc->ddadr = next_desc_phys; -		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -			dma_desc->dsadr = dma_buff_phys; -			dma_desc->dtadr = rtd->params->dev_addr; -		} else { -			dma_desc->dsadr = rtd->params->dev_addr; -			dma_desc->dtadr = dma_buff_phys; -		} -		if (period > totsize) -			period = totsize; -		dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN; -		dma_desc++; -		dma_buff_phys += period; -	} while (totsize -= period); -	dma_desc[-1].ddadr = rtd->dma_desc_array_phys; - -	return 0; -} - -static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream) -{ -	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; - -	*rtd->params->drcmr = 0; -	snd_pcm_set_runtime_buffer(substream, NULL); -	return 0; -} -  static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)  {  	struct pxa2xx_pcm_client *client = substream->private_data; -	struct snd_pcm_runtime *runtime = substream->runtime; -	struct pxa2xx_runtime_data *rtd = runtime->private_data; -	DCSR(rtd->dma_ch) &= ~DCSR_RUN; -	DCSR(rtd->dma_ch) = 0; -	DCMD(rtd->dma_ch) = 0; -	*rtd->params->drcmr = rtd->dma_ch | DRCMR_MAPVLD; +	__pxa2xx_pcm_prepare(substream);  	return client->prepare(substream);  } -static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ -	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; -	int ret = 0; - -	switch (cmd) { -	case SNDRV_PCM_TRIGGER_START: -		DDADR(rtd->dma_ch) = rtd->dma_desc_array_phys; -		DCSR(rtd->dma_ch) = DCSR_RUN; -		break; - -	case SNDRV_PCM_TRIGGER_STOP: -	case SNDRV_PCM_TRIGGER_SUSPEND: -	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -		DCSR(rtd->dma_ch) &= ~DCSR_RUN; -		break; - -	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -		DCSR(rtd->dma_ch) |= DCSR_RUN; -		break; - -	default: -		ret = -EINVAL; -	} - -	return ret; -} - -static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) -{ -	struct snd_pcm_substream *substream = dev_id; -	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; -	int dcsr; - -	dcsr = DCSR(dma_ch); -	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; - -	if (dcsr & DCSR_ENDINTR) { -		snd_pcm_period_elapsed(substream); -	} else { -		printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", -			rtd->params->name, dma_ch, dcsr ); -		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); -	} -} - -static snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) -{ -	struct snd_pcm_runtime *runtime = substream->runtime; -	struct pxa2xx_runtime_data *rtd = runtime->private_data; -	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? -			 DSADR(rtd->dma_ch) : DTADR(rtd->dma_ch); -	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); -	if (x == runtime->buffer_size) -		x = 0; -	return x; -} - -static int -pxa2xx_pcm_hw_rule_mult32(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) -{ -	struct snd_interval *i = hw_param_interval(params, rule->var); -	int changed = 0; - -	if (i->min & 31) { -		i->min = (i->min & ~31) + 32; -		i->openmin = 0; -		changed = 1; -	} - -	if (i->max & 31) { -		i->max &= ~31; -		i->openmax = 0; -		changed = 1; -	} - -	return changed; -} -  static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)  {  	struct pxa2xx_pcm_client *client = substream->private_data; @@ -194,33 +31,11 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)  	struct pxa2xx_runtime_data *rtd;  	int ret; -	runtime->hw = pxa2xx_pcm_hardware; - -	/* -	 * For mysterious reasons (and despite what the manual says) -	 * playback samples are lost if the DMA count is not a multiple -	 * of the DMA burst size.  Let's add a rule to enforce that. -	 */ -	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -				  pxa2xx_pcm_hw_rule_mult32, NULL, -				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1); -	if (ret) -		goto out; -	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -				  pxa2xx_pcm_hw_rule_mult32, NULL, -				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1); +	ret = __pxa2xx_pcm_open(substream);  	if (ret)  		goto out; -	ret = -ENOMEM; -	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); -	if (!rtd) -		goto out; -	rtd->dma_desc_array = -		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, -				       &rtd->dma_desc_array_phys, GFP_KERNEL); -	if (!rtd->dma_desc_array) -		goto err1; +	rtd = runtime->private_data;  	rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?  		      client->playback_params : client->capture_params; @@ -230,17 +45,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)  		goto err2;  	rtd->dma_ch = ret; -	runtime->private_data = rtd;  	ret = client->startup(substream);  	if (!ret)  		goto out;  	pxa_free_dma(rtd->dma_ch);   err2: -	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, -			      rtd->dma_desc_array, rtd->dma_desc_array_phys); - err1: -	kfree(rtd); +	__pxa2xx_pcm_close(substream);   out:  	return ret;  } @@ -252,69 +63,22 @@ static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)  	pxa_free_dma(rtd->dma_ch);  	client->shutdown(substream); -	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, -			      rtd->dma_desc_array, rtd->dma_desc_array_phys); -	kfree(rtd); -	return 0; -} -static int -pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) -{ -	struct snd_pcm_runtime *runtime = substream->runtime; -	return dma_mmap_writecombine(substream->pcm->card->dev, vma, -				     runtime->dma_area, -				     runtime->dma_addr, -				     runtime->dma_bytes); +	return __pxa2xx_pcm_close(substream);  }  static struct snd_pcm_ops pxa2xx_pcm_ops = {  	.open		= pxa2xx_pcm_open,  	.close		= pxa2xx_pcm_close,  	.ioctl		= snd_pcm_lib_ioctl, -	.hw_params	= pxa2xx_pcm_hw_params, -	.hw_free	= pxa2xx_pcm_hw_free, +	.hw_params	= __pxa2xx_pcm_hw_params, +	.hw_free	= __pxa2xx_pcm_hw_free,  	.prepare	= pxa2xx_pcm_prepare,  	.trigger	= pxa2xx_pcm_trigger,  	.pointer	= pxa2xx_pcm_pointer,  	.mmap		= pxa2xx_pcm_mmap,  }; -static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ -	struct snd_pcm_substream *substream = pcm->streams[stream].substream; -	struct snd_dma_buffer *buf = &substream->dma_buffer; -	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; -	buf->dev.type = SNDRV_DMA_TYPE_DEV; -	buf->dev.dev = pcm->card->dev; -	buf->private_data = NULL; -	buf->area = dma_alloc_writecombine(pcm->card->dev, size, -					   &buf->addr, GFP_KERNEL); -	if (!buf->area) -		return -ENOMEM; -	buf->bytes = size; -	return 0; -} - -static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ -	struct snd_pcm_substream *substream; -	struct snd_dma_buffer *buf; -	int stream; - -	for (stream = 0; stream < 2; stream++) { -		substream = pcm->streams[stream].substream; -		if (!substream) -			continue; -		buf = &substream->dma_buffer; -		if (!buf->area) -			continue; -		dma_free_writecombine(pcm->card->dev, buf->bytes, -				      buf->area, buf->addr); -		buf->area = NULL; -	} -} -  static u64 pxa2xx_pcm_dmamask = 0xffffffff;  int pxa2xx_pcm_new(struct snd_card *card, struct pxa2xx_pcm_client *client, diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h index b79f1e8..5c4a4d3 100644 --- a/sound/arm/pxa2xx-pcm.h +++ b/sound/arm/pxa2xx-pcm.h @@ -9,14 +9,15 @@   * it under the terms of the GNU General Public License version 2 as   * published by the Free Software Foundation.   */ +#include <asm/dma.h> -struct pxa2xx_pcm_dma_params { -	char *name;			/* stream identifier */ -	u32 dcmd;			/* DMA descriptor dcmd field */ -	volatile u32 *drcmr;		/* the DMA request channel to use */ -	u32 dev_addr;			/* device physical address for DMA */ +struct pxa2xx_runtime_data { +	int dma_ch; +	struct pxa2xx_pcm_dma_params *params; +	pxa_dma_desc *dma_desc_array; +	dma_addr_t dma_desc_array_phys;  }; -	 +  struct pxa2xx_pcm_client {  	struct pxa2xx_pcm_dma_params *playback_params;  	struct pxa2xx_pcm_dma_params *capture_params; diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c index b9c51bf..1dcd51d 100644 --- a/sound/arm/sa11xx-uda1341.c +++ b/sound/arm/sa11xx-uda1341.c @@ -442,7 +442,8 @@ static void audio_process_dma(struct audio_stream *s)  	/* we are requested to process synchronization DMA transfer */  	if (s->tx_spin) { -		snd_assert(s->stream_id == SNDRV_PCM_STREAM_PLAYBACK, return); +		if (snd_BUG_ON(s->stream_id != SNDRV_PCM_STREAM_PLAYBACK)) +			return;  		/* fill the xmit dma buffers and return */  #ifdef HH_VERSION  		sa1100_dma_set_spin(s->dmach, FORCE_CLOCK_ADDR, FORCE_CLOCK_SIZE); @@ -472,7 +473,7 @@ static void audio_process_dma(struct audio_stream *s)  				continue;		/* special case */  		} else {  			offset = dma_size * s->period; -			snd_assert(dma_size <= DMA_BUF_SIZE, ); +			snd_BUG_ON(dma_size > DMA_BUF_SIZE);  		}  #ifdef HH_VERSION  		ret = sa1100_dma_queue_buffer(s->dmach, s, runtime->dma_addr + offset, dma_size); @@ -879,7 +880,7 @@ void snd_sa11xx_uda1341_free(struct snd_card *card)  	audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]);  } -static int __init sa11xx_uda1341_probe(struct platform_device *devptr) +static int __devinit sa11xx_uda1341_probe(struct platform_device *devptr)  {  	int err;  	struct snd_card *card; diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 335d45e..66348c9 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -12,6 +12,12 @@ config SND_HWDEP  config SND_RAWMIDI  	tristate +# To be effective this also requires INPUT - users should say: +#    select SND_JACK if INPUT=y || INPUT=SND +# to avoid having to force INPUT on. +config SND_JACK +	bool +  config SND_SEQUENCER  	tristate "Sequencer support"  	select SND_TIMER @@ -38,6 +44,7 @@ config SND_SEQ_DUMMY  	  will be called snd-seq-dummy.  config SND_OSSEMUL +	select SOUND_OSS_CORE  	bool  config SND_MIXER_OSS @@ -101,6 +108,9 @@ config SND_RTCTIMER  	  To compile this driver as a module, choose M here: the module  	  will be called snd-rtctimer. +	  Note that this option is exclusive with the new RTC drivers +	  (CONFIG_RTC_CLASS) since this requires the old API. +  config SND_SEQ_RTCTIMER_DEFAULT  	bool "Use RTC as default sequencer timer"  	depends on SND_RTCTIMER && SND_SEQUENCER diff --git a/sound/core/Makefile b/sound/core/Makefile index da8e685..d57125a 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -7,6 +7,7 @@ snd-y     := sound.o init.o memory.o info.o control.o misc.o device.o  snd-$(CONFIG_ISA_DMA_API) += isadma.o  snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o info_oss.o  snd-$(CONFIG_SND_VMASTER) += vmaster.o +snd-$(CONFIG_SND_JACK)	  += jack.o  snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \  		pcm_memory.o diff --git a/sound/core/control.c b/sound/core/control.c index 281b2e2..6d71f9a 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -139,7 +139,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,  	struct snd_ctl_file *ctl;  	struct snd_kctl_event *ev; -	snd_assert(card != NULL && id != NULL, return); +	if (snd_BUG_ON(!card || !id)) +		return;  	read_lock(&card->ctl_files_rwlock);  #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)  	card->mixer_oss_change_count++; @@ -188,8 +189,8 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,  	struct snd_kcontrol *kctl;  	unsigned int idx; -	snd_assert(control != NULL, return NULL); -	snd_assert(control->count > 0, return NULL); +	if (snd_BUG_ON(!control || !control->count)) +		return NULL;  	kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);  	if (kctl == NULL) {  		snd_printk(KERN_ERR "Cannot allocate control instance\n"); @@ -218,8 +219,8 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,  	struct snd_kcontrol kctl;  	unsigned int access; -	snd_assert(ncontrol != NULL, return NULL); -	snd_assert(ncontrol->info != NULL, return NULL); +	if (snd_BUG_ON(!ncontrol || !ncontrol->info)) +		return NULL;  	memset(&kctl, 0, sizeof(kctl));  	kctl.id.iface = ncontrol->iface;  	kctl.id.device = ncontrol->device; @@ -315,8 +316,8 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)  	if (! kcontrol)  		return err; -	snd_assert(card != NULL, goto error); -	snd_assert(kcontrol->info != NULL, goto error); +	if (snd_BUG_ON(!card || !kcontrol->info)) +		goto error;  	id = kcontrol->id;  	down_write(&card->controls_rwsem);  	if (snd_ctl_find_id(card, &id)) { @@ -367,7 +368,8 @@ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)  	struct snd_ctl_elem_id id;  	unsigned int idx; -	snd_assert(card != NULL && kcontrol != NULL, return -EINVAL); +	if (snd_BUG_ON(!card || !kcontrol)) +		return -EINVAL;  	list_del(&kcontrol->list);  	card->controls_count -= kcontrol->count;  	id = kcontrol->id; @@ -487,7 +489,8 @@ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numi  {  	struct snd_kcontrol *kctl; -	snd_assert(card != NULL && numid != 0, return NULL); +	if (snd_BUG_ON(!card || !numid)) +		return NULL;  	list_for_each_entry(kctl, &card->controls, list) {  		if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)  			return kctl; @@ -514,7 +517,8 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,  {  	struct snd_kcontrol *kctl; -	snd_assert(card != NULL && id != NULL, return NULL); +	if (snd_BUG_ON(!card || !id)) +		return NULL;  	if (id->numid != 0)  		return snd_ctl_find_numid(card, id->numid);  	list_for_each_entry(kctl, &card->controls, list) { @@ -647,7 +651,7 @@ static int snd_ctl_elem_info(struct snd_ctl_file *ctl,  #endif  	result = kctl->info(kctl, info);  	if (result >= 0) { -		snd_assert(info->access == 0, ); +		snd_BUG_ON(info->access);  		index_offset = snd_ctl_get_ioff(kctl, &info->id);  		vd = &kctl->vd[index_offset];  		snd_ctl_build_ioff(&info->id, kctl, index_offset); @@ -1160,7 +1164,8 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg  	ctl = file->private_data;  	card = ctl->card; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	switch (cmd) {  	case SNDRV_CTL_IOCTL_PVERSION:  		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0; @@ -1222,7 +1227,8 @@ static ssize_t snd_ctl_read(struct file *file, char __user *buffer,  	ssize_t result = 0;  	ctl = file->private_data; -	snd_assert(ctl != NULL && ctl->card != NULL, return -ENXIO); +	if (snd_BUG_ON(!ctl || !ctl->card)) +		return -ENXIO;  	if (!ctl->subscribed)  		return -EBADFD;  	if (count < sizeof(struct snd_ctl_event)) @@ -1328,7 +1334,8 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,  {  	struct snd_kctl_ioctl *p; -	snd_assert(fcn != NULL, return -EINVAL); +	if (snd_BUG_ON(!fcn)) +		return -EINVAL;  	down_write(&snd_ioctl_rwsem);  	list_for_each_entry(p, lists, list) {  		if (p->fioctl == fcn) { @@ -1404,9 +1411,11 @@ static int snd_ctl_dev_register(struct snd_device *device)  	int err, cardnum;  	char name[16]; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	cardnum = card->number; -	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); +	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS)) +		return -ENXIO;  	sprintf(name, "controlC%i", cardnum);  	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,  				       &snd_ctl_f_ops, card, name)) < 0) @@ -1423,16 +1432,18 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)  	struct snd_ctl_file *ctl;  	int err, cardnum; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	cardnum = card->number; -	snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); +	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS)) +		return -ENXIO; -	down_read(&card->controls_rwsem); +	read_lock(&card->ctl_files_rwlock);  	list_for_each_entry(ctl, &card->ctl_files, list) {  		wake_up(&ctl->change_sleep);  		kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);  	} -	up_read(&card->controls_rwsem); +	read_unlock(&card->ctl_files_rwlock);  	if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,  					 card, -1)) < 0) @@ -1469,7 +1480,8 @@ int snd_ctl_create(struct snd_card *card)  		.dev_disconnect = snd_ctl_dev_disconnect,  	}; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);  } diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 6101259..368dc9c 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -398,7 +398,8 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns  	int err;  	ctl = file->private_data; -	snd_assert(ctl && ctl->card, return -ENXIO); +	if (snd_BUG_ON(!ctl || !ctl->card)) +		return -ENXIO;  	switch (cmd) {  	case SNDRV_CTL_IOCTL_PVERSION: diff --git a/sound/core/device.c b/sound/core/device.c index 202dac0..c58d822 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -45,9 +45,8 @@ int snd_device_new(struct snd_card *card, snd_device_type_t type,  {  	struct snd_device *dev; -	snd_assert(card != NULL, return -ENXIO); -	snd_assert(device_data != NULL, return -ENXIO); -	snd_assert(ops != NULL, return -ENXIO); +	if (snd_BUG_ON(!card || !device_data || !ops)) +		return -ENXIO;  	dev = kzalloc(sizeof(*dev), GFP_KERNEL);  	if (dev == NULL) {  		snd_printk(KERN_ERR "Cannot allocate device\n"); @@ -80,8 +79,8 @@ int snd_device_free(struct snd_card *card, void *device_data)  {  	struct snd_device *dev; -	snd_assert(card != NULL, return -ENXIO); -	snd_assert(device_data != NULL, return -ENXIO); +	if (snd_BUG_ON(!card || !device_data)) +		return -ENXIO;  	list_for_each_entry(dev, &card->devices, list) {  		if (dev->device_data != device_data)  			continue; @@ -123,8 +122,8 @@ int snd_device_disconnect(struct snd_card *card, void *device_data)  {  	struct snd_device *dev; -	snd_assert(card != NULL, return -ENXIO); -	snd_assert(device_data != NULL, return -ENXIO); +	if (snd_BUG_ON(!card || !device_data)) +		return -ENXIO;  	list_for_each_entry(dev, &card->devices, list) {  		if (dev->device_data != device_data)  			continue; @@ -159,8 +158,8 @@ int snd_device_register(struct snd_card *card, void *device_data)  	struct snd_device *dev;  	int err; -	snd_assert(card != NULL, return -ENXIO); -	snd_assert(device_data != NULL, return -ENXIO); +	if (snd_BUG_ON(!card || !device_data)) +		return -ENXIO;  	list_for_each_entry(dev, &card->devices, list) {  		if (dev->device_data != device_data)  			continue; @@ -188,7 +187,8 @@ int snd_device_register_all(struct snd_card *card)  	struct snd_device *dev;  	int err; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	list_for_each_entry(dev, &card->devices, list) {  		if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {  			if ((err = dev->ops->dev_register(dev)) < 0) @@ -208,7 +208,8 @@ int snd_device_disconnect_all(struct snd_card *card)  	struct snd_device *dev;  	int err = 0; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	list_for_each_entry(dev, &card->devices, list) {  		if (snd_device_disconnect(card, dev->device_data) < 0)  			err = -ENXIO; @@ -226,7 +227,8 @@ int snd_device_free_all(struct snd_card *card, snd_device_cmd_t cmd)  	int err;  	unsigned int range_low, range_high; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE;  	range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1;        __again: diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 6d6589f..195cafc 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -353,9 +353,10 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,  		.dev_disconnect = snd_hwdep_dev_disconnect,  	}; -	snd_assert(rhwdep != NULL, return -EINVAL); -	*rhwdep = NULL; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO; +	if (rhwdep) +		*rhwdep = NULL;  	hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL);  	if (hwdep == NULL) {  		snd_printk(KERN_ERR "hwdep: cannot allocate\n"); @@ -374,13 +375,15 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,  	}  	init_waitqueue_head(&hwdep->open_wait);  	mutex_init(&hwdep->open_mutex); -	*rhwdep = hwdep; +	if (rhwdep) +		*rhwdep = hwdep;  	return 0;  }  static int snd_hwdep_free(struct snd_hwdep *hwdep)  { -	snd_assert(hwdep != NULL, return -ENXIO); +	if (!hwdep) +		return 0;  	if (hwdep->private_free)  		hwdep->private_free(hwdep);  	kfree(hwdep); @@ -440,7 +443,8 @@ static int snd_hwdep_dev_disconnect(struct snd_device *device)  {  	struct snd_hwdep *hwdep = device->device_data; -	snd_assert(hwdep != NULL, return -ENXIO); +	if (snd_BUG_ON(!hwdep)) +		return -ENXIO;  	mutex_lock(®ister_mutex);  	if (snd_hwdep_search(hwdep->card, hwdep->device) != hwdep) {  		mutex_unlock(®ister_mutex); diff --git a/sound/core/info.c b/sound/core/info.c index c67773a..527b207 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -217,7 +217,8 @@ static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,  	loff_t pos;  	data = file->private_data; -	snd_assert(data != NULL, return -ENXIO); +	if (snd_BUG_ON(!data)) +		return -ENXIO;  	pos = *offset;  	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)  		return -EIO; @@ -258,7 +259,8 @@ static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer  	loff_t pos;  	data = file->private_data; -	snd_assert(data != NULL, return -ENXIO); +	if (snd_BUG_ON(!data)) +		return -ENXIO;  	entry = data->entry;  	pos = *offset;  	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) @@ -614,7 +616,8 @@ int snd_info_card_create(struct snd_card *card)  	char str[8];  	struct snd_info_entry *entry; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	sprintf(str, "card%i", card->number);  	if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL) @@ -636,7 +639,8 @@ int snd_info_card_register(struct snd_card *card)  {  	struct proc_dir_entry *p; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	if (!strcmp(card->id, card->proc_root->name))  		return 0; @@ -654,7 +658,8 @@ int snd_info_card_register(struct snd_card *card)   */  void snd_info_card_disconnect(struct snd_card *card)  { -	snd_assert(card != NULL, return); +	if (!card) +		return;  	mutex_lock(&info_mutex);  	if (card->proc_root_link) {  		snd_remove_proc_entry(snd_proc_root, card->proc_root_link); @@ -671,7 +676,8 @@ void snd_info_card_disconnect(struct snd_card *card)   */  int snd_info_card_free(struct snd_card *card)  { -	snd_assert(card != NULL, return -ENXIO); +	if (!card) +		return 0;  	snd_info_free_entry(card->proc_root);  	card->proc_root = NULL;  	return 0; @@ -849,7 +855,7 @@ static void snd_info_disconnect(struct snd_info_entry *entry)  		return;  	list_del_init(&entry->list);  	root = entry->parent == NULL ? snd_proc_root : entry->parent->p; -	snd_assert(root, return); +	snd_BUG_ON(!root);  	snd_remove_proc_entry(root, entry->p);  	entry->p = NULL;  } @@ -947,7 +953,8 @@ int snd_info_register(struct snd_info_entry * entry)  {  	struct proc_dir_entry *root, *p = NULL; -	snd_assert(entry != NULL, return -ENXIO); +	if (snd_BUG_ON(!entry)) +		return -ENXIO;  	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;  	mutex_lock(&info_mutex);  	p = snd_create_proc_entry(entry->name, entry->mode, root); diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index e35789a..e4af138 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -43,8 +43,10 @@ int snd_oss_info_register(int dev, int num, char *string)  {  	char *x; -	snd_assert(dev >= 0 && dev < SNDRV_OSS_INFO_DEV_COUNT, return -ENXIO); -	snd_assert(num >= 0 && num < SNDRV_CARDS, return -ENXIO); +	if (snd_BUG_ON(dev < 0 || dev >= SNDRV_OSS_INFO_DEV_COUNT)) +		return -ENXIO; +	if (snd_BUG_ON(num < 0 || num >= SNDRV_CARDS)) +		return -ENXIO;  	mutex_lock(&strings);  	if (string == NULL) {  		if ((x = snd_sndstat_strings[num][dev]) != NULL) { diff --git a/sound/core/init.c b/sound/core/init.c index df46bbc..8af467d 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -545,7 +545,8 @@ int snd_card_register(struct snd_card *card)  {  	int err; -	snd_assert(card != NULL, return -EINVAL); +	if (snd_BUG_ON(!card)) +		return -EINVAL;  #ifndef CONFIG_SYSFS_DEPRECATED  	if (!card->card_dev) {  		card->card_dev = device_create_drvdata(sound_class, card->dev, diff --git a/sound/core/jack.c b/sound/core/jack.c new file mode 100644 index 0000000..8133a2b --- /dev/null +++ b/sound/core/jack.c @@ -0,0 +1,163 @@ +/* + *  Jack abstraction layer + * + *  Copyright 2008 Wolfson Microelectronics + * + *   This program is free software; you can redistribute it and/or modify + *   it under the terms of the GNU General Public License as published by + *   the Free Software Foundation; either version 2 of the License, or + *   (at your option) any later version. + * + *   This program is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *   GNU General Public License for more details. + * + *   You should have received a copy of the GNU General Public License + *   along with this program; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + * + */ + +#include <linux/input.h> +#include <sound/jack.h> +#include <sound/core.h> + +static int snd_jack_dev_free(struct snd_device *device) +{ +	struct snd_jack *jack = device->device_data; + +	/* If the input device is registered with the input subsystem +	 * then we need to use a different deallocator. */ +	if (jack->registered) +		input_unregister_device(jack->input_dev); +	else +		input_free_device(jack->input_dev); + +	kfree(jack); + +	return 0; +} + +static int snd_jack_dev_register(struct snd_device *device) +{ +	struct snd_jack *jack = device->device_data; +	struct snd_card *card = device->card; +	int err; + +	snprintf(jack->name, sizeof(jack->name), "%s %s", +		 card->longname, jack->id); +	jack->input_dev->name = jack->name; + +	/* Default to the sound card device. */ +	if (!jack->input_dev->dev.parent) +		jack->input_dev->dev.parent = card->dev; + +	err = input_register_device(jack->input_dev); +	if (err == 0) +		jack->registered = 1; + +	return err; +} + +/** + * snd_jack_new - Create a new jack + * @card:  the card instance + * @id:    an identifying string for this jack + * @type:  a bitmask of enum snd_jack_type values that can be detected by + *         this jack + * @jjack: Used to provide the allocated jack object to the caller. + * + * Creates a new jack object. + * + * Returns zero if successful, or a negative error code on failure. + * On success jjack will be initialised. + */ +int snd_jack_new(struct snd_card *card, const char *id, int type, +		 struct snd_jack **jjack) +{ +	struct snd_jack *jack; +	int err; +	static struct snd_device_ops ops = { +		.dev_free = snd_jack_dev_free, +		.dev_register = snd_jack_dev_register, +	}; + +	jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL); +	if (jack == NULL) +		return -ENOMEM; + +	jack->id = id; + +	jack->input_dev = input_allocate_device(); +	if (jack->input_dev == NULL) { +		err = -ENOMEM; +		goto fail_input; +	} + +	jack->input_dev->phys = "ALSA"; + +	jack->type = type; + +	if (type & SND_JACK_HEADPHONE) +		input_set_capability(jack->input_dev, EV_SW, +				     SW_HEADPHONE_INSERT); +	if (type & SND_JACK_MICROPHONE) +		input_set_capability(jack->input_dev, EV_SW, +				     SW_MICROPHONE_INSERT); + +	err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); +	if (err < 0) +		goto fail_input; + +	*jjack = jack; + +	return 0; + +fail_input: +	input_free_device(jack->input_dev); +	kfree(jack); +	return err; +} +EXPORT_SYMBOL(snd_jack_new); + +/** + * snd_jack_set_parent - Set the parent device for a jack + * + * @jack:   The jack to configure + * @parent: The device to set as parent for the jack. + * + * Set the parent for the jack input device in the device tree.  This + * function is only valid prior to registration of the jack.  If no + * parent is configured then the parent device will be the sound card. + */ +void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) +{ +	WARN_ON(jack->registered); + +	jack->input_dev->dev.parent = parent; +} +EXPORT_SYMBOL(snd_jack_set_parent); + +/** + * snd_jack_report - Report the current status of a jack + * + * @jack:   The jack to report status for + * @status: The current status of the jack + */ +void snd_jack_report(struct snd_jack *jack, int status) +{ +	if (jack->type & SND_JACK_HEADPHONE) +		input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT, +				    status & SND_JACK_HEADPHONE); +	if (jack->type & SND_JACK_MICROPHONE) +		input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT, +				    status & SND_JACK_MICROPHONE); + +	input_sync(jack->input_dev); +} +EXPORT_SYMBOL(snd_jack_report); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("Jack detection support for ALSA"); +MODULE_LICENSE("GPL"); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 3733351..1b3534d 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -43,14 +43,6 @@ MODULE_LICENSE("GPL");  /*   */ -void *snd_malloc_sgbuf_pages(struct device *device, -                             size_t size, struct snd_dma_buffer *dmab, -			     size_t *res_size); -int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab); - -/* - */ -  static DEFINE_MUTEX(list_mutex);  static LIST_HEAD(mem_list_head); @@ -64,18 +56,6 @@ struct snd_mem_list {  /* id for pre-allocated buffers */  #define SNDRV_DMA_DEVICE_UNUSED (unsigned int)-1 -#ifdef CONFIG_SND_DEBUG -#define __ASTRING__(x) #x -#define snd_assert(expr, args...) do {\ -	if (!(expr)) {\ -		printk(KERN_ERR "snd-malloc: BUG? (%s) (called from %p)\n", __ASTRING__(expr), __builtin_return_address(0));\ -		args;\ -	}\ -} while (0) -#else -#define snd_assert(expr, args...) /**/ -#endif -  /*   *   *  Generic memory allocators @@ -108,8 +88,10 @@ void *snd_malloc_pages(size_t size, gfp_t gfp_flags)  	int pg;  	void *res; -	snd_assert(size > 0, return NULL); -	snd_assert(gfp_flags != 0, return NULL); +	if (WARN_ON(!size)) +		return NULL; +	if (WARN_ON(!gfp_flags)) +		return NULL;  	gfp_flags |= __GFP_COMP;	/* compound page lets parts be mapped */  	pg = get_order(size);  	if ((res = (void *) __get_free_pages(gfp_flags, pg)) != NULL) @@ -149,8 +131,8 @@ static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *d  	void *res;  	gfp_t gfp_flags; -	snd_assert(size > 0, return NULL); -	snd_assert(dma != NULL, return NULL); +	if (WARN_ON(!dma)) +		return NULL;  	pg = get_order(size);  	gfp_flags = GFP_KERNEL  		| __GFP_COMP	/* compound page lets parts be mapped */ @@ -200,8 +182,10 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr,  int snd_dma_alloc_pages(int type, struct device *device, size_t size,  			struct snd_dma_buffer *dmab)  { -	snd_assert(size > 0, return -ENXIO); -	snd_assert(dmab != NULL, return -ENXIO); +	if (WARN_ON(!size)) +		return -ENXIO; +	if (WARN_ON(!dmab)) +		return -ENXIO;  	dmab->dev.type = type;  	dmab->dev.dev = device; @@ -251,15 +235,17 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,  {  	int err; -	snd_assert(size > 0, return -ENXIO); -	snd_assert(dmab != NULL, return -ENXIO); -  	while ((err = snd_dma_alloc_pages(type, device, size, dmab)) < 0) { +		size_t aligned_size;  		if (err != -ENOMEM)  			return err; -		size >>= 1;  		if (size <= PAGE_SIZE)  			return -ENOMEM; +		aligned_size = PAGE_SIZE << get_order(size); +		if (size != aligned_size) +			size = aligned_size; +		else +			size >>= 1;  	}  	if (! dmab->area)  		return -ENOMEM; @@ -307,7 +293,8 @@ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)  {  	struct snd_mem_list *mem; -	snd_assert(dmab, return 0); +	if (WARN_ON(!dmab)) +		return 0;  	mutex_lock(&list_mutex);  	list_for_each_entry(mem, &mem_list_head, list) { @@ -341,7 +328,8 @@ int snd_dma_reserve_buf(struct snd_dma_buffer *dmab, unsigned int id)  {  	struct snd_mem_list *mem; -	snd_assert(dmab, return -EINVAL); +	if (WARN_ON(!dmab)) +		return -EINVAL;  	mem = kmalloc(sizeof(*mem), GFP_KERNEL);  	if (! mem)  		return -ENOMEM; diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c index 9ded30d..05b58d4 100644 --- a/sound/core/oss/copy.c +++ b/sound/core/oss/copy.c @@ -32,17 +32,18 @@ static snd_pcm_sframes_t copy_transfer(struct snd_pcm_plugin *plugin,  	unsigned int channel;  	unsigned int nchannels; -	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) +		return -ENXIO;  	if (frames == 0)  		return 0;  	nchannels = plugin->src_format.channels;  	for (channel = 0; channel < nchannels; channel++) { -		snd_assert(src_channels->area.first % 8 == 0 && -			   src_channels->area.step % 8 == 0, -			   return -ENXIO); -		snd_assert(dst_channels->area.first % 8 == 0 && -			   dst_channels->area.step % 8 == 0, -			   return -ENXIO); +		if (snd_BUG_ON(src_channels->area.first % 8 || +			       src_channels->area.step % 8)) +			return -ENXIO; +		if (snd_BUG_ON(dst_channels->area.first % 8 || +			       dst_channels->area.step % 8)) +			return -ENXIO;  		if (!src_channels->enabled) {  			if (dst_channels->wanted)  				snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format); @@ -66,15 +67,20 @@ int snd_pcm_plugin_build_copy(struct snd_pcm_substream *plug,  	struct snd_pcm_plugin *plugin;  	int width; -	snd_assert(r_plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!r_plugin)) +		return -ENXIO;  	*r_plugin = NULL; -	snd_assert(src_format->format == dst_format->format, return -ENXIO); -	snd_assert(src_format->rate == dst_format->rate, return -ENXIO); -	snd_assert(src_format->channels == dst_format->channels, return -ENXIO); +	if (snd_BUG_ON(src_format->format != dst_format->format)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->rate != dst_format->rate)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->channels != dst_format->channels)) +		return -ENXIO;  	width = snd_pcm_format_physical_width(src_format->format); -	snd_assert(width > 0, return -ENXIO); +	if (snd_BUG_ON(width <= 0)) +		return -ENXIO;  	err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format,  				   0, &plugin); diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c index f874f6c..6faa1d7 100644 --- a/sound/core/oss/io.c +++ b/sound/core/oss/io.c @@ -39,14 +39,17 @@ static snd_pcm_sframes_t io_playback_transfer(struct snd_pcm_plugin *plugin,  				    struct snd_pcm_plugin_channel *dst_channels,  				    snd_pcm_uframes_t frames)  { -	snd_assert(plugin != NULL, return -ENXIO); -	snd_assert(src_channels != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin)) +		return -ENXIO; +	if (snd_BUG_ON(!src_channels)) +		return -ENXIO;  	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {  		return pcm_write(plugin->plug, src_channels->area.addr, frames);  	} else {  		int channel, channels = plugin->dst_format.channels;  		void **bufs = (void**)plugin->extra_data; -		snd_assert(bufs != NULL, return -ENXIO); +		if (snd_BUG_ON(!bufs)) +			return -ENXIO;  		for (channel = 0; channel < channels; channel++) {  			if (src_channels[channel].enabled)  				bufs[channel] = src_channels[channel].area.addr; @@ -62,14 +65,17 @@ static snd_pcm_sframes_t io_capture_transfer(struct snd_pcm_plugin *plugin,  				   struct snd_pcm_plugin_channel *dst_channels,  				   snd_pcm_uframes_t frames)  { -	snd_assert(plugin != NULL, return -ENXIO); -	snd_assert(dst_channels != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin)) +		return -ENXIO; +	if (snd_BUG_ON(!dst_channels)) +		return -ENXIO;  	if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {  		return pcm_read(plugin->plug, dst_channels->area.addr, frames);  	} else {  		int channel, channels = plugin->dst_format.channels;  		void **bufs = (void**)plugin->extra_data; -		snd_assert(bufs != NULL, return -ENXIO); +		if (snd_BUG_ON(!bufs)) +			return -ENXIO;  		for (channel = 0; channel < channels; channel++) {  			if (dst_channels[channel].enabled)  				bufs[channel] = dst_channels[channel].area.addr; @@ -107,9 +113,11 @@ int snd_pcm_plugin_build_io(struct snd_pcm_substream *plug,  	struct snd_pcm_plugin_format format;  	struct snd_pcm_plugin *plugin; -	snd_assert(r_plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!r_plugin)) +		return -ENXIO;  	*r_plugin = NULL; -	snd_assert(plug != NULL && params != NULL, return -ENXIO); +	if (snd_BUG_ON(!plug || !params)) +		return -ENXIO;  	format.format = params_format(params);  	format.rate = params_rate(params);  	format.channels = params_channels(params); diff --git a/sound/core/oss/linear.c b/sound/core/oss/linear.c index da3dbd4..4c1d168 100644 --- a/sound/core/oss/linear.c +++ b/sound/core/oss/linear.c @@ -92,7 +92,8 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,  {  	struct linear_priv *data; -	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) +		return -ENXIO;  	data = (struct linear_priv *)plugin->extra_data;  	if (frames == 0)  		return 0; @@ -100,12 +101,12 @@ static snd_pcm_sframes_t linear_transfer(struct snd_pcm_plugin *plugin,  	{  		unsigned int channel;  		for (channel = 0; channel < plugin->src_format.channels; channel++) { -			snd_assert(src_channels[channel].area.first % 8 == 0 && -				   src_channels[channel].area.step % 8 == 0, -				   return -ENXIO); -			snd_assert(dst_channels[channel].area.first % 8 == 0 && -				   dst_channels[channel].area.step % 8 == 0, -				   return -ENXIO); +			if (snd_BUG_ON(src_channels[channel].area.first % 8 || +				       src_channels[channel].area.step % 8)) +				return -ENXIO; +			if (snd_BUG_ON(dst_channels[channel].area.first % 8 || +				       dst_channels[channel].area.step % 8)) +				return -ENXIO;  		}  	}  #endif @@ -154,13 +155,17 @@ int snd_pcm_plugin_build_linear(struct snd_pcm_substream *plug,  	struct linear_priv *data;  	struct snd_pcm_plugin *plugin; -	snd_assert(r_plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!r_plugin)) +		return -ENXIO;  	*r_plugin = NULL; -	snd_assert(src_format->rate == dst_format->rate, return -ENXIO); -	snd_assert(src_format->channels == dst_format->channels, return -ENXIO); -	snd_assert(snd_pcm_format_linear(src_format->format) && -		   snd_pcm_format_linear(dst_format->format), return -ENXIO); +	if (snd_BUG_ON(src_format->rate != dst_format->rate)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->channels != dst_format->channels)) +		return -ENXIO; +	if (snd_BUG_ON(!snd_pcm_format_linear(src_format->format) || +		       !snd_pcm_format_linear(dst_format->format))) +		return -ENXIO;  	err = snd_pcm_plugin_build(plug, "linear format conversion",  				   src_format, dst_format, diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 581aa2c..4690b8b 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -257,8 +257,10 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)  		result = pslot->get_volume(fmixer, pslot, &left, &right);  	if (!pslot->stereo)  		right = left; -	snd_assert(left >= 0 && left <= 100, return -EIO); -	snd_assert(right >= 0 && right <= 100, return -EIO); +	if (snd_BUG_ON(left < 0 || left > 100)) +		return -EIO; +	if (snd_BUG_ON(right < 0 || right > 100)) +		return -EIO;  	if (result >= 0) {  		pslot->volume[0] = left;  		pslot->volume[1] = right; @@ -298,7 +300,8 @@ static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int  	int __user *p = argp;  	int tmp; -	snd_assert(fmixer != NULL, return -ENXIO); +	if (snd_BUG_ON(!fmixer)) +		return -ENXIO;  	if (((cmd >> 8) & 0xff) == 'M') {  		switch (cmd) {  		case SOUND_MIXER_INFO: @@ -368,7 +371,8 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l  {  	struct snd_mixer_oss_file fmixer; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO;  	if (card->mixer_oss == NULL)  		return -ENXIO;  	memset(&fmixer, 0, sizeof(fmixer)); @@ -1284,9 +1288,11 @@ static int snd_mixer_oss_free1(void *private)  	struct snd_card *card;  	int idx; -	snd_assert(mixer != NULL, return -ENXIO); +	if (!mixer) +		return 0;  	card = mixer->card; -	snd_assert(mixer == card->mixer_oss, return -ENXIO); +	if (snd_BUG_ON(mixer != card->mixer_oss)) +		return -ENXIO;  	card->mixer_oss = NULL;  	for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {  		struct snd_mixer_oss_slot *chn = &mixer->slots[idx]; diff --git a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c index 77f9619..f7649d4 100644 --- a/sound/core/oss/mulaw.c +++ b/sound/core/oss/mulaw.c @@ -252,19 +252,20 @@ static snd_pcm_sframes_t mulaw_transfer(struct snd_pcm_plugin *plugin,  {  	struct mulaw_priv *data; -	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) +		return -ENXIO;  	if (frames == 0)  		return 0;  #ifdef CONFIG_SND_DEBUG  	{  		unsigned int channel;  		for (channel = 0; channel < plugin->src_format.channels; channel++) { -			snd_assert(src_channels[channel].area.first % 8 == 0 && -				   src_channels[channel].area.step % 8 == 0, -				   return -ENXIO); -			snd_assert(dst_channels[channel].area.first % 8 == 0 && -				   dst_channels[channel].area.step % 8 == 0, -				   return -ENXIO); +			if (snd_BUG_ON(src_channels[channel].area.first % 8 || +				       src_channels[channel].area.step % 8)) +				return -ENXIO; +			if (snd_BUG_ON(dst_channels[channel].area.first % 8 || +				       dst_channels[channel].area.step % 8)) +				return -ENXIO;  		}  	}  #endif @@ -305,11 +306,14 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,  	struct snd_pcm_plugin_format *format;  	mulaw_f func; -	snd_assert(r_plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!r_plugin)) +		return -ENXIO;  	*r_plugin = NULL; -	snd_assert(src_format->rate == dst_format->rate, return -ENXIO); -	snd_assert(src_format->channels == dst_format->channels, return -ENXIO); +	if (snd_BUG_ON(src_format->rate != dst_format->rate)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->channels != dst_format->channels)) +		return -ENXIO;  	if (dst_format->format == SNDRV_PCM_FORMAT_MU_LAW) {  		format = src_format; @@ -323,7 +327,8 @@ int snd_pcm_plugin_build_mulaw(struct snd_pcm_substream *plug,  		snd_BUG();  		return -EINVAL;  	} -	snd_assert(snd_pcm_format_linear(format->format) != 0, return -ENXIO); +	if (snd_BUG_ON(!snd_pcm_format_linear(format->format))) +		return -ENXIO;  	err = snd_pcm_plugin_build(plug, "Mu-Law<->linear conversion",  				   src_format, dst_format, diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 4c601b1..1af62b8 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -452,7 +452,8 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,  	} else {  		*params = *save;  		max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); -		snd_assert(max >= 0, return -EINVAL); +		if (max < 0) +			return max;  		last = 1;  	}   _end: @@ -461,7 +462,7 @@ static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm,  		v = snd_pcm_hw_param_last(pcm, params, var, dir);  	else  		v = snd_pcm_hw_param_first(pcm, params, var, dir); -	snd_assert(v >= 0, return -EINVAL); +	snd_BUG_ON(v < 0);  	return v;  } @@ -778,7 +779,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,  	while (oss_period_size * oss_periods > oss_buffer_size)  		oss_period_size /= 2; -	snd_assert(oss_period_size >= 16, return -EINVAL); +	if (oss_period_size < 16) +		return -EINVAL;  	runtime->oss.period_bytes = oss_period_size;  	runtime->oss.period_frames = 1;  	runtime->oss.periods = oss_periods; @@ -895,7 +897,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)  		}  	}  	err = _snd_pcm_hw_param_set(sparams, SNDRV_PCM_HW_PARAM_FORMAT, sformat, 0); -	snd_assert(err >= 0, goto failure); +	if (err < 0) +		goto failure;  	if (direct) {  		memcpy(params, sparams, sizeof(*params)); @@ -958,11 +961,13 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)  	n = snd_pcm_plug_slave_size(substream, runtime->oss.period_bytes / oss_frame_size);  	err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, n, NULL); -	snd_assert(err >= 0, goto failure); +	if (err < 0) +		goto failure;  	err = snd_pcm_hw_param_near(substream, sparams, SNDRV_PCM_HW_PARAM_PERIODS,  				     runtime->oss.periods, NULL); -	snd_assert(err >= 0, goto failure); +	if (err < 0) +		goto failure;  	snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); @@ -1006,7 +1011,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)  	runtime->oss.periods = params_periods(sparams);  	oss_period_size = snd_pcm_plug_client_size(substream, params_period_size(sparams)); -	snd_assert(oss_period_size >= 0, err = -EINVAL; goto failure); +	if (oss_period_size < 0) { +		err = -EINVAL; +		goto failure; +	}  #ifdef CONFIG_SND_PCM_OSS_PLUGINS  	if (runtime->oss.plugin_first) {  		err = snd_pcm_plug_alloc(substream, oss_period_size); @@ -1017,7 +1025,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)  	oss_period_size *= oss_frame_size;  	oss_buffer_size = oss_period_size * runtime->oss.periods; -	snd_assert(oss_buffer_size >= 0, err = -EINVAL; goto failure); +	if (oss_buffer_size < 0) { +		err = -EINVAL; +		goto failure; +	}  	runtime->oss.period_bytes = oss_period_size;  	runtime->oss.buffer_bytes = oss_buffer_size; @@ -1069,7 +1080,8 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil  				return err;  		}  	} -	snd_assert(asubstream != NULL, return -EIO); +	if (!asubstream) +		return -EIO;  	if (r_substream)  		*r_substream = asubstream;  	return 0; @@ -1764,7 +1776,8 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)  	err = snd_pcm_hw_refine(substream, params);  	format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);   	kfree(params); -	snd_assert(err >= 0, return err); +	if (err < 0) +		return err;  	for (fmt = 0; fmt < 32; ++fmt) {  		if (snd_mask_test(&format_mask, fmt)) {  			int f = snd_pcm_oss_format_to(fmt); @@ -2250,7 +2263,8 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,  static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)  {  	int cidx; -	snd_assert(pcm_oss_file != NULL, return -ENXIO); +	if (!pcm_oss_file) +		return 0;  	for (cidx = 0; cidx < 2; ++cidx) {  		struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx];  		if (substream) @@ -2271,8 +2285,8 @@ static int snd_pcm_oss_open_file(struct file *file,  	struct snd_pcm_substream *substream;  	unsigned int f_mode = file->f_mode; -	snd_assert(rpcm_oss_file != NULL, return -EINVAL); -	*rpcm_oss_file = NULL; +	if (rpcm_oss_file) +		*rpcm_oss_file = NULL;  	pcm_oss_file = kzalloc(sizeof(*pcm_oss_file), GFP_KERNEL);  	if (pcm_oss_file == NULL) @@ -2312,7 +2326,8 @@ static int snd_pcm_oss_open_file(struct file *file,  	}  	file->private_data = pcm_oss_file; -	*rpcm_oss_file = pcm_oss_file; +	if (rpcm_oss_file) +		*rpcm_oss_file = pcm_oss_file;  	return 0;  } @@ -2321,7 +2336,8 @@ static int snd_task_name(struct task_struct *task, char *name, size_t size)  {  	unsigned int idx; -	snd_assert(task != NULL && name != NULL && size >= 2, return -EINVAL); +	if (snd_BUG_ON(!task || !name || size < 2)) +		return -EINVAL;  	for (idx = 0; idx < sizeof(task->comm) && idx + 1 < size; idx++)  		name[idx] = task->comm[idx];  	name[idx] = '\0'; @@ -2415,7 +2431,8 @@ static int snd_pcm_oss_release(struct inode *inode, struct file *file)  	substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];  	if (substream == NULL)  		substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; -	snd_assert(substream != NULL, return -ENXIO); +	if (snd_BUG_ON(!substream)) +		return -ENXIO;  	pcm = substream->pcm;  	if (!pcm->card->shutdown)  		snd_pcm_oss_sync(pcm_oss_file); @@ -2448,7 +2465,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long  			if (substream != NULL)  				break;  		} -		snd_assert(substream != NULL, return -ENXIO); +		if (snd_BUG_ON(idx >= 2)) +			return -ENXIO;  		return snd_mixer_oss_ioctl_card(substream->pcm->card, cmd, arg);  	}  #endif diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index bec9413..6751daa 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -62,7 +62,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t  	if ((width = snd_pcm_format_physical_width(format->format)) < 0)  		return width;  	size = frames * format->channels * width; -	snd_assert((size % 8) == 0, return -ENXIO); +	if (snd_BUG_ON(size % 8)) +		return -ENXIO;  	size /= 8;  	if (plugin->buf_frames < frames) {  		vfree(plugin->buf); @@ -84,7 +85,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t  			c->area.step = format->channels * width;  		}  	} else if (plugin->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { -		snd_assert((size % format->channels) == 0,); +		if (snd_BUG_ON(size % format->channels)) +			return -EINVAL;  		size /= format->channels;  		for (channel = 0; channel < format->channels; channel++, c++) {  			c->frames = frames; @@ -102,13 +104,15 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t  int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)  {  	int err; -	snd_assert(snd_pcm_plug_first(plug) != NULL, return -ENXIO); +	if (snd_BUG_ON(!snd_pcm_plug_first(plug))) +		return -ENXIO;  	if (snd_pcm_plug_stream(plug) == SNDRV_PCM_STREAM_PLAYBACK) {  		struct snd_pcm_plugin *plugin = snd_pcm_plug_first(plug);  		while (plugin->next) {  			if (plugin->dst_frames)  				frames = plugin->dst_frames(plugin, frames); -			snd_assert(frames > 0, return -ENXIO); +			if (snd_BUG_ON(frames <= 0)) +				return -ENXIO;  			plugin = plugin->next;  			err = snd_pcm_plugin_alloc(plugin, frames);  			if (err < 0) @@ -119,7 +123,8 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames)  		while (plugin->prev) {  			if (plugin->src_frames)  				frames = plugin->src_frames(plugin, frames); -			snd_assert(frames > 0, return -ENXIO); +			if (snd_BUG_ON(frames <= 0)) +				return -ENXIO;  			plugin = plugin->prev;  			err = snd_pcm_plugin_alloc(plugin, frames);  			if (err < 0) @@ -148,8 +153,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug,  	struct snd_pcm_plugin *plugin;  	unsigned int channels; -	snd_assert(plug != NULL, return -ENXIO); -	snd_assert(src_format != NULL && dst_format != NULL, return -ENXIO); +	if (snd_BUG_ON(!plug)) +		return -ENXIO; +	if (snd_BUG_ON(!src_format || !dst_format)) +		return -ENXIO;  	plugin = kzalloc(sizeof(*plugin) + extra, GFP_KERNEL);  	if (plugin == NULL)  		return -ENOMEM; @@ -159,10 +166,10 @@ int snd_pcm_plugin_build(struct snd_pcm_substream *plug,  	plugin->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;  	plugin->src_format = *src_format;  	plugin->src_width = snd_pcm_format_physical_width(src_format->format); -	snd_assert(plugin->src_width > 0, ); +	snd_BUG_ON(plugin->src_width <= 0);  	plugin->dst_format = *dst_format;  	plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); -	snd_assert(plugin->dst_width > 0, ); +	snd_BUG_ON(plugin->dst_width <= 0);  	if (plugin->stream == SNDRV_PCM_STREAM_PLAYBACK)  		channels = src_format->channels;  	else @@ -194,7 +201,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *plug, snd_p  	struct snd_pcm_plugin *plugin, *plugin_prev, *plugin_next;  	int stream = snd_pcm_plug_stream(plug); -	snd_assert(plug != NULL, return -ENXIO); +	if (snd_BUG_ON(!plug)) +		return -ENXIO;  	if (drv_frames == 0)  		return 0;  	if (stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -224,7 +232,8 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc  	snd_pcm_sframes_t frames;  	int stream = snd_pcm_plug_stream(plug); -	snd_assert(plug != NULL, return -ENXIO); +	if (snd_BUG_ON(!plug)) +		return -ENXIO;  	if (clt_frames == 0)  		return 0;  	frames = clt_frames; @@ -540,7 +549,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu  	int width, nchannels, channel;  	int stream = snd_pcm_plug_stream(plug); -	snd_assert(buf != NULL, return -ENXIO); +	if (snd_BUG_ON(!buf)) +		return -ENXIO;  	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {  		plugin = snd_pcm_plug_first(plug);  		format = &plugin->src_format; @@ -553,7 +563,9 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu  	if ((width = snd_pcm_format_physical_width(format->format)) < 0)  		return width;  	nchannels = format->channels; -	snd_assert(plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || format->channels <= 1, return -ENXIO); +	if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && +		       format->channels > 1)) +		return -ENXIO;  	for (channel = 0; channel < nchannels; channel++, v++) {  		v->frames = count;  		v->enabled = 1; diff --git a/sound/core/oss/rate.c b/sound/core/oss/rate.c index 14dfb31..a466443 100644 --- a/sound/core/oss/rate.c +++ b/sound/core/oss/rate.c @@ -185,7 +185,8 @@ static snd_pcm_sframes_t rate_src_frames(struct snd_pcm_plugin *plugin, snd_pcm_  	struct rate_priv *data;  	snd_pcm_sframes_t res; -	snd_assert(plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin)) +		return -ENXIO;  	if (frames == 0)  		return 0;  	data = (struct rate_priv *)plugin->extra_data; @@ -217,7 +218,8 @@ static snd_pcm_sframes_t rate_dst_frames(struct snd_pcm_plugin *plugin, snd_pcm_  	struct rate_priv *data;  	snd_pcm_sframes_t res; -	snd_assert(plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin)) +		return -ENXIO;  	if (frames == 0)  		return 0;  	data = (struct rate_priv *)plugin->extra_data; @@ -252,19 +254,20 @@ static snd_pcm_sframes_t rate_transfer(struct snd_pcm_plugin *plugin,  	snd_pcm_uframes_t dst_frames;  	struct rate_priv *data; -	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) +		return -ENXIO;  	if (frames == 0)  		return 0;  #ifdef CONFIG_SND_DEBUG  	{  		unsigned int channel;  		for (channel = 0; channel < plugin->src_format.channels; channel++) { -			snd_assert(src_channels[channel].area.first % 8 == 0 && -				   src_channels[channel].area.step % 8 == 0, -				   return -ENXIO); -			snd_assert(dst_channels[channel].area.first % 8 == 0 && -				   dst_channels[channel].area.step % 8 == 0, -				   return -ENXIO); +			if (snd_BUG_ON(src_channels[channel].area.first % 8 || +				       src_channels[channel].area.step % 8)) +				return -ENXIO; +			if (snd_BUG_ON(dst_channels[channel].area.first % 8 || +				       dst_channels[channel].area.step % 8)) +				return -ENXIO;  		}  	}  #endif @@ -281,7 +284,8 @@ static int rate_action(struct snd_pcm_plugin *plugin,  		       enum snd_pcm_plugin_action action,  		       unsigned long udata)  { -	snd_assert(plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin)) +		return -ENXIO;  	switch (action) {  	case INIT:  	case PREPARE: @@ -302,14 +306,20 @@ int snd_pcm_plugin_build_rate(struct snd_pcm_substream *plug,  	struct rate_priv *data;  	struct snd_pcm_plugin *plugin; -	snd_assert(r_plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!r_plugin)) +		return -ENXIO;  	*r_plugin = NULL; -	snd_assert(src_format->channels == dst_format->channels, return -ENXIO); -	snd_assert(src_format->channels > 0, return -ENXIO); -	snd_assert(src_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO); -	snd_assert(dst_format->format == SNDRV_PCM_FORMAT_S16, return -ENXIO); -	snd_assert(src_format->rate != dst_format->rate, return -ENXIO); +	if (snd_BUG_ON(src_format->channels != dst_format->channels)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->channels <= 0)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->format != SNDRV_PCM_FORMAT_S16)) +		return -ENXIO; +	if (snd_BUG_ON(dst_format->format != SNDRV_PCM_FORMAT_S16)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->rate == dst_format->rate)) +		return -ENXIO;  	err = snd_pcm_plugin_build(plug, "rate conversion",  				   src_format, dst_format, diff --git a/sound/core/oss/route.c b/sound/core/oss/route.c index da7ab7a..0dcc287 100644 --- a/sound/core/oss/route.c +++ b/sound/core/oss/route.c @@ -54,7 +54,8 @@ static snd_pcm_sframes_t route_transfer(struct snd_pcm_plugin *plugin,  	struct snd_pcm_plugin_channel *dvp;  	int format; -	snd_assert(plugin != NULL && src_channels != NULL && dst_channels != NULL, return -ENXIO); +	if (snd_BUG_ON(!plugin || !src_channels || !dst_channels)) +		return -ENXIO;  	if (frames == 0)  		return 0; @@ -90,10 +91,13 @@ int snd_pcm_plugin_build_route(struct snd_pcm_substream *plug,  	struct snd_pcm_plugin *plugin;  	int err; -	snd_assert(r_plugin != NULL, return -ENXIO); +	if (snd_BUG_ON(!r_plugin)) +		return -ENXIO;  	*r_plugin = NULL; -	snd_assert(src_format->rate == dst_format->rate, return -ENXIO); -	snd_assert(src_format->format == dst_format->format, return -ENXIO); +	if (snd_BUG_ON(src_format->rate != dst_format->rate)) +		return -ENXIO; +	if (snd_BUG_ON(src_format->format != dst_format->format)) +		return -ENXIO;  	err = snd_pcm_plugin_build(plug, "route conversion",  				   src_format, dst_format, 0, &plugin); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 9dd9bc7..192a433 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -42,7 +42,7 @@ static int snd_pcm_dev_free(struct snd_device *device);  static int snd_pcm_dev_register(struct snd_device *device);  static int snd_pcm_dev_disconnect(struct snd_device *device); -static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device) +static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device)  {  	struct snd_pcm *pcm; @@ -53,6 +53,37 @@ static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)  	return NULL;  } +static int snd_pcm_next(struct snd_card *card, int device) +{ +	struct snd_pcm *pcm; + +	list_for_each_entry(pcm, &snd_pcm_devices, list) { +		if (pcm->card == card && pcm->device > device) +			return pcm->device; +		else if (pcm->card->number > card->number) +			return -1; +	} +	return -1; +} + +static int snd_pcm_add(struct snd_pcm *newpcm) +{ +	struct snd_pcm *pcm; + +	list_for_each_entry(pcm, &snd_pcm_devices, list) { +		if (pcm->card == newpcm->card && pcm->device == newpcm->device) +			return -EBUSY; +		if (pcm->card->number > newpcm->card->number || +				(pcm->card == newpcm->card && +				pcm->device > newpcm->device)) { +			list_add(&newpcm->list, pcm->list.prev); +			return 0; +		} +	} +	list_add_tail(&newpcm->list, &snd_pcm_devices); +	return 0; +} +  static int snd_pcm_control_ioctl(struct snd_card *card,  				 struct snd_ctl_file *control,  				 unsigned int cmd, unsigned long arg) @@ -65,14 +96,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,  			if (get_user(device, (int __user *)arg))  				return -EFAULT;  			mutex_lock(®ister_mutex); -			device = device < 0 ? 0 : device + 1; -			while (device < SNDRV_PCM_DEVICES) { -				if (snd_pcm_search(card, device)) -					break; -				device++; -			} -			if (device == SNDRV_PCM_DEVICES) -				device = -1; +			device = snd_pcm_next(card, device);  			mutex_unlock(®ister_mutex);  			if (put_user(device, (int __user *)arg))  				return -EFAULT; @@ -98,7 +122,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,  			if (get_user(subdevice, &info->subdevice))  				return -EFAULT;  			mutex_lock(®ister_mutex); -			pcm = snd_pcm_search(card, device); +			pcm = snd_pcm_get(card, device);  			if (pcm == NULL) {  				err = -ENXIO;  				goto _error; @@ -232,7 +256,6 @@ static char *snd_pcm_tstamp_mode_names[] = {  static const char *snd_pcm_stream_name(int stream)  { -	snd_assert(stream <= SNDRV_PCM_STREAM_LAST, return NULL);  	return snd_pcm_stream_names[stream];  } @@ -248,7 +271,6 @@ static const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)  static const char *snd_pcm_tstamp_mode_name(int mode)  { -	snd_assert(mode <= SNDRV_PCM_TSTAMP_LAST, return NULL);  	return snd_pcm_tstamp_mode_names[mode];  } @@ -682,9 +704,10 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,  		.dev_disconnect = snd_pcm_dev_disconnect,  	}; -	snd_assert(rpcm != NULL, return -EINVAL); -	*rpcm = NULL; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO; +	if (rpcm) +		*rpcm = NULL;  	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);  	if (pcm == NULL) {  		snd_printk(KERN_ERR "Cannot allocate PCM\n"); @@ -708,7 +731,8 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,  		snd_pcm_free(pcm);  		return err;  	} -	*rpcm = pcm; +	if (rpcm) +		*rpcm = pcm;  	return 0;  } @@ -742,7 +766,8 @@ static int snd_pcm_free(struct snd_pcm *pcm)  {  	struct snd_pcm_notify *notify; -	snd_assert(pcm != NULL, return -ENXIO); +	if (!pcm) +		return 0;  	list_for_each_entry(notify, &snd_pcm_notify_list, list) {  		notify->n_unregister(pcm);  	} @@ -773,15 +798,15 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,  	int prefer_subdevice = -1;  	size_t size; -	snd_assert(rsubstream != NULL, return -EINVAL); +	if (snd_BUG_ON(!pcm || !rsubstream)) +		return -ENXIO;  	*rsubstream = NULL; -	snd_assert(pcm != NULL, return -ENXIO);  	pstr = &pcm->streams[stream];  	if (pstr->substream == NULL || pstr->substream_count == 0)  		return -ENODEV;  	card = pcm->card; -	down_read(&card->controls_rwsem); +	read_lock(&card->ctl_files_rwlock);  	list_for_each_entry(kctl, &card->ctl_files, list) {  		if (kctl->pid == current->pid) {  			prefer_subdevice = kctl->prefer_pcm_subdevice; @@ -789,7 +814,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,  				break;  		}  	} -	up_read(&card->controls_rwsem); +	read_unlock(&card->ctl_files_rwlock);  	switch (stream) {  	case SNDRV_PCM_STREAM_PLAYBACK: @@ -883,8 +908,9 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime; +	if (PCM_RUNTIME_CHECK(substream)) +		return;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return);  	if (runtime->private_free != NULL)  		runtime->private_free(runtime);  	snd_free_pages((void*)runtime->status, @@ -929,13 +955,14 @@ static int snd_pcm_dev_register(struct snd_device *device)  	struct snd_pcm *pcm = device->device_data;  	struct device *dev; -	snd_assert(pcm != NULL && device != NULL, return -ENXIO); +	if (snd_BUG_ON(!pcm || !device)) +		return -ENXIO;  	mutex_lock(®ister_mutex); -	if (snd_pcm_search(pcm->card, pcm->device)) { +	err = snd_pcm_add(pcm); +	if (err) {  		mutex_unlock(®ister_mutex); -		return -EBUSY; +		return err;  	} -	list_add_tail(&pcm->list, &snd_pcm_devices);  	for (cidx = 0; cidx < 2; cidx++) {  		int devtype = -1;  		if (pcm->streams[cidx].substream == NULL) @@ -1019,10 +1046,11 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)  {  	struct snd_pcm *pcm; -	snd_assert(notify != NULL && -		   notify->n_register != NULL && -		   notify->n_unregister != NULL && -		   notify->n_disconnect, return -EINVAL); +	if (snd_BUG_ON(!notify || +		       !notify->n_register || +		       !notify->n_unregister || +		       !notify->n_disconnect)) +		return -EINVAL;  	mutex_lock(®ister_mutex);  	if (nfree) {  		list_del(¬ify->list); diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 49aa693..36d7a59 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -397,7 +397,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,  	snd_pcm_uframes_t boundary;  	int err; -	snd_assert(runtime, return -EINVAL); +	if (snd_BUG_ON(!runtime)) +		return -EINVAL;  	if (get_user(sflags, &src->flags) ||  	    get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1533f03..6ea5cfb 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -85,7 +85,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram  		}  		frames = runtime->buffer_size - runtime->silence_filled;  	} -	snd_assert(frames <= runtime->buffer_size, return); +	if (snd_BUG_ON(frames > runtime->buffer_size)) +		return;  	if (frames == 0)  		return;  	ofs = runtime->silence_start % runtime->buffer_size; @@ -96,7 +97,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram  			if (substream->ops->silence) {  				int err;  				err = substream->ops->silence(substream, -1, ofs, transfer); -				snd_assert(err >= 0, ); +				snd_BUG_ON(err < 0);  			} else {  				char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs);  				snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); @@ -108,7 +109,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram  				for (c = 0; c < channels; ++c) {  					int err;  					err = substream->ops->silence(substream, c, ofs, transfer); -					snd_assert(err >= 0, ); +					snd_BUG_ON(err < 0);  				}  			} else {  				size_t dma_csize = runtime->dma_bytes / channels; @@ -354,7 +355,7 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,  {  	u_int64_t n = (u_int64_t) a * b;  	if (c == 0) { -		snd_assert(n > 0, ); +		snd_BUG_ON(!n);  		*r = 0;  		return UINT_MAX;  	} @@ -380,7 +381,8 @@ static inline unsigned int muldiv32(unsigned int a, unsigned int b,  int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v)  {  	int changed = 0; -	snd_assert(!snd_interval_empty(i), return -EINVAL); +	if (snd_BUG_ON(snd_interval_empty(i))) +		return -EINVAL;  	if (i->min < v->min) {  		i->min = v->min;  		i->openmin = v->openmin; @@ -423,7 +425,8 @@ EXPORT_SYMBOL(snd_interval_refine);  static int snd_interval_refine_first(struct snd_interval *i)  { -	snd_assert(!snd_interval_empty(i), return -EINVAL); +	if (snd_BUG_ON(snd_interval_empty(i))) +		return -EINVAL;  	if (snd_interval_single(i))  		return 0;  	i->max = i->min; @@ -435,7 +438,8 @@ static int snd_interval_refine_first(struct snd_interval *i)  static int snd_interval_refine_last(struct snd_interval *i)  { -	snd_assert(!snd_interval_empty(i), return -EINVAL); +	if (snd_BUG_ON(snd_interval_empty(i))) +		return -EINVAL;  	if (snd_interval_single(i))  		return 0;  	i->min = i->max; @@ -889,7 +893,8 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond,  	c->private = private;  	k = 0;  	while (1) { -		snd_assert(k < ARRAY_SIZE(c->deps), return -EINVAL); +		if (snd_BUG_ON(k >= ARRAY_SIZE(c->deps))) +			return -EINVAL;  		c->deps[k++] = dep;  		if (dep < 0)  			break; @@ -1285,7 +1290,8 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm,  		return changed;  	if (params->rmask) {  		int err = snd_pcm_hw_refine(pcm, params); -		snd_assert(err >= 0, return err); +		if (snd_BUG_ON(err < 0)) +			return err;  	}  	return snd_pcm_hw_param_value(params, var, dir);  } @@ -1330,7 +1336,8 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm,  		return changed;  	if (params->rmask) {  		int err = snd_pcm_hw_refine(pcm, params); -		snd_assert(err >= 0, return err); +		if (snd_BUG_ON(err < 0)) +			return err;  	}  	return snd_pcm_hw_param_value(params, var, dir);  } @@ -1368,7 +1375,8 @@ int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,  			err = snd_pcm_hw_param_first(pcm, params, *v, NULL);  		else  			err = snd_pcm_hw_param_last(pcm, params, *v, NULL); -		snd_assert(err >= 0, return err); +		if (snd_BUG_ON(err < 0)) +			return err;  	}  	return 0;  } @@ -1466,9 +1474,9 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)  	struct snd_pcm_runtime *runtime;  	unsigned long flags; -	snd_assert(substream != NULL, return); +	if (PCM_RUNTIME_CHECK(substream)) +		return;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return);  	if (runtime->transfer_ack_begin)  		runtime->transfer_ack_begin(substream); @@ -1567,7 +1575,6 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,  			return err;  	} else {  		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); -		snd_assert(runtime->dma_area, return -EFAULT);  		if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))  			return -EFAULT;  	} @@ -1629,7 +1636,10 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,  		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;  		if (frames > cont)  			frames = cont; -		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); +		if (snd_BUG_ON(!frames)) { +			snd_pcm_stream_unlock_irq(substream); +			return -EINVAL; +		}  		appl_ptr = runtime->control->appl_ptr;  		appl_ofs = appl_ptr % runtime->buffer_size;  		snd_pcm_stream_unlock_irq(substream); @@ -1669,18 +1679,30 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,  	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;  } -snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) +/* sanity-check for read/write methods */ +static int pcm_sanity_check(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime; -	int nonblock; - -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -ENXIO); -	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); +	if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) +		return -EINVAL;  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)  		return -EBADFD; +	return 0; +} + +snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) +{ +	struct snd_pcm_runtime *runtime; +	int nonblock; +	int err; +	err = pcm_sanity_check(substream); +	if (err < 0) +		return err; +	runtime = substream->runtime;  	nonblock = !!(substream->f_flags & O_NONBLOCK);  	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && @@ -1703,7 +1725,8 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,  	int channels = runtime->channels;  	int c;  	if (substream->ops->copy) { -		snd_assert(substream->ops->silence != NULL, return -EINVAL); +		if (snd_BUG_ON(!substream->ops->silence)) +			return -EINVAL;  		for (c = 0; c < channels; ++c, ++bufs) {  			if (*bufs == NULL) {  				if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) @@ -1717,7 +1740,6 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,  	} else {  		/* default transfer behaviour */  		size_t dma_csize = runtime->dma_bytes / channels; -		snd_assert(runtime->dma_area, return -EFAULT);  		for (c = 0; c < channels; ++c, ++bufs) {  			char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);  			if (*bufs == NULL) { @@ -1738,14 +1760,12 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,  {  	struct snd_pcm_runtime *runtime;  	int nonblock; +	int err; -	snd_assert(substream != NULL, return -ENXIO); +	err = pcm_sanity_check(substream); +	if (err < 0) +		return err;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -ENXIO); -	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); -	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) -		return -EBADFD; -  	nonblock = !!(substream->f_flags & O_NONBLOCK);  	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) @@ -1769,7 +1789,6 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,  			return err;  	} else {  		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); -		snd_assert(runtime->dma_area, return -EFAULT);  		if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))  			return -EFAULT;  	} @@ -1841,7 +1860,10 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,  		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;  		if (frames > cont)  			frames = cont; -		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL); +		if (snd_BUG_ON(!frames)) { +			snd_pcm_stream_unlock_irq(substream); +			return -EINVAL; +		}  		appl_ptr = runtime->control->appl_ptr;  		appl_ofs = appl_ptr % runtime->buffer_size;  		snd_pcm_stream_unlock_irq(substream); @@ -1879,14 +1901,12 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u  {  	struct snd_pcm_runtime *runtime;  	int nonblock; +	int err; -	snd_assert(substream != NULL, return -ENXIO); +	err = pcm_sanity_check(substream); +	if (err < 0) +		return err;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -ENXIO); -	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL); -	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) -		return -EBADFD; -  	nonblock = !!(substream->f_flags & O_NONBLOCK);  	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)  		return -EINVAL; @@ -1916,7 +1936,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,  		}  	} else {  		snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; -		snd_assert(runtime->dma_area, return -EFAULT);  		for (c = 0; c < channels; ++c, ++bufs) {  			char *hwbuf;  			char __user *buf; @@ -1938,11 +1957,12 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,  {  	struct snd_pcm_runtime *runtime;  	int nonblock; +	int err; -	snd_assert(substream != NULL, return -ENXIO); +	err = pcm_sanity_check(substream); +	if (err < 0) +		return err;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -ENXIO); -	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)  		return -EBADFD; diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index ff07b4a..a6d4280 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -50,8 +50,6 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz  	struct snd_dma_buffer *dmab = &substream->dma_buffer;  	int err; -	snd_assert(size > 0, return -EINVAL); -  	/* already reserved? */  	if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {  		if (dmab->bytes >= size) @@ -326,6 +324,32 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne  EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); +/* + * compute the max chunk size with continuous pages on sg-buffer + */ +unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream, +					  unsigned int ofs, unsigned int size) +{ +	struct snd_sg_buf *sg = snd_pcm_substream_sgbuf(substream); +	unsigned int start, end, pg; + +	start = ofs >> PAGE_SHIFT; +	end = (ofs + size - 1) >> PAGE_SHIFT; +	/* check page continuity */ +	pg = sg->table[start].addr >> PAGE_SHIFT; +	for (;;) { +		start++; +		if (start > end) +			break; +		pg++; +		if ((sg->table[start].addr >> PAGE_SHIFT) != pg) +			return (start << PAGE_SHIFT) - ofs; +	} +	/* ok, all on continuous pages */ +	return size; +} +EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size); +  /**   * snd_pcm_lib_malloc_pages - allocate the DMA buffer   * @substream: the substream to allocate the DMA buffer to @@ -342,10 +366,12 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)  	struct snd_pcm_runtime *runtime;  	struct snd_dma_buffer *dmab = NULL; -	snd_assert(substream->dma_buffer.dev.type != SNDRV_DMA_TYPE_UNKNOWN, return -EINVAL); -	snd_assert(substream != NULL, return -EINVAL); +	if (PCM_RUNTIME_CHECK(substream)) +		return -EINVAL; +	if (snd_BUG_ON(substream->dma_buffer.dev.type == +		       SNDRV_DMA_TYPE_UNKNOWN)) +		return -EINVAL;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -EINVAL);  	if (runtime->dma_buffer_p) {  		/* perphaps, we might free the large DMA memory region @@ -391,9 +417,9 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime; -	snd_assert(substream != NULL, return -EINVAL); +	if (PCM_RUNTIME_CHECK(substream)) +		return -EINVAL;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -EINVAL);  	if (runtime->dma_area == NULL)  		return 0;  	if (runtime->dma_buffer_p != &substream->dma_buffer) { diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index c49b9d9..e61e125 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -95,7 +95,6 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)  	struct snd_pcm *pcm = substream->pcm;  	struct snd_pcm_str *pstr = substream->pstr; -	snd_assert(substream != NULL, return -ENXIO);  	memset(info, 0, sizeof(*info));  	info->card = pcm->card->number;  	info->device = pcm->device; @@ -370,9 +369,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,  	unsigned int bits;  	snd_pcm_uframes_t frames; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -ENXIO);  	snd_pcm_stream_lock_irq(substream);  	switch (runtime->status->state) {  	case SNDRV_PCM_STATE_OPEN: @@ -490,9 +489,9 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)  	struct snd_pcm_runtime *runtime;  	int result = 0; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -ENXIO);  	snd_pcm_stream_lock_irq(substream);  	switch (runtime->status->state) {  	case SNDRV_PCM_STATE_SETUP: @@ -518,9 +517,9 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,  {  	struct snd_pcm_runtime *runtime; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -ENXIO);  	snd_pcm_stream_lock_irq(substream);  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) {  		snd_pcm_stream_unlock_irq(substream); @@ -622,11 +621,8 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream,  			       struct snd_pcm_status __user * _status)  {  	struct snd_pcm_status status; -	struct snd_pcm_runtime *runtime;  	int res; -	snd_assert(substream != NULL, return -ENXIO); -	runtime = substream->runtime;  	memset(&status, 0, sizeof(status));  	res = snd_pcm_status(substream, &status);  	if (res < 0) @@ -642,7 +638,6 @@ static int snd_pcm_channel_info(struct snd_pcm_substream *substream,  	struct snd_pcm_runtime *runtime;  	unsigned int channel; -	snd_assert(substream != NULL, return -ENXIO);  	channel = info->channel;  	runtime = substream->runtime;  	snd_pcm_stream_lock_irq(substream); @@ -1250,7 +1245,6 @@ static int snd_pcm_do_reset(struct snd_pcm_substream *substream, int state)  	int err = substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_RESET, NULL);  	if (err < 0)  		return err; -	// snd_assert(runtime->status->hw_ptr < runtime->buffer_size, );  	runtime->hw_ptr_base = 0;  	runtime->hw_ptr_interrupt = runtime->status->hw_ptr -  		runtime->status->hw_ptr % runtime->period_size; @@ -1421,7 +1415,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)  	int i, num_drecs;  	struct drain_rec *drec, drec_tmp, *d; -	snd_assert(substream != NULL, return -ENXIO);  	card = substream->pcm->card;  	runtime = substream->runtime; @@ -1541,21 +1534,16 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)  	struct snd_card *card;  	int result = 0; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime;  	card = substream->pcm->card;  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN || -	    runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) +	    runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED || +	    runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)  		return -EBADFD; -	snd_power_lock(card); -	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { -		result = snd_power_wait(card, SNDRV_CTL_POWER_D0); -		if (result < 0) -			goto _unlock; -	} -  	snd_pcm_stream_lock_irq(substream);  	/* resume pause */  	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED) @@ -1564,8 +1552,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)  	snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);  	/* runtime->control->appl_ptr = runtime->status->hw_ptr; */  	snd_pcm_stream_unlock_irq(substream); - _unlock: -	snd_power_unlock(card); +  	return result;  } @@ -1941,33 +1928,41 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)  			mask |= 1 << SNDRV_PCM_ACCESS_MMAP_COMPLEX;  	}  	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_ACCESS, mask); -	snd_assert(err >= 0, return -EINVAL); +	if (err < 0) +		return err;  	err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats); -	snd_assert(err >= 0, return -EINVAL); +	if (err < 0) +		return err;  	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD); -	snd_assert(err >= 0, return -EINVAL); +	if (err < 0) +		return err;  	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS,  					   hw->channels_min, hw->channels_max); -	snd_assert(err >= 0, return -EINVAL); +	if (err < 0) +		return err;  	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,  					   hw->rate_min, hw->rate_max); -	snd_assert(err >= 0, return -EINVAL); +	 if (err < 0) +		 return err;  	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,  					   hw->period_bytes_min, hw->period_bytes_max); -	snd_assert(err >= 0, return -EINVAL); +	 if (err < 0) +		 return err;  	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,  					   hw->periods_min, hw->periods_max); -	snd_assert(err >= 0, return -EINVAL); +	if (err < 0) +		return err;  	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,  					   hw->period_bytes_min, hw->buffer_bytes_max); -	snd_assert(err >= 0, return -EINVAL); +	if (err < 0) +		return err;  	err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,   				  snd_pcm_hw_rule_buffer_bytes_max, substream, @@ -1978,7 +1973,8 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)  	/* FIXME: remove */  	if (runtime->dma_bytes) {  		err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 0, runtime->dma_bytes); -		snd_assert(err >= 0, return -EINVAL); +		if (err < 0) +			return -EINVAL;  	}  	if (!(hw->rates & (SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_CONTINUOUS))) { @@ -2074,8 +2070,8 @@ static int snd_pcm_open_file(struct file *file,  	struct snd_pcm_str *str;  	int err; -	snd_assert(rpcm_file != NULL, return -EINVAL); -	*rpcm_file = NULL; +	if (rpcm_file) +		*rpcm_file = NULL;  	err = snd_pcm_open_substream(pcm, stream, file, &substream);  	if (err < 0) @@ -2093,7 +2089,8 @@ static int snd_pcm_open_file(struct file *file,  		substream->pcm_release = pcm_release_private;  	}  	file->private_data = pcm_file; -	*rpcm_file = pcm_file; +	if (rpcm_file) +		*rpcm_file = pcm_file;  	return 0;  } @@ -2177,7 +2174,8 @@ static int snd_pcm_release(struct inode *inode, struct file *file)  	pcm_file = file->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, return -ENXIO); +	if (snd_BUG_ON(!substream)) +		return -ENXIO;  	pcm = substream->pcm;  	fasync_helper(-1, file, 0, &substream->runtime->fasync);  	mutex_lock(&pcm->open_mutex); @@ -2500,8 +2498,6 @@ static int snd_pcm_common_ioctl1(struct file *file,  				 struct snd_pcm_substream *substream,  				 unsigned int cmd, void __user *arg)  { -	snd_assert(substream != NULL, return -ENXIO); -  	switch (cmd) {  	case SNDRV_PCM_IOCTL_PVERSION:  		return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; @@ -2570,8 +2566,10 @@ static int snd_pcm_playback_ioctl1(struct file *file,  				   struct snd_pcm_substream *substream,  				   unsigned int cmd, void __user *arg)  { -	snd_assert(substream != NULL, return -ENXIO); -	snd_assert(substream->stream == SNDRV_PCM_STREAM_PLAYBACK, return -EINVAL); +	if (snd_BUG_ON(!substream)) +		return -ENXIO; +	if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) +		return -EINVAL;  	switch (cmd) {  	case SNDRV_PCM_IOCTL_WRITEI_FRAMES:  	{ @@ -2650,8 +2648,10 @@ static int snd_pcm_capture_ioctl1(struct file *file,  				  struct snd_pcm_substream *substream,  				  unsigned int cmd, void __user *arg)  { -	snd_assert(substream != NULL, return -ENXIO); -	snd_assert(substream->stream == SNDRV_PCM_STREAM_CAPTURE, return -EINVAL); +	if (snd_BUG_ON(!substream)) +		return -ENXIO; +	if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE)) +		return -EINVAL;  	switch (cmd) {  	case SNDRV_PCM_IOCTL_READI_FRAMES:  	{ @@ -2790,7 +2790,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,  	pcm_file = file->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime;  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)  		return -EBADFD; @@ -2813,21 +2814,17 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,  	pcm_file = file->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, result = -ENXIO; goto end); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime; -	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { -		result = -EBADFD; -		goto end; -	} -	if (!frame_aligned(runtime, count)) { -		result = -EINVAL; -		goto end; -	} +	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) +		return -EBADFD; +	if (!frame_aligned(runtime, count)) +		return -EINVAL;  	count = bytes_to_frames(runtime, count);  	result = snd_pcm_lib_write(substream, buf, count);  	if (result > 0)  		result = frames_to_bytes(runtime, result); - end:  	return result;  } @@ -2845,7 +2842,8 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov,  	pcm_file = iocb->ki_filp->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime;  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)  		return -EBADFD; @@ -2879,17 +2877,14 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,  	pcm_file = iocb->ki_filp->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, result = -ENXIO; goto end); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime; -	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { -		result = -EBADFD; -		goto end; -	} +	if (runtime->status->state == SNDRV_PCM_STATE_OPEN) +		return -EBADFD;  	if (nr_segs > 128 || nr_segs != runtime->channels || -	    !frame_aligned(runtime, iov->iov_len)) { -		result = -EINVAL; -		goto end; -	} +	    !frame_aligned(runtime, iov->iov_len)) +		return -EINVAL;  	frames = bytes_to_samples(runtime, iov->iov_len);  	bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL);  	if (bufs == NULL) @@ -2900,7 +2895,6 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov,  	if (result > 0)  		result = frames_to_bytes(runtime, result);  	kfree(bufs); - end:  	return result;  } @@ -2915,7 +2909,8 @@ static unsigned int snd_pcm_playback_poll(struct file *file, poll_table * wait)  	pcm_file = file->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime;  	poll_wait(file, &runtime->sleep, wait); @@ -2953,7 +2948,8 @@ static unsigned int snd_pcm_capture_poll(struct file *file, poll_table * wait)  	pcm_file = file->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	runtime = substream->runtime;  	poll_wait(file, &runtime->sleep, wait); @@ -3023,7 +3019,6 @@ static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file  	if (!(area->vm_flags & VM_READ))  		return -EINVAL;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -EAGAIN);  	size = area->vm_end - area->vm_start;  	if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)))  		return -EINVAL; @@ -3063,7 +3058,6 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file  	if (!(area->vm_flags & VM_READ))  		return -EINVAL;  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -EAGAIN);  	size = area->vm_end - area->vm_start;  	if (size != PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)))  		return -EINVAL; @@ -3195,7 +3189,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,  			return -EINVAL;  	}  	runtime = substream->runtime; -	snd_assert(runtime != NULL, return -EAGAIN);  	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)  		return -EBADFD;  	if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) @@ -3227,7 +3220,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)  	pcm_file = file->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, return -ENXIO); +	if (PCM_RUNTIME_CHECK(substream)) +		return -ENXIO;  	offset = area->vm_pgoff << PAGE_SHIFT;  	switch (offset) { @@ -3255,9 +3249,9 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)  	lock_kernel();  	pcm_file = file->private_data;  	substream = pcm_file->substream; -	snd_assert(substream != NULL, goto out); +	if (PCM_RUNTIME_CHECK(substream)) +		goto out;  	runtime = substream->runtime; -  	err = fasync_helper(fd, file, on, &runtime->fasync);  out:  	unlock_kernel(); @@ -3391,6 +3385,17 @@ out:  }  #endif /* CONFIG_SND_SUPPORT_OLD_API */ +#ifndef CONFIG_MMU +unsigned long dummy_get_unmapped_area(struct file *file, unsigned long addr, +				      unsigned long len, unsigned long pgoff, +				      unsigned long flags) +{ +	return 0; +} +#else +# define dummy_get_unmapped_area NULL +#endif +  /*   *  Register section   */ @@ -3407,6 +3412,7 @@ const struct file_operations snd_pcm_f_ops[2] = {  		.compat_ioctl = 	snd_pcm_ioctl_compat,  		.mmap =			snd_pcm_mmap,  		.fasync =		snd_pcm_fasync, +		.get_unmapped_area =	dummy_get_unmapped_area,  	},  	{  		.owner =		THIS_MODULE, @@ -3419,5 +3425,6 @@ const struct file_operations snd_pcm_f_ops[2] = {  		.compat_ioctl = 	snd_pcm_ioctl_compat,  		.mmap =			snd_pcm_mmap,  		.fasync =		snd_pcm_fasync, +		.get_unmapped_area =	dummy_get_unmapped_area,  	}  }; diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index 033a024..2c89c04 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -51,12 +51,14 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream)          mult = 1000000000;  	rate = runtime->rate; -	snd_assert(rate != 0, return); +	if (snd_BUG_ON(!rate)) +		return;  	l = gcd(mult, rate);  	mult /= l;  	rate /= l;  	fsize = runtime->period_size; -	snd_assert(fsize != 0, return); +	if (snd_BUG_ON(!fsize)) +		return;  	l = gcd(rate, fsize);  	rate /= l;  	fsize /= l; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index f7ea728..c4995c9 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -418,7 +418,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)  	mutex_lock(&rmidi->open_mutex);  	while (1) {  		subdevice = -1; -		down_read(&card->controls_rwsem); +		read_lock(&card->ctl_files_rwlock);  		list_for_each_entry(kctl, &card->ctl_files, list) {  			if (kctl->pid == current->pid) {  				subdevice = kctl->prefer_rawmidi_subdevice; @@ -426,7 +426,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)  					break;  			}  		} -		up_read(&card->controls_rwsem); +		read_unlock(&card->ctl_files_rwlock);  		err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device,  					      subdevice, fflags, rawmidi_file);  		if (err >= 0) @@ -470,8 +470,8 @@ int snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile)  	struct snd_rawmidi_substream *substream;  	struct snd_rawmidi_runtime *runtime; -	snd_assert(rfile != NULL, return -ENXIO); -	snd_assert(rfile->input != NULL || rfile->output != NULL, return -ENXIO); +	if (snd_BUG_ON(!rfile)) +		return -ENXIO;  	rmidi = rfile->rmidi;  	mutex_lock(&rmidi->open_mutex);  	if (rfile->input != NULL) { @@ -1100,7 +1100,7 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)  		return -EINVAL;  	}  	spin_lock_irqsave(&runtime->lock, flags); -	snd_assert(runtime->avail + count <= runtime->buffer_size, ); +	snd_BUG_ON(runtime->avail + count > runtime->buffer_size);  	runtime->hw_ptr += count;  	runtime->hw_ptr %= runtime->buffer_size;  	runtime->avail += count; @@ -1141,8 +1141,10 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,  	long count1, result;  	struct snd_rawmidi_runtime *runtime = substream->runtime; -	snd_assert(kernelbuf != NULL || userbuf != NULL, return -EINVAL); -	snd_assert(runtime->buffer != NULL, return -EINVAL); +	if (snd_BUG_ON(!kernelbuf && !userbuf)) +		return -EINVAL; +	if (snd_BUG_ON(!runtime->buffer)) +		return -EINVAL;  	result = 0;  	spin_lock_irqsave(&runtime->lock, flags); @@ -1420,9 +1422,10 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,  		.dev_disconnect = snd_rawmidi_dev_disconnect,  	}; -	snd_assert(rrawmidi != NULL, return -EINVAL); -	*rrawmidi = NULL; -	snd_assert(card != NULL, return -ENXIO); +	if (snd_BUG_ON(!card)) +		return -ENXIO; +	if (rrawmidi) +		*rrawmidi = NULL;  	rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL);  	if (rmidi == NULL) {  		snd_printk(KERN_ERR "rawmidi: cannot allocate\n"); @@ -1455,7 +1458,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,  		snd_rawmidi_free(rmidi);  		return err;  	} -	*rrawmidi = rmidi; +	if (rrawmidi) +		*rrawmidi = rmidi;  	return 0;  } @@ -1472,7 +1476,8 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)  static int snd_rawmidi_free(struct snd_rawmidi *rmidi)  { -	snd_assert(rmidi != NULL, return -ENXIO);	 +	if (!rmidi) +		return 0;  	snd_info_free_entry(rmidi->proc_entry);  	rmidi->proc_entry = NULL; diff --git a/sound/core/rtctimer.c b/sound/core/rtctimer.c index 97b30fb..51e64e3 100644 --- a/sound/core/rtctimer.c +++ b/sound/core/rtctimer.c @@ -91,7 +91,8 @@ static int  rtctimer_start(struct snd_timer *timer)  {  	rtc_task_t *rtc = timer->private_data; -	snd_assert(rtc != NULL, return -EINVAL); +	if (snd_BUG_ON(!rtc)) +		return -EINVAL;  	rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);  	rtc_control(rtc, RTC_PIE_ON, 0);  	return 0; @@ -101,7 +102,8 @@ static int  rtctimer_stop(struct snd_timer *timer)  {  	rtc_task_t *rtc = timer->private_data; -	snd_assert(rtc != NULL, return -EINVAL); +	if (snd_BUG_ON(!rtc)) +		return -EINVAL;  	rtc_control(rtc, RTC_PIE_OFF, 0);  	return 0;  } diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 777796e..f25e3cc 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -164,7 +164,8 @@ odev_read(struct file *file, char __user *buf, size_t count, loff_t *offset)  {  	struct seq_oss_devinfo *dp;  	dp = file->private_data; -	snd_assert(dp != NULL, return -EIO); +	if (snd_BUG_ON(!dp)) +		return -ENXIO;  	return snd_seq_oss_read(dp, buf, count);  } @@ -174,7 +175,8 @@ odev_write(struct file *file, const char __user *buf, size_t count, loff_t *offs  {  	struct seq_oss_devinfo *dp;  	dp = file->private_data; -	snd_assert(dp != NULL, return -EIO); +	if (snd_BUG_ON(!dp)) +		return -ENXIO;  	return snd_seq_oss_write(dp, buf, count, file);  } @@ -183,7 +185,8 @@ odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  {  	struct seq_oss_devinfo *dp;  	dp = file->private_data; -	snd_assert(dp != NULL, return -EIO); +	if (snd_BUG_ON(!dp)) +		return -ENXIO;  	return snd_seq_oss_ioctl(dp, cmd, arg);  } @@ -198,7 +201,8 @@ odev_poll(struct file *file, poll_table * wait)  {  	struct seq_oss_devinfo *dp;  	dp = file->private_data; -	snd_assert(dp != NULL, return 0); +	if (snd_BUG_ON(!dp)) +		return -ENXIO;  	return snd_seq_oss_poll(dp, file, wait);  } diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index e024e45..945a27c 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -308,7 +308,8 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)  	struct seq_oss_synth *rec;  	struct seq_oss_synthinfo *info; -	snd_assert(dp->max_synthdev <= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS, return); +	if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS)) +		return;  	for (i = 0; i < dp->max_synthdev; i++) {  		info = &dp->synths[i];  		if (! info->opened) @@ -402,7 +403,8 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)  	struct seq_oss_synth *rec;  	struct seq_oss_synthinfo *info; -	snd_assert(dev >= 0 && dev < dp->max_synthdev, return); +	if (snd_BUG_ON(dev < 0 || dev >= dp->max_synthdev)) +		return;  	info = &dp->synths[dev];  	if (! info->opened)  		return; diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 7a1545d..8ca2be3 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -266,7 +266,8 @@ static int seq_free_client1(struct snd_seq_client *client)  {  	unsigned long flags; -	snd_assert(client != NULL, return -EINVAL); +	if (!client) +		return 0;  	snd_seq_delete_all_ports(client);  	snd_seq_queue_client_leave(client->number);  	spin_lock_irqsave(&clients_lock, flags); @@ -403,7 +404,8 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,  		return -EFAULT;  	/* check client structures are in place */ -	snd_assert(client != NULL, return -ENXIO); +	if (snd_BUG_ON(!client)) +		return -ENXIO;  	if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)  		return -ENXIO; @@ -825,7 +827,8 @@ int snd_seq_dispatch_event(struct snd_seq_event_cell *cell, int atomic, int hop)  	struct snd_seq_client *client;  	int result; -	snd_assert(cell != NULL, return -EINVAL); +	if (snd_BUG_ON(!cell)) +		return -EINVAL;  	client = snd_seq_client_use_ptr(cell->event.source.client);  	if (client == NULL) { @@ -994,7 +997,8 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,  		return -ENXIO;  	/* check client structures are in place */ -	snd_assert(client != NULL, return -ENXIO); +	if (snd_BUG_ON(!client)) +		return -ENXIO;  	if (!client->accept_output || client->pool == NULL)  		return -ENXIO; @@ -1076,7 +1080,8 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait)  	unsigned int mask = 0;  	/* check client structures are in place */ -	snd_assert(client != NULL, return -ENXIO); +	if (snd_BUG_ON(!client)) +		return -ENXIO;  	if ((snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_INPUT) &&  	    client->data.user.fifo) { @@ -2195,7 +2200,8 @@ static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg  {  	struct snd_seq_client *client = file->private_data; -	snd_assert(client != NULL, return -ENXIO); +	if (snd_BUG_ON(!client)) +		return -ENXIO;  	return snd_seq_do_ioctl(client, cmd, (void __user *) arg);  } @@ -2216,7 +2222,8 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,  	struct snd_seq_client *client;  	va_list args; -	snd_assert(! in_interrupt(), return -EBUSY); +	if (snd_BUG_ON(in_interrupt())) +		return -EBUSY;  	if (card && client_index >= SNDRV_SEQ_CLIENTS_PER_CARD)  		return -EINVAL; @@ -2265,7 +2272,8 @@ int snd_seq_delete_kernel_client(int client)  {  	struct snd_seq_client *ptr; -	snd_assert(! in_interrupt(), return -EBUSY); +	if (snd_BUG_ON(in_interrupt())) +		return -EBUSY;  	ptr = clientptr(client);  	if (ptr == NULL) @@ -2288,7 +2296,8 @@ static int kernel_client_enqueue(int client, struct snd_seq_event *ev,  	struct snd_seq_client *cptr;  	int result; -	snd_assert(ev != NULL, return -EINVAL); +	if (snd_BUG_ON(!ev)) +		return -EINVAL;  	if (ev->type == SNDRV_SEQ_EVENT_NONE)  		return 0; /* ignore this */ @@ -2354,7 +2363,8 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,  	struct snd_seq_client *cptr;  	int result; -	snd_assert(ev != NULL, return -EINVAL); +	if (snd_BUG_ON(!ev)) +		return -EINVAL;  	/* fill in client number */  	ev->queue = SNDRV_SEQ_QUEUE_DIRECT; diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c index 9628c06..38693f4 100644 --- a/sound/core/seq/seq_compat.c +++ b/sound/core/seq/seq_compat.c @@ -92,7 +92,8 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l  	struct snd_seq_client *client = file->private_data;  	void __user *argp = compat_ptr(arg); -	snd_assert(client != NULL, return -ENXIO); +	if (snd_BUG_ON(!client)) +		return -ENXIO;  	switch (cmd) {  	case SNDRV_SEQ_IOCTL_PVERSION: diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 05410e5..1f99767 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -187,7 +187,8 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,  	if (result)  		*result = NULL; -	snd_assert(id != NULL, return -EINVAL); +	if (snd_BUG_ON(!id)) +		return -EINVAL;  	ops = find_driver(id, 1);  	if (ops == NULL) @@ -232,7 +233,8 @@ static int snd_seq_device_free(struct snd_seq_device *dev)  {  	struct ops_list *ops; -	snd_assert(dev != NULL, return -EINVAL); +	if (snd_BUG_ON(!dev)) +		return -EINVAL;  	ops = find_driver(dev->id, 0);  	if (ops == NULL) diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 3a94ed0..0d75afa 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -65,9 +65,11 @@ void snd_seq_fifo_delete(struct snd_seq_fifo **fifo)  {  	struct snd_seq_fifo *f; -	snd_assert(fifo != NULL, return); +	if (snd_BUG_ON(!fifo)) +		return;  	f = *fifo; -	snd_assert(f != NULL, return); +	if (snd_BUG_ON(!f)) +		return;  	*fifo = NULL;  	snd_seq_fifo_clear(f); @@ -116,7 +118,8 @@ int snd_seq_fifo_event_in(struct snd_seq_fifo *f,  	unsigned long flags;  	int err; -	snd_assert(f != NULL, return -EINVAL); +	if (snd_BUG_ON(!f)) +		return -EINVAL;  	snd_use_lock_use(&f->use_lock);  	err = snd_seq_event_dup(f->pool, event, &cell, 1, NULL); /* always non-blocking */ @@ -174,7 +177,8 @@ int snd_seq_fifo_cell_out(struct snd_seq_fifo *f,  	unsigned long flags;  	wait_queue_t wait; -	snd_assert(f != NULL, return -EINVAL); +	if (snd_BUG_ON(!f)) +		return -EINVAL;  	*cellp = NULL;  	init_waitqueue_entry(&wait, current); @@ -233,7 +237,8 @@ int snd_seq_fifo_resize(struct snd_seq_fifo *f, int poolsize)  	struct snd_seq_pool *newpool, *oldpool;  	struct snd_seq_event_cell *cell, *next, *oldhead; -	snd_assert(f != NULL && f->pool != NULL, return -EINVAL); +	if (snd_BUG_ON(!f || !f->pool)) +		return -EINVAL;  	/* allocate new pool */  	newpool = snd_seq_pool_new(poolsize); diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 0cf6ac4..7fb5543 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -187,9 +187,11 @@ void snd_seq_cell_free(struct snd_seq_event_cell * cell)  	unsigned long flags;  	struct snd_seq_pool *pool; -	snd_assert(cell != NULL, return); +	if (snd_BUG_ON(!cell)) +		return;  	pool = cell->pool; -	snd_assert(pool != NULL, return); +	if (snd_BUG_ON(!pool)) +		return;  	spin_lock_irqsave(&pool->lock, flags);  	free_cell(pool, cell); @@ -378,7 +380,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)  	struct snd_seq_event_cell *cellptr;  	unsigned long flags; -	snd_assert(pool != NULL, return -EINVAL); +	if (snd_BUG_ON(!pool)) +		return -EINVAL;  	if (pool->ptr)			/* should be atomic? */  		return 0; @@ -414,7 +417,8 @@ int snd_seq_pool_done(struct snd_seq_pool *pool)  	struct snd_seq_event_cell *ptr;  	int max_count = 5 * HZ; -	snd_assert(pool != NULL, return -EINVAL); +	if (snd_BUG_ON(!pool)) +		return -EINVAL;  	/* wait for closing all threads */  	spin_lock_irqsave(&pool->lock, flags); diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 99b3536..4d26146 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -116,7 +116,8 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i  	struct snd_rawmidi_runtime *runtime;  	int tmp; -	snd_assert(substream != NULL || buf != NULL, return -EINVAL); +	if (snd_BUG_ON(!substream || !buf)) +		return -EINVAL;  	runtime = substream->runtime;  	if ((tmp = runtime->avail) < count) {  		snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp); @@ -135,7 +136,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,  	struct snd_rawmidi_substream *substream;  	int len; -	snd_assert(msynth != NULL, return -EINVAL); +	if (snd_BUG_ON(!msynth)) +		return -EINVAL;  	substream = msynth->output_rfile.output;  	if (substream == NULL)  		return -ENODEV; @@ -210,7 +212,8 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri  	int err;  	struct seq_midisynth *msynth = private_data; -	snd_assert(msynth->input_rfile.input != NULL, return -EINVAL); +	if (snd_BUG_ON(!msynth->input_rfile.input)) +		return -EINVAL;  	err = snd_rawmidi_kernel_release(&msynth->input_rfile);  	return err;  } @@ -247,7 +250,8 @@ static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *in  	struct seq_midisynth *msynth = private_data;  	unsigned char buf = 0xff; /* MIDI reset */ -	snd_assert(msynth->output_rfile.output != NULL, return -EINVAL); +	if (snd_BUG_ON(!msynth->output_rfile.output)) +		return -EINVAL;  	/* sending single MIDI reset message to shut the device up */  	snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);  	snd_rawmidi_drain_output(msynth->output_rfile.output); @@ -285,7 +289,8 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev)  	int device = dev->device;  	unsigned int input_count = 0, output_count = 0; -	snd_assert(card != NULL && device >= 0 && device < SNDRV_RAWMIDI_DEVICES, return -EINVAL); +	if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES)) +		return -EINVAL;  	info = kmalloc(sizeof(*info), GFP_KERNEL);  	if (! info)  		return -ENOMEM; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 1c32a53..3bf7d73 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -130,7 +130,8 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,  	int num = -1;  	/* sanity check */ -	snd_assert(client, return NULL); +	if (snd_BUG_ON(!client)) +		return NULL;  	if (client->num_ports >= SNDRV_SEQ_MAX_PORTS - 1) {  		snd_printk(KERN_WARNING "too many ports for client %d\n", client->number); @@ -268,8 +269,8 @@ static int port_delete(struct snd_seq_client *client,  	if (port->private_free)  		port->private_free(port->private_data); -	snd_assert(port->c_src.count == 0,); -	snd_assert(port->c_dest.count == 0,); +	snd_BUG_ON(port->c_src.count != 0); +	snd_BUG_ON(port->c_dest.count != 0);  	kfree(port);  	return 0; @@ -336,7 +337,8 @@ int snd_seq_delete_all_ports(struct snd_seq_client *client)  int snd_seq_set_port_info(struct snd_seq_client_port * port,  			  struct snd_seq_port_info * info)  { -	snd_assert(port && info, return -EINVAL); +	if (snd_BUG_ON(!port || !info)) +		return -EINVAL;  	/* set port name */  	if (info->name[0]) @@ -365,7 +367,8 @@ int snd_seq_set_port_info(struct snd_seq_client_port * port,  int snd_seq_get_port_info(struct snd_seq_client_port * port,  			  struct snd_seq_port_info * info)  { -	snd_assert(port && info, return -EINVAL); +	if (snd_BUG_ON(!port || !info)) +		return -EINVAL;  	/* get port name */  	strlcpy(info->name, port->name, sizeof(info->name)); diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c index 85969db..0101a8b 100644 --- a/sound/core/seq/seq_prioq.c +++ b/sound/core/seq/seq_prioq.c @@ -153,8 +153,8 @@ int snd_seq_prioq_cell_in(struct snd_seq_prioq * f,  	int count;  	int prior; -	snd_assert(f, return -EINVAL); -	snd_assert(cell, return -EINVAL); +	if (snd_BUG_ON(!f || !cell)) +		return -EINVAL;  	/* check flags */  	prior = (cell->event.flags & SNDRV_SEQ_PRIORITY_MASK); diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 4a48c6e..e7a8e9e 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -315,7 +315,8 @@ int snd_seq_enqueue_event(struct snd_seq_event_cell *cell, int atomic, int hop)  	int dest, err;  	struct snd_seq_queue *q; -	snd_assert(cell != NULL, return -EINVAL); +	if (snd_BUG_ON(!cell)) +		return -EINVAL;  	dest = cell->event.queue;	/* destination queue */  	q = queueptr(dest);  	if (q == NULL) @@ -734,7 +735,8 @@ int snd_seq_control_queue(struct snd_seq_event *ev, int atomic, int hop)  {  	struct snd_seq_queue *q; -	snd_assert(ev != NULL, return -EINVAL); +	if (snd_BUG_ON(!ev)) +		return -EINVAL;  	q = queueptr(ev->data.queue.queue);  	if (q == NULL) diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index d8fcd62..f745c31 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -173,7 +173,8 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)  {  	unsigned long flags; -	snd_assert(tmr, return -EINVAL); +	if (snd_BUG_ON(!tmr)) +		return -EINVAL;  	if (tempo <= 0)  		return -EINVAL;  	spin_lock_irqsave(&tmr->lock, flags); @@ -190,7 +191,8 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)  {  	unsigned long flags; -	snd_assert(tmr, return -EINVAL); +	if (snd_BUG_ON(!tmr)) +		return -EINVAL;  	if (ppq <= 0)  		return -EINVAL;  	spin_lock_irqsave(&tmr->lock, flags); @@ -214,7 +216,8 @@ int snd_seq_timer_set_position_tick(struct snd_seq_timer *tmr,  {  	unsigned long flags; -	snd_assert(tmr, return -EINVAL); +	if (snd_BUG_ON(!tmr)) +		return -EINVAL;  	spin_lock_irqsave(&tmr->lock, flags);  	tmr->tick.cur_tick = position; @@ -229,7 +232,8 @@ int snd_seq_timer_set_position_time(struct snd_seq_timer *tmr,  {  	unsigned long flags; -	snd_assert(tmr, return -EINVAL); +	if (snd_BUG_ON(!tmr)) +		return -EINVAL;  	snd_seq_sanity_real_time(&position);  	spin_lock_irqsave(&tmr->lock, flags); @@ -244,7 +248,8 @@ int snd_seq_timer_set_skew(struct snd_seq_timer *tmr, unsigned int skew,  {  	unsigned long flags; -	snd_assert(tmr, return -EINVAL); +	if (snd_BUG_ON(!tmr)) +		return -EINVAL;  	/* FIXME */  	if (base != SKEW_BASE) { @@ -265,7 +270,8 @@ int snd_seq_timer_open(struct snd_seq_queue *q)  	int err;  	tmr = q->timer; -	snd_assert(tmr != NULL, return -EINVAL); +	if (snd_BUG_ON(!tmr)) +		return -EINVAL;  	if (tmr->timeri)  		return -EBUSY;  	sprintf(str, "sequencer queue %i", q->queue); @@ -302,7 +308,8 @@ int snd_seq_timer_close(struct snd_seq_queue *q)  	struct snd_seq_timer *tmr;  	tmr = q->timer; -	snd_assert(tmr != NULL, return -EINVAL); +	if (snd_BUG_ON(!tmr)) +		return -EINVAL;  	if (tmr->timeri) {  		snd_timer_stop(tmr->timeri);  		snd_timer_close(tmr->timeri); @@ -328,7 +335,8 @@ static int initialize_timer(struct snd_seq_timer *tmr)  	unsigned long freq;  	t = tmr->timeri->timer; -	snd_assert(t, return -EINVAL); +	if (snd_BUG_ON(!t)) +		return -EINVAL;  	freq = tmr->preferred_resolution;  	if (!freq) diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index cefd228..d4564ed 100644 --- a/sound/core/sgbuf.c +++ b/sound/core/sgbuf.c @@ -41,9 +41,11 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)  	tmpb.dev.type = SNDRV_DMA_TYPE_DEV;  	tmpb.dev.dev = sgbuf->dev;  	for (i = 0; i < sgbuf->pages; i++) { +		if (!(sgbuf->table[i].addr & ~PAGE_MASK)) +			continue; /* continuous pages */  		tmpb.area = sgbuf->table[i].buf; -		tmpb.addr = sgbuf->table[i].addr; -		tmpb.bytes = PAGE_SIZE; +		tmpb.addr = sgbuf->table[i].addr & PAGE_MASK; +		tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;  		snd_dma_free_pages(&tmpb);  	}  	if (dmab->area) @@ -58,13 +60,17 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)  	return 0;  } +#define MAX_ALLOC_PAGES		32 +  void *snd_malloc_sgbuf_pages(struct device *device,  			     size_t size, struct snd_dma_buffer *dmab,  			     size_t *res_size)  {  	struct snd_sg_buf *sgbuf; -	unsigned int i, pages; +	unsigned int i, pages, chunk, maxpages;  	struct snd_dma_buffer tmpb; +	struct snd_sg_page *table; +	struct page **pgtable;  	dmab->area = NULL;  	dmab->addr = 0; @@ -74,31 +80,55 @@ void *snd_malloc_sgbuf_pages(struct device *device,  	sgbuf->dev = device;  	pages = snd_sgbuf_aligned_pages(size);  	sgbuf->tblsize = sgbuf_align_table(pages); -	sgbuf->table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->table), GFP_KERNEL); -	if (! sgbuf->table) +	table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL); +	if (!table)  		goto _failed; -	sgbuf->page_table = kcalloc(sgbuf->tblsize, sizeof(*sgbuf->page_table), GFP_KERNEL); -	if (! sgbuf->page_table) +	sgbuf->table = table; +	pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL); +	if (!pgtable)  		goto _failed; +	sgbuf->page_table = pgtable; -	/* allocate each page */ -	for (i = 0; i < pages; i++) { -		if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, device, PAGE_SIZE, &tmpb) < 0) { -			if (res_size == NULL) +	/* allocate pages */ +	maxpages = MAX_ALLOC_PAGES; +	while (pages > 0) { +		chunk = pages; +		/* don't be too eager to take a huge chunk */ +		if (chunk > maxpages) +			chunk = maxpages; +		chunk <<= PAGE_SHIFT; +		if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device, +						 chunk, &tmpb) < 0) { +			if (!sgbuf->pages) +				return NULL; +			if (!res_size)  				goto _failed; -			*res_size = size = sgbuf->pages * PAGE_SIZE; +			size = sgbuf->pages * PAGE_SIZE;  			break;  		} -		sgbuf->table[i].buf = tmpb.area; -		sgbuf->table[i].addr = tmpb.addr; -		sgbuf->page_table[i] = virt_to_page(tmpb.area); -		sgbuf->pages++; +		chunk = tmpb.bytes >> PAGE_SHIFT; +		for (i = 0; i < chunk; i++) { +			table->buf = tmpb.area; +			table->addr = tmpb.addr; +			if (!i) +				table->addr |= chunk; /* mark head */ +			table++; +			*pgtable++ = virt_to_page(tmpb.area); +			tmpb.area += PAGE_SIZE; +			tmpb.addr += PAGE_SIZE; +		} +		sgbuf->pages += chunk; +		pages -= chunk; +		if (chunk < maxpages) +			maxpages = chunk;  	}  	sgbuf->size = size;  	dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL);  	if (! dmab->area)  		goto _failed; +	if (res_size) +		*res_size = sgbuf->size;  	return dmab->area;   _failed: diff --git a/sound/core/sound.c b/sound/core/sound.c index 1003ae3..c0685e2 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -34,8 +34,6 @@  #include <linux/kmod.h>  #include <linux/mutex.h> -#define SNDRV_OS_MINORS 256 -  static int major = CONFIG_SND_MAJOR;  int snd_major;  EXPORT_SYMBOL(snd_major); @@ -208,20 +206,23 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev)  		minor = type;  		break;  	case SNDRV_DEVICE_TYPE_CONTROL: -		snd_assert(card != NULL, return -EINVAL); +		if (snd_BUG_ON(!card)) +			return -EINVAL;  		minor = SNDRV_MINOR(card->number, type);  		break;  	case SNDRV_DEVICE_TYPE_HWDEP:  	case SNDRV_DEVICE_TYPE_RAWMIDI:  	case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:  	case SNDRV_DEVICE_TYPE_PCM_CAPTURE: -		snd_assert(card != NULL, return -EINVAL); +		if (snd_BUG_ON(!card)) +			return -EINVAL;  		minor = SNDRV_MINOR(card->number, type + dev);  		break;  	default:  		return -EINVAL;  	} -	snd_assert(minor >= 0 && minor < SNDRV_OS_MINORS, return -EINVAL); +	if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) +		return -EINVAL;  	return minor;  }  #endif @@ -249,7 +250,8 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,  	int minor;  	struct snd_minor *preg; -	snd_assert(name, return -EINVAL); +	if (snd_BUG_ON(!name)) +		return -EINVAL;  	preg = kmalloc(sizeof *preg, GFP_KERNEL);  	if (preg == NULL)  		return -ENOMEM; diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 7be5154..7fe1226 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -64,7 +64,8 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)  	switch (type) {  	case SNDRV_OSS_DEVICE_TYPE_MIXER: -		snd_assert(card != NULL && dev <= 1, return -EINVAL); +		if (snd_BUG_ON(!card || dev < 0 || dev > 1)) +			return -EINVAL;  		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIXER1 : SNDRV_MINOR_OSS_MIXER));  		break;  	case SNDRV_OSS_DEVICE_TYPE_SEQUENCER: @@ -74,11 +75,13 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)  		minor = SNDRV_MINOR_OSS_MUSIC;  		break;  	case SNDRV_OSS_DEVICE_TYPE_PCM: -		snd_assert(card != NULL && dev <= 1, return -EINVAL); +		if (snd_BUG_ON(!card || dev < 0 || dev > 1)) +			return -EINVAL;  		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_PCM1 : SNDRV_MINOR_OSS_PCM));  		break;  	case SNDRV_OSS_DEVICE_TYPE_MIDI: -		snd_assert(card != NULL && dev <= 1, return -EINVAL); +		if (snd_BUG_ON(!card || dev < 0 || dev > 1)) +			return -EINVAL;  		minor = SNDRV_MINOR_OSS(card->number, (dev ? SNDRV_MINOR_OSS_MIDI1 : SNDRV_MINOR_OSS_MIDI));  		break;  	case SNDRV_OSS_DEVICE_TYPE_DMFM: @@ -90,7 +93,8 @@ static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev)  	default:  		return -EINVAL;  	} -	snd_assert(minor >= 0 && minor < SNDRV_OSS_MINORS, return -EINVAL); +	if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OSS_MINORS)) +		return -EINVAL;  	return minor;  } diff --git a/sound/core/timer.c b/sound/core/timer.c index 0af337e..e582fac 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -306,7 +306,8 @@ int snd_timer_close(struct snd_timer_instance *timeri)  	struct snd_timer *timer = NULL;  	struct snd_timer_instance *slave, *tmp; -	snd_assert(timeri != NULL, return -ENXIO); +	if (snd_BUG_ON(!timeri)) +		return -ENXIO;  	/* force to stop the timer */  	snd_timer_stop(timeri); @@ -385,8 +386,9 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)  		do_posix_clock_monotonic_gettime(&tstamp);  	else  		getnstimeofday(&tstamp); -	snd_assert(event >= SNDRV_TIMER_EVENT_START && -		   event <= SNDRV_TIMER_EVENT_PAUSE, return); +	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || +		       event > SNDRV_TIMER_EVENT_PAUSE)) +		return;  	if (event == SNDRV_TIMER_EVENT_START ||  	    event == SNDRV_TIMER_EVENT_CONTINUE)  		resolution = snd_timer_resolution(ti); @@ -474,7 +476,8 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,  	struct snd_timer *timer;  	unsigned long flags; -	snd_assert(timeri != NULL, return -ENXIO); +	if (snd_BUG_ON(!timeri)) +		return -ENXIO;  	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {  		if (!keep_flag) { @@ -758,9 +761,10 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,  		.dev_disconnect = snd_timer_dev_disconnect,  	}; -	snd_assert(tid != NULL, return -EINVAL); -	snd_assert(rtimer != NULL, return -EINVAL); -	*rtimer = NULL; +	if (snd_BUG_ON(!tid)) +		return -EINVAL; +	if (rtimer) +		*rtimer = NULL;  	timer = kzalloc(sizeof(*timer), GFP_KERNEL);  	if (timer == NULL) {  		snd_printk(KERN_ERR "timer: cannot allocate\n"); @@ -788,13 +792,15 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,  			return err;  		}  	} -	*rtimer = timer; +	if (rtimer) +		*rtimer = timer;  	return 0;  }  static int snd_timer_free(struct snd_timer *timer)  { -	snd_assert(timer != NULL, return -ENXIO); +	if (!timer) +		return 0;  	mutex_lock(®ister_mutex);  	if (! list_empty(&timer->open_list_head)) { @@ -827,8 +833,8 @@ static int snd_timer_dev_register(struct snd_device *dev)  	struct snd_timer *timer = dev->device_data;  	struct snd_timer *timer1; -	snd_assert(timer != NULL && timer->hw.start != NULL && -		   timer->hw.stop != NULL, return -ENXIO); +	if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) +		return -ENXIO;  	if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&  	    !timer->hw.resolution && timer->hw.c_resolution == NULL)  	    	return -EINVAL; @@ -879,8 +885,9 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam  	if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))  		return; -	snd_assert(event >= SNDRV_TIMER_EVENT_MSTART && -		   event <= SNDRV_TIMER_EVENT_MRESUME, return); +	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || +		       event > SNDRV_TIMER_EVENT_MRESUME)) +		return;  	spin_lock_irqsave(&timer->lock, flags);  	if (event == SNDRV_TIMER_EVENT_MSTART ||  	    event == SNDRV_TIMER_EVENT_MCONTINUE || diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c index 5512f53..e05802a 100644 --- a/sound/core/timer_compat.c +++ b/sound/core/timer_compat.c @@ -40,9 +40,11 @@ static int snd_timer_user_info_compat(struct file *file,  	struct snd_timer *t;  	tu = file->private_data; -	snd_assert(tu->timeri != NULL, return -ENXIO); +	if (snd_BUG_ON(!tu->timeri)) +		return -ENXIO;  	t = tu->timeri->timer; -	snd_assert(t != NULL, return -ENXIO); +	if (snd_BUG_ON(!t)) +		return -ENXIO;  	memset(&info, 0, sizeof(info));  	info.card = t->card ? t->card->number : -1;  	if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) @@ -71,7 +73,8 @@ static int snd_timer_user_status_compat(struct file *file,  	struct snd_timer_status status;  	tu = file->private_data; -	snd_assert(tu->timeri != NULL, return -ENXIO); +	if (snd_BUG_ON(!tu->timeri)) +		return -ENXIO;  	memset(&status, 0, sizeof(status));  	status.tstamp = tu->tstamp;  	status.resolution = snd_timer_resolution(tu->timeri); diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 4e4c69e..e5e749f 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -47,9 +47,11 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");  static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)  {  	int err; -	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) +	err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); +	if (err < 0)  		return err; -	if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) +	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX); +	if (err) < 0)  		return err;  	return 0;  } @@ -354,6 +356,7 @@ static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)  	if ((dpcm = new_pcm_stream(substream)) == NULL)  		return -ENOMEM;  	runtime->private_data = dpcm; +	/* makes the infrastructure responsible for freeing dpcm */  	runtime->private_free = snd_card_dummy_runtime_free;  	runtime->hw = snd_card_dummy_playback;  	if (substream->pcm->device & 1) { @@ -362,10 +365,9 @@ static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)  	}  	if (substream->pcm->device & 2)  		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); -	if ((err = add_playback_constraints(runtime)) < 0) { -		kfree(dpcm); +	err = add_playback_constraints(runtime); +	if (err < 0)  		return err; -	}  	return 0;  } @@ -379,6 +381,7 @@ static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)  	if ((dpcm = new_pcm_stream(substream)) == NULL)  		return -ENOMEM;  	runtime->private_data = dpcm; +	/* makes the infrastructure responsible for freeing dpcm */  	runtime->private_free = snd_card_dummy_runtime_free;  	runtime->hw = snd_card_dummy_capture;  	if (substream->pcm->device == 1) { @@ -387,10 +390,9 @@ static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)  	}  	if (substream->pcm->device & 2)  		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); -	if ((err = add_capture_constraints(runtime)) < 0) { -		kfree(dpcm); +	err = add_capture_constraints(runtime); +	if (err < 0)  		return err; -	}  	return 0;  } @@ -433,8 +435,9 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,  	struct snd_pcm *pcm;  	int err; -	if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device, -			       substreams, substreams, &pcm)) < 0) +	err = snd_pcm_new(dummy->card, "Dummy PCM", device, +			       substreams, substreams, &pcm); +	if (err < 0)  		return err;  	dummy->pcm = pcm;  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); @@ -565,12 +568,14 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)  	unsigned int idx;  	int err; -	snd_assert(dummy != NULL, return -EINVAL); +	if (snd_BUG_ON(!dummy)) +		return -EINVAL;  	spin_lock_init(&dummy->mixer_lock);  	strcpy(card->mixername, "Dummy Mixer");  	for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) { -		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0) +		err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy)); +		if (err < 0)  			return err;  	}  	return 0; @@ -594,10 +599,12 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)  			pcm_substreams[dev] = 1;  		if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)  			pcm_substreams[dev] = MAX_PCM_SUBSTREAMS; -		if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0) +		err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev]); +		if (err < 0)  			goto __nodev;  	} -	if ((err = snd_card_dummy_new_mixer(dummy)) < 0) +	err = snd_card_dummy_new_mixer(dummy); +	if (err < 0)  		goto __nodev;  	strcpy(card->driver, "Dummy");  	strcpy(card->shortname, "Dummy"); @@ -605,7 +612,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)  	snd_card_set_dev(card, &devptr->dev); -	if ((err = snd_card_register(card)) == 0) { +	err = snd_card_register(card); +	if (err == 0) {  		platform_set_drvdata(devptr, card);  		return 0;  	} @@ -668,7 +676,8 @@ static int __init alsa_card_dummy_init(void)  {  	int i, cards, err; -	if ((err = platform_driver_register(&snd_dummy_driver)) < 0) +	err = platform_driver_register(&snd_dummy_driver); +	if (err < 0)  		return err;  	cards = 0; diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index b5e1a71..5b89c08 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -715,6 +715,10 @@ static int __devinit snd_mtpav_probe(struct platform_device *dev)  	card->private_free = snd_mtpav_free; +	err = snd_mtpav_get_RAWMIDI(mtp_card); +	if (err < 0) +		goto __error; +  	err = snd_mtpav_get_ISA(mtp_card);  	if (err < 0)  		goto __error; @@ -724,10 +728,6 @@ static int __devinit snd_mtpav_probe(struct platform_device *dev)  	snprintf(card->longname, sizeof(card->longname),  		 "MTPAV on parallel port at 0x%lx", port); -	err = snd_mtpav_get_RAWMIDI(mtp_card); -	if (err < 0) -		goto __error; -  	snd_mtpav_portscan(mtp_card);  	snd_card_set_dev(card, &dev->dev); diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index ebe4359..7805823 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -139,7 +139,8 @@ static int snd_opl3_detect(struct snd_opl3 * opl3)  		 * If we had an OPL4 chip, opl3->hardware would have been set  		 * by the OPL4 driver; so we can assume OPL3 here.  		 */ -		snd_assert(opl3->r_port != 0, return -ENODEV); +		if (snd_BUG_ON(!opl3->r_port)) +			return -ENODEV;  		opl3->hardware = OPL3_HW_OPL3;  	}  	return 0; @@ -324,7 +325,8 @@ EXPORT_SYMBOL(snd_opl3_interrupt);  static int snd_opl3_free(struct snd_opl3 *opl3)  { -	snd_assert(opl3 != NULL, return -ENXIO); +	if (snd_BUG_ON(!opl3)) +		return -ENXIO;  	if (opl3->private_free)  		opl3->private_free(opl3);  	snd_opl3_clear_patches(opl3); diff --git a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c index cebcb8b..16feafa 100644 --- a/sound/drivers/opl3/opl3_midi.c +++ b/sound/drivers/opl3/opl3_midi.c @@ -617,7 +617,8 @@ static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice)  	struct snd_opl3_voice *vp, *vp2; -	snd_assert(voice < MAX_OPL3_VOICES, return); +	if (snd_BUG_ON(voice >= MAX_OPL3_VOICES)) +		return;  	vp = &opl3->voices[voice];  	if (voice < MAX_OPL2_VOICES) { @@ -737,7 +738,8 @@ static void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice)  	struct snd_opl3_voice *vp; -	snd_assert(voice < MAX_OPL3_VOICES, return); +	if (snd_BUG_ON(voice >= MAX_OPL3_VOICES)) +		return;  	vp = &opl3->voices[voice];  	if (vp->chan == NULL) diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index 239347f..9a2271d 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -162,7 +162,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)  	struct snd_opl3 *opl3 = closure;  	int err; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	if ((err = snd_opl3_synth_setup(opl3)) < 0)  		return err; @@ -184,7 +185,8 @@ static int snd_opl3_close_seq_oss(struct snd_seq_oss_arg *arg)  {  	struct snd_opl3 *opl3; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	opl3 = arg->private_data;  	snd_opl3_synth_cleanup(opl3); @@ -206,7 +208,8 @@ static int snd_opl3_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,  	char name[32];  	int err, type; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	opl3 = arg->private_data;  	if (format == FM_PATCH) @@ -246,7 +249,8 @@ static int snd_opl3_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd,  {  	struct snd_opl3 *opl3; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	opl3 = arg->private_data;  	switch (cmd) {  		case SNDCTL_FM_LOAD_INSTR: @@ -271,7 +275,8 @@ static int snd_opl3_reset_seq_oss(struct snd_seq_oss_arg *arg)  {  	struct snd_opl3 *opl3; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	opl3 = arg->private_data;  	return 0; diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c index fb64c89..962bb9c 100644 --- a/sound/drivers/opl3/opl3_synth.c +++ b/sound/drivers/opl3/opl3_synth.c @@ -92,7 +92,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file,  	struct snd_opl3 *opl3 = hw->private_data;  	void __user *argp = (void __user *)arg; -	snd_assert(opl3 != NULL, return -EINVAL); +	if (snd_BUG_ON(!opl3)) +		return -EINVAL;  	switch (cmd) {  		/* get information */ diff --git a/sound/drivers/opl4/opl4_synth.c b/sound/drivers/opl4/opl4_synth.c index 74f6e53..49b9e24 100644 --- a/sound/drivers/opl4/opl4_synth.c +++ b/sound/drivers/opl4/opl4_synth.c @@ -467,7 +467,7 @@ static struct opl4_voice *snd_opl4_get_voice(struct snd_opl4 *opl4)  	if (!list_empty(&opl4->off_voices))  		return list_entry(opl4->off_voices.next, struct opl4_voice, list);  	/* then get the oldest key-on voice */ -	snd_assert(!list_empty(&opl4->on_voices), ); +	snd_BUG_ON(list_empty(&opl4->on_voices));  	return list_entry(opl4->on_voices.next, struct opl4_voice, list);  } diff --git a/sound/drivers/vx/vx_cmd.c b/sound/drivers/vx/vx_cmd.c index 9529e3b..23f4857 100644 --- a/sound/drivers/vx/vx_cmd.c +++ b/sound/drivers/vx/vx_cmd.c @@ -99,7 +99,8 @@ static struct vx_cmd_info vx_dsp_cmds[] = {   */  void vx_init_rmh(struct vx_rmh *rmh, unsigned int cmd)  { -	snd_assert(cmd < CMD_LAST_INDEX, return); +	if (snd_BUG_ON(cmd >= CMD_LAST_INDEX)) +		return;  	rmh->LgCmd = vx_dsp_cmds[cmd].length;  	rmh->LgStat = vx_dsp_cmds[cmd].st_length;  	rmh->DspStat = vx_dsp_cmds[cmd].st_type; diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index 585af2e..473b07f 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -205,7 +205,8 @@ static int vx_read_status(struct vx_core *chip, struct vx_rmh *rmh)  	if (size < 1)  		return 0; -	snd_assert(size <= SIZE_MAX_STATUS, return -EINVAL); +	if (snd_BUG_ON(size > SIZE_MAX_STATUS)) +		return -EINVAL;  	for (i = 1; i <= size; i++) {  		/* trigger an irq MESS_WRITE_NEXT */ @@ -425,13 +426,16 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)  	int no_fillup = vx_has_new_dsp(chip);  	/* check the length of boot image */ -	snd_assert(boot->size > 0, return -EINVAL); -	snd_assert(boot->size % 3 == 0, return -EINVAL); +	if (boot->size <= 0) +		return -EINVAL; +	if (boot->size % 3) +		return -EINVAL;  #if 0  	{  		/* more strict check */  		unsigned int c = ((u32)boot->data[0] << 16) | ((u32)boot->data[1] << 8) | boot->data[2]; -		snd_assert(boot->size == (c + 2) * 3, return -EINVAL); +		if (boot->size != (c + 2) * 3) +			return -EINVAL;  	}  #endif @@ -554,7 +558,8 @@ EXPORT_SYMBOL(snd_vx_irq_handler);   */  static void vx_reset_board(struct vx_core *chip, int cold_reset)  { -	snd_assert(chip->ops->reset_board, return); +	if (snd_BUG_ON(!chip->ops->reset_board)) +		return;  	/* current source, later sync'ed with target */  	chip->audio_source = VX_AUDIO_SRC_LINE; @@ -673,7 +678,8 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)  	unsigned int csum = 0;  	const unsigned char *image, *cptr; -	snd_assert(dsp->size % 3 == 0, return -EINVAL); +	if (dsp->size % 3) +		return -EINVAL;  	vx_toggle_dac_mute(chip, 1); @@ -775,7 +781,8 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,  {  	struct vx_core *chip; -	snd_assert(card && hw && ops, return NULL); +	if (snd_BUG_ON(!card || !hw || !ops)) +		return NULL;  	chip = kzalloc(sizeof(*chip) + extra_size, GFP_KERNEL);  	if (! chip) { diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c index efd22e9..8d6362e 100644 --- a/sound/drivers/vx/vx_hwdep.c +++ b/sound/drivers/vx/vx_hwdep.c @@ -141,7 +141,8 @@ static int vx_hwdep_dsp_status(struct snd_hwdep *hw,  	};  	struct vx_core *vx = hw->private_data; -	snd_assert(type_ids[vx->type], return -EINVAL); +	if (snd_BUG_ON(!type_ids[vx->type])) +		return -EINVAL;  	strcpy(info->id, type_ids[vx->type]);  	if (vx_is_pcmcia(vx))  		info->num_dsps = 4; @@ -168,7 +169,8 @@ static int vx_hwdep_dsp_load(struct snd_hwdep *hw,  	int index, err;  	struct firmware *fw; -	snd_assert(vx->ops->load_dsp, return -ENXIO); +	if (snd_BUG_ON(!vx->ops->load_dsp)) +		return -ENXIO;  	fw = kmalloc(sizeof(*fw), GFP_KERNEL);  	if (! fw) { diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index 5a34732..c71b8d1 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -34,7 +34,8 @@ static void vx_write_codec_reg(struct vx_core *chip, int codec, unsigned int dat  {  	unsigned long flags; -	snd_assert(chip->ops->write_codec, return); +	if (snd_BUG_ON(!chip->ops->write_codec)) +		return;  	if (chip->chip_status & VX_STAT_IS_STALE)  		return; diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index fdbf865..27de574 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -587,7 +587,8 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs)  		return -EBUSY;  	audio = subs->pcm->device * 2; -	snd_assert(audio < chip->audio_outs, return -EINVAL); +	if (snd_BUG_ON(audio >= chip->audio_outs)) +		return -EINVAL;  	/* playback pipe may have been already allocated for monitoring */  	pipe = chip->playback_pipes[audio]; @@ -996,7 +997,8 @@ static int vx_pcm_capture_open(struct snd_pcm_substream *subs)  		return -EBUSY;  	audio = subs->pcm->device * 2; -	snd_assert(audio < chip->audio_ins, return -EINVAL); +	if (snd_BUG_ON(audio >= chip->audio_ins)) +		return -EINVAL;  	err = vx_alloc_pipe(chip, 1, audio, 2, &pipe);  	if (err < 0)  		return err; @@ -1214,7 +1216,8 @@ void vx_pcm_update_intr(struct vx_core *chip, unsigned int events)  			}  			if (capture)  				continue; -			snd_assert(p >= 0 && (unsigned int)p < chip->audio_outs,); +			if (snd_BUG_ON(p < 0 || p >= chip->audio_outs)) +				continue;  			pipe = chip->playback_pipes[p];  			if (pipe && pipe->substream) {  				vx_pcm_playback_update(chip, pipe->substream, pipe); diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c index fb8932a..0e1ba9b 100644 --- a/sound/drivers/vx/vx_uer.c +++ b/sound/drivers/vx/vx_uer.c @@ -163,13 +163,15 @@ static int vx_calc_clock_from_freq(struct vx_core *chip, int freq)  {  	int hexfreq; -	snd_assert(freq > 0, return 0); +	if (snd_BUG_ON(freq <= 0)) +		return 0;  	hexfreq = (28224000 * 10) / freq;  	hexfreq = (hexfreq + 5) / 10;  	/* max freq = 55125 Hz */ -	snd_assert(hexfreq > 0x00000200, return 0); +	if (snd_BUG_ON(hexfreq <= 0x00000200)) +		return 0;  	if (hexfreq <= 0x03ff)  		return hexfreq - 0x00000201; diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c index 9c3d361..020a5d5 100644 --- a/sound/i2c/cs8427.c +++ b/sound/i2c/cs8427.c @@ -314,7 +314,8 @@ static void snd_cs8427_reset(struct snd_i2c_device *cs8427)  	unsigned long end_time;  	int data, aes3input = 0; -	snd_assert(cs8427, return); +	if (snd_BUG_ON(!cs8427)) +		return;  	chip = cs8427->private_data;  	snd_i2c_lock(cs8427->bus);  	if ((chip->regmap[CS8427_REG_CLOCKSOURCE] & CS8427_RXDAES3INPUT) == @@ -526,7 +527,8 @@ int snd_cs8427_iec958_build(struct snd_i2c_device *cs8427,  	unsigned int idx;  	int err; -	snd_assert(play_substream && cap_substream, return -EINVAL); +	if (snd_BUG_ON(!play_substream || !cap_substream)) +		return -EINVAL;  	for (idx = 0; idx < ARRAY_SIZE(snd_cs8427_iec958_controls); idx++) {  		kctl = snd_ctl_new1(&snd_cs8427_iec958_controls[idx], cs8427);  		if (kctl == NULL) @@ -543,7 +545,8 @@ int snd_cs8427_iec958_build(struct snd_i2c_device *cs8427,  	chip->playback.substream = play_substream;  	chip->capture.substream = cap_substream; -	snd_assert(chip->playback.pcm_ctl, return -EIO); +	if (snd_BUG_ON(!chip->playback.pcm_ctl)) +		return -EIO;  	return 0;  } @@ -553,7 +556,8 @@ int snd_cs8427_iec958_active(struct snd_i2c_device *cs8427, int active)  {  	struct cs8427 *chip; -	snd_assert(cs8427, return -ENXIO); +	if (snd_BUG_ON(!cs8427)) +		return -ENXIO;  	chip = cs8427->private_data;  	if (active)  		memcpy(chip->playback.pcm_status, @@ -573,7 +577,8 @@ int snd_cs8427_iec958_pcm(struct snd_i2c_device *cs8427, unsigned int rate)  	char *status;  	int err, reset; -	snd_assert(cs8427, return -ENXIO); +	if (snd_BUG_ON(!cs8427)) +		return -ENXIO;  	chip = cs8427->private_data;  	status = chip->playback.pcm_status;  	snd_i2c_lock(cs8427->bus); diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c index b1e74e4..5c0c77d 100644 --- a/sound/i2c/i2c.c +++ b/sound/i2c/i2c.c @@ -49,7 +49,8 @@ static int snd_i2c_bus_free(struct snd_i2c_bus *bus)  	struct snd_i2c_bus *slave;  	struct snd_i2c_device *device; -	snd_assert(bus != NULL, return -EINVAL); +	if (snd_BUG_ON(!bus)) +		return -EINVAL;  	while (!list_empty(&bus->devices)) {  		device = snd_i2c_device(bus->devices.next);  		snd_i2c_device_free(device); @@ -113,7 +114,8 @@ int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name,  	struct snd_i2c_device *device;  	*rdevice = NULL; -	snd_assert(bus != NULL, return -EINVAL); +	if (snd_BUG_ON(!bus)) +		return -EINVAL;  	device = kzalloc(sizeof(*device), GFP_KERNEL);  	if (device == NULL)  		return -ENOMEM; diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c index 1f4942e..9840eb4 100644 --- a/sound/i2c/l3/uda1341.c +++ b/sound/i2c/l3/uda1341.c @@ -771,7 +771,8 @@ int __init snd_chip_uda1341_mixer_new(struct snd_card *card, struct l3_client **  	struct l3_client *clnt;  	int idx, err; -	snd_assert(card != NULL, return -EINVAL); +	if (snd_BUG_ON(!card)) +		return -EINVAL;  	clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);  	if (clnt == NULL) diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index d20d893..0341451 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -475,7 +475,8 @@ int snd_ak4114_build(struct ak4114 *ak4114,  	unsigned int idx;  	int err; -	snd_assert(cap_substream, return -EINVAL); +	if (snd_BUG_ON(!cap_substream)) +		return -EINVAL;  	ak4114->playback_substream = ply_substream;  	ak4114->capture_substream = cap_substream;  	for (idx = 0; idx < AK4114_CONTROLS; idx++) { diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c index f350835..2cad2d6 100644 --- a/sound/i2c/other/ak4117.c +++ b/sound/i2c/other/ak4117.c @@ -431,7 +431,8 @@ int snd_ak4117_build(struct ak4117 *ak4117, struct snd_pcm_substream *cap_substr  	unsigned int idx;  	int err; -	snd_assert(cap_substream, return -EINVAL); +	if (snd_BUG_ON(!cap_substream)) +		return -EINVAL;  	ak4117->substream = cap_substream;  	for (idx = 0; idx < AK4117_CONTROLS; idx++) {  		kctl = snd_ctl_new1(&snd_ak4117_iec958_controls[idx], ak4117); diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 288926d..ee47aba 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -233,8 +233,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)  		0x01, 0x02, /* 1: reset and soft-mute */  		0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect,  			     * disable DZF, sharp roll-off, RSTN#=0 */ -		0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */ -		// 0x02, 0x2e, /* quad speed */ +		0x02, 0x4e, /* 2: DA's power up, normal speed, RSTN#=0 */ +		/* 0x02, 0x6e,*/ /* quad speed */  		0x03, 0x01, /* 3: de-emphasis off */  		0x04, 0x00, /* 4: LOUT1 volume muted */  		0x05, 0x00, /* 5: ROUT1 volume muted */ diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 5769a13..660beb4 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -1,10 +1,6 @@  # ALSA ISA drivers -config SND_AD1848_LIB -        tristate -        select SND_PCM - -config SND_CS4231_LIB +config SND_WSS_LIB          tristate          select SND_PCM @@ -55,7 +51,7 @@ config SND_AD1816A  config SND_AD1848  	tristate "Generic AD1848/CS4248 driver" -	select SND_AD1848_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for AD1848 (Analog Devices) or  	  CS4248 (Cirrus Logic - Crystal Semiconductors) chips. @@ -86,7 +82,7 @@ config SND_AZT2320  	select ISAPNP  	select SND_OPL3_LIB  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for soundcards based on the  	  Aztech Systems AZT2320 chip. @@ -96,7 +92,7 @@ config SND_AZT2320  config SND_CMI8330  	tristate "C-Media CMI8330" -	select SND_AD1848_LIB +	select SND_WSS_LIB  	select SND_SB16_DSP  	help  	  Say Y here to include support for soundcards based on the @@ -108,7 +104,7 @@ config SND_CMI8330  config SND_CS4231  	tristate "Generic Cirrus Logic CS4231 driver"  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for CS4231 chips from Cirrus  	  Logic - Crystal Semiconductors. @@ -120,7 +116,7 @@ config SND_CS4232  	tristate "Generic Cirrus Logic CS4232 driver"  	select SND_OPL3_LIB  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for CS4232 chips from Cirrus  	  Logic - Crystal Semiconductors. @@ -132,7 +128,7 @@ config SND_CS4236  	tristate "Generic Cirrus Logic CS4236+ driver"  	select SND_OPL3_LIB  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y to include support for CS4235,CS4236,CS4237B,CS4238B,  	  CS4239 chips from Cirrus Logic - Crystal Semiconductors. @@ -192,7 +188,7 @@ config SND_ES18XX  config SND_SC6000  	tristate "Gallant SC-6000, Audio Excel DSP 16"  	depends on HAS_IOPORT -	select SND_AD1848_LIB +	select SND_WSS_LIB  	select SND_OPL3_LIB  	select SND_MPU401_UART  	help @@ -228,7 +224,7 @@ config SND_GUSEXTREME  config SND_GUSMAX  	tristate "Gravis UltraSound MAX"  	select SND_RAWMIDI -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for Gravis UltraSound MAX  	  soundcards. @@ -240,7 +236,7 @@ config SND_INTERWAVE  	tristate "AMD InterWave, Gravis UltraSound PnP"  	depends on PNP  	select SND_RAWMIDI -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for AMD InterWave based  	  soundcards (Gravis UltraSound Plug & Play, STB SoundRage32, @@ -253,7 +249,7 @@ config SND_INTERWAVE_STB  	tristate "AMD InterWave + TEA6330T (UltraSound 32-Pro)"  	depends on PNP  	select SND_RAWMIDI -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for AMD InterWave based  	  soundcards with a TEA6330T bass and treble regulator @@ -266,7 +262,7 @@ config SND_OPL3SA2  	tristate "Yamaha OPL3-SA2/SA3"  	select SND_OPL3_LIB  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for Yamaha OPL3-SA2 and OPL3-SA3  	  chips. @@ -279,7 +275,7 @@ config SND_OPTI92X_AD1848  	select SND_OPL3_LIB  	select SND_OPL4_LIB  	select SND_MPU401_UART -	select SND_AD1848_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for soundcards based on Opti  	  82C92x or OTI-601 chips and using an AD1848 codec. @@ -292,7 +288,7 @@ config SND_OPTI92X_CS4231  	select SND_OPL3_LIB  	select SND_OPL4_LIB  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for soundcards based on Opti  	  82C92x chips and using a CS4231 codec. @@ -304,7 +300,7 @@ config SND_OPTI93X  	tristate "OPTi 82C93x"  	select SND_OPL3_LIB  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for soundcards based on Opti  	  82C93x chips. @@ -315,7 +311,7 @@ config SND_OPTI93X  config SND_MIRO  	tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"  	select SND_OPL4_LIB -	select SND_CS4231_LIB +	select SND_WSS_LIB  	select SND_MPU401_UART  	select SND_PCM  	help @@ -364,7 +360,7 @@ config SND_SBAWE  config SND_SB16_CSP  	bool "Sound Blaster 16/AWE CSP support"  	depends on (SND_SB16 || SND_SBAWE) && (BROKEN || !PPC) -	select FW_LOADER if !SND_SB16_CSP_FIRMWARE_IN_KERNEL +	select FW_LOADER  	help  	  Say Y here to include support for the CSP core.  This special  	  coprocessor can do variable tasks like various compression and @@ -372,7 +368,7 @@ config SND_SB16_CSP  config SND_SGALAXY  	tristate "Aztech Sound Galaxy" -	select SND_AD1848_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for Aztech Sound Galaxy  	  soundcards. @@ -384,7 +380,7 @@ config SND_SSCAPE  	tristate "Ensoniq SoundScape PnP driver"  	select SND_HWDEP  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for Ensoniq SoundScape PnP  	  soundcards. @@ -397,7 +393,7 @@ config SND_WAVEFRONT  	select FW_LOADER  	select SND_OPL3_LIB  	select SND_MPU401_UART -	select SND_CS4231_LIB +	select SND_WSS_LIB  	help  	  Say Y here to include support for Turtle Beach Maui, Tropez  	  and Tropez+ soundcards based on the Wavefront chip. diff --git a/sound/isa/Makefile b/sound/isa/Makefile index c0ce7db..63af13d 100644 --- a/sound/isa/Makefile +++ b/sound/isa/Makefile @@ -27,4 +27,4 @@ obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o  obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o  obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \ -		     sb/ wavefront/ +		     sb/ wavefront/ wss/ diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index 68f1260..7752424 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -83,8 +83,10 @@ static struct pnp_card_device_id snd_ad1816a_pnpids[] = {  	{ .id = "MDK1605", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },  	/* Shark Predator ISA - added by Ken Arromdee */  	{ .id = "SMM7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, -	/* Analog Devices AD1816A - Terratec AudioSystem EWS64S */ +	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */  	{ .id = "TER1112", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, +	/* Analog Devices AD1816A - Terratec AudioSystem EWS64 S */ +	{ .id = "TER1112", .devs = { { .id = "TER1100" }, { .id = "TER1101" } } },  	/* Analog Devices AD1816A - Terratec Base 64 */  	{ .id = "TER1411", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },  	/* end */ diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index 4b8dfe2..3bfca7c 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -394,7 +394,8 @@ static int snd_ad1816a_timer_open(struct snd_timer *timer)  static unsigned long snd_ad1816a_timer_resolution(struct snd_timer *timer)  { -	snd_assert(timer != NULL, return 0); +	if (snd_BUG_ON(!timer)) +		return 0;  	return 10000;  } @@ -961,7 +962,8 @@ int __devinit snd_ad1816a_mixer(struct snd_ad1816a *chip)  	unsigned int idx;  	int err; -	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip || !chip->card)) +		return -EINVAL;  	card = chip->card; diff --git a/sound/isa/ad1848/Makefile b/sound/isa/ad1848/Makefile index ae23331..3d6dea3 100644 --- a/sound/isa/ad1848/Makefile +++ b/sound/isa/ad1848/Makefile @@ -3,10 +3,8 @@  # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>  # -snd-ad1848-lib-objs := ad1848_lib.o  snd-ad1848-objs := ad1848.o  # Toplevel Module Dependency  obj-$(CONFIG_SND_AD1848) += snd-ad1848.o -obj-$(CONFIG_SND_AD1848_LIB) += snd-ad1848-lib.o diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c index 5f5271e..b68d20e 100644 --- a/sound/isa/ad1848/ad1848.c +++ b/sound/isa/ad1848/ad1848.c @@ -28,7 +28,7 @@  #include <linux/wait.h>  #include <linux/moduleparam.h>  #include <sound/core.h> -#include <sound/ad1848.h> +#include <sound/wss.h>  #include <sound/initval.h>  #define CRD_NAME "Generic AD1848/AD1847/CS4248" @@ -87,7 +87,7 @@ static int __devinit snd_ad1848_match(struct device *dev, unsigned int n)  static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)  {  	struct snd_card *card; -	struct snd_ad1848 *chip; +	struct snd_wss *chip;  	struct snd_pcm *pcm;  	int error; @@ -95,18 +95,19 @@ static int __devinit snd_ad1848_probe(struct device *dev, unsigned int n)  	if (!card)  		return -EINVAL; -	error = snd_ad1848_create(card, port[n], irq[n], dma1[n], -			thinkpad[n] ? AD1848_HW_THINKPAD : AD1848_HW_DETECT, &chip); +	error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], -1, +			thinkpad[n] ? WSS_HW_THINKPAD : WSS_HW_DETECT, +			0, &chip);  	if (error < 0)  		goto out;  	card->private_data = chip; -	error = snd_ad1848_pcm(chip, 0, &pcm); +	error = snd_wss_pcm(chip, 0, &pcm);  	if (error < 0)  		goto out; -	error = snd_ad1848_mixer(chip); +	error = snd_wss_mixer(chip);  	if (error < 0)  		goto out; @@ -142,7 +143,7 @@ static int __devexit snd_ad1848_remove(struct device *dev, unsigned int n)  static int snd_ad1848_suspend(struct device *dev, unsigned int n, pm_message_t state)  {  	struct snd_card *card = dev_get_drvdata(dev); -	struct snd_ad1848 *chip = card->private_data; +	struct snd_wss *chip = card->private_data;  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);  	chip->suspend(chip); @@ -152,7 +153,7 @@ static int snd_ad1848_suspend(struct device *dev, unsigned int n, pm_message_t s  static int snd_ad1848_resume(struct device *dev, unsigned int n)  {  	struct snd_card *card = dev_get_drvdata(dev); -	struct snd_ad1848 *chip = card->private_data; +	struct snd_wss *chip = card->private_data;  	chip->resume(chip);  	snd_power_change_state(card, SNDRV_CTL_POWER_D0); diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c deleted file mode 100644 index 630c90f..0000000 --- a/sound/isa/ad1848/ad1848_lib.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - *  Copyright (c) by Jaroslav Kysela <perex@perex.cz> - *  Routines for control of AD1848/AD1847/CS4248 - * - * - *   This program is free software; you can redistribute it and/or modify - *   it under the terms of the GNU General Public License as published by - *   the Free Software Foundation; either version 2 of the License, or - *   (at your option) any later version. - * - *   This program is distributed in the hope that it will be useful, - *   but WITHOUT ANY WARRANTY; without even the implied warranty of - *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *   GNU General Public License for more details. - * - *   You should have received a copy of the GNU General Public License - *   along with this program; if not, write to the Free Software - *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA - * - */ - -#define SNDRV_MAIN_OBJECT_FILE -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <sound/core.h> -#include <sound/ad1848.h> -#include <sound/control.h> -#include <sound/tlv.h> -#include <sound/pcm_params.h> - -#include <asm/io.h> -#include <asm/dma.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Routines for control of AD1848/AD1847/CS4248"); -MODULE_LICENSE("GPL"); - -#if 0 -#define SNDRV_DEBUG_MCE -#endif - -/* - *  Some variables - */ - -static unsigned char freq_bits[14] = { -	/* 5510 */	0x00 | AD1848_XTAL2, -	/* 6620 */	0x0E | AD1848_XTAL2, -	/* 8000 */	0x00 | AD1848_XTAL1, -	/* 9600 */	0x0E | AD1848_XTAL1, -	/* 11025 */	0x02 | AD1848_XTAL2, -	/* 16000 */	0x02 | AD1848_XTAL1, -	/* 18900 */	0x04 | AD1848_XTAL2, -	/* 22050 */	0x06 | AD1848_XTAL2, -	/* 27042 */	0x04 | AD1848_XTAL1, -	/* 32000 */	0x06 | AD1848_XTAL1, -	/* 33075 */	0x0C | AD1848_XTAL2, -	/* 37800 */	0x08 | AD1848_XTAL2, -	/* 44100 */	0x0A | AD1848_XTAL2, -	/* 48000 */	0x0C | AD1848_XTAL1 -}; - -static unsigned int rates[14] = { -	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, -	27042, 32000, 33075, 37800, 44100, 48000 -}; - -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { -	.count = ARRAY_SIZE(rates), -	.list = rates, -	.mask = 0, -}; - -static unsigned char snd_ad1848_original_image[16] = -{ -	0x00,			/* 00 - lic */ -	0x00,			/* 01 - ric */ -	0x9f,			/* 02 - la1ic */ -	0x9f,			/* 03 - ra1ic */ -	0x9f,			/* 04 - la2ic */ -	0x9f,			/* 05 - ra2ic */ -	0xbf,			/* 06 - loc */ -	0xbf,			/* 07 - roc */ -	0x20,			/* 08 - dfr */ -	AD1848_AUTOCALIB,	/* 09 - ic */ -	0x00,			/* 0a - pc */ -	0x00,			/* 0b - ti */ -	0x00,			/* 0c - mi */ -	0x00,			/* 0d - lbc */ -	0x00,			/* 0e - dru */ -	0x00,			/* 0f - drl */ -}; - -/* - *  Basic I/O functions - */ - -static void snd_ad1848_wait(struct snd_ad1848 *chip) -{ -	int timeout; - -	for (timeout = 250; timeout > 0; timeout--) { -		if ((inb(AD1848P(chip, REGSEL)) & AD1848_INIT) == 0) -			break; -		udelay(100); -	} -} - -void snd_ad1848_out(struct snd_ad1848 *chip, -			   unsigned char reg, -			   unsigned char value) -{ -	snd_ad1848_wait(chip); -#ifdef CONFIG_SND_DEBUG -	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) -		snd_printk(KERN_WARNING "auto calibration time out - " -			   "reg = 0x%x, value = 0x%x\n", reg, value); -#endif -	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); -	outb(chip->image[reg] = value, AD1848P(chip, REG)); -	mb(); -	snd_printdd("codec out - reg 0x%x = 0x%x\n", -			chip->mce_bit | reg, value); -} - -EXPORT_SYMBOL(snd_ad1848_out); - -static void snd_ad1848_dout(struct snd_ad1848 *chip, -			    unsigned char reg, unsigned char value) -{ -	snd_ad1848_wait(chip); -	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); -	outb(value, AD1848P(chip, REG)); -	mb(); -} - -static unsigned char snd_ad1848_in(struct snd_ad1848 *chip, unsigned char reg) -{ -	snd_ad1848_wait(chip); -#ifdef CONFIG_SND_DEBUG -	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) -		snd_printk(KERN_WARNING "auto calibration time out - " -			   "reg = 0x%x\n", reg); -#endif -	outb(chip->mce_bit | reg, AD1848P(chip, REGSEL)); -	mb(); -	return inb(AD1848P(chip, REG)); -} - -#if 0 - -static void snd_ad1848_debug(struct snd_ad1848 *chip) -{ -	printk("AD1848 REGS:      INDEX = 0x%02x  ", inb(AD1848P(chip, REGSEL))); -	printk("                 STATUS = 0x%02x\n", inb(AD1848P(chip, STATUS))); -	printk("  0x00: left input      = 0x%02x  ", snd_ad1848_in(chip, 0x00)); -	printk("  0x08: playback format = 0x%02x\n", snd_ad1848_in(chip, 0x08)); -	printk("  0x01: right input     = 0x%02x  ", snd_ad1848_in(chip, 0x01)); -	printk("  0x09: iface (CFIG 1)  = 0x%02x\n", snd_ad1848_in(chip, 0x09)); -	printk("  0x02: AUXA left       = 0x%02x  ", snd_ad1848_in(chip, 0x02)); -	printk("  0x0a: pin control     = 0x%02x\n", snd_ad1848_in(chip, 0x0a)); -	printk("  0x03: AUXA right      = 0x%02x  ", snd_ad1848_in(chip, 0x03)); -	printk("  0x0b: init & status   = 0x%02x\n", snd_ad1848_in(chip, 0x0b)); -	printk("  0x04: AUXB left       = 0x%02x  ", snd_ad1848_in(chip, 0x04)); -	printk("  0x0c: revision & mode = 0x%02x\n", snd_ad1848_in(chip, 0x0c)); -	printk("  0x05: AUXB right      = 0x%02x  ", snd_ad1848_in(chip, 0x05)); -	printk("  0x0d: loopback        = 0x%02x\n", snd_ad1848_in(chip, 0x0d)); -	printk("  0x06: left output     = 0x%02x  ", snd_ad1848_in(chip, 0x06)); -	printk("  0x0e: data upr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0e)); -	printk("  0x07: right output    = 0x%02x  ", snd_ad1848_in(chip, 0x07)); -	printk("  0x0f: data lwr count  = 0x%02x\n", snd_ad1848_in(chip, 0x0f)); -} - -#endif - -/* - *  AD1848 detection / MCE routines - */ - -static void snd_ad1848_mce_up(struct snd_ad1848 *chip) -{ -	unsigned long flags; -	int timeout; - -	snd_ad1848_wait(chip); -#ifdef CONFIG_SND_DEBUG -	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) -		snd_printk(KERN_WARNING "mce_up - auto calibration time out (0)\n"); -#endif -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->mce_bit |= AD1848_MCE; -	timeout = inb(AD1848P(chip, REGSEL)); -	if (timeout == 0x80) -		snd_printk(KERN_WARNING "mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); -	if (!(timeout & AD1848_MCE)) -		outb(chip->mce_bit | (timeout & 0x1f), AD1848P(chip, REGSEL)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -} - -static void snd_ad1848_mce_down(struct snd_ad1848 *chip) -{ -	unsigned long flags, timeout; -	int reg; - -	spin_lock_irqsave(&chip->reg_lock, flags); -	for (timeout = 5; timeout > 0; timeout--) -		inb(AD1848P(chip, REGSEL)); -	/* end of cleanup sequence */ -	for (timeout = 12000; timeout > 0 && (inb(AD1848P(chip, REGSEL)) & AD1848_INIT); timeout--) -		udelay(100); - -	snd_printdd("(1) timeout = %ld\n", timeout); - -#ifdef CONFIG_SND_DEBUG -	if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) -		snd_printk(KERN_WARNING "mce_down [0x%lx] - auto calibration time out (0)\n", AD1848P(chip, REGSEL)); -#endif - -	chip->mce_bit &= ~AD1848_MCE; -	reg = inb(AD1848P(chip, REGSEL)); -	outb(chip->mce_bit | (reg & 0x1f), AD1848P(chip, REGSEL)); -	if (reg == 0x80) -		snd_printk(KERN_WARNING "mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); -	if ((reg & AD1848_MCE) == 0) { -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		return; -	} - -	/* -	 * Wait for auto-calibration (AC) process to finish, i.e. ACI to go low. -	 * It may take up to 5 sample periods (at most 907 us @ 5.5125 kHz) for -	 * the process to _start_, so it is important to wait at least that long -	 * before checking.  Otherwise we might think AC has finished when it -	 * has in fact not begun.  It could take 128 (no AC) or 384 (AC) cycles -	 * for ACI to drop.  This gives a wait of at most 70 ms with a more -	 * typical value of 3-9 ms. -	 */ -	timeout = jiffies + msecs_to_jiffies(250); -	do { -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		msleep(1); -		spin_lock_irqsave(&chip->reg_lock, flags); -		reg = snd_ad1848_in(chip, AD1848_TEST_INIT) & -		      AD1848_CALIB_IN_PROGRESS; -	} while (reg && time_before(jiffies, timeout)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (reg) -		snd_printk(KERN_ERR -			   "mce_down - auto calibration time out (2)\n"); - -	snd_printdd("(4) jiffies = %lu\n", jiffies); -	snd_printd("mce_down - exit = 0x%x\n", inb(AD1848P(chip, REGSEL))); -} - -static unsigned int snd_ad1848_get_count(unsigned char format, -				         unsigned int size) -{ -	switch (format & 0xe0) { -	case AD1848_LINEAR_16: -		size >>= 1; -		break; -	} -	if (format & AD1848_STEREO) -		size >>= 1; -	return size; -} - -static int snd_ad1848_trigger(struct snd_ad1848 *chip, unsigned char what, -			      int channel, int cmd) -{ -	int result = 0; - -#if 0 -	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, inb(AD1848P(card, STATUS))); -#endif -	spin_lock(&chip->reg_lock); -	if (cmd == SNDRV_PCM_TRIGGER_START) { -		if (chip->image[AD1848_IFACE_CTRL] & what) { -			spin_unlock(&chip->reg_lock); -			return 0; -		} -		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] |= what); -		chip->mode |= AD1848_MODE_RUNNING; -	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) { -		if (!(chip->image[AD1848_IFACE_CTRL] & what)) { -			spin_unlock(&chip->reg_lock); -			return 0; -		} -		snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL] &= ~what); -		chip->mode &= ~AD1848_MODE_RUNNING; -	} else { -		result = -EINVAL; -	} -	spin_unlock(&chip->reg_lock); -	return result; -} - -/* - *  CODEC I/O - */ - -static unsigned char snd_ad1848_get_rate(unsigned int rate) -{ -	int i; - -	for (i = 0; i < ARRAY_SIZE(rates); i++) -		if (rate == rates[i]) -			return freq_bits[i]; -	snd_BUG(); -	return freq_bits[ARRAY_SIZE(rates) - 1]; -} - -static int snd_ad1848_ioctl(struct snd_pcm_substream *substream, -			    unsigned int cmd, void *arg) -{ -	return snd_pcm_lib_ioctl(substream, cmd, arg); -} - -static unsigned char snd_ad1848_get_format(int format, int channels) -{ -	unsigned char rformat; - -	rformat = AD1848_LINEAR_8; -	switch (format) { -	case SNDRV_PCM_FORMAT_A_LAW:	rformat = AD1848_ALAW_8; break; -	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = AD1848_ULAW_8; break; -	case SNDRV_PCM_FORMAT_S16_LE:	rformat = AD1848_LINEAR_16; break; -	} -	if (channels > 1) -		rformat |= AD1848_STEREO; -#if 0 -	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); -#endif -	return rformat; -} - -static void snd_ad1848_calibrate_mute(struct snd_ad1848 *chip, int mute) -{ -	unsigned long flags; -	 -	mute = mute ? 1 : 0; -	spin_lock_irqsave(&chip->reg_lock, flags); -	if (chip->calibrate_mute == mute) { -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		return; -	} -	if (!mute) { -		snd_ad1848_dout(chip, AD1848_LEFT_INPUT, chip->image[AD1848_LEFT_INPUT]); -		snd_ad1848_dout(chip, AD1848_RIGHT_INPUT, chip->image[AD1848_RIGHT_INPUT]); -	} -	snd_ad1848_dout(chip, AD1848_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_LEFT_INPUT]); -	snd_ad1848_dout(chip, AD1848_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX1_RIGHT_INPUT]); -	snd_ad1848_dout(chip, AD1848_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_LEFT_INPUT]); -	snd_ad1848_dout(chip, AD1848_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[AD1848_AUX2_RIGHT_INPUT]); -	snd_ad1848_dout(chip, AD1848_LEFT_OUTPUT, mute ? 0x80 : chip->image[AD1848_LEFT_OUTPUT]); -	snd_ad1848_dout(chip, AD1848_RIGHT_OUTPUT, mute ? 0x80 : chip->image[AD1848_RIGHT_OUTPUT]); -	chip->calibrate_mute = mute; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -} - -static void snd_ad1848_set_data_format(struct snd_ad1848 *chip, struct snd_pcm_hw_params *hw_params) -{ -	if (hw_params == NULL) { -		chip->image[AD1848_DATA_FORMAT] = 0x20; -	} else { -		chip->image[AD1848_DATA_FORMAT] = -		    snd_ad1848_get_format(params_format(hw_params), params_channels(hw_params)) | -		    snd_ad1848_get_rate(params_rate(hw_params)); -	} -	// snd_printk(">>> pmode = 0x%x, dfr = 0x%x\n", pstr->mode, chip->image[AD1848_DATA_FORMAT]); -} - -static int snd_ad1848_open(struct snd_ad1848 *chip, unsigned int mode) -{ -	unsigned long flags; - -	if (chip->mode & AD1848_MODE_OPEN) -		return -EAGAIN; - -	snd_ad1848_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("open: (1)\n"); -#endif -	snd_ad1848_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | -			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO | -			     AD1848_CALIB_MODE); -	chip->image[AD1848_IFACE_CTRL] |= AD1848_AUTOCALIB; -	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_ad1848_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("open: (2)\n"); -#endif - -	snd_ad1848_set_data_format(chip, NULL); - -	snd_ad1848_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_ad1848_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("open: (3)\n"); -#endif - -	/* ok. now enable and ack CODEC IRQ */ -	spin_lock_irqsave(&chip->reg_lock, flags); -	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */ -	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */ -	chip->image[AD1848_PIN_CTRL] |= AD1848_IRQ_ENABLE; -	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	chip->mode = mode; - -	return 0; -} - -static void snd_ad1848_close(struct snd_ad1848 *chip) -{ -	unsigned long flags; - -	if (!chip->mode) -		return; -	/* disable IRQ */ -	spin_lock_irqsave(&chip->reg_lock, flags); -	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */ -	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */ -	chip->image[AD1848_PIN_CTRL] &= ~AD1848_IRQ_ENABLE; -	snd_ad1848_out(chip, AD1848_PIN_CTRL, chip->image[AD1848_PIN_CTRL]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	/* now disable capture & playback */ - -	snd_ad1848_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO | -			     AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); -	snd_ad1848_out(chip, AD1848_IFACE_CTRL, chip->image[AD1848_IFACE_CTRL]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_ad1848_mce_down(chip); - -	/* clear IRQ again */ -	spin_lock_irqsave(&chip->reg_lock, flags); -	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */ -	outb(0, AD1848P(chip, STATUS));	/* clear IRQ */ -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	chip->mode = 0; -} - -/* - *  ok.. exported functions.. - */ - -static int snd_ad1848_playback_trigger(struct snd_pcm_substream *substream, -				       int cmd) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	return snd_ad1848_trigger(chip, AD1848_PLAYBACK_ENABLE, SNDRV_PCM_STREAM_PLAYBACK, cmd); -} - -static int snd_ad1848_capture_trigger(struct snd_pcm_substream *substream, -				      int cmd) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	return snd_ad1848_trigger(chip, AD1848_CAPTURE_ENABLE, SNDRV_PCM_STREAM_CAPTURE, cmd); -} - -static int snd_ad1848_playback_hw_params(struct snd_pcm_substream *substream, -					 struct snd_pcm_hw_params *hw_params) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	unsigned long flags; -	int err; - -	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) -		return err; -	snd_ad1848_calibrate_mute(chip, 1); -	snd_ad1848_set_data_format(chip, hw_params); -	snd_ad1848_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_ad1848_mce_down(chip); -	snd_ad1848_calibrate_mute(chip, 0); -	return 0; -} - -static int snd_ad1848_playback_hw_free(struct snd_pcm_substream *substream) -{ -	return snd_pcm_lib_free_pages(substream); -} - -static int snd_ad1848_playback_prepare(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	unsigned long flags; -	unsigned int size = snd_pcm_lib_buffer_bytes(substream); -	unsigned int count = snd_pcm_lib_period_bytes(substream); - -	chip->dma_size = size; -	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_PLAYBACK_ENABLE | AD1848_PLAYBACK_PIO); -	snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); -	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); -	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return 0; -} - -static int snd_ad1848_capture_hw_params(struct snd_pcm_substream *substream, -					struct snd_pcm_hw_params *hw_params) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	unsigned long flags; -	int err; - -	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) -		return err; -	snd_ad1848_calibrate_mute(chip, 1); -	snd_ad1848_set_data_format(chip, hw_params); -	snd_ad1848_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_ad1848_out(chip, AD1848_DATA_FORMAT, chip->image[AD1848_DATA_FORMAT]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_ad1848_mce_down(chip); -	snd_ad1848_calibrate_mute(chip, 0); -	return 0; -} - -static int snd_ad1848_capture_hw_free(struct snd_pcm_substream *substream) -{ -	return snd_pcm_lib_free_pages(substream); -} - -static int snd_ad1848_capture_prepare(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	unsigned long flags; -	unsigned int size = snd_pcm_lib_buffer_bytes(substream); -	unsigned int count = snd_pcm_lib_period_bytes(substream); - -	chip->dma_size = size; -	chip->image[AD1848_IFACE_CTRL] &= ~(AD1848_CAPTURE_ENABLE | AD1848_CAPTURE_PIO); -	snd_dma_program(chip->dma, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); -	count = snd_ad1848_get_count(chip->image[AD1848_DATA_FORMAT], count) - 1; -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_ad1848_out(chip, AD1848_DATA_LWR_CNT, (unsigned char) count); -	snd_ad1848_out(chip, AD1848_DATA_UPR_CNT, (unsigned char) (count >> 8)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return 0; -} - -static irqreturn_t snd_ad1848_interrupt(int irq, void *dev_id) -{ -	struct snd_ad1848 *chip = dev_id; - -	if ((chip->mode & AD1848_MODE_PLAY) && chip->playback_substream && -	    (chip->mode & AD1848_MODE_RUNNING)) -		snd_pcm_period_elapsed(chip->playback_substream); -	if ((chip->mode & AD1848_MODE_CAPTURE) && chip->capture_substream && -	    (chip->mode & AD1848_MODE_RUNNING)) -		snd_pcm_period_elapsed(chip->capture_substream); -	outb(0, AD1848P(chip, STATUS));	/* clear global interrupt bit */ -	return IRQ_HANDLED; -} - -static snd_pcm_uframes_t snd_ad1848_playback_pointer(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	size_t ptr; -	 -	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_PLAYBACK_ENABLE)) -		return 0; -	ptr = snd_dma_pointer(chip->dma, chip->dma_size); -	return bytes_to_frames(substream->runtime, ptr); -} - -static snd_pcm_uframes_t snd_ad1848_capture_pointer(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	size_t ptr; - -	if (!(chip->image[AD1848_IFACE_CTRL] & AD1848_CAPTURE_ENABLE)) -		return 0; -	ptr = snd_dma_pointer(chip->dma, chip->dma_size); -	return bytes_to_frames(substream->runtime, ptr); -} - -/* - - */ - -static void snd_ad1848_thinkpad_twiddle(struct snd_ad1848 *chip, int on) { - -	int tmp; - -	if (!chip->thinkpad_flag) return; - -	outb(0x1c, AD1848_THINKPAD_CTL_PORT1); -	tmp = inb(AD1848_THINKPAD_CTL_PORT2); - -	if (on) -		/* turn it on */ -		tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT; -	else -		/* turn it off */ -		tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT; -	 -	outb(tmp, AD1848_THINKPAD_CTL_PORT2); - -} - -#ifdef CONFIG_PM -static void snd_ad1848_suspend(struct snd_ad1848 *chip) -{ -	snd_pcm_suspend_all(chip->pcm); -	if (chip->thinkpad_flag) -		snd_ad1848_thinkpad_twiddle(chip, 0); -} - -static void snd_ad1848_resume(struct snd_ad1848 *chip) -{ -	int i; - -	if (chip->thinkpad_flag) -		snd_ad1848_thinkpad_twiddle(chip, 1); - -	/* clear any pendings IRQ */ -	inb(AD1848P(chip, STATUS)); -	outb(0, AD1848P(chip, STATUS)); -	mb(); - -	snd_ad1848_mce_down(chip); -	for (i = 0; i < 16; i++) -		snd_ad1848_out(chip, i, chip->image[i]); -	snd_ad1848_mce_up(chip); -	snd_ad1848_mce_down(chip); -} -#endif /* CONFIG_PM */ - -static int snd_ad1848_probe(struct snd_ad1848 * chip) -{ -	unsigned long flags; -	int i, id, rev, ad1847; -	unsigned char *ptr; - -#if 0 -	snd_ad1848_debug(chip); -#endif -	id = ad1847 = 0; -	for (i = 0; i < 1000; i++) { -		mb(); -		if (inb(AD1848P(chip, REGSEL)) & AD1848_INIT) -			udelay(500); -		else { -			spin_lock_irqsave(&chip->reg_lock, flags); -			snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); -			snd_ad1848_out(chip, AD1848_LEFT_INPUT, 0xaa); -			snd_ad1848_out(chip, AD1848_RIGHT_INPUT, 0x45); -			rev = snd_ad1848_in(chip, AD1848_RIGHT_INPUT); -			if (rev == 0x65) { -				spin_unlock_irqrestore(&chip->reg_lock, flags); -				id = 1; -				ad1847 = 1; -				break; -			} -			if (snd_ad1848_in(chip, AD1848_LEFT_INPUT) == 0xaa && rev == 0x45) { -				spin_unlock_irqrestore(&chip->reg_lock, flags); -				id = 1; -				break; -			} -			spin_unlock_irqrestore(&chip->reg_lock, flags); -		} -	} -	if (id != 1) -		return -ENODEV;	/* no valid device found */ -	if (chip->hardware == AD1848_HW_DETECT) { -		if (ad1847) { -			chip->hardware = AD1848_HW_AD1847; -		} else { -			chip->hardware = AD1848_HW_AD1848; -			rev = snd_ad1848_in(chip, AD1848_MISC_INFO); -			if (rev & 0x80) { -				chip->hardware = AD1848_HW_CS4248; -			} else if ((rev & 0x0f) == 0x0a) { -				snd_ad1848_out(chip, AD1848_MISC_INFO, 0x40); -				for (i = 0; i < 16; ++i) { -					if (snd_ad1848_in(chip, i) != snd_ad1848_in(chip, i + 16)) { -						chip->hardware = AD1848_HW_CMI8330; -						break; -					} -				} -				snd_ad1848_out(chip, AD1848_MISC_INFO, 0x00); -			} -		} -	} -	spin_lock_irqsave(&chip->reg_lock, flags); -	inb(AD1848P(chip, STATUS));	/* clear any pendings IRQ */ -	outb(0, AD1848P(chip, STATUS)); -	mb(); -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	chip->image[AD1848_MISC_INFO] = 0x00; -	chip->image[AD1848_IFACE_CTRL] = -	    (chip->image[AD1848_IFACE_CTRL] & ~AD1848_SINGLE_DMA) | AD1848_SINGLE_DMA; -	ptr = (unsigned char *) &chip->image; -	snd_ad1848_mce_down(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	for (i = 0; i < 16; i++)	/* ok.. fill all AD1848 registers */ -		snd_ad1848_out(chip, i, *ptr++); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_ad1848_mce_up(chip); -	snd_ad1848_mce_down(chip); -	return 0;		/* all things are ok.. */ -} - -/* - - */ - -static struct snd_pcm_hardware snd_ad1848_playback = -{ -	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | -				 SNDRV_PCM_INFO_MMAP_VALID), -	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | -				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), -	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, -	.rate_min =		5510, -	.rate_max =		48000, -	.channels_min =		1, -	.channels_max =		2, -	.buffer_bytes_max =	(128*1024), -	.period_bytes_min =	64, -	.period_bytes_max =	(128*1024), -	.periods_min =		1, -	.periods_max =		1024, -	.fifo_size =		0, -}; - -static struct snd_pcm_hardware snd_ad1848_capture = -{ -	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | -				 SNDRV_PCM_INFO_MMAP_VALID), -	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | -				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE), -	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, -	.rate_min =		5510, -	.rate_max =		48000, -	.channels_min =		1, -	.channels_max =		2, -	.buffer_bytes_max =	(128*1024), -	.period_bytes_min =	64, -	.period_bytes_max =	(128*1024), -	.periods_min =		1, -	.periods_max =		1024, -	.fifo_size =		0, -}; - -/* - - */ - -static int snd_ad1848_playback_open(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	int err; - -	if ((err = snd_ad1848_open(chip, AD1848_MODE_PLAY)) < 0) -		return err; -	chip->playback_substream = substream; -	runtime->hw = snd_ad1848_playback; -	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); -	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); -	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); -	return 0; -} - -static int snd_ad1848_capture_open(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	int err; - -	if ((err = snd_ad1848_open(chip, AD1848_MODE_CAPTURE)) < 0) -		return err; -	chip->capture_substream = substream; -	runtime->hw = snd_ad1848_capture; -	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.buffer_bytes_max); -	snd_pcm_limit_isa_dma_size(chip->dma, &runtime->hw.period_bytes_max); -	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); -	return 0; -} - -static int snd_ad1848_playback_close(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); - -	chip->mode &= ~AD1848_MODE_PLAY; -	chip->playback_substream = NULL; -	snd_ad1848_close(chip); -	return 0; -} - -static int snd_ad1848_capture_close(struct snd_pcm_substream *substream) -{ -	struct snd_ad1848 *chip = snd_pcm_substream_chip(substream); - -	chip->mode &= ~AD1848_MODE_CAPTURE; -	chip->capture_substream = NULL; -	snd_ad1848_close(chip); -	return 0; -} - -static int snd_ad1848_free(struct snd_ad1848 *chip) -{ -	release_and_free_resource(chip->res_port); -	if (chip->irq >= 0) -		free_irq(chip->irq, (void *) chip); -	if (chip->dma >= 0) { -		snd_dma_disable(chip->dma); -		free_dma(chip->dma); -	} -	kfree(chip); -	return 0; -} - -static int snd_ad1848_dev_free(struct snd_device *device) -{ -	struct snd_ad1848 *chip = device->device_data; -	return snd_ad1848_free(chip); -} - -static const char *snd_ad1848_chip_id(struct snd_ad1848 *chip) -{ -	switch (chip->hardware) { -	case AD1848_HW_AD1847:	return "AD1847"; -	case AD1848_HW_AD1848:	return "AD1848"; -	case AD1848_HW_CS4248:	return "CS4248"; -	case AD1848_HW_CMI8330: return "CMI8330/C3D"; -	default:		return "???"; -	} -} - -int snd_ad1848_create(struct snd_card *card, -		      unsigned long port, -		      int irq, int dma, -		      unsigned short hardware, -		      struct snd_ad1848 ** rchip) -{ -	static struct snd_device_ops ops = { -		.dev_free =	snd_ad1848_dev_free, -	}; -	struct snd_ad1848 *chip; -	int err; - -	*rchip = NULL; -	chip = kzalloc(sizeof(*chip), GFP_KERNEL); -	if (chip == NULL) -		return -ENOMEM; -	spin_lock_init(&chip->reg_lock); -	chip->card = card; -	chip->port = port; -	chip->irq = -1; -	chip->dma = -1; -	chip->hardware = hardware; -	memcpy(&chip->image, &snd_ad1848_original_image, sizeof(snd_ad1848_original_image)); -	 -	if ((chip->res_port = request_region(port, 4, "AD1848")) == NULL) { -		snd_printk(KERN_ERR "ad1848: can't grab port 0x%lx\n", port); -		snd_ad1848_free(chip); -		return -EBUSY; -	} -	if (request_irq(irq, snd_ad1848_interrupt, IRQF_DISABLED, "AD1848", (void *) chip)) { -		snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq); -		snd_ad1848_free(chip); -		return -EBUSY; -	} -	chip->irq = irq; -	if (request_dma(dma, "AD1848")) { -		snd_printk(KERN_ERR "ad1848: can't grab DMA %d\n", dma); -		snd_ad1848_free(chip); -		return -EBUSY; -	} -	chip->dma = dma; - -	if (hardware == AD1848_HW_THINKPAD) { -		chip->thinkpad_flag = 1; -		chip->hardware = AD1848_HW_DETECT; /* reset */ -		snd_ad1848_thinkpad_twiddle(chip, 1); -	} - -	if (snd_ad1848_probe(chip) < 0) { -		snd_ad1848_free(chip); -		return -ENODEV; -	} - -	/* Register device */ -	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { -		snd_ad1848_free(chip); -		return err; -	} - -#ifdef CONFIG_PM -	chip->suspend = snd_ad1848_suspend; -	chip->resume = snd_ad1848_resume; -#endif - -	*rchip = chip; -	return 0; -} - -EXPORT_SYMBOL(snd_ad1848_create); - -static struct snd_pcm_ops snd_ad1848_playback_ops = { -	.open =		snd_ad1848_playback_open, -	.close =	snd_ad1848_playback_close, -	.ioctl =	snd_ad1848_ioctl, -	.hw_params =	snd_ad1848_playback_hw_params, -	.hw_free =	snd_ad1848_playback_hw_free, -	.prepare =	snd_ad1848_playback_prepare, -	.trigger =	snd_ad1848_playback_trigger, -	.pointer =	snd_ad1848_playback_pointer, -}; - -static struct snd_pcm_ops snd_ad1848_capture_ops = { -	.open =		snd_ad1848_capture_open, -	.close =	snd_ad1848_capture_close, -	.ioctl =	snd_ad1848_ioctl, -	.hw_params =	snd_ad1848_capture_hw_params, -	.hw_free =	snd_ad1848_capture_hw_free, -	.prepare =	snd_ad1848_capture_prepare, -	.trigger =	snd_ad1848_capture_trigger, -	.pointer =	snd_ad1848_capture_pointer, -}; - -int snd_ad1848_pcm(struct snd_ad1848 *chip, int device, struct snd_pcm **rpcm) -{ -	struct snd_pcm *pcm; -	int err; - -	if ((err = snd_pcm_new(chip->card, "AD1848", device, 1, 1, &pcm)) < 0) -		return err; - -	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1848_playback_ops); -	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1848_capture_ops); - -	pcm->private_data = chip; -	pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; -	strcpy(pcm->name, snd_ad1848_chip_id(chip)); - -	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, -					      snd_dma_isa_data(), -					      64*1024, chip->dma > 3 ? 128*1024 : 64*1024); - -	chip->pcm = pcm; -	if (rpcm) -		*rpcm = pcm; -	return 0; -} - -EXPORT_SYMBOL(snd_ad1848_pcm); - -const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction) -{ -	return direction == SNDRV_PCM_STREAM_PLAYBACK ? -		&snd_ad1848_playback_ops : &snd_ad1848_capture_ops; -} - -EXPORT_SYMBOL(snd_ad1848_get_pcm_ops); - -/* - *  MIXER part - */ - -static int snd_ad1848_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	static char *texts[4] = { -		"Line", "Aux", "Mic", "Mix" -	}; - -	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	uinfo->count = 2; -	uinfo->value.enumerated.items = 4; -	if (uinfo->value.enumerated.item > 3) -		uinfo->value.enumerated.item = 3; -	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); -	return 0; -} - -static int snd_ad1848_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	 -	spin_lock_irqsave(&chip->reg_lock, flags); -	ucontrol->value.enumerated.item[0] = (chip->image[AD1848_LEFT_INPUT] & AD1848_MIXS_ALL) >> 6; -	ucontrol->value.enumerated.item[1] = (chip->image[AD1848_RIGHT_INPUT] & AD1848_MIXS_ALL) >> 6; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return 0; -} - -static int snd_ad1848_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	unsigned short left, right; -	int change; -	 -	if (ucontrol->value.enumerated.item[0] > 3 || -	    ucontrol->value.enumerated.item[1] > 3) -		return -EINVAL; -	left = ucontrol->value.enumerated.item[0] << 6; -	right = ucontrol->value.enumerated.item[1] << 6; -	spin_lock_irqsave(&chip->reg_lock, flags); -	left = (chip->image[AD1848_LEFT_INPUT] & ~AD1848_MIXS_ALL) | left; -	right = (chip->image[AD1848_RIGHT_INPUT] & ~AD1848_MIXS_ALL) | right; -	change = left != chip->image[AD1848_LEFT_INPUT] || -	         right != chip->image[AD1848_RIGHT_INPUT]; -	snd_ad1848_out(chip, AD1848_LEFT_INPUT, left); -	snd_ad1848_out(chip, AD1848_RIGHT_INPUT, right); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return change; -} - -static int snd_ad1848_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	int mask = (kcontrol->private_value >> 16) & 0xff; - -	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -	uinfo->count = 1; -	uinfo->value.integer.min = 0; -	uinfo->value.integer.max = mask; -	return 0; -} - -static int snd_ad1848_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0xff; -	int mask = (kcontrol->private_value >> 16) & 0xff; -	int invert = (kcontrol->private_value >> 24) & 0xff; -	 -	spin_lock_irqsave(&chip->reg_lock, flags); -	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (invert) -		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; -	return 0; -} - -static int snd_ad1848_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0xff; -	int mask = (kcontrol->private_value >> 16) & 0xff; -	int invert = (kcontrol->private_value >> 24) & 0xff; -	int change; -	unsigned short val; -	 -	val = (ucontrol->value.integer.value[0] & mask); -	if (invert) -		val = mask - val; -	val <<= shift; -	spin_lock_irqsave(&chip->reg_lock, flags); -	val = (chip->image[reg] & ~(mask << shift)) | val; -	change = val != chip->image[reg]; -	snd_ad1848_out(chip, reg, val); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return change; -} - -static int snd_ad1848_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	int mask = (kcontrol->private_value >> 24) & 0xff; - -	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -	uinfo->count = 2; -	uinfo->value.integer.min = 0; -	uinfo->value.integer.max = mask; -	return 0; -} - -static int snd_ad1848_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int left_reg = kcontrol->private_value & 0xff; -	int right_reg = (kcontrol->private_value >> 8) & 0xff; -	int shift_left = (kcontrol->private_value >> 16) & 0x07; -	int shift_right = (kcontrol->private_value >> 19) & 0x07; -	int mask = (kcontrol->private_value >> 24) & 0xff; -	int invert = (kcontrol->private_value >> 22) & 1; -	 -	spin_lock_irqsave(&chip->reg_lock, flags); -	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; -	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (invert) { -		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; -		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; -	} -	return 0; -} - -static int snd_ad1848_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_ad1848 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int left_reg = kcontrol->private_value & 0xff; -	int right_reg = (kcontrol->private_value >> 8) & 0xff; -	int shift_left = (kcontrol->private_value >> 16) & 0x07; -	int shift_right = (kcontrol->private_value >> 19) & 0x07; -	int mask = (kcontrol->private_value >> 24) & 0xff; -	int invert = (kcontrol->private_value >> 22) & 1; -	int change; -	unsigned short val1, val2; -	 -	val1 = ucontrol->value.integer.value[0] & mask; -	val2 = ucontrol->value.integer.value[1] & mask; -	if (invert) { -		val1 = mask - val1; -		val2 = mask - val2; -	} -	val1 <<= shift_left; -	val2 <<= shift_right; -	spin_lock_irqsave(&chip->reg_lock, flags); -	if (left_reg != right_reg) { -		val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; -		val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; -		change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; -		snd_ad1848_out(chip, left_reg, val1); -		snd_ad1848_out(chip, right_reg, val2); -	} else { -		val1 = (chip->image[left_reg] & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; -		change = val1 != chip->image[left_reg]; -		snd_ad1848_out(chip, left_reg, val1);		 -	} -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return change; -} - -/* - */ -int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, -			    const struct ad1848_mix_elem *c) -{ -	static struct snd_kcontrol_new newctls[] = { -		[AD1848_MIX_SINGLE] = { -			.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -			.info = snd_ad1848_info_single, -			.get = snd_ad1848_get_single, -			.put = snd_ad1848_put_single, -		}, -		[AD1848_MIX_DOUBLE] = { -			.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -			.info = snd_ad1848_info_double, -			.get = snd_ad1848_get_double, -			.put = snd_ad1848_put_double, -		}, -		[AD1848_MIX_CAPTURE] = { -			.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -			.info = snd_ad1848_info_mux, -			.get = snd_ad1848_get_mux, -			.put = snd_ad1848_put_mux, -		}, -	}; -	struct snd_kcontrol *ctl; -	int err; - -	ctl = snd_ctl_new1(&newctls[c->type], chip); -	if (! ctl) -		return -ENOMEM; -	strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name)); -	ctl->id.index = c->index; -	ctl->private_value = c->private_value; -	if (c->tlv) { -		ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; -		ctl->tlv.p = c->tlv; -	} -	if ((err = snd_ctl_add(chip->card, ctl)) < 0) -		return err; -	return 0; -} - -EXPORT_SYMBOL(snd_ad1848_add_ctl_elem); - -static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); -static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); -static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); - -static struct ad1848_mix_elem snd_ad1848_controls[] = { -AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), -AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1, -		  db_scale_6bit), -AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1), -AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1, -		  db_scale_5bit_12db_max), -AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1), -AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1, -		  db_scale_5bit_12db_max), -AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0, -		  db_scale_rec_gain), -{ -	.name = "Capture Source", -	.type = AD1848_MIX_CAPTURE, -}, -AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0), -AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0, -		  db_scale_6bit), -}; -                                         -int snd_ad1848_mixer(struct snd_ad1848 *chip) -{ -	struct snd_card *card; -	struct snd_pcm *pcm; -	unsigned int idx; -	int err; - -	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); - -	pcm = chip->pcm; -	card = chip->card; - -	strcpy(card->mixername, pcm->name); - -	for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) -		if ((err = snd_ad1848_add_ctl_elem(chip, &snd_ad1848_controls[idx])) < 0) -			return err; - -	return 0; -} - -EXPORT_SYMBOL(snd_ad1848_mixer); - -/* - *  INIT part - */ - -static int __init alsa_ad1848_init(void) -{ -	return 0; -} - -static void __exit alsa_ad1848_exit(void) -{ -} - -module_init(alsa_ad1848_init) -module_exit(alsa_ad1848_exit) diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c index 154e728..3e74d1a 100644 --- a/sound/isa/azt2320.c +++ b/sound/isa/azt2320.c @@ -38,7 +38,7 @@  #include <linux/moduleparam.h>  #include <sound/core.h>  #include <sound/initval.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #include <sound/mpu401.h>  #include <sound/opl3.h> @@ -76,7 +76,7 @@ struct snd_card_azt2320 {  	int dev_no;  	struct pnp_dev *dev;  	struct pnp_dev *devmpu; -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  };  static struct pnp_card_device_id snd_azt2320_pnpids[] = { @@ -181,7 +181,7 @@ static int __devinit snd_card_azt2320_probe(int dev,  	int error;  	struct snd_card *card;  	struct snd_card_azt2320 *acard; -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	struct snd_opl3 *opl3;  	if ((card = snd_card_new(index[dev], id[dev], THIS_MODULE, @@ -200,11 +200,11 @@ static int __devinit snd_card_azt2320_probe(int dev,  		return error;  	} -	if ((error = snd_cs4231_create(card, wss_port[dev], -1, -				       irq[dev], -				       dma1[dev], -				       dma2[dev], -				       CS4231_HW_DETECT, 0, &chip)) < 0) { +	error = snd_wss_create(card, wss_port[dev], -1, +			       irq[dev], +			       dma1[dev], dma2[dev], +			       WSS_HW_DETECT, 0, &chip); +	if (error < 0) {  		snd_card_free(card);  		return error;  	} @@ -214,15 +214,18 @@ static int __devinit snd_card_azt2320_probe(int dev,  	sprintf(card->longname, "%s, WSS at 0x%lx, irq %i, dma %i&%i",  		card->shortname, chip->port, irq[dev], dma1[dev], dma2[dev]); -	if ((error = snd_cs4231_pcm(chip, 0, NULL)) < 0) { +	error = snd_wss_pcm(chip, 0, NULL); +	if (error < 0) {  		snd_card_free(card);  		return error;  	} -	if ((error = snd_cs4231_mixer(chip)) < 0) { +	error = snd_wss_mixer(chip); +	if (error < 0) {  		snd_card_free(card);  		return error;  	} -	if ((error = snd_cs4231_timer(chip, 0, NULL)) < 0) { +	error = snd_wss_timer(chip, 0, NULL); +	if (error < 0) {  		snd_card_free(card);  		return error;  	} @@ -293,7 +296,7 @@ static int snd_azt2320_pnp_suspend(struct pnp_card_link *pcard, pm_message_t sta  {  	struct snd_card *card = pnp_get_card_drvdata(pcard);  	struct snd_card_azt2320 *acard = card->private_data; -	struct snd_cs4231 *chip = acard->chip; +	struct snd_wss *chip = acard->chip;  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);  	chip->suspend(chip); @@ -304,7 +307,7 @@ static int snd_azt2320_pnp_resume(struct pnp_card_link *pcard)  {  	struct snd_card *card = pnp_get_card_drvdata(pcard);  	struct snd_card_azt2320 *acard = card->private_data; -	struct snd_cs4231 *chip = acard->chip; +	struct snd_wss *chip = acard->chip;  	chip->resume(chip);  	snd_power_change_state(card, SNDRV_CTL_POWER_D0); diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 4d198ec..e49aec7 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -50,7 +50,7 @@  #include <linux/pnp.h>  #include <linux/moduleparam.h>  #include <sound/core.h> -#include <sound/ad1848.h> +#include <sound/wss.h>  #include <sound/sb.h>  #include <sound/initval.h> @@ -151,7 +151,7 @@ struct snd_cmi8330 {  	struct pnp_dev *play;  #endif  	struct snd_card *card; -	struct snd_ad1848 *wss; +	struct snd_wss *wss;  	struct snd_sb *sb;  	struct snd_pcm *pcm; @@ -174,32 +174,57 @@ MODULE_DEVICE_TABLE(pnp_card, snd_cmi8330_pnpids);  #endif -static struct ad1848_mix_elem snd_cmi8330_controls[] __devinitdata = { -AD1848_DOUBLE("Master Playback Volume", 0, CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), -AD1848_SINGLE("Loud Playback Switch", 0, CMI8330_MUTEMUX, 6, 1, 1), -AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1), -AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1), -AD1848_DOUBLE("Line Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0), -AD1848_DOUBLE("Line Playback Volume", 0, CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0), -AD1848_DOUBLE("Line Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0), -AD1848_DOUBLE("Line Capture Volume", 0, CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0), -AD1848_DOUBLE("CD Playback Switch", 0, CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), -AD1848_DOUBLE("CD Capture Switch", 0, CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), -AD1848_DOUBLE("CD Playback Volume", 0, CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), -AD1848_DOUBLE("CD Capture Volume", 0, CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), -AD1848_SINGLE("Mic Playback Switch", 0, CMI8330_MUTEMUX, 0, 1, 0), -AD1848_SINGLE("Mic Playback Volume", 0, CMI8330_OUTPUTVOL, 0, 7, 0), -AD1848_SINGLE("Mic Capture Switch", 0, CMI8330_RMUX3D, 0, 1, 0), -AD1848_SINGLE("Mic Capture Volume", 0, CMI8330_OUTPUTVOL, 5, 7, 0), -AD1848_DOUBLE("Wavetable Playback Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0), -AD1848_DOUBLE("Wavetable Playback Volume", 0, CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0), -AD1848_DOUBLE("Wavetable Capture Switch", 0, CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0), -AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0), -AD1848_SINGLE("3D Control - Switch", 0, CMI8330_RMUX3D, 5, 1, 1), -AD1848_SINGLE("PC Speaker Playback Volume", 0, CMI8330_OUTPUTVOL, 3, 3, 0), -AD1848_SINGLE("FM Playback Switch", 0, CMI8330_RECMUX, 3, 1, 1), -AD1848_SINGLE(SNDRV_CTL_NAME_IEC958("Input ",CAPTURE,SWITCH), 0, CMI8330_RMUX3D, 7, 1, 1), -AD1848_SINGLE(SNDRV_CTL_NAME_IEC958("Input ",PLAYBACK,SWITCH), 0, CMI8330_MUTEMUX, 7, 1, 1), +static struct snd_kcontrol_new snd_cmi8330_controls[] __devinitdata = { +WSS_DOUBLE("Master Playback Volume", 0, +		CMI8330_MASTVOL, CMI8330_MASTVOL, 4, 0, 15, 0), +WSS_SINGLE("Loud Playback Switch", 0, +		CMI8330_MUTEMUX, 6, 1, 1), +WSS_DOUBLE("PCM Playback Switch", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +WSS_DOUBLE("PCM Playback Volume", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), +WSS_DOUBLE("Line Playback Switch", 0, +		CMI8330_MUTEMUX, CMI8330_MUTEMUX, 4, 3, 1, 0), +WSS_DOUBLE("Line Playback Volume", 0, +		CMI8330_LINVOL, CMI8330_LINVOL, 4, 0, 15, 0), +WSS_DOUBLE("Line Capture Switch", 0, +		CMI8330_RMUX3D, CMI8330_RMUX3D, 2, 1, 1, 0), +WSS_DOUBLE("Line Capture Volume", 0, +		CMI8330_LINGAIN, CMI8330_LINGAIN, 4, 0, 15, 0), +WSS_DOUBLE("CD Playback Switch", 0, +		CMI8330_MUTEMUX, CMI8330_MUTEMUX, 2, 1, 1, 0), +WSS_DOUBLE("CD Capture Switch", 0, +		CMI8330_RMUX3D, CMI8330_RMUX3D, 4, 3, 1, 0), +WSS_DOUBLE("CD Playback Volume", 0, +		CMI8330_CDINVOL, CMI8330_CDINVOL, 4, 0, 15, 0), +WSS_DOUBLE("CD Capture Volume", 0, +		CMI8330_CDINGAIN, CMI8330_CDINGAIN, 4, 0, 15, 0), +WSS_SINGLE("Mic Playback Switch", 0, +		CMI8330_MUTEMUX, 0, 1, 0), +WSS_SINGLE("Mic Playback Volume", 0, +		CMI8330_OUTPUTVOL, 0, 7, 0), +WSS_SINGLE("Mic Capture Switch", 0, +		CMI8330_RMUX3D, 0, 1, 0), +WSS_SINGLE("Mic Capture Volume", 0, +		CMI8330_OUTPUTVOL, 5, 7, 0), +WSS_DOUBLE("Wavetable Playback Switch", 0, +		CMI8330_RECMUX, CMI8330_RECMUX, 1, 0, 1, 0), +WSS_DOUBLE("Wavetable Playback Volume", 0, +		CMI8330_WAVVOL, CMI8330_WAVVOL, 4, 0, 15, 0), +WSS_DOUBLE("Wavetable Capture Switch", 0, +		CMI8330_RECMUX, CMI8330_RECMUX, 5, 4, 1, 0), +WSS_DOUBLE("Wavetable Capture Volume", 0, +		CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4, 0, 15, 0), +WSS_SINGLE("3D Control - Switch", 0, +		CMI8330_RMUX3D, 5, 1, 1), +WSS_SINGLE("PC Speaker Playback Volume", 0, +		CMI8330_OUTPUTVOL, 3, 3, 0), +WSS_SINGLE("FM Playback Switch", 0, +		CMI8330_RECMUX, 3, 1, 1), +WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", CAPTURE, SWITCH), 0, +		CMI8330_RMUX3D, 7, 1, 1), +WSS_SINGLE(SNDRV_CTL_NAME_IEC958("Input ", PLAYBACK, SWITCH), 0, +		CMI8330_MUTEMUX, 7, 1, 1),  };  #ifdef ENABLE_SB_MIXER @@ -268,7 +293,10 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330  	strcpy(card->mixername, "CMI8330/C3D");  	for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) { -		if ((err = snd_ad1848_add_ctl_elem(acard->wss, &snd_cmi8330_controls[idx])) < 0) +		err = snd_ctl_add(card, +				snd_ctl_new1(&snd_cmi8330_controls[idx], +					     acard->wss)); +		if (err < 0)  			return err;  	} @@ -385,7 +413,7 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *  	chip->streams[CMI_SB_STREAM].private_data = chip->sb;  	/* AD1848 */ -	ops = snd_ad1848_get_pcm_ops(CMI_AD_STREAM); +	ops = snd_wss_get_pcm_ops(CMI_AD_STREAM);  	chip->streams[CMI_AD_STREAM].ops = *ops;  	chip->streams[CMI_AD_STREAM].open = ops->open;  	chip->streams[CMI_AD_STREAM].ops.open = cmi_open_callbacks[CMI_AD_STREAM]; @@ -461,16 +489,15 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)  	int i, err;  	acard = card->private_data; -	if ((err = snd_ad1848_create(card, -				     wssport[dev] + 4, -				     wssirq[dev], -				     wssdma[dev], -				     AD1848_HW_DETECT, -				     &acard->wss)) < 0) { +	err = snd_wss_create(card, wssport[dev] + 4, -1, +			     wssirq[dev], +			     wssdma[dev], -1, +			     WSS_HW_DETECT, 0, &acard->wss); +	if (err < 0) {  		snd_printk(KERN_ERR PFX "(AD1848) device busy??\n");  		return err;  	} -	if (acard->wss->hardware != AD1848_HW_CMI8330) { +	if (acard->wss->hardware != WSS_HW_CMI8330) {  		snd_printk(KERN_ERR PFX "(AD1848) not found during probe\n");  		return -ENODEV;  	} @@ -489,9 +516,10 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)  		return err;  	} -	snd_ad1848_out(acard->wss, AD1848_MISC_INFO, 0x40); /* switch on MODE2 */ +	snd_wss_out(acard->wss, CS4231_MISC_INFO, 0x40); /* switch on MODE2 */  	for (i = CMI8330_RMUX3D; i <= CMI8330_CDINGAIN; i++) -		snd_ad1848_out(acard->wss, i, snd_cmi8330_image[i - CMI8330_RMUX3D]); +		snd_wss_out(acard->wss, i, +			    snd_cmi8330_image[i - CMI8330_RMUX3D]);  	if ((err = snd_cmi8330_mixer(card, acard)) < 0) {  		snd_printk(KERN_ERR PFX "failed to create mixers\n"); diff --git a/sound/isa/cs423x/Makefile b/sound/isa/cs423x/Makefile index 5067ee0..5870ca2 100644 --- a/sound/isa/cs423x/Makefile +++ b/sound/isa/cs423x/Makefile @@ -3,14 +3,12 @@  # Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>  # -snd-cs4231-lib-objs := cs4231_lib.o  snd-cs4236-lib-objs := cs4236_lib.o  snd-cs4231-objs := cs4231.o  snd-cs4232-objs := cs4232.o  snd-cs4236-objs := cs4236.o  # Toplevel Module Dependency -obj-$(CONFIG_SND_CS4231_LIB) += snd-cs4231-lib.o  obj-$(CONFIG_SND_CS4231) += snd-cs4231.o  obj-$(CONFIG_SND_CS4232) += snd-cs4232.o  obj-$(CONFIG_SND_CS4236) += snd-cs4236.o snd-cs4236-lib.o diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c index e9462b9..ddd2891 100644 --- a/sound/isa/cs423x/cs4231.c +++ b/sound/isa/cs423x/cs4231.c @@ -27,7 +27,7 @@  #include <linux/wait.h>  #include <linux/moduleparam.h>  #include <sound/core.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #include <sound/mpu401.h>  #include <sound/initval.h> @@ -91,7 +91,7 @@ static int __devinit snd_cs4231_match(struct device *dev, unsigned int n)  static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)  {  	struct snd_card *card; -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	struct snd_pcm *pcm;  	int error; @@ -99,14 +99,14 @@ static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)  	if (!card)  		return -EINVAL; -	error = snd_cs4231_create(card, port[n], -1, irq[n], dma1[n], dma2[n], -			CS4231_HW_DETECT, 0, &chip); +	error = snd_wss_create(card, port[n], -1, irq[n], dma1[n], dma2[n], +			WSS_HW_DETECT, 0, &chip);  	if (error < 0)  		goto out;  	card->private_data = chip; -	error = snd_cs4231_pcm(chip, 0, &pcm); +	error = snd_wss_pcm(chip, 0, &pcm);  	if (error < 0)  		goto out; @@ -118,11 +118,11 @@ static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)  	if (dma2[n] >= 0)  		sprintf(card->longname + strlen(card->longname), "&%d", dma2[n]); -	error = snd_cs4231_mixer(chip); +	error = snd_wss_mixer(chip);  	if (error < 0)  		goto out; -	error = snd_cs4231_timer(chip, 0, NULL); +	error = snd_wss_timer(chip, 0, NULL);  	if (error < 0)  		goto out; @@ -160,7 +160,7 @@ static int __devexit snd_cs4231_remove(struct device *dev, unsigned int n)  static int snd_cs4231_suspend(struct device *dev, unsigned int n, pm_message_t state)  {  	struct snd_card *card = dev_get_drvdata(dev); -	struct snd_cs4231 *chip = card->private_data; +	struct snd_wss *chip = card->private_data;  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);  	chip->suspend(chip); @@ -170,7 +170,7 @@ static int snd_cs4231_suspend(struct device *dev, unsigned int n, pm_message_t s  static int snd_cs4231_resume(struct device *dev, unsigned int n)  {  	struct snd_card *card = dev_get_drvdata(dev); -	struct snd_cs4231 *chip = card->private_data; +	struct snd_wss *chip = card->private_data;  	chip->resume(chip);  	snd_power_change_state(card, SNDRV_CTL_POWER_D0); diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c deleted file mode 100644 index 521db70..0000000 --- a/sound/isa/cs423x/cs4231_lib.c +++ /dev/null @@ -1,1945 +0,0 @@ -/* - *  Copyright (c) by Jaroslav Kysela <perex@perex.cz> - *  Routines for control of CS4231(A)/CS4232/InterWave & compatible chips - * - *  Bugs: - *     - sometimes record brokes playback with WSS portion of  - *       Yamaha OPL3-SA3 chip - *     - CS4231 (GUS MAX) - still trouble with occasional noises - *                        - broken initialization? - * - *   This program is free software; you can redistribute it and/or modify - *   it under the terms of the GNU General Public License as published by - *   the Free Software Foundation; either version 2 of the License, or - *   (at your option) any later version. - * - *   This program is distributed in the hope that it will be useful, - *   but WITHOUT ANY WARRANTY; without even the implied warranty of - *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *   GNU General Public License for more details. - * - *   You should have received a copy of the GNU General Public License - *   along with this program; if not, write to the Free Software - *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA - * - */ - -#include <linux/delay.h> -#include <linux/pm.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <sound/core.h> -#include <sound/cs4231.h> -#include <sound/pcm_params.h> - -#include <asm/io.h> -#include <asm/dma.h> -#include <asm/irq.h> - -MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); -MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips"); -MODULE_LICENSE("GPL"); - -#if 0 -#define SNDRV_DEBUG_MCE -#endif - -/* - *  Some variables - */ - -static unsigned char freq_bits[14] = { -	/* 5510 */	0x00 | CS4231_XTAL2, -	/* 6620 */	0x0E | CS4231_XTAL2, -	/* 8000 */	0x00 | CS4231_XTAL1, -	/* 9600 */	0x0E | CS4231_XTAL1, -	/* 11025 */	0x02 | CS4231_XTAL2, -	/* 16000 */	0x02 | CS4231_XTAL1, -	/* 18900 */	0x04 | CS4231_XTAL2, -	/* 22050 */	0x06 | CS4231_XTAL2, -	/* 27042 */	0x04 | CS4231_XTAL1, -	/* 32000 */	0x06 | CS4231_XTAL1, -	/* 33075 */	0x0C | CS4231_XTAL2, -	/* 37800 */	0x08 | CS4231_XTAL2, -	/* 44100 */	0x0A | CS4231_XTAL2, -	/* 48000 */	0x0C | CS4231_XTAL1 -}; - -static unsigned int rates[14] = { -	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, -	27042, 32000, 33075, 37800, 44100, 48000 -}; - -static struct snd_pcm_hw_constraint_list hw_constraints_rates = { -	.count = ARRAY_SIZE(rates), -	.list = rates, -	.mask = 0, -}; - -static int snd_cs4231_xrate(struct snd_pcm_runtime *runtime) -{ -	return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); -} - -static unsigned char snd_cs4231_original_image[32] = -{ -	0x00,			/* 00/00 - lic */ -	0x00,			/* 01/01 - ric */ -	0x9f,			/* 02/02 - la1ic */ -	0x9f,			/* 03/03 - ra1ic */ -	0x9f,			/* 04/04 - la2ic */ -	0x9f,			/* 05/05 - ra2ic */ -	0xbf,			/* 06/06 - loc */ -	0xbf,			/* 07/07 - roc */ -	0x20,			/* 08/08 - pdfr */ -	CS4231_AUTOCALIB,	/* 09/09 - ic */ -	0x00,			/* 0a/10 - pc */ -	0x00,			/* 0b/11 - ti */ -	CS4231_MODE2,		/* 0c/12 - mi */ -	0xfc,			/* 0d/13 - lbc */ -	0x00,			/* 0e/14 - pbru */ -	0x00,			/* 0f/15 - pbrl */ -	0x80,			/* 10/16 - afei */ -	0x01,			/* 11/17 - afeii */ -	0x9f,			/* 12/18 - llic */ -	0x9f,			/* 13/19 - rlic */ -	0x00,			/* 14/20 - tlb */ -	0x00,			/* 15/21 - thb */ -	0x00,			/* 16/22 - la3mic/reserved */ -	0x00,			/* 17/23 - ra3mic/reserved */ -	0x00,			/* 18/24 - afs */ -	0x00,			/* 19/25 - lamoc/version */ -	0xcf,			/* 1a/26 - mioc */ -	0x00,			/* 1b/27 - ramoc/reserved */ -	0x20,			/* 1c/28 - cdfr */ -	0x00,			/* 1d/29 - res4 */ -	0x00,			/* 1e/30 - cbru */ -	0x00,			/* 1f/31 - cbrl */ -}; - -static unsigned char snd_opti93x_original_image[32] = -{ -	0x00,		/* 00/00 - l_mixout_outctrl */ -	0x00,		/* 01/01 - r_mixout_outctrl */ -	0x88,		/* 02/02 - l_cd_inctrl */ -	0x88,		/* 03/03 - r_cd_inctrl */ -	0x88,		/* 04/04 - l_a1/fm_inctrl */ -	0x88,		/* 05/05 - r_a1/fm_inctrl */ -	0x80,		/* 06/06 - l_dac_inctrl */ -	0x80,		/* 07/07 - r_dac_inctrl */ -	0x00,		/* 08/08 - ply_dataform_reg */ -	0x00,		/* 09/09 - if_conf */ -	0x00,		/* 0a/10 - pin_ctrl */ -	0x00,		/* 0b/11 - err_init_reg */ -	0x0a,		/* 0c/12 - id_reg */ -	0x00,		/* 0d/13 - reserved */ -	0x00,		/* 0e/14 - ply_upcount_reg */ -	0x00,		/* 0f/15 - ply_lowcount_reg */ -	0x88,		/* 10/16 - reserved/l_a1_inctrl */ -	0x88,		/* 11/17 - reserved/r_a1_inctrl */ -	0x88,		/* 12/18 - l_line_inctrl */ -	0x88,		/* 13/19 - r_line_inctrl */ -	0x88,		/* 14/20 - l_mic_inctrl */ -	0x88,		/* 15/21 - r_mic_inctrl */ -	0x80,		/* 16/22 - l_out_outctrl */ -	0x80,		/* 17/23 - r_out_outctrl */ -	0x00,		/* 18/24 - reserved */ -	0x00,		/* 19/25 - reserved */ -	0x00,		/* 1a/26 - reserved */ -	0x00,		/* 1b/27 - reserved */ -	0x00,		/* 1c/28 - cap_dataform_reg */ -	0x00,		/* 1d/29 - reserved */ -	0x00,		/* 1e/30 - cap_upcount_reg */ -	0x00		/* 1f/31 - cap_lowcount_reg */ -}; - -/* - *  Basic I/O functions - */ - -static inline void cs4231_outb(struct snd_cs4231 *chip, u8 offset, u8 val) -{ -	outb(val, chip->port + offset); -} - -static inline u8 cs4231_inb(struct snd_cs4231 *chip, u8 offset) -{ -	return inb(chip->port + offset); -} - -static void snd_cs4231_wait(struct snd_cs4231 *chip) -{ -	int timeout; - -	for (timeout = 250; -	     timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); -	     timeout--) -	     	udelay(100); -} - -static void snd_cs4231_outm(struct snd_cs4231 *chip, unsigned char reg, -			    unsigned char mask, unsigned char value) -{ -	unsigned char tmp = (chip->image[reg] & mask) | value; - -	snd_cs4231_wait(chip); -#ifdef CONFIG_SND_DEBUG -	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) -		snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); -#endif -	chip->image[reg] = tmp; -	if (!chip->calibrate_mute) { -		cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); -		wmb(); -		cs4231_outb(chip, CS4231P(REG), tmp); -		mb(); -	} -} - -static void snd_cs4231_dout(struct snd_cs4231 *chip, unsigned char reg, unsigned char value) -{ -	int timeout; - -	for (timeout = 250; -	     timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); -	     timeout--) -	     	udelay(10); -	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); -	cs4231_outb(chip, CS4231P(REG), value); -	mb(); -} - -void snd_cs4231_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char value) -{ -	snd_cs4231_wait(chip); -#ifdef CONFIG_SND_DEBUG -	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) -		snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); -#endif -	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); -	cs4231_outb(chip, CS4231P(REG), value); -	chip->image[reg] = value; -	mb(); -	snd_printdd("codec out - reg 0x%x = 0x%x\n", -			chip->mce_bit | reg, value); -} - -unsigned char snd_cs4231_in(struct snd_cs4231 *chip, unsigned char reg) -{ -	snd_cs4231_wait(chip); -#ifdef CONFIG_SND_DEBUG -	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) -		snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); -#endif -	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); -	mb(); -	return cs4231_inb(chip, CS4231P(REG)); -} - -void snd_cs4236_ext_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val) -{ -	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); -	cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01)); -	cs4231_outb(chip, CS4231P(REG), val); -	chip->eimage[CS4236_REG(reg)] = val; -#if 0 -	printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val); -#endif -} - -unsigned char snd_cs4236_ext_in(struct snd_cs4231 *chip, unsigned char reg) -{ -	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); -	cs4231_outb(chip, CS4231P(REG), reg | (chip->image[CS4236_EXT_REG] & 0x01)); -#if 1 -	return cs4231_inb(chip, CS4231P(REG)); -#else -	{ -		unsigned char res; -		res = cs4231_inb(chip, CS4231P(REG)); -		printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res); -		return res; -	} -#endif -} - -#if 0 - -static void snd_cs4231_debug(struct snd_cs4231 *chip) -{ -	printk("CS4231 REGS:      INDEX = 0x%02x  ", cs4231_inb(chip, CS4231P(REGSEL))); -	printk("                 STATUS = 0x%02x\n", cs4231_inb(chip, CS4231P(STATUS))); -	printk("  0x00: left input      = 0x%02x  ", snd_cs4231_in(chip, 0x00)); -	printk("  0x10: alt 1 (CFIG 2)  = 0x%02x\n", snd_cs4231_in(chip, 0x10)); -	printk("  0x01: right input     = 0x%02x  ", snd_cs4231_in(chip, 0x01)); -	printk("  0x11: alt 2 (CFIG 3)  = 0x%02x\n", snd_cs4231_in(chip, 0x11)); -	printk("  0x02: GF1 left input  = 0x%02x  ", snd_cs4231_in(chip, 0x02)); -	printk("  0x12: left line in    = 0x%02x\n", snd_cs4231_in(chip, 0x12)); -	printk("  0x03: GF1 right input = 0x%02x  ", snd_cs4231_in(chip, 0x03)); -	printk("  0x13: right line in   = 0x%02x\n", snd_cs4231_in(chip, 0x13)); -	printk("  0x04: CD left input   = 0x%02x  ", snd_cs4231_in(chip, 0x04)); -	printk("  0x14: timer low       = 0x%02x\n", snd_cs4231_in(chip, 0x14)); -	printk("  0x05: CD right input  = 0x%02x  ", snd_cs4231_in(chip, 0x05)); -	printk("  0x15: timer high      = 0x%02x\n", snd_cs4231_in(chip, 0x15)); -	printk("  0x06: left output     = 0x%02x  ", snd_cs4231_in(chip, 0x06)); -	printk("  0x16: left MIC (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x16)); -	printk("  0x07: right output    = 0x%02x  ", snd_cs4231_in(chip, 0x07)); -	printk("  0x17: right MIC (PnP) = 0x%02x\n", snd_cs4231_in(chip, 0x17)); -	printk("  0x08: playback format = 0x%02x  ", snd_cs4231_in(chip, 0x08)); -	printk("  0x18: IRQ status      = 0x%02x\n", snd_cs4231_in(chip, 0x18)); -	printk("  0x09: iface (CFIG 1)  = 0x%02x  ", snd_cs4231_in(chip, 0x09)); -	printk("  0x19: left line out   = 0x%02x\n", snd_cs4231_in(chip, 0x19)); -	printk("  0x0a: pin control     = 0x%02x  ", snd_cs4231_in(chip, 0x0a)); -	printk("  0x1a: mono control    = 0x%02x\n", snd_cs4231_in(chip, 0x1a)); -	printk("  0x0b: init & status   = 0x%02x  ", snd_cs4231_in(chip, 0x0b)); -	printk("  0x1b: right line out  = 0x%02x\n", snd_cs4231_in(chip, 0x1b)); -	printk("  0x0c: revision & mode = 0x%02x  ", snd_cs4231_in(chip, 0x0c)); -	printk("  0x1c: record format   = 0x%02x\n", snd_cs4231_in(chip, 0x1c)); -	printk("  0x0d: loopback        = 0x%02x  ", snd_cs4231_in(chip, 0x0d)); -	printk("  0x1d: var freq (PnP)  = 0x%02x\n", snd_cs4231_in(chip, 0x1d)); -	printk("  0x0e: ply upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0e)); -	printk("  0x1e: ply lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1e)); -	printk("  0x0f: rec upr count   = 0x%02x  ", snd_cs4231_in(chip, 0x0f)); -	printk("  0x1f: rec lwr count   = 0x%02x\n", snd_cs4231_in(chip, 0x1f)); -} - -#endif - -/* - *  CS4231 detection / MCE routines - */ - -static void snd_cs4231_busy_wait(struct snd_cs4231 *chip) -{ -	int timeout; - -	/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ -	for (timeout = 5; timeout > 0; timeout--) -		cs4231_inb(chip, CS4231P(REGSEL)); -	/* end of cleanup sequence */ -	for (timeout = 250; -	     timeout > 0 && (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); -	     timeout--) -	     	udelay(10); -} - -void snd_cs4231_mce_up(struct snd_cs4231 *chip) -{ -	unsigned long flags; -	int timeout; - -	snd_cs4231_wait(chip); -#ifdef CONFIG_SND_DEBUG -	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) -		snd_printk("mce_up - auto calibration time out (0)\n"); -#endif -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->mce_bit |= CS4231_MCE; -	timeout = cs4231_inb(chip, CS4231P(REGSEL)); -	if (timeout == 0x80) -		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); -	if (!(timeout & CS4231_MCE)) -		cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -} - -void snd_cs4231_mce_down(struct snd_cs4231 *chip) -{ -	unsigned long flags; -	unsigned long end_time; -	int timeout; - -	snd_cs4231_busy_wait(chip); - -#ifdef CONFIG_SND_DEBUG -	if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) -		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL)); -#endif -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->mce_bit &= ~CS4231_MCE; -	timeout = cs4231_inb(chip, CS4231P(REGSEL)); -	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (timeout == 0x80) -		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); -	if ((timeout & CS4231_MCE) == 0 || -	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { -		return; -	} - -	/* -	 * Wait for (possible -- during init auto-calibration may not be set) -	 * calibration process to start. Needs upto 5 sample periods on AD1848 -	 * which at the slowest possible rate of 5.5125 kHz means 907 us. -	 */ -	msleep(1); - -	snd_printdd("(1) jiffies = %lu\n", jiffies); - -	/* check condition up to 250 ms */ -	end_time = jiffies + msecs_to_jiffies(250); -	while (snd_cs4231_in(chip, CS4231_TEST_INIT) & -		CS4231_CALIB_IN_PROGRESS) { - -		if (time_after(jiffies, end_time)) { -			snd_printk(KERN_ERR "mce_down - " -					"auto calibration time out (2)\n"); -			return; -		} -		msleep(1); -	} - -	snd_printdd("(2) jiffies = %lu\n", jiffies); - -	/* check condition up to 100 ms */ -	end_time = jiffies + msecs_to_jiffies(100); -	while (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) { -		if (time_after(jiffies, end_time)) { -			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); -			return; -		} -		msleep(1); -	} - -	snd_printdd("(3) jiffies = %lu\n", jiffies); -	snd_printd("mce_down - exit = 0x%x\n", cs4231_inb(chip, CS4231P(REGSEL))); -} - -static unsigned int snd_cs4231_get_count(unsigned char format, unsigned int size) -{ -	switch (format & 0xe0) { -	case CS4231_LINEAR_16: -	case CS4231_LINEAR_16_BIG: -		size >>= 1; -		break; -	case CS4231_ADPCM_16: -		return size >> 2; -	} -	if (format & CS4231_STEREO) -		size >>= 1; -	return size; -} - -static int snd_cs4231_trigger(struct snd_pcm_substream *substream, -			      int cmd) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	int result = 0; -	unsigned int what; -	struct snd_pcm_substream *s; -	int do_start; - -#if 0 -	printk("codec trigger!!! - what = %i, enable = %i, status = 0x%x\n", what, enable, cs4231_inb(chip, CS4231P(STATUS))); -#endif - -	switch (cmd) { -	case SNDRV_PCM_TRIGGER_START: -	case SNDRV_PCM_TRIGGER_RESUME: -		do_start = 1; break; -	case SNDRV_PCM_TRIGGER_STOP: -	case SNDRV_PCM_TRIGGER_SUSPEND: -		do_start = 0; break; -	default: -		return -EINVAL; -	} - -	what = 0; -	snd_pcm_group_for_each_entry(s, substream) { -		if (s == chip->playback_substream) { -			what |= CS4231_PLAYBACK_ENABLE; -			snd_pcm_trigger_done(s, substream); -		} else if (s == chip->capture_substream) { -			what |= CS4231_RECORD_ENABLE; -			snd_pcm_trigger_done(s, substream); -		} -	} -	spin_lock(&chip->reg_lock); -	if (do_start) { -		chip->image[CS4231_IFACE_CTRL] |= what; -		if (chip->trigger) -			chip->trigger(chip, what, 1); -	} else { -		chip->image[CS4231_IFACE_CTRL] &= ~what; -		if (chip->trigger) -			chip->trigger(chip, what, 0); -	} -	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); -	spin_unlock(&chip->reg_lock); -#if 0 -	snd_cs4231_debug(chip); -#endif -	return result; -} - -/* - *  CODEC I/O - */ - -static unsigned char snd_cs4231_get_rate(unsigned int rate) -{ -	int i; - -	for (i = 0; i < ARRAY_SIZE(rates); i++) -		if (rate == rates[i]) -			return freq_bits[i]; -	// snd_BUG(); -	return freq_bits[ARRAY_SIZE(rates) - 1]; -} - -static unsigned char snd_cs4231_get_format(struct snd_cs4231 *chip, -				           int format, -                                           int channels) -{ -	unsigned char rformat; - -	rformat = CS4231_LINEAR_8; -	switch (format) { -	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = CS4231_ULAW_8; break; -	case SNDRV_PCM_FORMAT_A_LAW:	rformat = CS4231_ALAW_8; break; -	case SNDRV_PCM_FORMAT_S16_LE:	rformat = CS4231_LINEAR_16; break; -	case SNDRV_PCM_FORMAT_S16_BE:	rformat = CS4231_LINEAR_16_BIG; break; -	case SNDRV_PCM_FORMAT_IMA_ADPCM:	rformat = CS4231_ADPCM_16; break; -	} -	if (channels > 1) -		rformat |= CS4231_STEREO; -#if 0 -	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); -#endif -	return rformat; -} - -static void snd_cs4231_calibrate_mute(struct snd_cs4231 *chip, int mute) -{ -	unsigned long flags; - -	mute = mute ? 1 : 0; -	spin_lock_irqsave(&chip->reg_lock, flags); -	if (chip->calibrate_mute == mute) { -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		return; -	} -	if (!mute) { -		snd_cs4231_dout(chip, CS4231_LEFT_INPUT, chip->image[CS4231_LEFT_INPUT]); -		snd_cs4231_dout(chip, CS4231_RIGHT_INPUT, chip->image[CS4231_RIGHT_INPUT]); -		snd_cs4231_dout(chip, CS4231_LOOPBACK, chip->image[CS4231_LOOPBACK]); -	} -	snd_cs4231_dout(chip, CS4231_AUX1_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_LEFT_INPUT]); -	snd_cs4231_dout(chip, CS4231_AUX1_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX1_RIGHT_INPUT]); -	snd_cs4231_dout(chip, CS4231_AUX2_LEFT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_LEFT_INPUT]); -	snd_cs4231_dout(chip, CS4231_AUX2_RIGHT_INPUT, mute ? 0x80 : chip->image[CS4231_AUX2_RIGHT_INPUT]); -	snd_cs4231_dout(chip, CS4231_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LEFT_OUTPUT]); -	snd_cs4231_dout(chip, CS4231_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_OUTPUT]); -	snd_cs4231_dout(chip, CS4231_LEFT_LINE_IN, mute ? 0x80 : chip->image[CS4231_LEFT_LINE_IN]); -	snd_cs4231_dout(chip, CS4231_RIGHT_LINE_IN, mute ? 0x80 : chip->image[CS4231_RIGHT_LINE_IN]); -	snd_cs4231_dout(chip, CS4231_MONO_CTRL, mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); -	if (chip->hardware == CS4231_HW_INTERWAVE) { -		snd_cs4231_dout(chip, CS4231_LEFT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_LEFT_MIC_INPUT]); -		snd_cs4231_dout(chip, CS4231_RIGHT_MIC_INPUT, mute ? 0x80 : chip->image[CS4231_RIGHT_MIC_INPUT]);		 -		snd_cs4231_dout(chip, CS4231_LINE_LEFT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_LEFT_OUTPUT]); -		snd_cs4231_dout(chip, CS4231_LINE_RIGHT_OUTPUT, mute ? 0x80 : chip->image[CS4231_LINE_RIGHT_OUTPUT]); -	} -	chip->calibrate_mute = mute; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -} - -static void snd_cs4231_playback_format(struct snd_cs4231 *chip, -				       struct snd_pcm_hw_params *params, -				       unsigned char pdfr) -{ -	unsigned long flags; -	int full_calib = 1; - -	mutex_lock(&chip->mce_mutex); -	snd_cs4231_calibrate_mute(chip, 1); -	if (chip->hardware == CS4231_HW_CS4231A || -	    (chip->hardware & CS4231_HW_CS4232_MASK)) { -		spin_lock_irqsave(&chip->reg_lock, flags); -		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) {	/* rate is same? */ -			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); -			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); -			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x10); -			udelay(100); /* Fixes audible clicks at least on GUS MAX */ -			full_calib = 0; -		} -		spin_unlock_irqrestore(&chip->reg_lock, flags); -	} -	if (full_calib) { -		snd_cs4231_mce_up(chip); -		spin_lock_irqsave(&chip->reg_lock, flags); -		if (chip->hardware != CS4231_HW_INTERWAVE && !chip->single_dma) { -			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, -					(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) ? -					(pdfr & 0xf0) | (chip->image[CS4231_REC_FORMAT] & 0x0f) : -				        pdfr); -		} else { -			snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT] = pdfr); -		} -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		if (chip->hardware == CS4231_HW_OPL3SA2) -			udelay(100);	/* this seems to help */ -		snd_cs4231_mce_down(chip); -	} -	snd_cs4231_calibrate_mute(chip, 0); -	mutex_unlock(&chip->mce_mutex); -} - -static void snd_cs4231_capture_format(struct snd_cs4231 *chip, -				      struct snd_pcm_hw_params *params, -                                      unsigned char cdfr) -{ -	unsigned long flags; -	int full_calib = 1; - -	mutex_lock(&chip->mce_mutex); -	snd_cs4231_calibrate_mute(chip, 1); -	if (chip->hardware == CS4231_HW_CS4231A || -	    (chip->hardware & CS4231_HW_CS4232_MASK)) { -		spin_lock_irqsave(&chip->reg_lock, flags); -		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) ||	/* rate is same? */ -		    (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { -			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); -			snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT] = cdfr); -			snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~0x20); -			full_calib = 0; -		} -		spin_unlock_irqrestore(&chip->reg_lock, flags); -	} -	if (full_calib) { -		snd_cs4231_mce_up(chip); -		spin_lock_irqsave(&chip->reg_lock, flags); -		if (chip->hardware != CS4231_HW_INTERWAVE) { -			if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { -				snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, -					       ((chip->single_dma ? cdfr : chip->image[CS4231_PLAYBK_FORMAT]) & 0xf0) | -					       (cdfr & 0x0f)); -				spin_unlock_irqrestore(&chip->reg_lock, flags); -				snd_cs4231_mce_down(chip); -				snd_cs4231_mce_up(chip); -				spin_lock_irqsave(&chip->reg_lock, flags); -			} -		} -		snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr); -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		snd_cs4231_mce_down(chip); -	} -	snd_cs4231_calibrate_mute(chip, 0); -	mutex_unlock(&chip->mce_mutex); -} - -/* - *  Timer interface - */ - -static unsigned long snd_cs4231_timer_resolution(struct snd_timer * timer) -{ -	struct snd_cs4231 *chip = snd_timer_chip(timer); -	if (chip->hardware & CS4231_HW_CS4236B_MASK) -		return 14467; -	else -		return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920; -} - -static int snd_cs4231_timer_start(struct snd_timer * timer) -{ -	unsigned long flags; -	unsigned int ticks; -	struct snd_cs4231 *chip = snd_timer_chip(timer); -	spin_lock_irqsave(&chip->reg_lock, flags); -	ticks = timer->sticks; -	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || -	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || -	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { -		snd_cs4231_out(chip, CS4231_TIMER_HIGH, chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8)); -		snd_cs4231_out(chip, CS4231_TIMER_LOW, chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks); -		snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | CS4231_TIMER_ENABLE); -	} -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return 0; -} - -static int snd_cs4231_timer_stop(struct snd_timer * timer) -{ -	unsigned long flags; -	struct snd_cs4231 *chip = snd_timer_chip(timer); -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return 0; -} - -static void snd_cs4231_init(struct snd_cs4231 *chip) -{ -	unsigned long flags; - -	snd_cs4231_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("init: (1)\n"); -#endif -	snd_cs4231_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | -			     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO | -			     CS4231_CALIB_MODE); -	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; -	snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_cs4231_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("init: (2)\n"); -#endif - -	snd_cs4231_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_cs4231_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("init: (3) - afei = 0x%x\n", chip->image[CS4231_ALT_FEATURE_1]); -#endif - -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_2, chip->image[CS4231_ALT_FEATURE_2]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	snd_cs4231_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, chip->image[CS4231_PLAYBK_FORMAT]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_cs4231_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("init: (4)\n"); -#endif - -	snd_cs4231_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_cs4231_out(chip, CS4231_REC_FORMAT, chip->image[CS4231_REC_FORMAT]); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_cs4231_mce_down(chip); - -#ifdef SNDRV_DEBUG_MCE -	snd_printk("init: (5)\n"); -#endif -} - -static int snd_cs4231_open(struct snd_cs4231 *chip, unsigned int mode) -{ -	unsigned long flags; - -	mutex_lock(&chip->open_mutex); -	if ((chip->mode & mode) || -	    ((chip->mode & CS4231_MODE_OPEN) && chip->single_dma)) { -		mutex_unlock(&chip->open_mutex); -		return -EAGAIN; -	} -	if (chip->mode & CS4231_MODE_OPEN) { -		chip->mode |= mode; -		mutex_unlock(&chip->open_mutex); -		return 0; -	} -	/* ok. now enable and ack CODEC IRQ */ -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | -		       CS4231_RECORD_IRQ | -		       CS4231_TIMER_IRQ); -	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); -	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ -	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ -	chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE; -	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); -	snd_cs4231_out(chip, CS4231_IRQ_STATUS, CS4231_PLAYBACK_IRQ | -		       CS4231_RECORD_IRQ | -		       CS4231_TIMER_IRQ); -	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	chip->mode = mode; -	mutex_unlock(&chip->open_mutex); -	return 0; -} - -static void snd_cs4231_close(struct snd_cs4231 *chip, unsigned int mode) -{ -	unsigned long flags; - -	mutex_lock(&chip->open_mutex); -	chip->mode &= ~mode; -	if (chip->mode & CS4231_MODE_OPEN) { -		mutex_unlock(&chip->open_mutex); -		return; -	} -	snd_cs4231_calibrate_mute(chip, 1); - -	/* disable IRQ */ -	spin_lock_irqsave(&chip->reg_lock, flags); -	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); -	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ -	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ -	chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE; -	snd_cs4231_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); - -	/* now disable record & playback */ - -	if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | -					       CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		snd_cs4231_mce_up(chip); -		spin_lock_irqsave(&chip->reg_lock, flags); -		chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | -						     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); -		snd_cs4231_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); -		spin_unlock_irqrestore(&chip->reg_lock, flags); -		snd_cs4231_mce_down(chip); -		spin_lock_irqsave(&chip->reg_lock, flags); -	} - -	/* clear IRQ again */ -	snd_cs4231_out(chip, CS4231_IRQ_STATUS, 0); -	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ -	cs4231_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	snd_cs4231_calibrate_mute(chip, 0); - -	chip->mode = 0; -	mutex_unlock(&chip->open_mutex); -} - -/* - *  timer open/close - */ - -static int snd_cs4231_timer_open(struct snd_timer * timer) -{ -	struct snd_cs4231 *chip = snd_timer_chip(timer); -	snd_cs4231_open(chip, CS4231_MODE_TIMER); -	return 0; -} - -static int snd_cs4231_timer_close(struct snd_timer * timer) -{ -	struct snd_cs4231 *chip = snd_timer_chip(timer); -	snd_cs4231_close(chip, CS4231_MODE_TIMER); -	return 0; -} - -static struct snd_timer_hardware snd_cs4231_timer_table = -{ -	.flags =	SNDRV_TIMER_HW_AUTO, -	.resolution =	9945, -	.ticks =	65535, -	.open =		snd_cs4231_timer_open, -	.close =	snd_cs4231_timer_close, -	.c_resolution = snd_cs4231_timer_resolution, -	.start =	snd_cs4231_timer_start, -	.stop =		snd_cs4231_timer_stop, -}; - -/* - *  ok.. exported functions.. - */ - -static int snd_cs4231_playback_hw_params(struct snd_pcm_substream *substream, -					 struct snd_pcm_hw_params *hw_params) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	unsigned char new_pdfr; -	int err; - -	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) -		return err; -	new_pdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | -		   snd_cs4231_get_rate(params_rate(hw_params)); -	chip->set_playback_format(chip, hw_params, new_pdfr); -	return 0; -} - -static int snd_cs4231_playback_hw_free(struct snd_pcm_substream *substream) -{ -	return snd_pcm_lib_free_pages(substream); -} - -static int snd_cs4231_playback_prepare(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	unsigned long flags; -	unsigned int size = snd_pcm_lib_buffer_bytes(substream); -	unsigned int count = snd_pcm_lib_period_bytes(substream); - -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->p_dma_size = size; -	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); -	snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); -	count = snd_cs4231_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1; -	snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); -	snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -#if 0 -	snd_cs4231_debug(chip); -#endif -	return 0; -} - -static int snd_cs4231_capture_hw_params(struct snd_pcm_substream *substream, -					struct snd_pcm_hw_params *hw_params) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	unsigned char new_cdfr; -	int err; - -	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) -		return err; -	new_cdfr = snd_cs4231_get_format(chip, params_format(hw_params), params_channels(hw_params)) | -		   snd_cs4231_get_rate(params_rate(hw_params)); -	chip->set_capture_format(chip, hw_params, new_cdfr); -	return 0; -} - -static int snd_cs4231_capture_hw_free(struct snd_pcm_substream *substream) -{ -	return snd_pcm_lib_free_pages(substream); -} - -static int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	unsigned long flags; -	unsigned int size = snd_pcm_lib_buffer_bytes(substream); -	unsigned int count = snd_pcm_lib_period_bytes(substream); - -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->c_dma_size = size; -	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); -	snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); -	count = snd_cs4231_get_count(chip->image[CS4231_REC_FORMAT], count) - 1; -	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { -		snd_cs4231_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); -		snd_cs4231_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); -	} else { -		snd_cs4231_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count); -		snd_cs4231_out(chip, CS4231_REC_UPR_CNT, (unsigned char) (count >> 8)); -	} -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return 0; -} - -void snd_cs4231_overrange(struct snd_cs4231 *chip) -{ -	unsigned long flags; -	unsigned char res; - -	spin_lock_irqsave(&chip->reg_lock, flags); -	res = snd_cs4231_in(chip, CS4231_TEST_INIT); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (res & (0x08 | 0x02))	/* detect overrange only above 0dB; may be user selectable? */ -		chip->capture_substream->runtime->overrange++; -} - -irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id) -{ -	struct snd_cs4231 *chip = dev_id; -	unsigned char status; - -	status = snd_cs4231_in(chip, CS4231_IRQ_STATUS); -	if (status & CS4231_TIMER_IRQ) { -		if (chip->timer) -			snd_timer_interrupt(chip->timer, chip->timer->sticks); -	}		 -	if (chip->single_dma && chip->hardware != CS4231_HW_INTERWAVE) { -		if (status & CS4231_PLAYBACK_IRQ) { -			if (chip->mode & CS4231_MODE_PLAY) { -				if (chip->playback_substream) -					snd_pcm_period_elapsed(chip->playback_substream); -			} -			if (chip->mode & CS4231_MODE_RECORD) { -				if (chip->capture_substream) { -					snd_cs4231_overrange(chip); -					snd_pcm_period_elapsed(chip->capture_substream); -				} -			} -		} -	} else { -		if (status & CS4231_PLAYBACK_IRQ) { -			if (chip->playback_substream) -				snd_pcm_period_elapsed(chip->playback_substream); -		} -		if (status & CS4231_RECORD_IRQ) { -			if (chip->capture_substream) { -				snd_cs4231_overrange(chip); -				snd_pcm_period_elapsed(chip->capture_substream); -			} -		} -	} - -	spin_lock(&chip->reg_lock); -	snd_cs4231_outm(chip, CS4231_IRQ_STATUS, ~CS4231_ALL_IRQS | ~status, 0); -	spin_unlock(&chip->reg_lock); -	return IRQ_HANDLED; -} - -static snd_pcm_uframes_t snd_cs4231_playback_pointer(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	size_t ptr; - -	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) -		return 0; -	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); -	return bytes_to_frames(substream->runtime, ptr); -} - -static snd_pcm_uframes_t snd_cs4231_capture_pointer(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	size_t ptr; -	 -	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) -		return 0; -	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); -	return bytes_to_frames(substream->runtime, ptr); -} - -/* - - */ - -static int snd_cs4231_probe(struct snd_cs4231 *chip) -{ -	unsigned long flags; -	int i, id, rev; -	unsigned char *ptr; -	unsigned int hw; - -#if 0 -	snd_cs4231_debug(chip); -#endif -	id = 0; -	for (i = 0; i < 50; i++) { -		mb(); -		if (cs4231_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) -			udelay(2000); -		else { -			spin_lock_irqsave(&chip->reg_lock, flags); -			snd_cs4231_out(chip, CS4231_MISC_INFO, CS4231_MODE2); -			id = snd_cs4231_in(chip, CS4231_MISC_INFO) & 0x0f; -			spin_unlock_irqrestore(&chip->reg_lock, flags); -			if (id == 0x0a) -				break;	/* this is valid value */ -		} -	} -	snd_printdd("cs4231: port = 0x%lx, id = 0x%x\n", chip->port, id); -	if (id != 0x0a) -		return -ENODEV;	/* no valid device found */ - -	if (((hw = chip->hardware) & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { -		rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7; -		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev); -		if (rev == 0x80) { -			unsigned char tmp = snd_cs4231_in(chip, 23); -			snd_cs4231_out(chip, 23, ~tmp); -			if (snd_cs4231_in(chip, 23) != tmp) -				chip->hardware = CS4231_HW_AD1845; -			else -				chip->hardware = CS4231_HW_CS4231; -		} else if (rev == 0xa0) { -			chip->hardware = CS4231_HW_CS4231A; -		} else if (rev == 0xa2) { -			chip->hardware = CS4231_HW_CS4232; -		} else if (rev == 0xb2) { -			chip->hardware = CS4231_HW_CS4232A; -		} else if (rev == 0x83) { -			chip->hardware = CS4231_HW_CS4236; -		} else if (rev == 0x03) { -			chip->hardware = CS4231_HW_CS4236B; -		} else { -			snd_printk("unknown CS chip with version 0x%x\n", rev); -			return -ENODEV;		/* unknown CS4231 chip? */ -		} -	} -	spin_lock_irqsave(&chip->reg_lock, flags); -	cs4231_inb(chip, CS4231P(STATUS));	/* clear any pendings IRQ */ -	cs4231_outb(chip, CS4231P(STATUS), 0); -	mb(); -	spin_unlock_irqrestore(&chip->reg_lock, flags); - -	chip->image[CS4231_MISC_INFO] = CS4231_MODE2; -	switch (chip->hardware) { -	case CS4231_HW_INTERWAVE: -		chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3; -		break; -	case CS4231_HW_CS4235: -	case CS4231_HW_CS4236B: -	case CS4231_HW_CS4237B: -	case CS4231_HW_CS4238B: -	case CS4231_HW_CS4239: -		if (hw == CS4231_HW_DETECT3) -			chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3; -		else -			chip->hardware = CS4231_HW_CS4236; -		break; -	} - -	chip->image[CS4231_IFACE_CTRL] = -	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | -	    (chip->single_dma ? CS4231_SINGLE_DMA : 0); -	if (chip->hardware != CS4231_HW_OPTI93X) { -		chip->image[CS4231_ALT_FEATURE_1] = 0x80; -		chip->image[CS4231_ALT_FEATURE_2] = -			chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; -	} -	ptr = (unsigned char *) &chip->image; -	snd_cs4231_mce_down(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	for (i = 0; i < 32; i++)	/* ok.. fill all CS4231 registers */ -		snd_cs4231_out(chip, i, *ptr++); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_cs4231_mce_up(chip); -	snd_cs4231_mce_down(chip); - -	mdelay(2); - -	/* ok.. try check hardware version for CS4236+ chips */ -	if ((hw & CS4231_HW_TYPE_MASK) == CS4231_HW_DETECT) { -		if (chip->hardware == CS4231_HW_CS4236B) { -			rev = snd_cs4236_ext_in(chip, CS4236_VERSION); -			snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff); -			id = snd_cs4236_ext_in(chip, CS4236_VERSION); -			snd_cs4236_ext_out(chip, CS4236_VERSION, rev); -			snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id); -			if ((id & 0x1f) == 0x1d) {	/* CS4235 */ -				chip->hardware = CS4231_HW_CS4235; -				switch (id >> 5) { -				case 4: -				case 5: -				case 6: -					break; -				default: -					snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id); -				} -			} else if ((id & 0x1f) == 0x0b) {	/* CS4236/B */ -				switch (id >> 5) { -				case 4: -				case 5: -				case 6: -				case 7: -					chip->hardware = CS4231_HW_CS4236B; -					break; -				default: -					snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id); -				} -			} else if ((id & 0x1f) == 0x08) {	/* CS4237B */ -				chip->hardware = CS4231_HW_CS4237B; -				switch (id >> 5) { -				case 4: -				case 5: -				case 6: -				case 7: -					break; -				default: -					snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id); -				} -			} else if ((id & 0x1f) == 0x09) {	/* CS4238B */ -				chip->hardware = CS4231_HW_CS4238B; -				switch (id >> 5) { -				case 5: -				case 6: -				case 7: -					break; -				default: -					snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id); -				} -			} else if ((id & 0x1f) == 0x1e) {	/* CS4239 */ -				chip->hardware = CS4231_HW_CS4239; -				switch (id >> 5) { -				case 4: -				case 5: -				case 6: -					break; -				default: -					snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id); -				} -			} else { -				snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id); -			} -		} -	} -	return 0;		/* all things are ok.. */ -} - -/* - - */ - -static struct snd_pcm_hardware snd_cs4231_playback = -{ -	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | -				 SNDRV_PCM_INFO_MMAP_VALID | -				 SNDRV_PCM_INFO_RESUME | -				 SNDRV_PCM_INFO_SYNC_START), -	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | -				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), -	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, -	.rate_min =		5510, -	.rate_max =		48000, -	.channels_min =		1, -	.channels_max =		2, -	.buffer_bytes_max =	(128*1024), -	.period_bytes_min =	64, -	.period_bytes_max =	(128*1024), -	.periods_min =		1, -	.periods_max =		1024, -	.fifo_size =		0, -}; - -static struct snd_pcm_hardware snd_cs4231_capture = -{ -	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | -				 SNDRV_PCM_INFO_MMAP_VALID | -				 SNDRV_PCM_INFO_RESUME | -				 SNDRV_PCM_INFO_SYNC_START), -	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | -				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), -	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, -	.rate_min =		5510, -	.rate_max =		48000, -	.channels_min =		1, -	.channels_max =		2, -	.buffer_bytes_max =	(128*1024), -	.period_bytes_min =	64, -	.period_bytes_max =	(128*1024), -	.periods_min =		1, -	.periods_max =		1024, -	.fifo_size =		0, -}; - -/* - - */ - -static int snd_cs4231_playback_open(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	int err; - -	runtime->hw = snd_cs4231_playback; - -	/* hardware bug in InterWave chipset */ -	if (chip->hardware == CS4231_HW_INTERWAVE && chip->dma1 > 3) -	    	runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW; -	 -	/* hardware limitation of cheap chips */ -	if (chip->hardware == CS4231_HW_CS4235 || -	    chip->hardware == CS4231_HW_CS4239) -		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; - -	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); -	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); - -	if (chip->claim_dma) { -		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0) -			return err; -	} - -	if ((err = snd_cs4231_open(chip, CS4231_MODE_PLAY)) < 0) { -		if (chip->release_dma) -			chip->release_dma(chip, chip->dma_private_data, chip->dma1); -		snd_free_pages(runtime->dma_area, runtime->dma_bytes); -		return err; -	} -	chip->playback_substream = substream; -	snd_pcm_set_sync(substream); -	chip->rate_constraint(runtime); -	return 0; -} - -static int snd_cs4231_capture_open(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); -	struct snd_pcm_runtime *runtime = substream->runtime; -	int err; - -	runtime->hw = snd_cs4231_capture; - -	/* hardware limitation of cheap chips */ -	if (chip->hardware == CS4231_HW_CS4235 || -	    chip->hardware == CS4231_HW_CS4239) -		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; - -	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); -	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); - -	if (chip->claim_dma) { -		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0) -			return err; -	} - -	if ((err = snd_cs4231_open(chip, CS4231_MODE_RECORD)) < 0) { -		if (chip->release_dma) -			chip->release_dma(chip, chip->dma_private_data, chip->dma2); -		snd_free_pages(runtime->dma_area, runtime->dma_bytes); -		return err; -	} -	chip->capture_substream = substream; -	snd_pcm_set_sync(substream); -	chip->rate_constraint(runtime); -	return 0; -} - -static int snd_cs4231_playback_close(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); - -	chip->playback_substream = NULL; -	snd_cs4231_close(chip, CS4231_MODE_PLAY); -	return 0; -} - -static int snd_cs4231_capture_close(struct snd_pcm_substream *substream) -{ -	struct snd_cs4231 *chip = snd_pcm_substream_chip(substream); - -	chip->capture_substream = NULL; -	snd_cs4231_close(chip, CS4231_MODE_RECORD); -	return 0; -} - -#ifdef CONFIG_PM - -/* lowlevel suspend callback for CS4231 */ -static void snd_cs4231_suspend(struct snd_cs4231 *chip) -{ -	int reg; -	unsigned long flags; -	 -	snd_pcm_suspend_all(chip->pcm); -	spin_lock_irqsave(&chip->reg_lock, flags); -	for (reg = 0; reg < 32; reg++) -		chip->image[reg] = snd_cs4231_in(chip, reg); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -} - -/* lowlevel resume callback for CS4231 */ -static void snd_cs4231_resume(struct snd_cs4231 *chip) -{ -	int reg; -	unsigned long flags; -	/* int timeout; */ -	 -	snd_cs4231_mce_up(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	for (reg = 0; reg < 32; reg++) { -		switch (reg) { -		case CS4231_VERSION: -			break; -		default: -			snd_cs4231_out(chip, reg, chip->image[reg]); -			break; -		} -	} -	spin_unlock_irqrestore(&chip->reg_lock, flags); -#if 1 -	snd_cs4231_mce_down(chip); -#else -	/* The following is a workaround to avoid freeze after resume on TP600E. -	   This is the first half of copy of snd_cs4231_mce_down(), but doesn't -	   include rescheduling.  -- iwai -	   */ -	snd_cs4231_busy_wait(chip); -	spin_lock_irqsave(&chip->reg_lock, flags); -	chip->mce_bit &= ~CS4231_MCE; -	timeout = cs4231_inb(chip, CS4231P(REGSEL)); -	cs4231_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (timeout == 0x80) -		snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); -	if ((timeout & CS4231_MCE) == 0 || -	    !(chip->hardware & (CS4231_HW_CS4231_MASK | CS4231_HW_CS4232_MASK))) { -		return; -	} -	snd_cs4231_busy_wait(chip); -#endif -} -#endif /* CONFIG_PM */ - -static int snd_cs4231_free(struct snd_cs4231 *chip) -{ -	release_and_free_resource(chip->res_port); -	release_and_free_resource(chip->res_cport); -	if (chip->irq >= 0) { -		disable_irq(chip->irq); -		if (!(chip->hwshare & CS4231_HWSHARE_IRQ)) -			free_irq(chip->irq, (void *) chip); -	} -	if (!(chip->hwshare & CS4231_HWSHARE_DMA1) && chip->dma1 >= 0) { -		snd_dma_disable(chip->dma1); -		free_dma(chip->dma1); -	} -	if (!(chip->hwshare & CS4231_HWSHARE_DMA2) && chip->dma2 >= 0 && chip->dma2 != chip->dma1) { -		snd_dma_disable(chip->dma2); -		free_dma(chip->dma2); -	} -	if (chip->timer) -		snd_device_free(chip->card, chip->timer); -	kfree(chip); -	return 0; -} - -static int snd_cs4231_dev_free(struct snd_device *device) -{ -	struct snd_cs4231 *chip = device->device_data; -	return snd_cs4231_free(chip);	 -} - -const char *snd_cs4231_chip_id(struct snd_cs4231 *chip) -{ -	switch (chip->hardware) { -	case CS4231_HW_CS4231:	return "CS4231"; -	case CS4231_HW_CS4231A: return "CS4231A"; -	case CS4231_HW_CS4232:	return "CS4232"; -	case CS4231_HW_CS4232A:	return "CS4232A"; -	case CS4231_HW_CS4235:	return "CS4235"; -	case CS4231_HW_CS4236:  return "CS4236"; -	case CS4231_HW_CS4236B: return "CS4236B"; -	case CS4231_HW_CS4237B: return "CS4237B"; -	case CS4231_HW_CS4238B: return "CS4238B"; -	case CS4231_HW_CS4239:	return "CS4239"; -	case CS4231_HW_INTERWAVE: return "AMD InterWave"; -	case CS4231_HW_OPL3SA2: return chip->card->shortname; -	case CS4231_HW_AD1845: return "AD1845"; -	case CS4231_HW_OPTI93X: return "OPTi 93x"; -	default: return "???"; -	} -} - -static int snd_cs4231_new(struct snd_card *card, -			  unsigned short hardware, -			  unsigned short hwshare, -			  struct snd_cs4231 ** rchip) -{ -	struct snd_cs4231 *chip; - -	*rchip = NULL; -	chip = kzalloc(sizeof(*chip), GFP_KERNEL); -	if (chip == NULL) -		return -ENOMEM; -	chip->hardware = hardware; -	chip->hwshare = hwshare; - -	spin_lock_init(&chip->reg_lock); -	mutex_init(&chip->mce_mutex); -	mutex_init(&chip->open_mutex); -	chip->card = card; -	chip->rate_constraint = snd_cs4231_xrate; -	chip->set_playback_format = snd_cs4231_playback_format; -	chip->set_capture_format = snd_cs4231_capture_format; -	if (chip->hardware == CS4231_HW_OPTI93X) -		memcpy(&chip->image, &snd_opti93x_original_image, -		       sizeof(snd_opti93x_original_image)); -	else -		memcpy(&chip->image, &snd_cs4231_original_image, -		       sizeof(snd_cs4231_original_image)); - -        *rchip = chip; -        return 0; -} - -int snd_cs4231_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_cs4231 ** rchip) -{ -	static struct snd_device_ops ops = { -		.dev_free =	snd_cs4231_dev_free, -	}; -	struct snd_cs4231 *chip; -	int err; - -	err = snd_cs4231_new(card, hardware, hwshare, &chip); -	if (err < 0) -		return err; -	 -	chip->irq = -1; -	chip->dma1 = -1; -	chip->dma2 = -1; - -	if ((chip->res_port = request_region(port, 4, "CS4231")) == NULL) { -		snd_printk(KERN_ERR "cs4231: can't grab port 0x%lx\n", port); -		snd_cs4231_free(chip); -		return -EBUSY; -	} -	chip->port = port; -	if ((long)cport >= 0 && (chip->res_cport = request_region(cport, 8, "CS4232 Control")) == NULL) { -		snd_printk(KERN_ERR "cs4231: can't grab control port 0x%lx\n", cport); -		snd_cs4231_free(chip); -		return -ENODEV; -	} -	chip->cport = cport; -	if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, IRQF_DISABLED, "CS4231", (void *) chip)) { -		snd_printk(KERN_ERR "cs4231: can't grab IRQ %d\n", irq); -		snd_cs4231_free(chip); -		return -EBUSY; -	} -	chip->irq = irq; -	if (!(hwshare & CS4231_HWSHARE_DMA1) && request_dma(dma1, "CS4231 - 1")) { -		snd_printk(KERN_ERR "cs4231: can't grab DMA1 %d\n", dma1); -		snd_cs4231_free(chip); -		return -EBUSY; -	} -	chip->dma1 = dma1; -	if (!(hwshare & CS4231_HWSHARE_DMA2) && dma1 != dma2 && dma2 >= 0 && request_dma(dma2, "CS4231 - 2")) { -		snd_printk(KERN_ERR "cs4231: can't grab DMA2 %d\n", dma2); -		snd_cs4231_free(chip); -		return -EBUSY; -	} -	if (dma1 == dma2 || dma2 < 0) { -		chip->single_dma = 1; -		chip->dma2 = chip->dma1; -	} else -		chip->dma2 = dma2; - -	/* global setup */ -	if (snd_cs4231_probe(chip) < 0) { -		snd_cs4231_free(chip); -		return -ENODEV; -	} -	snd_cs4231_init(chip); - -#if 0 -	if (chip->hardware & CS4231_HW_CS4232_MASK) { -		if (chip->res_cport == NULL) -			snd_printk("CS4232 control port features are not accessible\n"); -	} -#endif - -	/* Register device */ -	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { -		snd_cs4231_free(chip); -		return err; -	} - -#ifdef CONFIG_PM -	/* Power Management */ -	chip->suspend = snd_cs4231_suspend; -	chip->resume = snd_cs4231_resume; -#endif - -	*rchip = chip; -	return 0; -} - -static struct snd_pcm_ops snd_cs4231_playback_ops = { -	.open =		snd_cs4231_playback_open, -	.close =	snd_cs4231_playback_close, -	.ioctl =	snd_pcm_lib_ioctl, -	.hw_params =	snd_cs4231_playback_hw_params, -	.hw_free =	snd_cs4231_playback_hw_free, -	.prepare =	snd_cs4231_playback_prepare, -	.trigger =	snd_cs4231_trigger, -	.pointer =	snd_cs4231_playback_pointer, -}; - -static struct snd_pcm_ops snd_cs4231_capture_ops = { -	.open =		snd_cs4231_capture_open, -	.close =	snd_cs4231_capture_close, -	.ioctl =	snd_pcm_lib_ioctl, -	.hw_params =	snd_cs4231_capture_hw_params, -	.hw_free =	snd_cs4231_capture_hw_free, -	.prepare =	snd_cs4231_capture_prepare, -	.trigger =	snd_cs4231_trigger, -	.pointer =	snd_cs4231_capture_pointer, -}; - -int snd_cs4231_pcm(struct snd_cs4231 *chip, int device, struct snd_pcm **rpcm) -{ -	struct snd_pcm *pcm; -	int err; - -	if ((err = snd_pcm_new(chip->card, "CS4231", device, 1, 1, &pcm)) < 0) -		return err; - -	spin_lock_init(&chip->reg_lock); -	mutex_init(&chip->mce_mutex); -	mutex_init(&chip->open_mutex); - -	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_cs4231_playback_ops); -	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cs4231_capture_ops); -	 -	/* global setup */ -	pcm->private_data = chip; -	pcm->info_flags = 0; -	if (chip->single_dma) -		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; -	if (chip->hardware != CS4231_HW_INTERWAVE) -		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; -	strcpy(pcm->name, snd_cs4231_chip_id(chip)); - -	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, -					      snd_dma_isa_data(), -					      64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); - -	chip->pcm = pcm; -	if (rpcm) -		*rpcm = pcm; -	return 0; -} - -static void snd_cs4231_timer_free(struct snd_timer *timer) -{ -	struct snd_cs4231 *chip = timer->private_data; -	chip->timer = NULL; -} - -int snd_cs4231_timer(struct snd_cs4231 *chip, int device, struct snd_timer **rtimer) -{ -	struct snd_timer *timer; -	struct snd_timer_id tid; -	int err; - -	/* Timer initialization */ -	tid.dev_class = SNDRV_TIMER_CLASS_CARD; -	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; -	tid.card = chip->card->number; -	tid.device = device; -	tid.subdevice = 0; -	if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0) -		return err; -	strcpy(timer->name, snd_cs4231_chip_id(chip)); -	timer->private_data = chip; -	timer->private_free = snd_cs4231_timer_free; -	timer->hw = snd_cs4231_timer_table; -	chip->timer = timer; -	if (rtimer) -		*rtimer = timer; -	return 0; -} -	 -/* - *  MIXER part - */ - -static int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	static char *texts[4] = { -		"Line", "Aux", "Mic", "Mix" -	}; -	static char *opl3sa_texts[4] = { -		"Line", "CD", "Mic", "Mix" -	}; -	static char *gusmax_texts[4] = { -		"Line", "Synth", "Mic", "Mix" -	}; -	char **ptexts = texts; -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); - -	snd_assert(chip->card != NULL, return -EINVAL); -	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; -	uinfo->count = 2; -	uinfo->value.enumerated.items = 4; -	if (uinfo->value.enumerated.item > 3) -		uinfo->value.enumerated.item = 3; -	if (!strcmp(chip->card->driver, "GUS MAX")) -		ptexts = gusmax_texts; -	switch (chip->hardware) { -	case CS4231_HW_INTERWAVE: ptexts = gusmax_texts; break; -	case CS4231_HW_OPL3SA2: ptexts = opl3sa_texts; break; -	} -	strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); -	return 0; -} - -static int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	 -	spin_lock_irqsave(&chip->reg_lock, flags); -	ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6; -	ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return 0; -} - -static int snd_cs4231_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	unsigned short left, right; -	int change; -	 -	if (ucontrol->value.enumerated.item[0] > 3 || -	    ucontrol->value.enumerated.item[1] > 3) -		return -EINVAL; -	left = ucontrol->value.enumerated.item[0] << 6; -	right = ucontrol->value.enumerated.item[1] << 6; -	spin_lock_irqsave(&chip->reg_lock, flags); -	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left; -	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right; -	change = left != chip->image[CS4231_LEFT_INPUT] || -	         right != chip->image[CS4231_RIGHT_INPUT]; -	snd_cs4231_out(chip, CS4231_LEFT_INPUT, left); -	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, right); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return change; -} - -int snd_cs4231_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	int mask = (kcontrol->private_value >> 16) & 0xff; - -	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -	uinfo->count = 1; -	uinfo->value.integer.min = 0; -	uinfo->value.integer.max = mask; -	return 0; -} - -int snd_cs4231_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0xff; -	int mask = (kcontrol->private_value >> 16) & 0xff; -	int invert = (kcontrol->private_value >> 24) & 0xff; -	 -	spin_lock_irqsave(&chip->reg_lock, flags); -	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (invert) -		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; -	return 0; -} - -int snd_cs4231_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0xff; -	int mask = (kcontrol->private_value >> 16) & 0xff; -	int invert = (kcontrol->private_value >> 24) & 0xff; -	int change; -	unsigned short val; -	 -	val = (ucontrol->value.integer.value[0] & mask); -	if (invert) -		val = mask - val; -	val <<= shift; -	spin_lock_irqsave(&chip->reg_lock, flags); -	val = (chip->image[reg] & ~(mask << shift)) | val; -	change = val != chip->image[reg]; -	snd_cs4231_out(chip, reg, val); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return change; -} - -int snd_cs4231_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	int mask = (kcontrol->private_value >> 24) & 0xff; - -	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -	uinfo->count = 2; -	uinfo->value.integer.min = 0; -	uinfo->value.integer.max = mask; -	return 0; -} - -int snd_cs4231_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int left_reg = kcontrol->private_value & 0xff; -	int right_reg = (kcontrol->private_value >> 8) & 0xff; -	int shift_left = (kcontrol->private_value >> 16) & 0x07; -	int shift_right = (kcontrol->private_value >> 19) & 0x07; -	int mask = (kcontrol->private_value >> 24) & 0xff; -	int invert = (kcontrol->private_value >> 22) & 1; -	 -	spin_lock_irqsave(&chip->reg_lock, flags); -	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; -	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	if (invert) { -		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; -		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; -	} -	return 0; -} - -int snd_cs4231_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) -{ -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); -	unsigned long flags; -	int left_reg = kcontrol->private_value & 0xff; -	int right_reg = (kcontrol->private_value >> 8) & 0xff; -	int shift_left = (kcontrol->private_value >> 16) & 0x07; -	int shift_right = (kcontrol->private_value >> 19) & 0x07; -	int mask = (kcontrol->private_value >> 24) & 0xff; -	int invert = (kcontrol->private_value >> 22) & 1; -	int change; -	unsigned short val1, val2; -	 -	val1 = ucontrol->value.integer.value[0] & mask; -	val2 = ucontrol->value.integer.value[1] & mask; -	if (invert) { -		val1 = mask - val1; -		val2 = mask - val2; -	} -	val1 <<= shift_left; -	val2 <<= shift_right; -	spin_lock_irqsave(&chip->reg_lock, flags); -	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; -	val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; -	change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; -	snd_cs4231_out(chip, left_reg, val1); -	snd_cs4231_out(chip, right_reg, val2); -	spin_unlock_irqrestore(&chip->reg_lock, flags); -	return change; -} - -static struct snd_kcontrol_new snd_cs4231_controls[] = { -CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), -CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), -CS4231_DOUBLE("Line Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), -CS4231_DOUBLE("Line Playback Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), -CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Aux Playback Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), -CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Aux Playback Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), -CS4231_SINGLE("Mono Playback Switch", 0, CS4231_MONO_CTRL, 7, 1, 1), -CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), -CS4231_SINGLE("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, 6, 1, 1), -CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), -CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), -{ -	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = "Capture Source", -	.info = snd_cs4231_info_mux, -	.get = snd_cs4231_get_mux, -	.put = snd_cs4231_put_mux, -}, -CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), -CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), -CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1) -}; -                                         -static struct snd_kcontrol_new snd_opti93x_controls[] = { -CS4231_DOUBLE("Master Playback Switch", 0, -	      OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), -CS4231_DOUBLE("Master Playback Volume", 0, -	      OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), -CS4231_DOUBLE("PCM Playback Switch", 0, -	      CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), -CS4231_DOUBLE("PCM Playback Volume", 0, -	      CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1), -CS4231_DOUBLE("FM Playback Switch", 0, -	      CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("FM Playback Volume", 0, -	      CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1), -CS4231_DOUBLE("Line Playback Switch", 0, -	      CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), -CS4231_DOUBLE("Line Playback Volume", 0, -	      CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1), -CS4231_DOUBLE("Mic Playback Switch", 0, -	      OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Mic Playback Volume", 0, -	      OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), -CS4231_DOUBLE("Mic Boost", 0, -	      CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), -CS4231_DOUBLE("CD Playback Switch", 0, -	      CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("CD Playback Volume", 0, -	      CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1), -CS4231_DOUBLE("Aux Playback Switch", 0, -	      OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Aux Playback Volume", 0, -	      OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), -CS4231_DOUBLE("Capture Volume", 0, -	      CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), -{ -	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = "Capture Source", -	.info = snd_cs4231_info_mux, -	.get = snd_cs4231_get_mux, -	.put = snd_cs4231_put_mux, -} -}; - -int snd_cs4231_mixer(struct snd_cs4231 *chip) -{ -	struct snd_card *card; -	unsigned int idx; -	int err; - -	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); - -	card = chip->card; - -	strcpy(card->mixername, chip->pcm->name); - -	if (chip->hardware == CS4231_HW_OPTI93X) -		for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { -			err = snd_ctl_add(card, -					snd_ctl_new1(&snd_opti93x_controls[idx], -						     chip)); -			if (err < 0) -				return err; -		} -	else -		for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { -			err = snd_ctl_add(card, -					snd_ctl_new1(&snd_cs4231_controls[idx], -						     chip)); -			if (err < 0) -				return err; -		} -	return 0; -} - -EXPORT_SYMBOL(snd_cs4231_out); -EXPORT_SYMBOL(snd_cs4231_in); -EXPORT_SYMBOL(snd_cs4236_ext_out); -EXPORT_SYMBOL(snd_cs4236_ext_in); -EXPORT_SYMBOL(snd_cs4231_mce_up); -EXPORT_SYMBOL(snd_cs4231_mce_down); -EXPORT_SYMBOL(snd_cs4231_overrange); -EXPORT_SYMBOL(snd_cs4231_interrupt); -EXPORT_SYMBOL(snd_cs4231_chip_id); -EXPORT_SYMBOL(snd_cs4231_create); -EXPORT_SYMBOL(snd_cs4231_pcm); -EXPORT_SYMBOL(snd_cs4231_mixer); -EXPORT_SYMBOL(snd_cs4231_timer); -EXPORT_SYMBOL(snd_cs4231_info_single); -EXPORT_SYMBOL(snd_cs4231_get_single); -EXPORT_SYMBOL(snd_cs4231_put_single); -EXPORT_SYMBOL(snd_cs4231_info_double); -EXPORT_SYMBOL(snd_cs4231_get_double); -EXPORT_SYMBOL(snd_cs4231_put_double); - -/* - *  INIT part - */ - -static int __init alsa_cs4231_init(void) -{ -	return 0; -} - -static void __exit alsa_cs4231_exit(void) -{ -} - -module_init(alsa_cs4231_init) -module_exit(alsa_cs4231_exit) diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index 4d4b8dd..91f9c15 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -26,7 +26,7 @@  #include <linux/pnp.h>  #include <linux/moduleparam.h>  #include <sound/core.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #include <sound/mpu401.h>  #include <sound/opl3.h>  #include <sound/initval.h> @@ -134,7 +134,7 @@ static int pnp_registered;  #endif /* CONFIG_PNP */  struct snd_card_cs4236 { -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	struct resource *res_sb_port;  #ifdef CONFIG_PNP  	struct pnp_dev *wss; @@ -239,6 +239,8 @@ static struct pnp_card_device_id snd_cs423x_pnpids[] = {  	{ .id = "CSC9836", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },  	/* Gallant SC-70P */  	{ .id = "CSC9837", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } }, +	/* Techmakers MF-4236PW */ +	{ .id = "CSCa736", .devs = { { "CSC0000" }, { "CSC0010" }, { "CSC0003" } } },  	/* TerraTec AudioSystem EWS64XL - CS4236B */  	{ .id = "CSCa836", .devs = { { "CSCa800" }, { "CSCa810" }, { "CSCa803" } } },  	/* TerraTec AudioSystem EWS64XL - CS4236B */ @@ -396,7 +398,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)  {  	struct snd_card_cs4236 *acard;  	struct snd_pcm *pcm; -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	struct snd_opl3 *opl3;  	int err; @@ -408,41 +410,37 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)  		}  #ifdef CS4232 -	if ((err = snd_cs4231_create(card, -				     port[dev], -				     cport[dev], -				     irq[dev], -				     dma1[dev], -				     dma2[dev], -				     CS4231_HW_DETECT, -				     0, -				     &chip)) < 0) +	err = snd_wss_create(card, port[dev], cport[dev], +			     irq[dev], +			     dma1[dev], dma2[dev], +			     WSS_HW_DETECT, 0, &chip); +	if (err < 0)  		return err;  	acard->chip = chip; -	if ((err = snd_cs4231_pcm(chip, 0, &pcm)) < 0) +	err = snd_wss_pcm(chip, 0, &pcm); +	if (err < 0)  		return err; -	if ((err = snd_cs4231_mixer(chip)) < 0) +	err = snd_wss_mixer(chip); +	if (err < 0)  		return err;  #else /* CS4236 */ -	if ((err = snd_cs4236_create(card, -				     port[dev], -				     cport[dev], -				     irq[dev], -				     dma1[dev], -				     dma2[dev], -				     CS4231_HW_DETECT, -				     0, -				     &chip)) < 0) +	err = snd_cs4236_create(card, +				port[dev], cport[dev], +				irq[dev], dma1[dev], dma2[dev], +				WSS_HW_DETECT, 0, &chip); +	if (err < 0)  		return err;  	acard->chip = chip; -	if ((err = snd_cs4236_pcm(chip, 0, &pcm)) < 0) +	err = snd_cs4236_pcm(chip, 0, &pcm); +	if (err < 0)  		return err; -	if ((err = snd_cs4236_mixer(chip)) < 0) +	err = snd_cs4236_mixer(chip); +	if (err < 0)  		return err;  #endif  	strcpy(card->driver, pcm->name); @@ -455,7 +453,8 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)  	if (dma2[dev] >= 0)  		sprintf(card->longname + strlen(card->longname), "&%d", dma2[dev]); -	if ((err = snd_cs4231_timer(chip, 0, NULL)) < 0) +	err = snd_wss_timer(chip, 0, NULL); +	if (err < 0)  		return err;  	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) { diff --git a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c index de71910..6a85fdc 100644 --- a/sound/isa/cs423x/cs4236_lib.c +++ b/sound/isa/cs423x/cs4236_lib.c @@ -85,7 +85,7 @@  #include <linux/time.h>  #include <linux/wait.h>  #include <sound/core.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #include <sound/asoundef.h>  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); @@ -121,13 +121,14 @@ static unsigned char snd_cs4236_ext_map[18] = {   *   */ -static void snd_cs4236_ctrl_out(struct snd_cs4231 *chip, unsigned char reg, unsigned char val) +static void snd_cs4236_ctrl_out(struct snd_wss *chip, +				unsigned char reg, unsigned char val)  {  	outb(reg, chip->cport + 3);  	outb(chip->cimage[reg] = val, chip->cport + 4);  } -static unsigned char snd_cs4236_ctrl_in(struct snd_cs4231 *chip, unsigned char reg) +static unsigned char snd_cs4236_ctrl_in(struct snd_wss *chip, unsigned char reg)  {  	outb(reg, chip->cport + 3);  	return inb(chip->cport + 4); @@ -180,44 +181,52 @@ static unsigned char divisor_to_rate_register(unsigned int divisor)  	}  } -static void snd_cs4236_playback_format(struct snd_cs4231 *chip, struct snd_pcm_hw_params *params, unsigned char pdfr) +static void snd_cs4236_playback_format(struct snd_wss *chip, +				       struct snd_pcm_hw_params *params, +				       unsigned char pdfr)  {  	unsigned long flags;  	unsigned char rate = divisor_to_rate_register(params->rate_den);  	spin_lock_irqsave(&chip->reg_lock, flags);  	/* set fast playback format change and clean playback FIFO */ -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); -	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10); +	snd_wss_out(chip, CS4231_ALT_FEATURE_1, +		    chip->image[CS4231_ALT_FEATURE_1] | 0x10); +	snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); +	snd_wss_out(chip, CS4231_ALT_FEATURE_1, +		    chip->image[CS4231_ALT_FEATURE_1] & ~0x10);  	snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate);  	spin_unlock_irqrestore(&chip->reg_lock, flags);  } -static void snd_cs4236_capture_format(struct snd_cs4231 *chip, struct snd_pcm_hw_params *params, unsigned char cdfr) +static void snd_cs4236_capture_format(struct snd_wss *chip, +				      struct snd_pcm_hw_params *params, +				      unsigned char cdfr)  {  	unsigned long flags;  	unsigned char rate = divisor_to_rate_register(params->rate_den);  	spin_lock_irqsave(&chip->reg_lock, flags);  	/* set fast capture format change and clean capture FIFO */ -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); -	snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20); +	snd_wss_out(chip, CS4231_ALT_FEATURE_1, +		    chip->image[CS4231_ALT_FEATURE_1] | 0x20); +	snd_wss_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); +	snd_wss_out(chip, CS4231_ALT_FEATURE_1, +		    chip->image[CS4231_ALT_FEATURE_1] & ~0x20);  	snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate);  	spin_unlock_irqrestore(&chip->reg_lock, flags);  }  #ifdef CONFIG_PM -static void snd_cs4236_suspend(struct snd_cs4231 *chip) +static void snd_cs4236_suspend(struct snd_wss *chip)  {  	int reg;  	unsigned long flags;  	spin_lock_irqsave(&chip->reg_lock, flags);  	for (reg = 0; reg < 32; reg++) -		chip->image[reg] = snd_cs4231_in(chip, reg); +		chip->image[reg] = snd_wss_in(chip, reg);  	for (reg = 0; reg < 18; reg++)  		chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg));  	for (reg = 2; reg < 9; reg++) @@ -225,12 +234,12 @@ static void snd_cs4236_suspend(struct snd_cs4231 *chip)  	spin_unlock_irqrestore(&chip->reg_lock, flags);  } -static void snd_cs4236_resume(struct snd_cs4231 *chip) +static void snd_cs4236_resume(struct snd_wss *chip)  {  	int reg;  	unsigned long flags; -	snd_cs4231_mce_up(chip); +	snd_wss_mce_up(chip);  	spin_lock_irqsave(&chip->reg_lock, flags);  	for (reg = 0; reg < 32; reg++) {  		switch (reg) { @@ -240,7 +249,7 @@ static void snd_cs4236_resume(struct snd_cs4231 *chip)  		case 29:	/* why? CS4235 - master right */  			break;  		default: -			snd_cs4231_out(chip, reg, chip->image[reg]); +			snd_wss_out(chip, reg, chip->image[reg]);  			break;  		}  	} @@ -255,7 +264,7 @@ static void snd_cs4236_resume(struct snd_cs4231 *chip)  		}  	}  	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_cs4231_mce_down(chip); +	snd_wss_mce_down(chip);  }  #endif /* CONFIG_PM */ @@ -266,24 +275,26 @@ int snd_cs4236_create(struct snd_card *card,  		      int irq, int dma1, int dma2,  		      unsigned short hardware,  		      unsigned short hwshare, -		      struct snd_cs4231 ** rchip) +		      struct snd_wss **rchip)  { -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	unsigned char ver1, ver2;  	unsigned int reg;  	int err;  	*rchip = NULL; -	if (hardware == CS4231_HW_DETECT) -		hardware = CS4231_HW_DETECT3; +	if (hardware == WSS_HW_DETECT) +		hardware = WSS_HW_DETECT3;  	if (cport < 0x100) {  		snd_printk("please, specify control port for CS4236+ chips\n");  		return -ENODEV;  	} -	if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0) +	err = snd_wss_create(card, port, cport, +			     irq, dma1, dma2, hardware, hwshare, &chip); +	if (err < 0)  		return err; -	if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) { +	if (!(chip->hardware & WSS_HW_CS4236B_MASK)) {  	        snd_printk("CS4236+: MODE3 and extended registers not available, hardware=0x%x\n",chip->hardware);  		snd_device_free(card, chip);  		return -ENODEV; @@ -330,20 +341,20 @@ int snd_cs4236_create(struct snd_card *card,  		snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), snd_cs4236_ext_map[reg]);          /* initialize compatible but more featured registers */ -	snd_cs4231_out(chip, CS4231_LEFT_INPUT, 0x40); -	snd_cs4231_out(chip, CS4231_RIGHT_INPUT, 0x40); -	snd_cs4231_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); -	snd_cs4231_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); -	snd_cs4231_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); -	snd_cs4231_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); -	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); -	snd_cs4231_out(chip, CS4231_LEFT_LINE_IN, 0xff); -	snd_cs4231_out(chip, CS4231_RIGHT_LINE_IN, 0xff); +	snd_wss_out(chip, CS4231_LEFT_INPUT, 0x40); +	snd_wss_out(chip, CS4231_RIGHT_INPUT, 0x40); +	snd_wss_out(chip, CS4231_AUX1_LEFT_INPUT, 0xff); +	snd_wss_out(chip, CS4231_AUX1_RIGHT_INPUT, 0xff); +	snd_wss_out(chip, CS4231_AUX2_LEFT_INPUT, 0xdf); +	snd_wss_out(chip, CS4231_AUX2_RIGHT_INPUT, 0xdf); +	snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff); +	snd_wss_out(chip, CS4231_LEFT_LINE_IN, 0xff); +	snd_wss_out(chip, CS4231_RIGHT_LINE_IN, 0xff);  	switch (chip->hardware) { -	case CS4231_HW_CS4235: -	case CS4231_HW_CS4239: -		snd_cs4231_out(chip, CS4235_LEFT_MASTER, 0xff); -		snd_cs4231_out(chip, CS4235_RIGHT_MASTER, 0xff); +	case WSS_HW_CS4235: +	case WSS_HW_CS4239: +		snd_wss_out(chip, CS4235_LEFT_MASTER, 0xff); +		snd_wss_out(chip, CS4235_RIGHT_MASTER, 0xff);  		break;  	} @@ -351,12 +362,13 @@ int snd_cs4236_create(struct snd_card *card,  	return 0;  } -int snd_cs4236_pcm(struct snd_cs4231 *chip, int device, struct snd_pcm **rpcm) +int snd_cs4236_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm)  {  	struct snd_pcm *pcm;  	int err; -	if ((err = snd_cs4231_pcm(chip, device, &pcm)) < 0) +	err = snd_wss_pcm(chip, device, &pcm); +	if (err < 0)  		return err;  	pcm->info_flags &= ~SNDRV_PCM_INFO_JOINT_DUPLEX;  	if (rpcm) @@ -387,7 +399,7 @@ static int snd_cs4236_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_  static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int reg = kcontrol->private_value & 0xff;  	int shift = (kcontrol->private_value >> 8) & 0xff; @@ -404,7 +416,7 @@ static int snd_cs4236_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e  static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int reg = kcontrol->private_value & 0xff;  	int shift = (kcontrol->private_value >> 8) & 0xff; @@ -433,7 +445,7 @@ static int snd_cs4236_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e  static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int reg = kcontrol->private_value & 0xff;  	int shift = (kcontrol->private_value >> 8) & 0xff; @@ -450,7 +462,7 @@ static int snd_cs4236_get_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_  static int snd_cs4236_put_singlec(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int reg = kcontrol->private_value & 0xff;  	int shift = (kcontrol->private_value >> 8) & 0xff; @@ -490,7 +502,7 @@ static int snd_cs4236_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_  static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int left_reg = kcontrol->private_value & 0xff;  	int right_reg = (kcontrol->private_value >> 8) & 0xff; @@ -512,7 +524,7 @@ static int snd_cs4236_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e  static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int left_reg = kcontrol->private_value & 0xff;  	int right_reg = (kcontrol->private_value >> 8) & 0xff; @@ -555,7 +567,7 @@ static int snd_cs4236_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e  static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int left_reg = kcontrol->private_value & 0xff;  	int right_reg = (kcontrol->private_value >> 8) & 0xff; @@ -577,7 +589,7 @@ static int snd_cs4236_get_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_  static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int left_reg = kcontrol->private_value & 0xff;  	int right_reg = (kcontrol->private_value >> 8) & 0xff; @@ -600,7 +612,7 @@ static int snd_cs4236_put_double1(struct snd_kcontrol *kcontrol, struct snd_ctl_  	val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1;  	val2 = (chip->eimage[CS4236_REG(right_reg)] & ~(mask << shift_right)) | val2;  	change = val1 != chip->image[left_reg] || val2 != chip->eimage[CS4236_REG(right_reg)]; -	snd_cs4231_out(chip, left_reg, val1); +	snd_wss_out(chip, left_reg, val1);  	snd_cs4236_ext_out(chip, right_reg, val2);  	spin_unlock_irqrestore(&chip->reg_lock, flags);  	return change; @@ -619,7 +631,7 @@ static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)  static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	spin_lock_irqsave(&chip->reg_lock, flags); @@ -631,7 +643,7 @@ static int snd_cs4236_get_master_digital(struct snd_kcontrol *kcontrol, struct s  static int snd_cs4236_put_master_digital(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int change;  	unsigned short val1, val2; @@ -678,7 +690,7 @@ static inline int snd_cs4235_mixer_output_accu_set_volume(int vol)  static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	spin_lock_irqsave(&chip->reg_lock, flags); @@ -690,7 +702,7 @@ static int snd_cs4235_get_output_accu(struct snd_kcontrol *kcontrol, struct snd_  static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int change;  	unsigned short val1, val2; @@ -701,108 +713,160 @@ static int snd_cs4235_put_output_accu(struct snd_kcontrol *kcontrol, struct snd_  	val1 = (chip->image[CS4235_LEFT_MASTER] & ~(3 << 5)) | val1;  	val2 = (chip->image[CS4235_RIGHT_MASTER] & ~(3 << 5)) | val2;  	change = val1 != chip->image[CS4235_LEFT_MASTER] || val2 != chip->image[CS4235_RIGHT_MASTER]; -	snd_cs4231_out(chip, CS4235_LEFT_MASTER, val1); -	snd_cs4231_out(chip, CS4235_RIGHT_MASTER, val2); +	snd_wss_out(chip, CS4235_LEFT_MASTER, val1); +	snd_wss_out(chip, CS4235_RIGHT_MASTER, val2);  	spin_unlock_irqrestore(&chip->reg_lock, flags);  	return change;  }  static struct snd_kcontrol_new snd_cs4236_controls[] = { -CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), -CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_DOUBLE("Master Digital Playback Switch", 0, +		CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, +		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),  CS4236_MASTER_DIGITAL("Master Digital Volume", 0), -CS4236_DOUBLE("Capture Boost Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), - -CS4231_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), -CS4231_DOUBLE("PCM Playback Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), - -CS4236_DOUBLE("DSP Playback Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), -CS4236_DOUBLE("DSP Playback Volume", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1), - -CS4236_DOUBLE("FM Playback Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), -CS4236_DOUBLE("FM Playback Volume", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1), - -CS4236_DOUBLE("Wavetable Playback Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), -CS4236_DOUBLE("Wavetable Playback Volume", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1), - -CS4231_DOUBLE("Synth Playback Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), -CS4231_DOUBLE("Synth Volume", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), -CS4231_DOUBLE("Synth Capture Switch", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), -CS4231_DOUBLE("Synth Capture Bypass", 0, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), - -CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), -CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Capture Boost Volume", 0, +		CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), + +WSS_DOUBLE("PCM Playback Switch", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +WSS_DOUBLE("PCM Playback Volume", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), + +CS4236_DOUBLE("DSP Playback Switch", 0, +		CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1), +CS4236_DOUBLE("DSP Playback Volume", 0, +		CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 0, 0, 63, 1), + +CS4236_DOUBLE("FM Playback Switch", 0, +		CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), +CS4236_DOUBLE("FM Playback Volume", 0, +		CS4236_LEFT_FM, CS4236_RIGHT_FM, 0, 0, 63, 1), + +CS4236_DOUBLE("Wavetable Playback Switch", 0, +		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), +CS4236_DOUBLE("Wavetable Playback Volume", 0, +		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 0, 0, 63, 1), + +WSS_DOUBLE("Synth Playback Switch", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +WSS_DOUBLE("Synth Volume", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +WSS_DOUBLE("Synth Capture Switch", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +WSS_DOUBLE("Synth Capture Bypass", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 5, 5, 1, 1), + +CS4236_DOUBLE("Mic Playback Switch", 0, +		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_DOUBLE("Mic Capture Switch", 0, +		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1),  CS4236_DOUBLE("Mic Volume", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 0, 0, 31, 1), -CS4236_DOUBLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), - -CS4231_DOUBLE("Line Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Line Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), -CS4231_DOUBLE("Line Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), -CS4231_DOUBLE("Line Capture Bypass", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), - -CS4231_DOUBLE("CD Playback Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("CD Volume", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), -CS4231_DOUBLE("CD Capture Switch", 0, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), - -CS4236_DOUBLE1("Mono Output Playback Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), -CS4236_DOUBLE1("Mono Playback Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), -CS4231_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), -CS4231_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), - -CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), -CS4231_DOUBLE("Analog Loopback Capture Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), - -CS4231_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), -CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1) +CS4236_DOUBLE("Mic Playback Boost", 0, +		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 5, 5, 1, 0), + +WSS_DOUBLE("Line Playback Switch", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Line Volume", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +WSS_DOUBLE("Line Capture Switch", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +WSS_DOUBLE("Line Capture Bypass", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 5, 5, 1, 1), + +WSS_DOUBLE("CD Playback Switch", 0, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("CD Volume", 0, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +WSS_DOUBLE("CD Capture Switch", 0, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), + +CS4236_DOUBLE1("Mono Output Playback Switch", 0, +		CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), +CS4236_DOUBLE1("Mono Playback Switch", 0, +		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +WSS_SINGLE("Mono Playback Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), +WSS_SINGLE("Mono Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0), + +WSS_DOUBLE("Capture Volume", 0, +		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +WSS_DOUBLE("Analog Loopback Capture Switch", 0, +		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), + +WSS_SINGLE("Digital Loopback Playback Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +CS4236_DOUBLE1("Digital Loopback Playback Volume", 0, +		CS4231_LOOPBACK, CS4236_RIGHT_LOOPBACK, 2, 0, 63, 1)  };  static struct snd_kcontrol_new snd_cs4235_controls[] = { -CS4231_DOUBLE("Master Switch", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), -CS4231_DOUBLE("Master Volume", 0, CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1), +WSS_DOUBLE("Master Switch", 0, +		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 7, 7, 1, 1), +WSS_DOUBLE("Master Volume", 0, +		CS4235_LEFT_MASTER, CS4235_RIGHT_MASTER, 0, 0, 31, 1),  CS4235_OUTPUT_ACCU("Playback Volume", 0), -CS4236_DOUBLE("Master Digital Playback Switch", 0, CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), -CS4236_DOUBLE("Master Digital Capture Switch", 0, CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1), +CS4236_DOUBLE("Master Digital Playback Switch", 0, +		CS4236_LEFT_MASTER, CS4236_RIGHT_MASTER, 7, 7, 1, 1), +CS4236_DOUBLE("Master Digital Capture Switch", 0, +		CS4236_DAC_MUTE, CS4236_DAC_MUTE, 7, 6, 1, 1),  CS4236_MASTER_DIGITAL("Master Digital Volume", 0), -CS4231_DOUBLE("Master Digital Playback Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), -CS4231_DOUBLE("Master Digital Capture Switch", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), -CS4231_DOUBLE("Master Digital Volume", 1, CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +WSS_DOUBLE("Master Digital Playback Switch", 1, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +WSS_DOUBLE("Master Digital Capture Switch", 1, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 6, 6, 1, 1), +WSS_DOUBLE("Master Digital Volume", 1, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), -CS4236_DOUBLE("Capture Volume", 0, CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), +CS4236_DOUBLE("Capture Volume", 0, +		CS4236_LEFT_MIX_CTRL, CS4236_RIGHT_MIX_CTRL, 5, 5, 3, 1), -CS4231_DOUBLE("PCM Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), -CS4231_DOUBLE("PCM Volume", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), +WSS_DOUBLE("PCM Switch", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +WSS_DOUBLE("PCM Volume", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1),  CS4236_DOUBLE("DSP Switch", 0, CS4236_LEFT_DSP, CS4236_RIGHT_DSP, 7, 7, 1, 1),  CS4236_DOUBLE("FM Switch", 0, CS4236_LEFT_FM, CS4236_RIGHT_FM, 7, 7, 1, 1), -CS4236_DOUBLE("Wavetable Switch", 0, CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), +CS4236_DOUBLE("Wavetable Switch", 0, +		CS4236_LEFT_WAVE, CS4236_RIGHT_WAVE, 7, 7, 1, 1), -CS4236_DOUBLE("Mic Capture Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), -CS4236_DOUBLE("Mic Playback Switch", 0, CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1), +CS4236_DOUBLE("Mic Capture Switch", 0, +		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 7, 7, 1, 1), +CS4236_DOUBLE("Mic Playback Switch", 0, +		CS4236_LEFT_MIC, CS4236_RIGHT_MIC, 6, 6, 1, 1),  CS4236_SINGLE("Mic Volume", 0, CS4236_LEFT_MIC, 0, 31, 1),  CS4236_SINGLE("Mic Playback Boost", 0, CS4236_LEFT_MIC, 5, 1, 0), -CS4231_DOUBLE("Aux Playback Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Aux Capture Switch", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), -CS4231_DOUBLE("Aux Volume", 0, CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), - -CS4231_DOUBLE("Aux Playback Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Aux Capture Switch", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), -CS4231_DOUBLE("Aux Volume", 1, CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), - -CS4236_DOUBLE1("Master Mono Switch", 0, CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), - -CS4236_DOUBLE1("Mono Switch", 0, CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), -CS4231_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), - -CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0), +WSS_DOUBLE("Aux Playback Switch", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Aux Capture Switch", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 6, 6, 1, 1), +WSS_DOUBLE("Aux Volume", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), + +WSS_DOUBLE("Aux Playback Switch", 1, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Aux Capture Switch", 1, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 6, 6, 1, 1), +WSS_DOUBLE("Aux Volume", 1, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), + +CS4236_DOUBLE1("Master Mono Switch", 0, +		CS4231_MONO_CTRL, CS4236_RIGHT_MIX_CTRL, 6, 7, 1, 1), + +CS4236_DOUBLE1("Mono Switch", 0, +		CS4231_MONO_CTRL, CS4236_LEFT_MIX_CTRL, 7, 7, 1, 1), +WSS_SINGLE("Mono Volume", 0, CS4231_MONO_CTRL, 0, 15, 1), + +WSS_DOUBLE("Analog Loopback Switch", 0, +		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 7, 7, 1, 0),  };  #define CS4236_IEC958_ENABLE(xname, xindex) \ @@ -813,14 +877,14 @@ CS4231_DOUBLE("Analog Loopback Switch", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT  static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	spin_lock_irqsave(&chip->reg_lock, flags);  	ucontrol->value.integer.value[0] = chip->image[CS4231_ALT_FEATURE_1] & 0x02 ? 1 : 0;  #if 0  	printk("get valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", -			snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), +			snd_wss_in(chip, CS4231_ALT_FEATURE_1),  			snd_cs4236_ctrl_in(chip, 3),  			snd_cs4236_ctrl_in(chip, 4),  			snd_cs4236_ctrl_in(chip, 5), @@ -833,7 +897,7 @@ static int snd_cs4236_get_iec958_switch(struct snd_kcontrol *kcontrol, struct sn  static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kcontrol); +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol);  	unsigned long flags;  	int change;  	unsigned short enable, val; @@ -841,23 +905,23 @@ static int snd_cs4236_put_iec958_switch(struct snd_kcontrol *kcontrol, struct sn  	enable = ucontrol->value.integer.value[0] & 1;  	mutex_lock(&chip->mce_mutex); -	snd_cs4231_mce_up(chip); +	snd_wss_mce_up(chip);  	spin_lock_irqsave(&chip->reg_lock, flags);  	val = (chip->image[CS4231_ALT_FEATURE_1] & ~0x0e) | (0<<2) | (enable << 1);  	change = val != chip->image[CS4231_ALT_FEATURE_1]; -	snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, val); +	snd_wss_out(chip, CS4231_ALT_FEATURE_1, val);  	val = snd_cs4236_ctrl_in(chip, 4) | 0xc0;  	snd_cs4236_ctrl_out(chip, 4, val);  	udelay(100);  	val &= ~0x40;  	snd_cs4236_ctrl_out(chip, 4, val);  	spin_unlock_irqrestore(&chip->reg_lock, flags); -	snd_cs4231_mce_down(chip); +	snd_wss_mce_down(chip);  	mutex_unlock(&chip->mce_mutex);  #if 0  	printk("set valid: ALT = 0x%x, C3 = 0x%x, C4 = 0x%x, C5 = 0x%x, C6 = 0x%x, C8 = 0x%x\n", -			snd_cs4231_in(chip, CS4231_ALT_FEATURE_1), +			snd_wss_in(chip, CS4231_ALT_FEATURE_1),  			snd_cs4236_ctrl_in(chip, 3),  			snd_cs4236_ctrl_in(chip, 4),  			snd_cs4236_ctrl_in(chip, 5), @@ -896,19 +960,20 @@ CS4236_SINGLEC("3D Control - Volume", 0, 2, 0, 15, 1),  CS4236_SINGLEC("3D Control - IEC958", 0, 3, 5, 1, 0)  }; -int snd_cs4236_mixer(struct snd_cs4231 *chip) +int snd_cs4236_mixer(struct snd_wss *chip)  {  	struct snd_card *card;  	unsigned int idx, count;  	int err;  	struct snd_kcontrol_new *kcontrol; -	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip || !chip->card)) +		return -EINVAL;  	card = chip->card; -	strcpy(card->mixername, snd_cs4231_chip_id(chip)); +	strcpy(card->mixername, snd_wss_chip_id(chip)); -	if (chip->hardware == CS4231_HW_CS4235 || -	    chip->hardware == CS4231_HW_CS4239) { +	if (chip->hardware == WSS_HW_CS4235 || +	    chip->hardware == WSS_HW_CS4239) {  		for (idx = 0; idx < ARRAY_SIZE(snd_cs4235_controls); idx++) {  			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4235_controls[idx], chip))) < 0)  				return err; @@ -920,16 +985,16 @@ int snd_cs4236_mixer(struct snd_cs4231 *chip)  		}  	}  	switch (chip->hardware) { -	case CS4231_HW_CS4235: -	case CS4231_HW_CS4239: +	case WSS_HW_CS4235: +	case WSS_HW_CS4239:  		count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4235);  		kcontrol = snd_cs4236_3d_controls_cs4235;  		break; -	case CS4231_HW_CS4237B: +	case WSS_HW_CS4237B:  		count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4237);  		kcontrol = snd_cs4236_3d_controls_cs4237;  		break; -	case CS4231_HW_CS4238B: +	case WSS_HW_CS4238B:  		count = ARRAY_SIZE(snd_cs4236_3d_controls_cs4238);  		kcontrol = snd_cs4236_3d_controls_cs4238;  		break; @@ -941,8 +1006,8 @@ int snd_cs4236_mixer(struct snd_cs4231 *chip)  		if ((err = snd_ctl_add(card, snd_ctl_new1(kcontrol, chip))) < 0)  			return err;  	} -	if (chip->hardware == CS4231_HW_CS4237B || -	    chip->hardware == CS4231_HW_CS4238B) { +	if (chip->hardware == WSS_HW_CS4237B || +	    chip->hardware == WSS_HW_CS4238B) {  		for (idx = 0; idx < ARRAY_SIZE(snd_cs4236_iec958_controls); idx++) {  			if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4236_iec958_controls[idx], chip))) < 0)  				return err; diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index 1e1e575..4fbb508 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -1009,7 +1009,8 @@ int snd_es1688_mixer(struct snd_es1688 *chip)  	int err;  	unsigned char reg, val; -	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip || !chip->card)) +		return -EINVAL;  	card = chip->card; diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index cccc16c..12eb98f 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -276,9 +276,11 @@ static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)  	static unsigned char dmas[8] =  		{6, 1, 0, 2, 0, 3, 4, 5}; -	snd_assert(gus != NULL, return -EINVAL); +	if (snd_BUG_ON(!gus)) +		return -EINVAL;  	card = gus->card; -	snd_assert(card != NULL, return -EINVAL); +	if (snd_BUG_ON(!card)) +		return -EINVAL;  	gus->mix_cntrl_reg &= 0xf8;  	gus->mix_cntrl_reg |= 0x01;	/* disable MIC, LINE IN, enable LINE OUT */ diff --git a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c index ebdb334..0dd4341 100644 --- a/sound/isa/gus/gus_mixer.c +++ b/sound/isa/gus/gus_mixer.c @@ -161,9 +161,11 @@ int snd_gf1_new_mixer(struct snd_gus_card * gus)  	unsigned int idx, max;  	int err; -	snd_assert(gus != NULL, return -EINVAL); +	if (snd_BUG_ON(!gus)) +		return -EINVAL;  	card = gus->card; -	snd_assert(card != NULL, return -EINVAL); +	if (snd_BUG_ON(!card)) +		return -EINVAL;  	if (gus->ics_flag)  		snd_component_add(card, "ICS2101"); diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 99731dc..38510ae 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -352,8 +352,10 @@ static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,  	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));  	len = samples_to_bytes(runtime, count); -	snd_assert(bpos <= pcmp->dma_size, return -EIO); -	snd_assert(bpos + len <= pcmp->dma_size, return -EIO); +	if (snd_BUG_ON(bpos > pcmp->dma_size)) +		return -EIO; +	if (snd_BUG_ON(bpos + len > pcmp->dma_size)) +		return -EIO;  	if (copy_from_user(runtime->dma_area + bpos, src, len))  		return -EFAULT;  	if (snd_gf1_pcm_use_dma && len > 32) { @@ -381,8 +383,10 @@ static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,  	bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2));  	len = samples_to_bytes(runtime, count); -	snd_assert(bpos <= pcmp->dma_size, return -EIO); -	snd_assert(bpos + len <= pcmp->dma_size, return -EIO); +	if (snd_BUG_ON(bpos > pcmp->dma_size)) +		return -EIO; +	if (snd_BUG_ON(bpos + len > pcmp->dma_size)) +		return -EIO;  	snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, count);  	if (snd_gf1_pcm_use_dma && len > 32) {  		return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index f87c623..f94c197 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -28,7 +28,7 @@  #include <asm/dma.h>  #include <sound/core.h>  #include <sound/gus.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #define SNDRV_LEGACY_FIND_FREE_IRQ  #define SNDRV_LEGACY_FIND_FREE_DMA  #include <sound/initval.h> @@ -75,7 +75,7 @@ struct snd_gusmax {  	int irq;  	struct snd_card *card;  	struct snd_gus_card *gus; -	struct snd_cs4231 *cs4231; +	struct snd_wss *wss;  	unsigned short gus_status_reg;  	unsigned short pcm_status_reg;  }; @@ -117,7 +117,7 @@ static irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id)  		}  		if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */  			handled = 1; -			snd_cs4231_interrupt(irq, maxcard->cs4231); +			snd_wss_interrupt(irq, maxcard->wss);  			loop++;  		}  	} while (loop && --max > 0); @@ -140,10 +140,7 @@ static void __devinit snd_gusmax_init(int dev, struct snd_card *card,  	outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));  } -#define CS4231_PRIVATE( left, right, shift, mute ) \ -			((left << 24)|(right << 16)|(shift<<8)|mute) - -static int __devinit snd_gusmax_mixer(struct snd_cs4231 *chip) +static int __devinit snd_gusmax_mixer(struct snd_wss *chip)  {  	struct snd_card *card = chip->card;  	struct snd_ctl_elem_id id1, id2; @@ -214,7 +211,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)  	int xirq, xdma1, xdma2, err;  	struct snd_card *card;  	struct snd_gus_card *gus = NULL; -	struct snd_cs4231 *cs4231; +	struct snd_wss *wss;  	struct snd_gusmax *maxcard;  	card = snd_card_new(index[dev], id[dev], THIS_MODULE, @@ -301,33 +298,39 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)  	}  	maxcard->irq = xirq; -	if ((err = snd_cs4231_create(card, -				     gus->gf1.port + 0x10c, -1, xirq, -				     xdma2 < 0 ? xdma1 : xdma2, xdma1, -				     CS4231_HW_DETECT, -				     CS4231_HWSHARE_IRQ | -				     CS4231_HWSHARE_DMA1 | -				     CS4231_HWSHARE_DMA2, -				     &cs4231)) < 0) +	err = snd_wss_create(card, +			     gus->gf1.port + 0x10c, -1, xirq, +			     xdma2 < 0 ? xdma1 : xdma2, xdma1, +			     WSS_HW_DETECT, +			     WSS_HWSHARE_IRQ | +			     WSS_HWSHARE_DMA1 | +			     WSS_HWSHARE_DMA2, +			     &wss); +	if (err < 0)  		goto _err; -	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) +	err = snd_wss_pcm(wss, 0, NULL); +	if (err < 0)  		goto _err; -	if ((err = snd_cs4231_mixer(cs4231)) < 0) +	err = snd_wss_mixer(wss); +	if (err < 0)  		goto _err; -	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) +	err = snd_wss_timer(wss, 2, NULL); +	if (err < 0)  		goto _err;  	if (pcm_channels[dev] > 0) {  		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0)  			goto _err;  	} -	if ((err = snd_gusmax_mixer(cs4231)) < 0) +	err = snd_gusmax_mixer(wss); +	if (err < 0)  		goto _err; -	if ((err = snd_gf1_rawmidi_new(gus, 0, NULL)) < 0) +	err = snd_gf1_rawmidi_new(gus, 0, NULL); +	if (err < 0)  		goto _err;  	sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1); @@ -336,11 +339,12 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)  	snd_card_set_dev(card, pdev); -	if ((err = snd_card_register(card)) < 0) +	err = snd_card_register(card); +	if (err < 0)  		goto _err;  	maxcard->gus = gus; -	maxcard->cs4231 = cs4231; +	maxcard->wss = wss;  	dev_set_drvdata(pdev, card);  	return 0; diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index ca0d7ac..5faecfb 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -32,7 +32,7 @@  #include <asm/dma.h>  #include <sound/core.h>  #include <sound/gus.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #ifdef SNDRV_STB  #include <sound/tea6330t.h>  #endif @@ -118,7 +118,7 @@ struct snd_interwave {  	int irq;  	struct snd_card *card;  	struct snd_gus_card *gus; -	struct snd_cs4231 *cs4231; +	struct snd_wss *wss;  #ifdef SNDRV_STB  	struct resource *i2c_res;  #endif @@ -312,7 +312,7 @@ static irqreturn_t snd_interwave_interrupt(int irq, void *dev_id)  		}  		if (inb(iwcard->pcm_status_reg) & 0x01) {	/* IRQ bit is set? */  			handled = 1; -			snd_cs4231_interrupt(irq, iwcard->cs4231); +			snd_wss_interrupt(irq, iwcard->wss);  			loop++;  		}  	} while (loop && --max > 0); @@ -498,13 +498,17 @@ static void __devinit snd_interwave_init(int dev, struct snd_gus_card * gus)  }  static struct snd_kcontrol_new snd_interwave_controls[] = { -CS4231_DOUBLE("Master Playback Switch", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Master Playback Volume", 0, CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), -CS4231_DOUBLE("Mic Playback Switch", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), -CS4231_DOUBLE("Mic Playback Volume", 0, CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1) +WSS_DOUBLE("Master Playback Switch", 0, +		CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 7, 7, 1, 1), +WSS_DOUBLE("Master Playback Volume", 0, +		CS4231_LINE_LEFT_OUTPUT, CS4231_LINE_RIGHT_OUTPUT, 0, 0, 31, 1), +WSS_DOUBLE("Mic Playback Switch", 0, +		CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Mic Playback Volume", 0, +		CS4231_LEFT_MIC_INPUT, CS4231_RIGHT_MIC_INPUT, 0, 0, 31, 1)  }; -static int __devinit snd_interwave_mixer(struct snd_cs4231 *chip) +static int __devinit snd_interwave_mixer(struct snd_wss *chip)  {  	struct snd_card *card = chip->card;  	struct snd_ctl_elem_id id1, id2; @@ -527,10 +531,10 @@ static int __devinit snd_interwave_mixer(struct snd_cs4231 *chip)  	for (idx = 0; idx < ARRAY_SIZE(snd_interwave_controls); idx++)  		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_interwave_controls[idx], chip))) < 0)  			return err; -	snd_cs4231_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); -	snd_cs4231_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); -	snd_cs4231_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); -	snd_cs4231_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f); +	snd_wss_out(chip, CS4231_LINE_LEFT_OUTPUT, 0x9f); +	snd_wss_out(chip, CS4231_LINE_RIGHT_OUTPUT, 0x9f); +	snd_wss_out(chip, CS4231_LEFT_MIC_INPUT, 0x9f); +	snd_wss_out(chip, CS4231_RIGHT_MIC_INPUT, 0x9f);  	/* reassign AUXA to SYNTHESIZER */  	strcpy(id1.name, "Aux Playback Switch");  	strcpy(id2.name, "Synth Playback Switch"); @@ -642,7 +646,7 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)  {  	int xirq, xdma1, xdma2;  	struct snd_interwave *iwcard = card->private_data; -	struct snd_cs4231 *cs4231; +	struct snd_wss *wss;  	struct snd_gus_card *gus;  #ifdef SNDRV_STB  	struct snd_i2c_bus *i2c_bus; @@ -684,33 +688,39 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)  	}  	iwcard->irq = xirq; -	if ((err = snd_cs4231_create(card, -				     gus->gf1.port + 0x10c, -1, xirq, -				     xdma2 < 0 ? xdma1 : xdma2, xdma1, -				     CS4231_HW_INTERWAVE, -				     CS4231_HWSHARE_IRQ | -				     CS4231_HWSHARE_DMA1 | -				     CS4231_HWSHARE_DMA2, -				     &cs4231)) < 0) +	err = snd_wss_create(card, +			     gus->gf1.port + 0x10c, -1, xirq, +			     xdma2 < 0 ? xdma1 : xdma2, xdma1, +			     WSS_HW_INTERWAVE, +			     WSS_HWSHARE_IRQ | +			     WSS_HWSHARE_DMA1 | +			     WSS_HWSHARE_DMA2, +			     &wss); +	if (err < 0)  		return err; -	if ((err = snd_cs4231_pcm(cs4231, 0, &pcm)) < 0) +	err = snd_wss_pcm(wss, 0, &pcm); +	if (err < 0)  		return err;  	sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');  	strcat(pcm->name, " (codec)"); -	if ((err = snd_cs4231_timer(cs4231, 2, NULL)) < 0) +	err = snd_wss_timer(wss, 2, NULL); +	if (err < 0)  		return err; -	if ((err = snd_cs4231_mixer(cs4231)) < 0) +	err = snd_wss_mixer(wss); +	if (err < 0)  		return err;  	if (pcm_channels[dev] > 0) { -		if ((err = snd_gf1_pcm_new(gus, 1, 1, NULL)) < 0) +		err = snd_gf1_pcm_new(gus, 1, 1, NULL); +		if (err < 0)  			return err;  	} -	if ((err = snd_interwave_mixer(cs4231)) < 0) +	err = snd_interwave_mixer(wss); +	if (err < 0)  		return err;  #ifdef SNDRV_STB @@ -754,10 +764,11 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)  	if (xdma2 >= 0)  		sprintf(card->longname + strlen(card->longname), "&%d", xdma2); -	if ((err = snd_card_register(card)) < 0) +	err = snd_card_register(card); +	if (err < 0)  		return err; -	iwcard->cs4231 = cs4231; +	iwcard->wss = wss;  	iwcard->gus = gus;  	return 0;  } diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 854a9f7..58c972b 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -28,7 +28,7 @@  #include <linux/pnp.h>  #include <linux/moduleparam.h>  #include <sound/core.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #include <sound/mpu401.h>  #include <sound/opl3.h>  #include <sound/initval.h> @@ -124,7 +124,6 @@ static int pnpc_registered;  #define OPL3SA2_PM_D3	(OPL3SA2_PM_ADOWN|OPL3SA2_PM_PSV|OPL3SA2_PM_PDN|OPL3SA2_PM_PDX)  struct snd_opl3sa2 { -	struct snd_card *card;  	int version;		/* 2 or 3 */  	unsigned long port;	/* control port */  	struct resource *res_port; /* control port resource */ @@ -133,7 +132,7 @@ struct snd_opl3sa2 {  	spinlock_t reg_lock;  	struct snd_hwdep *synth;  	struct snd_rawmidi *rmidi; -	struct snd_cs4231 *cs4231; +	struct snd_wss *wss;  	unsigned char ctlregs[0x20];  	int ymode;		/* SL added */  	struct snd_kcontrol *master_switch; @@ -222,14 +221,13 @@ static void snd_opl3sa2_write(struct snd_opl3sa2 *chip, unsigned char reg, unsig  	spin_unlock_irqrestore(&chip->reg_lock, flags);  } -static int __devinit snd_opl3sa2_detect(struct snd_opl3sa2 *chip) +static int __devinit snd_opl3sa2_detect(struct snd_card *card)  { -	struct snd_card *card; +	struct snd_opl3sa2 *chip = card->private_data;  	unsigned long port;  	unsigned char tmp, tmp1;  	char str[2]; -	card = chip->card;  	port = chip->port;  	if ((chip->res_port = request_region(port, 2, "OPL3-SA control")) == NULL) {  		snd_printk(KERN_ERR PFX "can't grab port 0x%lx\n", port); @@ -298,12 +296,14 @@ static int __devinit snd_opl3sa2_detect(struct snd_opl3sa2 *chip)  static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)  {  	unsigned short status; -	struct snd_opl3sa2 *chip = dev_id; +	struct snd_card *card = dev_id; +	struct snd_opl3sa2 *chip;  	int handled = 0; -	if (chip == NULL || chip->card == NULL) +	if (card == NULL)  		return IRQ_NONE; +	chip = card->private_data;  	status = snd_opl3sa2_read(chip, OPL3SA2_IRQ_STATUS);  	if (status & 0x20) { @@ -318,7 +318,7 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)  	if (status & 0x07) {	/* TI,CI,PI */  		handled = 1; -		snd_cs4231_interrupt(irq, chip->cs4231); +		snd_wss_interrupt(irq, chip->wss);  	}  	if (status & 0x40) { /* hardware volume change */ @@ -327,8 +327,10 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)  		snd_opl3sa2_read(chip, OPL3SA2_MASTER_RIGHT);  		snd_opl3sa2_read(chip, OPL3SA2_MASTER_LEFT);  		if (chip->master_switch && chip->master_volume) { -			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id); -			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id); +			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +					&chip->master_switch->id); +			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, +					&chip->master_volume->id);  		}  	}  	return IRQ_RETVAL(handled); @@ -336,29 +338,18 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id)  #define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ -  .info = snd_opl3sa2_info_single, \ +  .info = snd_wss_info_single, \    .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \    .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }  #define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \    .name = xname, .index = xindex, \ -  .info = snd_opl3sa2_info_single, \ +  .info = snd_wss_info_single, \    .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \    .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \    .tlv = { .p = (xtlv) } } -static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	int mask = (kcontrol->private_value >> 16) & 0xff; - -	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -	uinfo->count = 1; -	uinfo->value.integer.min = 0; -	uinfo->value.integer.max = mask; -	return 0; -} -  static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  {  	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol); @@ -402,29 +393,18 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_  #define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ -  .info = snd_opl3sa2_info_double, \ +  .info = snd_wss_info_double, \    .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \    .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }  #define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \  { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \    .name = xname, .index = xindex, \ -  .info = snd_opl3sa2_info_double, \ +  .info = snd_wss_info_double, \    .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \    .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \    .tlv = { .p = (xtlv) } } -static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ -	int mask = (kcontrol->private_value >> 24) & 0xff; - -	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; -	uinfo->count = 2; -	uinfo->value.integer.min = 0; -	uinfo->value.integer.max = mask; -	return 0; -} -  static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  {  	struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol); @@ -512,9 +492,9 @@ static void snd_opl3sa2_master_free(struct snd_kcontrol *kcontrol)  	chip->master_volume = NULL;  } -static int __devinit snd_opl3sa2_mixer(struct snd_opl3sa2 *chip) +static int __devinit snd_opl3sa2_mixer(struct snd_card *card)  { -	struct snd_card *card = chip->card; +	struct snd_opl3sa2 *chip = card->private_data;  	struct snd_ctl_elem_id id1, id2;  	struct snd_kcontrol *kctl;  	unsigned int idx; @@ -573,7 +553,7 @@ static int snd_opl3sa2_suspend(struct snd_card *card, pm_message_t state)  	struct snd_opl3sa2 *chip = card->private_data;  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); -	chip->cs4231->suspend(chip->cs4231); +	chip->wss->suspend(chip->wss);  	/* power down */  	snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3); @@ -597,8 +577,8 @@ static int snd_opl3sa2_resume(struct snd_card *card)  		for (i = 0x12; i <= 0x16; i++)  			snd_opl3sa2_write(chip, i, chip->ctlregs[i]);  	} -	/* restore cs4231 */ -	chip->cs4231->resume(chip->cs4231); +	/* restore wss */ +	chip->wss->resume(chip->wss);  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);  	return 0; @@ -650,7 +630,6 @@ static struct snd_card *snd_opl3sa2_card_new(int dev)  	chip = card->private_data;  	spin_lock_init(&chip->reg_lock);  	chip->irq = -1; -	chip->card = card;  	card->private_free = snd_opl3sa2_free;  	return card;  } @@ -659,7 +638,7 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)  {  	int xirq, xdma1, xdma2;  	struct snd_opl3sa2 *chip; -	struct snd_cs4231 *cs4231; +	struct snd_wss *wss;  	struct snd_opl3 *opl3;  	int err; @@ -672,30 +651,36 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)  	xdma2 = dma2[dev];  	if (xdma2 < 0)  		chip->single_dma = 1; -	if ((err = snd_opl3sa2_detect(chip)) < 0) +	err = snd_opl3sa2_detect(card); +	if (err < 0)  		return err; -	if (request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED, "OPL3-SA2", chip)) { +	err = request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED, +			  "OPL3-SA2", card); +	if (err) {  		snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);  		return -ENODEV;  	}  	chip->irq = xirq; -	if ((err = snd_cs4231_create(card, -				     wss_port[dev] + 4, -1, -				     xirq, xdma1, xdma2, -				     CS4231_HW_OPL3SA2, -				     CS4231_HWSHARE_IRQ, -				     &cs4231)) < 0) { +	err = snd_wss_create(card, +			     wss_port[dev] + 4, -1, +			     xirq, xdma1, xdma2, +			     WSS_HW_OPL3SA2, WSS_HWSHARE_IRQ, &wss); +	if (err < 0) {  		snd_printd("Oops, WSS not detected at 0x%lx\n", wss_port[dev] + 4);  		return err;  	} -	chip->cs4231 = cs4231; -	if ((err = snd_cs4231_pcm(cs4231, 0, NULL)) < 0) +	chip->wss = wss; +	err = snd_wss_pcm(wss, 0, NULL); +	if (err < 0)  		return err; -	if ((err = snd_cs4231_mixer(cs4231)) < 0) +	err = snd_wss_mixer(wss); +	if (err < 0)  		return err; -	if ((err = snd_opl3sa2_mixer(chip)) < 0) +	err = snd_opl3sa2_mixer(card); +	if (err < 0)  		return err; -	if ((err = snd_cs4231_timer(cs4231, 0, NULL)) < 0) +	err = snd_wss_timer(wss, 0, NULL); +	if (err < 0)  		return err;  	if (fm_port[dev] >= 0x340 && fm_port[dev] < 0x400) {  		if ((err = snd_opl3_create(card, fm_port[dev], diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index 2a1e2f5..440755c 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -32,7 +32,7 @@  #include <asm/io.h>  #include <asm/dma.h>  #include <sound/core.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #include <sound/mpu401.h>  #include <sound/opl4.h>  #include <sound/control.h> @@ -675,7 +675,8 @@ static int __devinit snd_miro_mixer(struct snd_miro *miro)  	unsigned int idx;  	int err; -	snd_assert(miro != NULL && miro->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!miro || !miro->card)) +		return -EINVAL;  	card = miro->card; @@ -1221,7 +1222,7 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)  	int error;  	struct snd_miro *miro; -	struct snd_cs4231 *codec; +	struct snd_wss *codec;  	struct snd_timer *timer;  	struct snd_card *card;  	struct snd_pcm *pcm; @@ -1310,29 +1311,32 @@ static int __devinit snd_miro_probe(struct device *devptr, unsigned int n)  		}  	} -	if ((error = snd_miro_configure(miro))) { +	error = snd_miro_configure(miro); +	if (error) {  		snd_card_free(card);  		return error;  	} -	if ((error = snd_cs4231_create(card, miro->wss_base + 4, -1, -				       miro->irq, miro->dma1, miro->dma2, -				       CS4231_HW_AD1845, -				       0, -				       &codec)) < 0) { +	error = snd_wss_create(card, miro->wss_base + 4, -1, +				miro->irq, miro->dma1, miro->dma2, +				WSS_HW_AD1845, 0, &codec); +	if (error < 0) {  		snd_card_free(card);  		return error;  	} -	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) { +	error = snd_wss_pcm(codec, 0, &pcm); +	if (error < 0)  {  		snd_card_free(card);  		return error;  	} -	if ((error = snd_cs4231_mixer(codec)) < 0) { +	error = snd_wss_mixer(codec); +	if (error < 0) {  		snd_card_free(card);  		return error;  	} -	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) { +	error = snd_wss_timer(codec, 0, &timer); +	if (error < 0) {  		snd_card_free(card);  		return error;  	} diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index 0797ca4..19706b0 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -33,11 +33,7 @@  #include <asm/io.h>  #include <asm/dma.h>  #include <sound/core.h> -#if defined(CS4231) || defined(OPTi93X) -#include <sound/cs4231.h> -#else -#include <sound/ad1848.h> -#endif	/* CS4231 */ +#include <sound/wss.h>  #include <sound/mpu401.h>  #include <sound/opl3.h>  #ifndef OPTi93X @@ -139,7 +135,7 @@ struct snd_opti9xx {  	unsigned long mc_base_size;  #ifdef OPTi93X  	unsigned long mc_indir_index; -	struct snd_cs4231 *codec; +	struct snd_wss *codec;  #endif	/* OPTi93X */  	unsigned long pwd_reg; @@ -148,9 +144,7 @@ struct snd_opti9xx {  	long wss_base;  	int irq;  	int dma1; -#if defined(CS4231) || defined(OPTi93X)  	int dma2; -#endif	/* CS4231 || OPTi93X */  	long fm_port; @@ -225,9 +219,7 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,  	chip->wss_base = -1;  	chip->irq = -1;  	chip->dma1 = -1; -#if defined(CS4231) || defined (OPTi93X)  	chip->dma2 = -1; -#endif 	/* CS4231 || OPTi93X */  	chip->fm_port = -1;  	chip->mpu_port = -1;  	chip->mpu_irq = -1; @@ -562,7 +554,7 @@ __skip_mpu:  static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)  { -	struct snd_cs4231 *codec = dev_id; +	struct snd_wss *codec = dev_id;  	struct snd_opti9xx *chip = codec->card->private_data;  	unsigned char status; @@ -570,7 +562,7 @@ static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)  	if ((status & OPTi93X_IRQ_PLAYBACK) && codec->playback_substream)  		snd_pcm_period_elapsed(codec->playback_substream);  	if ((status & OPTi93X_IRQ_CAPTURE) && codec->capture_substream) { -		snd_cs4231_overrange(codec); +		snd_wss_overrange(codec);  		snd_pcm_period_elapsed(codec->capture_substream);  	}  	outb(0x00, OPTi93X_PORT(codec, STATUS)); @@ -691,7 +683,7 @@ static void snd_card_opti9xx_free(struct snd_card *card)  	if (chip) {  #ifdef OPTi93X -		struct snd_cs4231 *codec = chip->codec; +		struct snd_wss *codec = chip->codec;  		if (codec && codec->irq > 0) {  			disable_irq(codec->irq);  			free_irq(codec->irq, codec); @@ -706,14 +698,10 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)  	static long possible_ports[] = {0x530, 0xe80, 0xf40, 0x604, -1};  	int error;  	struct snd_opti9xx *chip = card->private_data; -#if defined(CS4231) || defined(OPTi93X) -	struct snd_cs4231 *codec; +	struct snd_wss *codec;  #ifdef CS4231  	struct snd_timer *timer;  #endif -#else -	struct snd_ad1848 *codec; -#endif  	struct snd_pcm *pcm;  	struct snd_rawmidi *rmidi;  	struct snd_hwdep *synth; @@ -731,38 +719,46 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)  	chip->dma1 = dma1;  #if defined(CS4231) || defined(OPTi93X)  	chip->dma2 = dma2; +#else +	chip->dma2 = -1;  #endif  	if (chip->wss_base == SNDRV_AUTO_PORT) { -		if ((chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4)) < 0) { +		chip->wss_base = snd_legacy_find_free_ioport(possible_ports, 4); +		if (chip->wss_base < 0) {  			snd_printk("unable to find a free WSS port\n");  			return -EBUSY;  		}  	} -	if ((error = snd_opti9xx_configure(chip))) +	error = snd_opti9xx_configure(chip); +	if (error)  		return error; -#if defined(CS4231) || defined(OPTi93X) -	if ((error = snd_cs4231_create(card, chip->wss_base + 4, -1, -				       chip->irq, chip->dma1, chip->dma2, -#ifdef CS4231 -				       CS4231_HW_DETECT, 0, -#else /* OPTi93x */ -				       CS4231_HW_OPTI93X, CS4231_HWSHARE_IRQ, +	error = snd_wss_create(card, chip->wss_base + 4, -1, +			       chip->irq, chip->dma1, chip->dma2, +#ifdef OPTi93X +			       WSS_HW_OPTI93X, WSS_HWSHARE_IRQ, +#else +			       WSS_HW_DETECT, 0,  #endif -				       &codec)) < 0) +			       &codec); +	if (error < 0)  		return error;  #ifdef OPTi93X  	chip->codec = codec;  #endif -	if ((error = snd_cs4231_pcm(codec, 0, &pcm)) < 0) +	error = snd_wss_pcm(codec, 0, &pcm); +	if (error < 0)  		return error; -	if ((error = snd_cs4231_mixer(codec)) < 0) +	error = snd_wss_mixer(codec); +	if (error < 0)  		return error;  #ifdef CS4231 -	if ((error = snd_cs4231_timer(codec, 0, &timer)) < 0) +	error = snd_wss_timer(codec, 0, &timer); +	if (error < 0)  		return error; -#else /* OPTI93X */ +#endif +#ifdef OPTi93X  	error = request_irq(chip->irq, snd_opti93x_interrupt,  			    IRQF_DISABLED, DEV_NAME" - WSS", codec);  	if (error < 0) { @@ -770,16 +766,6 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)  		return error;  	}  #endif -#else -	if ((error = snd_ad1848_create(card, chip->wss_base + 4, -				       chip->irq, chip->dma1, -				       AD1848_HW_DETECT, &codec)) < 0) -		return error; -	if ((error = snd_ad1848_pcm(codec, 0, &pcm)) < 0) -		return error; -	if ((error = snd_ad1848_mixer(codec)) < 0) -		return error; -#endif  	strcpy(card->driver, chip->name);  	sprintf(card->shortname, "OPTi %s", card->driver);  #if defined(CS4231) || defined(OPTi93X) diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c index b35be7d..96678d5 100644 --- a/sound/isa/sb/emu8000.c +++ b/sound/isa/sb/emu8000.c @@ -1023,7 +1023,8 @@ snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu)  {  	int i, err = 0; -	snd_assert(emu != NULL && card != NULL, return -EINVAL); +	if (snd_BUG_ON(!emu || !card)) +		return -EINVAL;  	spin_lock_init(&emu->control_lock); diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c index 1be16c9..c99c607 100644 --- a/sound/isa/sb/emu8000_patch.c +++ b/sound/isa/sb/emu8000_patch.c @@ -156,7 +156,8 @@ snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,  	struct snd_emu8000 *emu;  	emu = rec->hw; -	snd_assert(sp != NULL, return -EINVAL); +	if (snd_BUG_ON(!sp)) +		return -EINVAL;  	if (sp->v.size == 0)  		return 0; diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 35f3d7b..49037d0 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -198,7 +198,8 @@ static int snd_sb_csp_ioctl(struct snd_hwdep * hw, struct file *file, unsigned i  	struct snd_sb_csp_start start_info;  	int err; -	snd_assert(p != NULL, return -EINVAL); +	if (snd_BUG_ON(!p)) +		return -EINVAL;  	if (snd_sb_csp_check_version(p))  		return -ENODEV; @@ -1046,7 +1047,8 @@ static int snd_sb_qsound_build(struct snd_sb_csp * p)  	struct snd_card *card;  	int err; -	snd_assert(p != NULL, return -EINVAL); +	if (snd_BUG_ON(!p)) +		return -EINVAL;  	card = p->chip->card;  	p->qpos_left = p->qpos_right = SNDRV_SB_CSP_QSOUND_MAX_RIGHT / 2; @@ -1071,7 +1073,8 @@ static void snd_sb_qsound_destroy(struct snd_sb_csp * p)  	struct snd_card *card;  	unsigned long flags; -	snd_assert(p != NULL, return); +	if (snd_BUG_ON(!p)) +		return;  	card = p->chip->card;	 diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index f7e8192..2a6cc1c 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -669,7 +669,8 @@ static int snd_sb16_capture_close(struct snd_pcm_substream *substream)  static int snd_sb16_set_dma_mode(struct snd_sb *chip, int what)  {  	if (chip->dma8 < 0 || chip->dma16 < 0) { -		snd_assert(what == 0, return -EINVAL); +		if (snd_BUG_ON(what)) +			return -EINVAL;  		return 0;  	}  	if (what == 0) { diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c index fe03bb8..658d557 100644 --- a/sound/isa/sb/sb8_main.c +++ b/sound/isa/sb/sb8_main.c @@ -111,7 +111,9 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)  	switch (chip->hardware) {  	case SB_HW_PRO:  		if (runtime->channels > 1) { -			snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); +			if (snd_BUG_ON(rate != SB8_RATE(11025) && +				       rate != SB8_RATE(22050))) +				return -EINVAL;  			chip->playback_format = SB_DSP_HI_OUTPUT_AUTO;  			break;  		} @@ -237,7 +239,9 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)  	switch (chip->hardware) {  	case SB_HW_PRO:  		if (runtime->channels > 1) { -			snd_assert(rate == SB8_RATE(11025) || rate == SB8_RATE(22050), return -EINVAL); +			if (snd_BUG_ON(rate != SB8_RATE(11025) && +				       rate != SB8_RATE(22050))) +				return -EINVAL;  			chip->capture_format = SB_DSP_HI_INPUT_AUTO;  			break;  		} diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index b432d9a..27a6515 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -219,7 +219,8 @@ int snd_sbdsp_create(struct snd_card *card,  		.dev_free =	snd_sbdsp_dev_free,  	}; -	snd_assert(r_chip != NULL, return -EINVAL); +	if (snd_BUG_ON(!r_chip)) +		return -EINVAL;  	*r_chip = NULL;  	chip = kzalloc(sizeof(*chip), GFP_KERNEL);  	if (chip == NULL) diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index 73d4572..406a431 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -792,7 +792,8 @@ int snd_sbmixer_new(struct snd_sb *chip)  	struct snd_card *card;  	int err; -	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip || !chip->card)) +		return -EINVAL;  	card = chip->card; @@ -925,7 +926,8 @@ static unsigned char als4000_saved_regs[] = {  static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)  {  	unsigned char *val = chip->saved_regs; -	snd_assert(num_regs <= ARRAY_SIZE(chip->saved_regs), return); +	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs))) +		return;  	for (; num_regs; num_regs--)  		*val++ = snd_sbmixer_read(chip, *regs++);  } @@ -933,7 +935,8 @@ static void save_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)  static void restore_mixer(struct snd_sb *chip, unsigned char *regs, int num_regs)  {  	unsigned char *val = chip->saved_regs; -	snd_assert(num_regs <= ARRAY_SIZE(chip->saved_regs), return); +	if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs))) +		return;  	for (; num_regs; num_regs--)  		snd_sbmixer_write(chip, *regs++, *val++);  } diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c index da3d152..ca35924 100644 --- a/sound/isa/sc6000.c +++ b/sound/isa/sc6000.c @@ -29,7 +29,7 @@  #include <linux/io.h>  #include <asm/dma.h>  #include <sound/core.h> -#include <sound/ad1848.h> +#include <sound/wss.h>  #include <sound/opl3.h>  #include <sound/mpu401.h>  #include <sound/control.h> @@ -397,7 +397,7 @@ static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,  	return 0;  } -static int __devinit snd_sc6000_mixer(struct snd_ad1848 *chip) +static int __devinit snd_sc6000_mixer(struct snd_wss *chip)  {  	struct snd_card *card = chip->card;  	struct snd_ctl_elem_id id1, id2; @@ -483,7 +483,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)  	int xirq = irq[dev];  	int xdma = dma[dev];  	struct snd_card *card; -	struct snd_ad1848 *chip; +	struct snd_wss *chip;  	struct snd_opl3 *opl3;  	char __iomem *vport;  	char __iomem *vmss_port; @@ -548,21 +548,21 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)  	if (err < 0)  		goto err_unmap2; -	err = snd_ad1848_create(card, mss_port[dev] + 4, xirq, xdma, -				AD1848_HW_DETECT, &chip); +	err = snd_wss_create(card, mss_port[dev] + 4,  -1, xirq, xdma, -1, +			     WSS_HW_DETECT, 0, &chip);  	if (err < 0)  		goto err_unmap2;  	card->private_data = chip; -	err = snd_ad1848_pcm(chip, 0, NULL); +	err = snd_wss_pcm(chip, 0, NULL);  	if (err < 0) {  		snd_printk(KERN_ERR PFX -			   "error creating new ad1848 PCM device\n"); +			   "error creating new WSS PCM device\n");  		goto err_unmap2;  	} -	err = snd_ad1848_mixer(chip); +	err = snd_wss_mixer(chip);  	if (err < 0) { -		snd_printk(KERN_ERR PFX "error creating new ad1848 mixer\n"); +		snd_printk(KERN_ERR PFX "error creating new WSS mixer\n");  		goto err_unmap2;  	}  	err = snd_sc6000_mixer(chip); diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c index a07274e..2c7503b 100644 --- a/sound/isa/sgalaxy.c +++ b/sound/isa/sgalaxy.c @@ -31,7 +31,7 @@  #include <asm/dma.h>  #include <sound/core.h>  #include <sound/sb.h> -#include <sound/ad1848.h> +#include <sound/wss.h>  #include <sound/control.h>  #define SNDRV_LEGACY_FIND_FREE_IRQ  #define SNDRV_LEGACY_FIND_FREE_DMA @@ -175,12 +175,14 @@ static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma)  	return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);  } -static struct ad1848_mix_elem snd_sgalaxy_controls[] = { -AD1848_DOUBLE("Aux Playback Switch", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), -AD1848_DOUBLE("Aux Playback Volume", 0, SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0) +static struct snd_kcontrol_new snd_sgalaxy_controls[] = { +WSS_DOUBLE("Aux Playback Switch", 0, +		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1), +WSS_DOUBLE("Aux Playback Volume", 0, +		SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)  }; -static int __devinit snd_sgalaxy_mixer(struct snd_ad1848 *chip) +static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)  {  	struct snd_card *card = chip->card;  	struct snd_ctl_elem_id id1, id2; @@ -210,7 +212,9 @@ static int __devinit snd_sgalaxy_mixer(struct snd_ad1848 *chip)  		return err;  	/* build AUX2 input */  	for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) { -		if ((err = snd_ad1848_add_ctl_elem(chip, &snd_sgalaxy_controls[idx])) < 0) +		err = snd_ctl_add(card, +				snd_ctl_new1(&snd_sgalaxy_controls[idx], chip)); +		if (err < 0)  			return err;  	}  	return 0; @@ -237,7 +241,7 @@ static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)  	static int possible_dmas[] = {1, 3, 0, -1};  	int err, xirq, xdma1;  	struct snd_card *card; -	struct snd_ad1848 *chip; +	struct snd_wss *chip;  	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);  	if (card == NULL) @@ -263,18 +267,21 @@ static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)  	if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)  		goto _err; -	if ((err = snd_ad1848_create(card, wssport[dev] + 4, -				     xirq, xdma1, -				     AD1848_HW_DETECT, &chip)) < 0) +	err = snd_wss_create(card, wssport[dev] + 4, -1, +			     xirq, xdma1, -1, +			     WSS_HW_DETECT, 0, &chip); +	if (err < 0)  		goto _err;  	card->private_data = chip; -	if ((err = snd_ad1848_pcm(chip, 0, NULL)) < 0) { -		snd_printdd(PFX "error creating new ad1848 PCM device\n"); +	err = snd_wss_pcm(chip, 0, NULL); +	if (err < 0) { +		snd_printdd(PFX "error creating new WSS PCM device\n");  		goto _err;  	} -	if ((err = snd_ad1848_mixer(chip)) < 0) { -		snd_printdd(PFX "error creating new ad1848 mixer\n"); +	err = snd_wss_mixer(chip); +	if (err < 0) { +		snd_printdd(PFX "error creating new WSS mixer\n");  		goto _err;  	}  	if ((err = snd_sgalaxy_mixer(chip)) < 0) { @@ -312,7 +319,7 @@ static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,  			       pm_message_t state)  {  	struct snd_card *card = dev_get_drvdata(pdev); -	struct snd_ad1848 *chip = card->private_data; +	struct snd_wss *chip = card->private_data;  	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);  	chip->suspend(chip); @@ -322,11 +329,11 @@ static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,  static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)  {  	struct snd_card *card = dev_get_drvdata(pdev); -	struct snd_ad1848 *chip = card->private_data; +	struct snd_wss *chip = card->private_data;  	chip->resume(chip); -	snd_ad1848_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]); -	snd_ad1848_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]); +	snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]); +	snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);  	return 0; diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 06ad786..48a16d8 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -31,7 +31,7 @@  #include <asm/dma.h>  #include <sound/core.h>  #include <sound/hwdep.h> -#include <sound/cs4231.h> +#include <sound/wss.h>  #include <sound/mpu401.h>  #include <sound/initval.h> @@ -147,7 +147,7 @@ struct soundscape {  	enum card_type type;  	struct resource *io_res;  	struct resource *wss_res; -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	struct snd_mpu401 *mpu;  	struct snd_hwdep *hw; @@ -726,7 +726,7 @@ static int sscape_midi_info(struct snd_kcontrol *ctl,  static int sscape_midi_get(struct snd_kcontrol *kctl,                             struct snd_ctl_elem_value *uctl)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kctl); +	struct snd_wss *chip = snd_kcontrol_chip(kctl);  	struct snd_card *card = chip->card;  	register struct soundscape *s = get_card_soundscape(card);  	unsigned long flags; @@ -746,7 +746,7 @@ static int sscape_midi_get(struct snd_kcontrol *kctl,  static int sscape_midi_put(struct snd_kcontrol *kctl,                             struct snd_ctl_elem_value *uctl)  { -	struct snd_cs4231 *chip = snd_kcontrol_chip(kctl); +	struct snd_wss *chip = snd_kcontrol_chip(kctl);  	struct snd_card *card = chip->card;  	register struct soundscape *s = get_card_soundscape(card);  	unsigned long flags; @@ -958,7 +958,9 @@ static int __devinit create_mpu401(struct snd_card *card, int devnum, unsigned l   * Override for the CS4231 playback format function.   * The AD1845 has much simpler format and rate selection.   */ -static void ad1845_playback_format(struct snd_cs4231 * chip, struct snd_pcm_hw_params *params, unsigned char format) +static void ad1845_playback_format(struct snd_wss *chip, +				   struct snd_pcm_hw_params *params, +				   unsigned char format)  {  	unsigned long flags;  	unsigned rate = params_rate(params); @@ -983,9 +985,9 @@ static void ad1845_playback_format(struct snd_cs4231 * chip, struct snd_pcm_hw_p  	 * NOTE: We seem to need to write to the MSB before the LSB  	 *       to get the correct sample frequency.  	 */ -	snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0)); -	snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8)); -	snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate); +	snd_wss_out(chip, CS4231_PLAYBK_FORMAT, (format & 0xf0)); +	snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8)); +	snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);  	spin_unlock_irqrestore(&chip->reg_lock, flags);  } @@ -994,7 +996,9 @@ static void ad1845_playback_format(struct snd_cs4231 * chip, struct snd_pcm_hw_p   * Override for the CS4231 capture format function.    * The AD1845 has much simpler format and rate selection.   */ -static void ad1845_capture_format(struct snd_cs4231 * chip, struct snd_pcm_hw_params *params, unsigned char format) +static void ad1845_capture_format(struct snd_wss *chip, +				  struct snd_pcm_hw_params *params, +				  unsigned char format)  {  	unsigned long flags;  	unsigned rate = params_rate(params); @@ -1019,9 +1023,9 @@ static void ad1845_capture_format(struct snd_cs4231 * chip, struct snd_pcm_hw_pa  	 * NOTE: We seem to need to write to the MSB before the LSB  	 *       to get the correct sample frequency.  	 */ -	snd_cs4231_out(chip, CS4231_REC_FORMAT, (format & 0xf0)); -	snd_cs4231_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8)); -	snd_cs4231_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate); +	snd_wss_out(chip, CS4231_REC_FORMAT, (format & 0xf0)); +	snd_wss_out(chip, AD1845_FREQ_SEL_MSB, (unsigned char) (rate >> 8)); +	snd_wss_out(chip, AD1845_FREQ_SEL_LSB, (unsigned char) rate);  	spin_unlock_irqrestore(&chip->reg_lock, flags);  } @@ -1036,7 +1040,7 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,  				   int irq, int dma1, int dma2)  {  	register struct soundscape *sscape = get_card_soundscape(card); -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	int err;  	if (sscape->type == SSCAPE_VIVO) @@ -1045,9 +1049,8 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,  	if (dma1 == dma2)  		dma2 = -1; -	err = snd_cs4231_create(card, -				port, -1, irq, dma1, dma2, -				CS4231_HW_DETECT, CS4231_HWSHARE_DMA1, &chip); +	err = snd_wss_create(card, port, -1, irq, dma1, dma2, +			     WSS_HW_DETECT, WSS_HWSHARE_DMA1, &chip);  	if (!err) {  		unsigned long flags;  		struct snd_pcm *pcm; @@ -1063,11 +1066,11 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,   *  #define AD1845_IFACE_CONFIG  \             (CS4231_AUTOCALIB | CS4231_RECORD_ENABLE | CS4231_PLAYBACK_ENABLE) -    snd_cs4231_mce_up(chip); +    snd_wss_mce_up(chip);      spin_lock_irqsave(&chip->reg_lock, flags); -    snd_cs4231_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG); +    snd_wss_out(chip, CS4231_IFACE_CTRL, AD1845_IFACE_CONFIG);      spin_unlock_irqrestore(&chip->reg_lock, flags); -    snd_cs4231_mce_down(chip); +    snd_wss_mce_down(chip);   */  		if (sscape->type != SSCAPE_VIVO) { @@ -1077,11 +1080,11 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,  			 * be 14.31818 MHz, because we must set this register  			 * to get the playback to sound correct ...  			 */ -			snd_cs4231_mce_up(chip); +			snd_wss_mce_up(chip);  			spin_lock_irqsave(&chip->reg_lock, flags); -			snd_cs4231_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20); +			snd_wss_out(chip, AD1845_CRYS_CLOCK_SEL, 0x20);  			spin_unlock_irqrestore(&chip->reg_lock, flags); -			snd_cs4231_mce_down(chip); +			snd_wss_mce_down(chip);  			/*  			 * More custom configuration: @@ -1089,28 +1092,28 @@ static int __devinit create_ad1845(struct snd_card *card, unsigned port,  			 * b) enable frequency selection (for capture/playback)  			 */  			spin_lock_irqsave(&chip->reg_lock, flags); -			snd_cs4231_out(chip, CS4231_MISC_INFO, -					CS4231_MODE2 | 0x10); -			val = snd_cs4231_in(chip, AD1845_PWR_DOWN_CTRL); -			snd_cs4231_out(chip, AD1845_PWR_DOWN_CTRL, -					val | AD1845_FREQ_SEL_ENABLE); +			snd_wss_out(chip, CS4231_MISC_INFO, +				    CS4231_MODE2 | 0x10); +			val = snd_wss_in(chip, AD1845_PWR_DOWN_CTRL); +			snd_wss_out(chip, AD1845_PWR_DOWN_CTRL, +				    val | AD1845_FREQ_SEL_ENABLE);  			spin_unlock_irqrestore(&chip->reg_lock, flags);  		} -		err = snd_cs4231_pcm(chip, 0, &pcm); +		err = snd_wss_pcm(chip, 0, &pcm);  		if (err < 0) {  			snd_printk(KERN_ERR "sscape: No PCM device "  					    "for AD1845 chip\n");  			goto _error;  		} -		err = snd_cs4231_mixer(chip); +		err = snd_wss_mixer(chip);  		if (err < 0) {  			snd_printk(KERN_ERR "sscape: No mixer device "  					    "for AD1845 chip\n");  			goto _error;  		} -		err = snd_cs4231_timer(chip, 0, NULL); +		err = snd_wss_timer(chip, 0, NULL);  		if (err < 0) {  			snd_printk(KERN_ERR "sscape: No timer device "  					    "for AD1845 chip\n"); diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index 3a6c6fe..4c095bc 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -1,6 +1,6 @@  /*   *  ALSA card-level driver for Turtle Beach Wavefront cards  - *                                              (Maui,Tropez,Tropez+) + *						(Maui,Tropez,Tropez+)   *   *  Copyright (c) 1997-1999 by Paul Barton-Davis <pbd@op.net>   * @@ -29,6 +29,7 @@  #include <sound/core.h>  #include <sound/initval.h>  #include <sound/opl3.h> +#include <sound/wss.h>  #include <sound/snd_wavefront.h>  MODULE_AUTHOR("Paul Barton-Davis <pbd@op.net>"); @@ -319,8 +320,8 @@ snd_wavefront_new_midi (struct snd_card *card,  	snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_wavefront_midi_input);  	rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | -	                     SNDRV_RAWMIDI_INFO_INPUT | -	                     SNDRV_RAWMIDI_INFO_DUPLEX; +			     SNDRV_RAWMIDI_INFO_INPUT | +			     SNDRV_RAWMIDI_INFO_DUPLEX;  	return rmidi;  } @@ -363,7 +364,7 @@ static int __devinit  snd_wavefront_probe (struct snd_card *card, int dev)  {  	snd_wavefront_card_t *acard = card->private_data; -	struct snd_cs4231 *chip; +	struct snd_wss *chip;  	struct snd_hwdep *wavefront_synth;  	struct snd_rawmidi *ics2115_internal_rmidi = NULL;  	struct snd_rawmidi *ics2115_external_rmidi = NULL; @@ -372,21 +373,20 @@ snd_wavefront_probe (struct snd_card *card, int dev)  	/* --------- PCM --------------- */ -	if ((err = snd_cs4231_create (card, -				      cs4232_pcm_port[dev], -				      -1, -				      cs4232_pcm_irq[dev], -				      dma1[dev], -				      dma2[dev], -				      CS4231_HW_DETECT, 0, &chip)) < 0) { -		snd_printk (KERN_ERR "can't allocate CS4231 device\n"); +	err = snd_wss_create(card, cs4232_pcm_port[dev], -1, +			     cs4232_pcm_irq[dev], dma1[dev], dma2[dev], +			     WSS_HW_DETECT, 0, &chip); +	if (err < 0) { +		snd_printk(KERN_ERR "can't allocate WSS device\n");  		return err;  	} -	if ((err = snd_cs4231_pcm (chip, 0, NULL)) < 0) +	err = snd_wss_pcm(chip, 0, NULL); +	if (err < 0)  		return err; -	if ((err = snd_cs4231_timer (chip, 0, NULL)) < 0) +	err = snd_wss_timer(chip, 0, NULL); +	if (err < 0)  		return err;  	/* ---------- OPL3 synth --------- */ @@ -394,24 +394,24 @@ snd_wavefront_probe (struct snd_card *card, int dev)  	if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {  		struct snd_opl3 *opl3; -	        if ((err = snd_opl3_create(card, -					   fm_port[dev], -					   fm_port[dev] + 2, -					   OPL3_HW_OPL3_CS, -					   0, &opl3)) < 0) { +		err = snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2, +				      OPL3_HW_OPL3_CS, 0, &opl3); +		if (err < 0) {  			snd_printk (KERN_ERR "can't allocate or detect OPL3 synth\n");  			return err;  		} -		if ((err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL)) < 0) +		err = snd_opl3_hwdep_new(opl3, hw_dev, 1, NULL); +		if (err < 0)  			return err;  		hw_dev++;  	}  	/* ------- ICS2115 Wavetable synth ------- */ -	if ((acard->wavefront.res_base = request_region(ics2115_port[dev], 16, -							"ICS2115")) == NULL) { +	acard->wavefront.res_base = request_region(ics2115_port[dev], 16, +						   "ICS2115"); +	if (acard->wavefront.res_base == NULL) {  		snd_printk(KERN_ERR "unable to grab ICS2115 i/o region 0x%lx-0x%lx\n",  			   ics2115_port[dev], ics2115_port[dev] + 16 - 1);  		return -EBUSY; @@ -425,7 +425,8 @@ snd_wavefront_probe (struct snd_card *card, int dev)  	acard->wavefront.irq = ics2115_irq[dev];  	acard->wavefront.base = ics2115_port[dev]; -	if ((wavefront_synth = snd_wavefront_new_synth (card, hw_dev, acard)) == NULL) { +	wavefront_synth = snd_wavefront_new_synth(card, hw_dev, acard); +	if (wavefront_synth == NULL) {  		snd_printk (KERN_ERR "can't create WaveFront synth device\n");  		return -ENOMEM;  	} @@ -436,7 +437,8 @@ snd_wavefront_probe (struct snd_card *card, int dev)  	/* --------- Mixer ------------ */ -	if ((err = snd_cs4231_mixer(chip)) < 0) { +	err = snd_wss_mixer(chip); +	if (err < 0) {  		snd_printk (KERN_ERR "can't allocate mixer device\n");  		return err;  	} @@ -444,11 +446,11 @@ snd_wavefront_probe (struct snd_card *card, int dev)  	/* -------- CS4232 MPU-401 interface -------- */  	if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) { -		if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, -					       cs4232_mpu_port[dev], 0, -					       cs4232_mpu_irq[dev], -					       IRQF_DISABLED, -					       NULL)) < 0) { +		err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, +					  cs4232_mpu_port[dev], 0, +					  cs4232_mpu_irq[dev], IRQF_DISABLED, +					  NULL); +		if (err < 0) {  			snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");  			return err;  		} @@ -601,7 +603,7 @@ static struct isa_driver snd_wavefront_driver = {  #ifdef CONFIG_PNP  static int __devinit snd_wavefront_pnp_detect(struct pnp_card_link *pcard, -                                              const struct pnp_card_device_id *pid) +					const struct pnp_card_device_id *pid)  {  	static int dev;  	struct snd_card *card; diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index 2efaa7f..dfc449a 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -180,11 +180,11 @@ snd_wavefront_fx_ioctl (struct snd_hwdep *sdev, struct file *file,  	unsigned short *pd;  	int err = 0; -	snd_assert(sdev->card != NULL, return -ENODEV); -	  	card = sdev->card; - -	snd_assert(card->private_data != NULL, return -ENODEV); +	if (snd_BUG_ON(!card)) +		return -ENODEV; +	if (snd_BUG_ON(!card->private_data)) +		return -ENODEV;  	acard = card->private_data;  	dev = &acard->wavefront; diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index a33384a..f14a7c0 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -235,8 +235,10 @@ static int snd_wavefront_midi_input_open(struct snd_rawmidi_substream *substream  	snd_wavefront_midi_t *midi;  	snd_wavefront_mpu_id mpu; -	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); -	snd_assert(substream->rmidi->private_data != NULL, return -EIO); +	if (snd_BUG_ON(!substream || !substream->rmidi)) +		return -ENXIO; +	if (snd_BUG_ON(!substream->rmidi->private_data)) +		return -ENXIO;  	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); @@ -257,8 +259,10 @@ static int snd_wavefront_midi_output_open(struct snd_rawmidi_substream *substrea  	snd_wavefront_midi_t *midi;  	snd_wavefront_mpu_id mpu; -	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); -	snd_assert(substream->rmidi->private_data != NULL, return -EIO); +	if (snd_BUG_ON(!substream || !substream->rmidi)) +		return -ENXIO; +	if (snd_BUG_ON(!substream->rmidi->private_data)) +		return -ENXIO;  	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); @@ -279,8 +283,10 @@ static int snd_wavefront_midi_input_close(struct snd_rawmidi_substream *substrea  	snd_wavefront_midi_t *midi;  	snd_wavefront_mpu_id mpu; -	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); -	snd_assert(substream->rmidi->private_data != NULL, return -EIO); +	if (snd_BUG_ON(!substream || !substream->rmidi)) +		return -ENXIO; +	if (snd_BUG_ON(!substream->rmidi->private_data)) +		return -ENXIO;  	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); @@ -300,8 +306,10 @@ static int snd_wavefront_midi_output_close(struct snd_rawmidi_substream *substre  	snd_wavefront_midi_t *midi;  	snd_wavefront_mpu_id mpu; -	snd_assert(substream != NULL && substream->rmidi != NULL, return -EIO); -	snd_assert(substream->rmidi->private_data != NULL, return -EIO); +	if (snd_BUG_ON(!substream || !substream->rmidi)) +		return -ENXIO; +	if (snd_BUG_ON(!substream->rmidi->private_data)) +		return -ENXIO;  	mpu = *((snd_wavefront_mpu_id *) substream->rmidi->private_data); diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 0bb9b92..4c41082 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -1648,9 +1648,10 @@ snd_wavefront_synth_ioctl (struct snd_hwdep *hw, struct file *file,  	card = (struct snd_card *) hw->card; -	snd_assert(card != NULL, return -ENODEV); - -	snd_assert(card->private_data != NULL, return -ENODEV); +	if (snd_BUG_ON(!card)) +		return -ENODEV; +	if (snd_BUG_ON(!card->private_data)) +		return -ENODEV;  	acard = card->private_data;  	dev = &acard->wavefront; diff --git a/sound/isa/wss/Makefile b/sound/isa/wss/Makefile new file mode 100644 index 0000000..454fee7 --- /dev/null +++ b/sound/isa/wss/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for ALSA +# Copyright (c) 2008 by Jaroslav Kysela <perex@perex.cz> +# + +snd-wss-lib-objs := wss_lib.o + +# Toplevel Module Dependency +obj-$(CONFIG_SND_WSS_LIB) += snd-wss-lib.o + diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c new file mode 100644 index 0000000..3d6c5f2 --- /dev/null +++ b/sound/isa/wss/wss_lib.c @@ -0,0 +1,2322 @@ +/* + *  Copyright (c) by Jaroslav Kysela <perex@perex.cz> + *  Routines for control of CS4231(A)/CS4232/InterWave & compatible chips + * + *  Bugs: + *     - sometimes record brokes playback with WSS portion of + *       Yamaha OPL3-SA3 chip + *     - CS4231 (GUS MAX) - still trouble with occasional noises + *			  - broken initialization? + * + *   This program is free software; you can redistribute it and/or modify + *   it under the terms of the GNU General Public License as published by + *   the Free Software Foundation; either version 2 of the License, or + *   (at your option) any later version. + * + *   This program is distributed in the hope that it will be useful, + *   but WITHOUT ANY WARRANTY; without even the implied warranty of + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *   GNU General Public License for more details. + * + *   You should have received a copy of the GNU General Public License + *   along with this program; if not, write to the Free Software + *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + * + */ + +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/ioport.h> +#include <sound/core.h> +#include <sound/wss.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/irq.h> + +MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); +MODULE_DESCRIPTION("Routines for control of CS4231(A)/CS4232/InterWave & compatible chips"); +MODULE_LICENSE("GPL"); + +#if 0 +#define SNDRV_DEBUG_MCE +#endif + +/* + *  Some variables + */ + +static unsigned char freq_bits[14] = { +	/* 5510 */	0x00 | CS4231_XTAL2, +	/* 6620 */	0x0E | CS4231_XTAL2, +	/* 8000 */	0x00 | CS4231_XTAL1, +	/* 9600 */	0x0E | CS4231_XTAL1, +	/* 11025 */	0x02 | CS4231_XTAL2, +	/* 16000 */	0x02 | CS4231_XTAL1, +	/* 18900 */	0x04 | CS4231_XTAL2, +	/* 22050 */	0x06 | CS4231_XTAL2, +	/* 27042 */	0x04 | CS4231_XTAL1, +	/* 32000 */	0x06 | CS4231_XTAL1, +	/* 33075 */	0x0C | CS4231_XTAL2, +	/* 37800 */	0x08 | CS4231_XTAL2, +	/* 44100 */	0x0A | CS4231_XTAL2, +	/* 48000 */	0x0C | CS4231_XTAL1 +}; + +static unsigned int rates[14] = { +	5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, +	27042, 32000, 33075, 37800, 44100, 48000 +}; + +static struct snd_pcm_hw_constraint_list hw_constraints_rates = { +	.count = ARRAY_SIZE(rates), +	.list = rates, +	.mask = 0, +}; + +static int snd_wss_xrate(struct snd_pcm_runtime *runtime) +{ +	return snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, +					  &hw_constraints_rates); +} + +static unsigned char snd_wss_original_image[32] = +{ +	0x00,			/* 00/00 - lic */ +	0x00,			/* 01/01 - ric */ +	0x9f,			/* 02/02 - la1ic */ +	0x9f,			/* 03/03 - ra1ic */ +	0x9f,			/* 04/04 - la2ic */ +	0x9f,			/* 05/05 - ra2ic */ +	0xbf,			/* 06/06 - loc */ +	0xbf,			/* 07/07 - roc */ +	0x20,			/* 08/08 - pdfr */ +	CS4231_AUTOCALIB,	/* 09/09 - ic */ +	0x00,			/* 0a/10 - pc */ +	0x00,			/* 0b/11 - ti */ +	CS4231_MODE2,		/* 0c/12 - mi */ +	0xfc,			/* 0d/13 - lbc */ +	0x00,			/* 0e/14 - pbru */ +	0x00,			/* 0f/15 - pbrl */ +	0x80,			/* 10/16 - afei */ +	0x01,			/* 11/17 - afeii */ +	0x9f,			/* 12/18 - llic */ +	0x9f,			/* 13/19 - rlic */ +	0x00,			/* 14/20 - tlb */ +	0x00,			/* 15/21 - thb */ +	0x00,			/* 16/22 - la3mic/reserved */ +	0x00,			/* 17/23 - ra3mic/reserved */ +	0x00,			/* 18/24 - afs */ +	0x00,			/* 19/25 - lamoc/version */ +	0xcf,			/* 1a/26 - mioc */ +	0x00,			/* 1b/27 - ramoc/reserved */ +	0x20,			/* 1c/28 - cdfr */ +	0x00,			/* 1d/29 - res4 */ +	0x00,			/* 1e/30 - cbru */ +	0x00,			/* 1f/31 - cbrl */ +}; + +static unsigned char snd_opti93x_original_image[32] = +{ +	0x00,		/* 00/00 - l_mixout_outctrl */ +	0x00,		/* 01/01 - r_mixout_outctrl */ +	0x88,		/* 02/02 - l_cd_inctrl */ +	0x88,		/* 03/03 - r_cd_inctrl */ +	0x88,		/* 04/04 - l_a1/fm_inctrl */ +	0x88,		/* 05/05 - r_a1/fm_inctrl */ +	0x80,		/* 06/06 - l_dac_inctrl */ +	0x80,		/* 07/07 - r_dac_inctrl */ +	0x00,		/* 08/08 - ply_dataform_reg */ +	0x00,		/* 09/09 - if_conf */ +	0x00,		/* 0a/10 - pin_ctrl */ +	0x00,		/* 0b/11 - err_init_reg */ +	0x0a,		/* 0c/12 - id_reg */ +	0x00,		/* 0d/13 - reserved */ +	0x00,		/* 0e/14 - ply_upcount_reg */ +	0x00,		/* 0f/15 - ply_lowcount_reg */ +	0x88,		/* 10/16 - reserved/l_a1_inctrl */ +	0x88,		/* 11/17 - reserved/r_a1_inctrl */ +	0x88,		/* 12/18 - l_line_inctrl */ +	0x88,		/* 13/19 - r_line_inctrl */ +	0x88,		/* 14/20 - l_mic_inctrl */ +	0x88,		/* 15/21 - r_mic_inctrl */ +	0x80,		/* 16/22 - l_out_outctrl */ +	0x80,		/* 17/23 - r_out_outctrl */ +	0x00,		/* 18/24 - reserved */ +	0x00,		/* 19/25 - reserved */ +	0x00,		/* 1a/26 - reserved */ +	0x00,		/* 1b/27 - reserved */ +	0x00,		/* 1c/28 - cap_dataform_reg */ +	0x00,		/* 1d/29 - reserved */ +	0x00,		/* 1e/30 - cap_upcount_reg */ +	0x00		/* 1f/31 - cap_lowcount_reg */ +}; + +/* + *  Basic I/O functions + */ + +static inline void wss_outb(struct snd_wss *chip, u8 offset, u8 val) +{ +	outb(val, chip->port + offset); +} + +static inline u8 wss_inb(struct snd_wss *chip, u8 offset) +{ +	return inb(chip->port + offset); +} + +static void snd_wss_wait(struct snd_wss *chip) +{ +	int timeout; + +	for (timeout = 250; +	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); +	     timeout--) +		udelay(100); +} + +static void snd_wss_outm(struct snd_wss *chip, unsigned char reg, +			    unsigned char mask, unsigned char value) +{ +	unsigned char tmp = (chip->image[reg] & mask) | value; + +	snd_wss_wait(chip); +#ifdef CONFIG_SND_DEBUG +	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) +		snd_printk("outm: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif +	chip->image[reg] = tmp; +	if (!chip->calibrate_mute) { +		wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); +		wmb(); +		wss_outb(chip, CS4231P(REG), tmp); +		mb(); +	} +} + +static void snd_wss_dout(struct snd_wss *chip, unsigned char reg, +			 unsigned char value) +{ +	int timeout; + +	for (timeout = 250; +	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); +	     timeout--) +		udelay(10); +	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); +	wss_outb(chip, CS4231P(REG), value); +	mb(); +} + +void snd_wss_out(struct snd_wss *chip, unsigned char reg, unsigned char value) +{ +	snd_wss_wait(chip); +#ifdef CONFIG_SND_DEBUG +	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) +		snd_printk("out: auto calibration time out - reg = 0x%x, value = 0x%x\n", reg, value); +#endif +	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); +	wss_outb(chip, CS4231P(REG), value); +	chip->image[reg] = value; +	mb(); +	snd_printdd("codec out - reg 0x%x = 0x%x\n", +			chip->mce_bit | reg, value); +} +EXPORT_SYMBOL(snd_wss_out); + +unsigned char snd_wss_in(struct snd_wss *chip, unsigned char reg) +{ +	snd_wss_wait(chip); +#ifdef CONFIG_SND_DEBUG +	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) +		snd_printk("in: auto calibration time out - reg = 0x%x\n", reg); +#endif +	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | reg); +	mb(); +	return wss_inb(chip, CS4231P(REG)); +} +EXPORT_SYMBOL(snd_wss_in); + +void snd_cs4236_ext_out(struct snd_wss *chip, unsigned char reg, +			unsigned char val) +{ +	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); +	wss_outb(chip, CS4231P(REG), +		 reg | (chip->image[CS4236_EXT_REG] & 0x01)); +	wss_outb(chip, CS4231P(REG), val); +	chip->eimage[CS4236_REG(reg)] = val; +#if 0 +	printk("ext out : reg = 0x%x, val = 0x%x\n", reg, val); +#endif +} +EXPORT_SYMBOL(snd_cs4236_ext_out); + +unsigned char snd_cs4236_ext_in(struct snd_wss *chip, unsigned char reg) +{ +	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | 0x17); +	wss_outb(chip, CS4231P(REG), +		 reg | (chip->image[CS4236_EXT_REG] & 0x01)); +#if 1 +	return wss_inb(chip, CS4231P(REG)); +#else +	{ +		unsigned char res; +		res = wss_inb(chip, CS4231P(REG)); +		printk("ext in : reg = 0x%x, val = 0x%x\n", reg, res); +		return res; +	} +#endif +} +EXPORT_SYMBOL(snd_cs4236_ext_in); + +#if 0 + +static void snd_wss_debug(struct snd_wss *chip) +{ +	printk(KERN_DEBUG +		"CS4231 REGS:      INDEX = 0x%02x  " +		"                 STATUS = 0x%02x\n", +					wss_inb(chip, CS4231P(REGSEL)), +					wss_inb(chip, CS4231P(STATUS))); +	printk(KERN_DEBUG +		"  0x00: left input      = 0x%02x  " +		"  0x10: alt 1 (CFIG 2)  = 0x%02x\n", +					snd_wss_in(chip, 0x00), +					snd_wss_in(chip, 0x10)); +	printk(KERN_DEBUG +		"  0x01: right input     = 0x%02x  " +		"  0x11: alt 2 (CFIG 3)  = 0x%02x\n", +					snd_wss_in(chip, 0x01), +					snd_wss_in(chip, 0x11)); +	printk(KERN_DEBUG +		"  0x02: GF1 left input  = 0x%02x  " +		"  0x12: left line in    = 0x%02x\n", +					snd_wss_in(chip, 0x02), +					snd_wss_in(chip, 0x12)); +	printk(KERN_DEBUG +		"  0x03: GF1 right input = 0x%02x  " +		"  0x13: right line in   = 0x%02x\n", +					snd_wss_in(chip, 0x03), +					snd_wss_in(chip, 0x13)); +	printk(KERN_DEBUG +		"  0x04: CD left input   = 0x%02x  " +		"  0x14: timer low       = 0x%02x\n", +					snd_wss_in(chip, 0x04), +					snd_wss_in(chip, 0x14)); +	printk(KERN_DEBUG +		"  0x05: CD right input  = 0x%02x  " +		"  0x15: timer high      = 0x%02x\n", +					snd_wss_in(chip, 0x05), +					snd_wss_in(chip, 0x15)); +	printk(KERN_DEBUG +		"  0x06: left output     = 0x%02x  " +		"  0x16: left MIC (PnP)  = 0x%02x\n", +					snd_wss_in(chip, 0x06), +					snd_wss_in(chip, 0x16)); +	printk(KERN_DEBUG +		"  0x07: right output    = 0x%02x  " +		"  0x17: right MIC (PnP) = 0x%02x\n", +					snd_wss_in(chip, 0x07), +					snd_wss_in(chip, 0x17)); +	printk(KERN_DEBUG +		"  0x08: playback format = 0x%02x  " +		"  0x18: IRQ status      = 0x%02x\n", +					snd_wss_in(chip, 0x08), +					snd_wss_in(chip, 0x18)); +	printk(KERN_DEBUG +		"  0x09: iface (CFIG 1)  = 0x%02x  " +		"  0x19: left line out   = 0x%02x\n", +					snd_wss_in(chip, 0x09), +					snd_wss_in(chip, 0x19)); +	printk(KERN_DEBUG +		"  0x0a: pin control     = 0x%02x  " +		"  0x1a: mono control    = 0x%02x\n", +					snd_wss_in(chip, 0x0a), +					snd_wss_in(chip, 0x1a)); +	printk(KERN_DEBUG +		"  0x0b: init & status   = 0x%02x  " +		"  0x1b: right line out  = 0x%02x\n", +					snd_wss_in(chip, 0x0b), +					snd_wss_in(chip, 0x1b)); +	printk(KERN_DEBUG +		"  0x0c: revision & mode = 0x%02x  " +		"  0x1c: record format   = 0x%02x\n", +					snd_wss_in(chip, 0x0c), +					snd_wss_in(chip, 0x1c)); +	printk(KERN_DEBUG +		"  0x0d: loopback        = 0x%02x  " +		"  0x1d: var freq (PnP)  = 0x%02x\n", +					snd_wss_in(chip, 0x0d), +					snd_wss_in(chip, 0x1d)); +	printk(KERN_DEBUG +		"  0x0e: ply upr count   = 0x%02x  " +		"  0x1e: ply lwr count   = 0x%02x\n", +					snd_wss_in(chip, 0x0e), +					snd_wss_in(chip, 0x1e)); +	printk(KERN_DEBUG +		"  0x0f: rec upr count   = 0x%02x  " +		"  0x1f: rec lwr count   = 0x%02x\n", +					snd_wss_in(chip, 0x0f), +					snd_wss_in(chip, 0x1f)); +} + +#endif + +/* + *  CS4231 detection / MCE routines + */ + +static void snd_wss_busy_wait(struct snd_wss *chip) +{ +	int timeout; + +	/* huh.. looks like this sequence is proper for CS4231A chip (GUS MAX) */ +	for (timeout = 5; timeout > 0; timeout--) +		wss_inb(chip, CS4231P(REGSEL)); +	/* end of cleanup sequence */ +	for (timeout = 25000; +	     timeout > 0 && (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT); +	     timeout--) +		udelay(10); +} + +void snd_wss_mce_up(struct snd_wss *chip) +{ +	unsigned long flags; +	int timeout; + +	snd_wss_wait(chip); +#ifdef CONFIG_SND_DEBUG +	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) +		snd_printk("mce_up - auto calibration time out (0)\n"); +#endif +	spin_lock_irqsave(&chip->reg_lock, flags); +	chip->mce_bit |= CS4231_MCE; +	timeout = wss_inb(chip, CS4231P(REGSEL)); +	if (timeout == 0x80) +		snd_printk("mce_up [0x%lx]: serious init problem - codec still busy\n", chip->port); +	if (!(timeout & CS4231_MCE)) +		wss_outb(chip, CS4231P(REGSEL), +			 chip->mce_bit | (timeout & 0x1f)); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +} +EXPORT_SYMBOL(snd_wss_mce_up); + +void snd_wss_mce_down(struct snd_wss *chip) +{ +	unsigned long flags; +	unsigned long end_time; +	int timeout; +	int hw_mask = WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK | WSS_HW_AD1848; + +	snd_wss_busy_wait(chip); + +#ifdef CONFIG_SND_DEBUG +	if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) +		snd_printk("mce_down [0x%lx] - auto calibration time out (0)\n", (long)CS4231P(REGSEL)); +#endif +	spin_lock_irqsave(&chip->reg_lock, flags); +	chip->mce_bit &= ~CS4231_MCE; +	timeout = wss_inb(chip, CS4231P(REGSEL)); +	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	if (timeout == 0x80) +		snd_printk("mce_down [0x%lx]: serious init problem - codec still busy\n", chip->port); +	if ((timeout & CS4231_MCE) == 0 || !(chip->hardware & hw_mask)) +		return; + +	/* +	 * Wait for (possible -- during init auto-calibration may not be set) +	 * calibration process to start. Needs upto 5 sample periods on AD1848 +	 * which at the slowest possible rate of 5.5125 kHz means 907 us. +	 */ +	msleep(1); + +	snd_printdd("(1) jiffies = %lu\n", jiffies); + +	/* check condition up to 250 ms */ +	end_time = jiffies + msecs_to_jiffies(250); +	while (snd_wss_in(chip, CS4231_TEST_INIT) & +		CS4231_CALIB_IN_PROGRESS) { + +		if (time_after(jiffies, end_time)) { +			snd_printk(KERN_ERR "mce_down - " +					"auto calibration time out (2)\n"); +			return; +		} +		msleep(1); +	} + +	snd_printdd("(2) jiffies = %lu\n", jiffies); + +	/* check condition up to 100 ms */ +	end_time = jiffies + msecs_to_jiffies(100); +	while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) { +		if (time_after(jiffies, end_time)) { +			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n"); +			return; +		} +		msleep(1); +	} + +	snd_printdd("(3) jiffies = %lu\n", jiffies); +	snd_printd("mce_down - exit = 0x%x\n", wss_inb(chip, CS4231P(REGSEL))); +} +EXPORT_SYMBOL(snd_wss_mce_down); + +static unsigned int snd_wss_get_count(unsigned char format, unsigned int size) +{ +	switch (format & 0xe0) { +	case CS4231_LINEAR_16: +	case CS4231_LINEAR_16_BIG: +		size >>= 1; +		break; +	case CS4231_ADPCM_16: +		return size >> 2; +	} +	if (format & CS4231_STEREO) +		size >>= 1; +	return size; +} + +static int snd_wss_trigger(struct snd_pcm_substream *substream, +			   int cmd) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	int result = 0; +	unsigned int what; +	struct snd_pcm_substream *s; +	int do_start; + +	switch (cmd) { +	case SNDRV_PCM_TRIGGER_START: +	case SNDRV_PCM_TRIGGER_RESUME: +		do_start = 1; break; +	case SNDRV_PCM_TRIGGER_STOP: +	case SNDRV_PCM_TRIGGER_SUSPEND: +		do_start = 0; break; +	default: +		return -EINVAL; +	} + +	what = 0; +	snd_pcm_group_for_each_entry(s, substream) { +		if (s == chip->playback_substream) { +			what |= CS4231_PLAYBACK_ENABLE; +			snd_pcm_trigger_done(s, substream); +		} else if (s == chip->capture_substream) { +			what |= CS4231_RECORD_ENABLE; +			snd_pcm_trigger_done(s, substream); +		} +	} +	spin_lock(&chip->reg_lock); +	if (do_start) { +		chip->image[CS4231_IFACE_CTRL] |= what; +		if (chip->trigger) +			chip->trigger(chip, what, 1); +	} else { +		chip->image[CS4231_IFACE_CTRL] &= ~what; +		if (chip->trigger) +			chip->trigger(chip, what, 0); +	} +	snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); +	spin_unlock(&chip->reg_lock); +#if 0 +	snd_wss_debug(chip); +#endif +	return result; +} + +/* + *  CODEC I/O + */ + +static unsigned char snd_wss_get_rate(unsigned int rate) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(rates); i++) +		if (rate == rates[i]) +			return freq_bits[i]; +	// snd_BUG(); +	return freq_bits[ARRAY_SIZE(rates) - 1]; +} + +static unsigned char snd_wss_get_format(struct snd_wss *chip, +					int format, +					int channels) +{ +	unsigned char rformat; + +	rformat = CS4231_LINEAR_8; +	switch (format) { +	case SNDRV_PCM_FORMAT_MU_LAW:	rformat = CS4231_ULAW_8; break; +	case SNDRV_PCM_FORMAT_A_LAW:	rformat = CS4231_ALAW_8; break; +	case SNDRV_PCM_FORMAT_S16_LE:	rformat = CS4231_LINEAR_16; break; +	case SNDRV_PCM_FORMAT_S16_BE:	rformat = CS4231_LINEAR_16_BIG; break; +	case SNDRV_PCM_FORMAT_IMA_ADPCM:	rformat = CS4231_ADPCM_16; break; +	} +	if (channels > 1) +		rformat |= CS4231_STEREO; +#if 0 +	snd_printk("get_format: 0x%x (mode=0x%x)\n", format, mode); +#endif +	return rformat; +} + +static void snd_wss_calibrate_mute(struct snd_wss *chip, int mute) +{ +	unsigned long flags; + +	mute = mute ? 0x80 : 0; +	spin_lock_irqsave(&chip->reg_lock, flags); +	if (chip->calibrate_mute == mute) { +		spin_unlock_irqrestore(&chip->reg_lock, flags); +		return; +	} +	if (!mute) { +		snd_wss_dout(chip, CS4231_LEFT_INPUT, +			     chip->image[CS4231_LEFT_INPUT]); +		snd_wss_dout(chip, CS4231_RIGHT_INPUT, +			     chip->image[CS4231_RIGHT_INPUT]); +		snd_wss_dout(chip, CS4231_LOOPBACK, +			     chip->image[CS4231_LOOPBACK]); +	} +	snd_wss_dout(chip, CS4231_AUX1_LEFT_INPUT, +		     mute | chip->image[CS4231_AUX1_LEFT_INPUT]); +	snd_wss_dout(chip, CS4231_AUX1_RIGHT_INPUT, +		     mute | chip->image[CS4231_AUX1_RIGHT_INPUT]); +	snd_wss_dout(chip, CS4231_AUX2_LEFT_INPUT, +		     mute | chip->image[CS4231_AUX2_LEFT_INPUT]); +	snd_wss_dout(chip, CS4231_AUX2_RIGHT_INPUT, +		     mute | chip->image[CS4231_AUX2_RIGHT_INPUT]); +	snd_wss_dout(chip, CS4231_LEFT_OUTPUT, +		     mute | chip->image[CS4231_LEFT_OUTPUT]); +	snd_wss_dout(chip, CS4231_RIGHT_OUTPUT, +		     mute | chip->image[CS4231_RIGHT_OUTPUT]); +	if (!(chip->hardware & WSS_HW_AD1848_MASK)) { +		snd_wss_dout(chip, CS4231_LEFT_LINE_IN, +			     mute | chip->image[CS4231_LEFT_LINE_IN]); +		snd_wss_dout(chip, CS4231_RIGHT_LINE_IN, +			     mute | chip->image[CS4231_RIGHT_LINE_IN]); +		snd_wss_dout(chip, CS4231_MONO_CTRL, +			     mute ? 0xc0 : chip->image[CS4231_MONO_CTRL]); +	} +	if (chip->hardware == WSS_HW_INTERWAVE) { +		snd_wss_dout(chip, CS4231_LEFT_MIC_INPUT, +			     mute | chip->image[CS4231_LEFT_MIC_INPUT]); +		snd_wss_dout(chip, CS4231_RIGHT_MIC_INPUT, +			     mute | chip->image[CS4231_RIGHT_MIC_INPUT]); +		snd_wss_dout(chip, CS4231_LINE_LEFT_OUTPUT, +			     mute | chip->image[CS4231_LINE_LEFT_OUTPUT]); +		snd_wss_dout(chip, CS4231_LINE_RIGHT_OUTPUT, +			     mute | chip->image[CS4231_LINE_RIGHT_OUTPUT]); +	} +	chip->calibrate_mute = mute; +	spin_unlock_irqrestore(&chip->reg_lock, flags); +} + +static void snd_wss_playback_format(struct snd_wss *chip, +				       struct snd_pcm_hw_params *params, +				       unsigned char pdfr) +{ +	unsigned long flags; +	int full_calib = 1; + +	mutex_lock(&chip->mce_mutex); +	snd_wss_calibrate_mute(chip, 1); +	if (chip->hardware == WSS_HW_CS4231A || +	    (chip->hardware & WSS_HW_CS4232_MASK)) { +		spin_lock_irqsave(&chip->reg_lock, flags); +		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (pdfr & 0x0f)) {	/* rate is same? */ +			snd_wss_out(chip, CS4231_ALT_FEATURE_1, +				    chip->image[CS4231_ALT_FEATURE_1] | 0x10); +			chip->image[CS4231_PLAYBK_FORMAT] = pdfr; +			snd_wss_out(chip, CS4231_PLAYBK_FORMAT, +				    chip->image[CS4231_PLAYBK_FORMAT]); +			snd_wss_out(chip, CS4231_ALT_FEATURE_1, +				    chip->image[CS4231_ALT_FEATURE_1] &= ~0x10); +			udelay(100); /* Fixes audible clicks at least on GUS MAX */ +			full_calib = 0; +		} +		spin_unlock_irqrestore(&chip->reg_lock, flags); +	} +	if (full_calib) { +		snd_wss_mce_up(chip); +		spin_lock_irqsave(&chip->reg_lock, flags); +		if (chip->hardware != WSS_HW_INTERWAVE && !chip->single_dma) { +			if (chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE) +				pdfr = (pdfr & 0xf0) | +				       (chip->image[CS4231_REC_FORMAT] & 0x0f); +		} else { +			chip->image[CS4231_PLAYBK_FORMAT] = pdfr; +		} +		snd_wss_out(chip, CS4231_PLAYBK_FORMAT, pdfr); +		spin_unlock_irqrestore(&chip->reg_lock, flags); +		if (chip->hardware == WSS_HW_OPL3SA2) +			udelay(100);	/* this seems to help */ +		snd_wss_mce_down(chip); +	} +	snd_wss_calibrate_mute(chip, 0); +	mutex_unlock(&chip->mce_mutex); +} + +static void snd_wss_capture_format(struct snd_wss *chip, +				   struct snd_pcm_hw_params *params, +				   unsigned char cdfr) +{ +	unsigned long flags; +	int full_calib = 1; + +	mutex_lock(&chip->mce_mutex); +	snd_wss_calibrate_mute(chip, 1); +	if (chip->hardware == WSS_HW_CS4231A || +	    (chip->hardware & WSS_HW_CS4232_MASK)) { +		spin_lock_irqsave(&chip->reg_lock, flags); +		if ((chip->image[CS4231_PLAYBK_FORMAT] & 0x0f) == (cdfr & 0x0f) ||	/* rate is same? */ +		    (chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { +			snd_wss_out(chip, CS4231_ALT_FEATURE_1, +				chip->image[CS4231_ALT_FEATURE_1] | 0x20); +			snd_wss_out(chip, CS4231_REC_FORMAT, +				chip->image[CS4231_REC_FORMAT] = cdfr); +			snd_wss_out(chip, CS4231_ALT_FEATURE_1, +				chip->image[CS4231_ALT_FEATURE_1] &= ~0x20); +			full_calib = 0; +		} +		spin_unlock_irqrestore(&chip->reg_lock, flags); +	} +	if (full_calib) { +		snd_wss_mce_up(chip); +		spin_lock_irqsave(&chip->reg_lock, flags); +		if (chip->hardware != WSS_HW_INTERWAVE && +		    !(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) { +			if (chip->single_dma) +				snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr); +			else +				snd_wss_out(chip, CS4231_PLAYBK_FORMAT, +				   (chip->image[CS4231_PLAYBK_FORMAT] & 0xf0) | +				   (cdfr & 0x0f)); +			spin_unlock_irqrestore(&chip->reg_lock, flags); +			snd_wss_mce_down(chip); +			snd_wss_mce_up(chip); +			spin_lock_irqsave(&chip->reg_lock, flags); +		} +		if (chip->hardware & WSS_HW_AD1848_MASK) +			snd_wss_out(chip, CS4231_PLAYBK_FORMAT, cdfr); +		else +			snd_wss_out(chip, CS4231_REC_FORMAT, cdfr); +		spin_unlock_irqrestore(&chip->reg_lock, flags); +		snd_wss_mce_down(chip); +	} +	snd_wss_calibrate_mute(chip, 0); +	mutex_unlock(&chip->mce_mutex); +} + +/* + *  Timer interface + */ + +static unsigned long snd_wss_timer_resolution(struct snd_timer *timer) +{ +	struct snd_wss *chip = snd_timer_chip(timer); +	if (chip->hardware & WSS_HW_CS4236B_MASK) +		return 14467; +	else +		return chip->image[CS4231_PLAYBK_FORMAT] & 1 ? 9969 : 9920; +} + +static int snd_wss_timer_start(struct snd_timer *timer) +{ +	unsigned long flags; +	unsigned int ticks; +	struct snd_wss *chip = snd_timer_chip(timer); +	spin_lock_irqsave(&chip->reg_lock, flags); +	ticks = timer->sticks; +	if ((chip->image[CS4231_ALT_FEATURE_1] & CS4231_TIMER_ENABLE) == 0 || +	    (unsigned char)(ticks >> 8) != chip->image[CS4231_TIMER_HIGH] || +	    (unsigned char)ticks != chip->image[CS4231_TIMER_LOW]) { +		chip->image[CS4231_TIMER_HIGH] = (unsigned char) (ticks >> 8); +		snd_wss_out(chip, CS4231_TIMER_HIGH, +			    chip->image[CS4231_TIMER_HIGH]); +		chip->image[CS4231_TIMER_LOW] = (unsigned char) ticks; +		snd_wss_out(chip, CS4231_TIMER_LOW, +			    chip->image[CS4231_TIMER_LOW]); +		snd_wss_out(chip, CS4231_ALT_FEATURE_1, +			    chip->image[CS4231_ALT_FEATURE_1] | +			    CS4231_TIMER_ENABLE); +	} +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return 0; +} + +static int snd_wss_timer_stop(struct snd_timer *timer) +{ +	unsigned long flags; +	struct snd_wss *chip = snd_timer_chip(timer); +	spin_lock_irqsave(&chip->reg_lock, flags); +	chip->image[CS4231_ALT_FEATURE_1] &= ~CS4231_TIMER_ENABLE; +	snd_wss_out(chip, CS4231_ALT_FEATURE_1, +		    chip->image[CS4231_ALT_FEATURE_1]); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return 0; +} + +static void snd_wss_init(struct snd_wss *chip) +{ +	unsigned long flags; + +	snd_wss_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE +	snd_printk("init: (1)\n"); +#endif +	snd_wss_mce_up(chip); +	spin_lock_irqsave(&chip->reg_lock, flags); +	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | +					    CS4231_PLAYBACK_PIO | +					    CS4231_RECORD_ENABLE | +					    CS4231_RECORD_PIO | +					    CS4231_CALIB_MODE); +	chip->image[CS4231_IFACE_CTRL] |= CS4231_AUTOCALIB; +	snd_wss_out(chip, CS4231_IFACE_CTRL, chip->image[CS4231_IFACE_CTRL]); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	snd_wss_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE +	snd_printk("init: (2)\n"); +#endif + +	snd_wss_mce_up(chip); +	spin_lock_irqsave(&chip->reg_lock, flags); +	snd_wss_out(chip, +		    CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1]); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	snd_wss_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE +	snd_printk("init: (3) - afei = 0x%x\n", +		   chip->image[CS4231_ALT_FEATURE_1]); +#endif + +	spin_lock_irqsave(&chip->reg_lock, flags); +	snd_wss_out(chip, CS4231_ALT_FEATURE_2, +		    chip->image[CS4231_ALT_FEATURE_2]); +	spin_unlock_irqrestore(&chip->reg_lock, flags); + +	snd_wss_mce_up(chip); +	spin_lock_irqsave(&chip->reg_lock, flags); +	snd_wss_out(chip, CS4231_PLAYBK_FORMAT, +		    chip->image[CS4231_PLAYBK_FORMAT]); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	snd_wss_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE +	snd_printk("init: (4)\n"); +#endif + +	snd_wss_mce_up(chip); +	spin_lock_irqsave(&chip->reg_lock, flags); +	if (!(chip->hardware & WSS_HW_AD1848_MASK)) +		snd_wss_out(chip, CS4231_REC_FORMAT, +			    chip->image[CS4231_REC_FORMAT]); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	snd_wss_mce_down(chip); + +#ifdef SNDRV_DEBUG_MCE +	snd_printk("init: (5)\n"); +#endif +} + +static int snd_wss_open(struct snd_wss *chip, unsigned int mode) +{ +	unsigned long flags; + +	mutex_lock(&chip->open_mutex); +	if ((chip->mode & mode) || +	    ((chip->mode & WSS_MODE_OPEN) && chip->single_dma)) { +		mutex_unlock(&chip->open_mutex); +		return -EAGAIN; +	} +	if (chip->mode & WSS_MODE_OPEN) { +		chip->mode |= mode; +		mutex_unlock(&chip->open_mutex); +		return 0; +	} +	/* ok. now enable and ack CODEC IRQ */ +	spin_lock_irqsave(&chip->reg_lock, flags); +	if (!(chip->hardware & WSS_HW_AD1848_MASK)) { +		snd_wss_out(chip, CS4231_IRQ_STATUS, +			    CS4231_PLAYBACK_IRQ | +			    CS4231_RECORD_IRQ | +			    CS4231_TIMER_IRQ); +		snd_wss_out(chip, CS4231_IRQ_STATUS, 0); +	} +	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ +	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ +	chip->image[CS4231_PIN_CTRL] |= CS4231_IRQ_ENABLE; +	snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); +	if (!(chip->hardware & WSS_HW_AD1848_MASK)) { +		snd_wss_out(chip, CS4231_IRQ_STATUS, +			    CS4231_PLAYBACK_IRQ | +			    CS4231_RECORD_IRQ | +			    CS4231_TIMER_IRQ); +		snd_wss_out(chip, CS4231_IRQ_STATUS, 0); +	} +	spin_unlock_irqrestore(&chip->reg_lock, flags); + +	chip->mode = mode; +	mutex_unlock(&chip->open_mutex); +	return 0; +} + +static void snd_wss_close(struct snd_wss *chip, unsigned int mode) +{ +	unsigned long flags; + +	mutex_lock(&chip->open_mutex); +	chip->mode &= ~mode; +	if (chip->mode & WSS_MODE_OPEN) { +		mutex_unlock(&chip->open_mutex); +		return; +	} +	snd_wss_calibrate_mute(chip, 1); + +	/* disable IRQ */ +	spin_lock_irqsave(&chip->reg_lock, flags); +	if (!(chip->hardware & WSS_HW_AD1848_MASK)) +		snd_wss_out(chip, CS4231_IRQ_STATUS, 0); +	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ +	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ +	chip->image[CS4231_PIN_CTRL] &= ~CS4231_IRQ_ENABLE; +	snd_wss_out(chip, CS4231_PIN_CTRL, chip->image[CS4231_PIN_CTRL]); + +	/* now disable record & playback */ + +	if (chip->image[CS4231_IFACE_CTRL] & (CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | +					       CS4231_RECORD_ENABLE | CS4231_RECORD_PIO)) { +		spin_unlock_irqrestore(&chip->reg_lock, flags); +		snd_wss_mce_up(chip); +		spin_lock_irqsave(&chip->reg_lock, flags); +		chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO | +						     CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); +		snd_wss_out(chip, CS4231_IFACE_CTRL, +			    chip->image[CS4231_IFACE_CTRL]); +		spin_unlock_irqrestore(&chip->reg_lock, flags); +		snd_wss_mce_down(chip); +		spin_lock_irqsave(&chip->reg_lock, flags); +	} + +	/* clear IRQ again */ +	if (!(chip->hardware & WSS_HW_AD1848_MASK)) +		snd_wss_out(chip, CS4231_IRQ_STATUS, 0); +	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ +	wss_outb(chip, CS4231P(STATUS), 0);	/* clear IRQ */ +	spin_unlock_irqrestore(&chip->reg_lock, flags); + +	snd_wss_calibrate_mute(chip, 0); + +	chip->mode = 0; +	mutex_unlock(&chip->open_mutex); +} + +/* + *  timer open/close + */ + +static int snd_wss_timer_open(struct snd_timer *timer) +{ +	struct snd_wss *chip = snd_timer_chip(timer); +	snd_wss_open(chip, WSS_MODE_TIMER); +	return 0; +} + +static int snd_wss_timer_close(struct snd_timer *timer) +{ +	struct snd_wss *chip = snd_timer_chip(timer); +	snd_wss_close(chip, WSS_MODE_TIMER); +	return 0; +} + +static struct snd_timer_hardware snd_wss_timer_table = +{ +	.flags =	SNDRV_TIMER_HW_AUTO, +	.resolution =	9945, +	.ticks =	65535, +	.open =		snd_wss_timer_open, +	.close =	snd_wss_timer_close, +	.c_resolution = snd_wss_timer_resolution, +	.start =	snd_wss_timer_start, +	.stop =		snd_wss_timer_stop, +}; + +/* + *  ok.. exported functions.. + */ + +static int snd_wss_playback_hw_params(struct snd_pcm_substream *substream, +					 struct snd_pcm_hw_params *hw_params) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	unsigned char new_pdfr; +	int err; + +	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) +		return err; +	new_pdfr = snd_wss_get_format(chip, params_format(hw_params), +				params_channels(hw_params)) | +				snd_wss_get_rate(params_rate(hw_params)); +	chip->set_playback_format(chip, hw_params, new_pdfr); +	return 0; +} + +static int snd_wss_playback_hw_free(struct snd_pcm_substream *substream) +{ +	return snd_pcm_lib_free_pages(substream); +} + +static int snd_wss_playback_prepare(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	struct snd_pcm_runtime *runtime = substream->runtime; +	unsigned long flags; +	unsigned int size = snd_pcm_lib_buffer_bytes(substream); +	unsigned int count = snd_pcm_lib_period_bytes(substream); + +	spin_lock_irqsave(&chip->reg_lock, flags); +	chip->p_dma_size = size; +	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_PLAYBACK_ENABLE | CS4231_PLAYBACK_PIO); +	snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); +	count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT], count) - 1; +	snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); +	snd_wss_out(chip, CS4231_PLY_UPR_CNT, (unsigned char) (count >> 8)); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 0 +	snd_wss_debug(chip); +#endif +	return 0; +} + +static int snd_wss_capture_hw_params(struct snd_pcm_substream *substream, +					struct snd_pcm_hw_params *hw_params) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	unsigned char new_cdfr; +	int err; + +	if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) +		return err; +	new_cdfr = snd_wss_get_format(chip, params_format(hw_params), +			   params_channels(hw_params)) | +			   snd_wss_get_rate(params_rate(hw_params)); +	chip->set_capture_format(chip, hw_params, new_cdfr); +	return 0; +} + +static int snd_wss_capture_hw_free(struct snd_pcm_substream *substream) +{ +	return snd_pcm_lib_free_pages(substream); +} + +static int snd_wss_capture_prepare(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	struct snd_pcm_runtime *runtime = substream->runtime; +	unsigned long flags; +	unsigned int size = snd_pcm_lib_buffer_bytes(substream); +	unsigned int count = snd_pcm_lib_period_bytes(substream); + +	spin_lock_irqsave(&chip->reg_lock, flags); +	chip->c_dma_size = size; +	chip->image[CS4231_IFACE_CTRL] &= ~(CS4231_RECORD_ENABLE | CS4231_RECORD_PIO); +	snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); +	if (chip->hardware & WSS_HW_AD1848_MASK) +		count = snd_wss_get_count(chip->image[CS4231_PLAYBK_FORMAT], +					  count); +	else +		count = snd_wss_get_count(chip->image[CS4231_REC_FORMAT], +					  count); +	count--; +	if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) { +		snd_wss_out(chip, CS4231_PLY_LWR_CNT, (unsigned char) count); +		snd_wss_out(chip, CS4231_PLY_UPR_CNT, +			    (unsigned char) (count >> 8)); +	} else { +		snd_wss_out(chip, CS4231_REC_LWR_CNT, (unsigned char) count); +		snd_wss_out(chip, CS4231_REC_UPR_CNT, +			    (unsigned char) (count >> 8)); +	} +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return 0; +} + +void snd_wss_overrange(struct snd_wss *chip) +{ +	unsigned long flags; +	unsigned char res; + +	spin_lock_irqsave(&chip->reg_lock, flags); +	res = snd_wss_in(chip, CS4231_TEST_INIT); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	if (res & (0x08 | 0x02))	/* detect overrange only above 0dB; may be user selectable? */ +		chip->capture_substream->runtime->overrange++; +} +EXPORT_SYMBOL(snd_wss_overrange); + +irqreturn_t snd_wss_interrupt(int irq, void *dev_id) +{ +	struct snd_wss *chip = dev_id; +	unsigned char status; + +	if (chip->hardware & WSS_HW_AD1848_MASK) +		/* pretend it was the only possible irq for AD1848 */ +		status = CS4231_PLAYBACK_IRQ; +	else +		status = snd_wss_in(chip, CS4231_IRQ_STATUS); +	if (status & CS4231_TIMER_IRQ) { +		if (chip->timer) +			snd_timer_interrupt(chip->timer, chip->timer->sticks); +	} +	if (chip->single_dma && chip->hardware != WSS_HW_INTERWAVE) { +		if (status & CS4231_PLAYBACK_IRQ) { +			if (chip->mode & WSS_MODE_PLAY) { +				if (chip->playback_substream) +					snd_pcm_period_elapsed(chip->playback_substream); +			} +			if (chip->mode & WSS_MODE_RECORD) { +				if (chip->capture_substream) { +					snd_wss_overrange(chip); +					snd_pcm_period_elapsed(chip->capture_substream); +				} +			} +		} +	} else { +		if (status & CS4231_PLAYBACK_IRQ) { +			if (chip->playback_substream) +				snd_pcm_period_elapsed(chip->playback_substream); +		} +		if (status & CS4231_RECORD_IRQ) { +			if (chip->capture_substream) { +				snd_wss_overrange(chip); +				snd_pcm_period_elapsed(chip->capture_substream); +			} +		} +	} + +	spin_lock(&chip->reg_lock); +	status = ~CS4231_ALL_IRQS | ~status; +	if (chip->hardware & WSS_HW_AD1848_MASK) +		wss_outb(chip, CS4231P(STATUS), 0); +	else +		snd_wss_outm(chip, CS4231_IRQ_STATUS, status, 0); +	spin_unlock(&chip->reg_lock); +	return IRQ_HANDLED; +} +EXPORT_SYMBOL(snd_wss_interrupt); + +static snd_pcm_uframes_t snd_wss_playback_pointer(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	size_t ptr; + +	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_PLAYBACK_ENABLE)) +		return 0; +	ptr = snd_dma_pointer(chip->dma1, chip->p_dma_size); +	return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_uframes_t snd_wss_capture_pointer(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	size_t ptr; + +	if (!(chip->image[CS4231_IFACE_CTRL] & CS4231_RECORD_ENABLE)) +		return 0; +	ptr = snd_dma_pointer(chip->dma2, chip->c_dma_size); +	return bytes_to_frames(substream->runtime, ptr); +} + +/* + + */ + +static int snd_ad1848_probe(struct snd_wss *chip) +{ +	unsigned long timeout = jiffies + msecs_to_jiffies(1000); +	unsigned long flags; +	unsigned char r; +	unsigned short hardware = 0; +	int err = 0; +	int i; + +	while (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) { +		if (time_after(jiffies, timeout)) +			return -ENODEV; +		cond_resched(); +	} +	spin_lock_irqsave(&chip->reg_lock, flags); + +	/* set CS423x MODE 1 */ +	snd_wss_dout(chip, CS4231_MISC_INFO, 0); + +	snd_wss_dout(chip, CS4231_RIGHT_INPUT, 0x45); /* 0x55 & ~0x10 */ +	r = snd_wss_in(chip, CS4231_RIGHT_INPUT); +	if (r != 0x45) { +		/* RMGE always high on AD1847 */ +		if ((r & ~CS4231_ENABLE_MIC_GAIN) != 0x45) { +			err = -ENODEV; +			goto out; +		} +		hardware = WSS_HW_AD1847; +	} else { +		snd_wss_dout(chip, CS4231_LEFT_INPUT,  0xaa); +		r = snd_wss_in(chip, CS4231_LEFT_INPUT); +		/* L/RMGE always low on AT2320 */ +		if ((r | CS4231_ENABLE_MIC_GAIN) != 0xaa) { +			err = -ENODEV; +			goto out; +		} +	} + +	/* clear pending IRQ */ +	wss_inb(chip, CS4231P(STATUS)); +	wss_outb(chip, CS4231P(STATUS), 0); +	mb(); + +	if ((chip->hardware & WSS_HW_TYPE_MASK) != WSS_HW_DETECT) +		goto out; + +	if (hardware) { +		chip->hardware = hardware; +		goto out; +	} + +	r = snd_wss_in(chip, CS4231_MISC_INFO); + +	/* set CS423x MODE 2 */ +	snd_wss_dout(chip, CS4231_MISC_INFO, CS4231_MODE2); +	for (i = 0; i < 16; i++) { +		if (snd_wss_in(chip, i) != snd_wss_in(chip, 16 + i)) { +			/* we have more than 16 registers: check ID */ +			if ((r & 0xf) != 0xa) +				goto out_mode; +			/* +			 * on CMI8330, CS4231_VERSION is volume control and +			 * can be set to 0 +			 */ +			snd_wss_dout(chip, CS4231_VERSION, 0); +			r = snd_wss_in(chip, CS4231_VERSION) & 0xe7; +			if (!r) +				chip->hardware = WSS_HW_CMI8330; +			goto out_mode; +		} +	} +	if (r & 0x80) +		chip->hardware = WSS_HW_CS4248; +	else +		chip->hardware = WSS_HW_AD1848; +out_mode: +	snd_wss_dout(chip, CS4231_MISC_INFO, 0); +out: +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return err; +} + +static int snd_wss_probe(struct snd_wss *chip) +{ +	unsigned long flags; +	int i, id, rev, regnum; +	unsigned char *ptr; +	unsigned int hw; + +	id = snd_ad1848_probe(chip); +	if (id < 0) +		return id; + +	hw = chip->hardware; +	if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) { +		for (i = 0; i < 50; i++) { +			mb(); +			if (wss_inb(chip, CS4231P(REGSEL)) & CS4231_INIT) +				msleep(2); +			else { +				spin_lock_irqsave(&chip->reg_lock, flags); +				snd_wss_out(chip, CS4231_MISC_INFO, +					    CS4231_MODE2); +				id = snd_wss_in(chip, CS4231_MISC_INFO) & 0x0f; +				spin_unlock_irqrestore(&chip->reg_lock, flags); +				if (id == 0x0a) +					break;	/* this is valid value */ +			} +		} +		snd_printdd("wss: port = 0x%lx, id = 0x%x\n", chip->port, id); +		if (id != 0x0a) +			return -ENODEV;	/* no valid device found */ + +		rev = snd_wss_in(chip, CS4231_VERSION) & 0xe7; +		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev); +		if (rev == 0x80) { +			unsigned char tmp = snd_wss_in(chip, 23); +			snd_wss_out(chip, 23, ~tmp); +			if (snd_wss_in(chip, 23) != tmp) +				chip->hardware = WSS_HW_AD1845; +			else +				chip->hardware = WSS_HW_CS4231; +		} else if (rev == 0xa0) { +			chip->hardware = WSS_HW_CS4231A; +		} else if (rev == 0xa2) { +			chip->hardware = WSS_HW_CS4232; +		} else if (rev == 0xb2) { +			chip->hardware = WSS_HW_CS4232A; +		} else if (rev == 0x83) { +			chip->hardware = WSS_HW_CS4236; +		} else if (rev == 0x03) { +			chip->hardware = WSS_HW_CS4236B; +		} else { +			snd_printk("unknown CS chip with version 0x%x\n", rev); +			return -ENODEV;		/* unknown CS4231 chip? */ +		} +	} +	spin_lock_irqsave(&chip->reg_lock, flags); +	wss_inb(chip, CS4231P(STATUS));	/* clear any pendings IRQ */ +	wss_outb(chip, CS4231P(STATUS), 0); +	mb(); +	spin_unlock_irqrestore(&chip->reg_lock, flags); + +	if (!(chip->hardware & WSS_HW_AD1848_MASK)) +		chip->image[CS4231_MISC_INFO] = CS4231_MODE2; +	switch (chip->hardware) { +	case WSS_HW_INTERWAVE: +		chip->image[CS4231_MISC_INFO] = CS4231_IW_MODE3; +		break; +	case WSS_HW_CS4235: +	case WSS_HW_CS4236B: +	case WSS_HW_CS4237B: +	case WSS_HW_CS4238B: +	case WSS_HW_CS4239: +		if (hw == WSS_HW_DETECT3) +			chip->image[CS4231_MISC_INFO] = CS4231_4236_MODE3; +		else +			chip->hardware = WSS_HW_CS4236; +		break; +	} + +	chip->image[CS4231_IFACE_CTRL] = +	    (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | +	    (chip->single_dma ? CS4231_SINGLE_DMA : 0); +	if (chip->hardware != WSS_HW_OPTI93X) { +		chip->image[CS4231_ALT_FEATURE_1] = 0x80; +		chip->image[CS4231_ALT_FEATURE_2] = +			chip->hardware == WSS_HW_INTERWAVE ? 0xc2 : 0x01; +	} +	ptr = (unsigned char *) &chip->image; +	regnum = (chip->hardware & WSS_HW_AD1848_MASK) ? 16 : 32; +	snd_wss_mce_down(chip); +	spin_lock_irqsave(&chip->reg_lock, flags); +	for (i = 0; i < regnum; i++)	/* ok.. fill all registers */ +		snd_wss_out(chip, i, *ptr++); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	snd_wss_mce_up(chip); +	snd_wss_mce_down(chip); + +	mdelay(2); + +	/* ok.. try check hardware version for CS4236+ chips */ +	if ((hw & WSS_HW_TYPE_MASK) == WSS_HW_DETECT) { +		if (chip->hardware == WSS_HW_CS4236B) { +			rev = snd_cs4236_ext_in(chip, CS4236_VERSION); +			snd_cs4236_ext_out(chip, CS4236_VERSION, 0xff); +			id = snd_cs4236_ext_in(chip, CS4236_VERSION); +			snd_cs4236_ext_out(chip, CS4236_VERSION, rev); +			snd_printdd("CS4231: ext version; rev = 0x%x, id = 0x%x\n", rev, id); +			if ((id & 0x1f) == 0x1d) {	/* CS4235 */ +				chip->hardware = WSS_HW_CS4235; +				switch (id >> 5) { +				case 4: +				case 5: +				case 6: +					break; +				default: +					snd_printk("unknown CS4235 chip (enhanced version = 0x%x)\n", id); +				} +			} else if ((id & 0x1f) == 0x0b) {	/* CS4236/B */ +				switch (id >> 5) { +				case 4: +				case 5: +				case 6: +				case 7: +					chip->hardware = WSS_HW_CS4236B; +					break; +				default: +					snd_printk("unknown CS4236 chip (enhanced version = 0x%x)\n", id); +				} +			} else if ((id & 0x1f) == 0x08) {	/* CS4237B */ +				chip->hardware = WSS_HW_CS4237B; +				switch (id >> 5) { +				case 4: +				case 5: +				case 6: +				case 7: +					break; +				default: +					snd_printk("unknown CS4237B chip (enhanced version = 0x%x)\n", id); +				} +			} else if ((id & 0x1f) == 0x09) {	/* CS4238B */ +				chip->hardware = WSS_HW_CS4238B; +				switch (id >> 5) { +				case 5: +				case 6: +				case 7: +					break; +				default: +					snd_printk("unknown CS4238B chip (enhanced version = 0x%x)\n", id); +				} +			} else if ((id & 0x1f) == 0x1e) {	/* CS4239 */ +				chip->hardware = WSS_HW_CS4239; +				switch (id >> 5) { +				case 4: +				case 5: +				case 6: +					break; +				default: +					snd_printk("unknown CS4239 chip (enhanced version = 0x%x)\n", id); +				} +			} else { +				snd_printk("unknown CS4236/CS423xB chip (enhanced version = 0x%x)\n", id); +			} +		} +	} +	return 0;		/* all things are ok.. */ +} + +/* + + */ + +static struct snd_pcm_hardware snd_wss_playback = +{ +	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | +				 SNDRV_PCM_INFO_MMAP_VALID | +				 SNDRV_PCM_INFO_RESUME | +				 SNDRV_PCM_INFO_SYNC_START), +	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | +				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), +	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, +	.rate_min =		5510, +	.rate_max =		48000, +	.channels_min =		1, +	.channels_max =		2, +	.buffer_bytes_max =	(128*1024), +	.period_bytes_min =	64, +	.period_bytes_max =	(128*1024), +	.periods_min =		1, +	.periods_max =		1024, +	.fifo_size =		0, +}; + +static struct snd_pcm_hardware snd_wss_capture = +{ +	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | +				 SNDRV_PCM_INFO_MMAP_VALID | +				 SNDRV_PCM_INFO_RESUME | +				 SNDRV_PCM_INFO_SYNC_START), +	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM | +				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE), +	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000, +	.rate_min =		5510, +	.rate_max =		48000, +	.channels_min =		1, +	.channels_max =		2, +	.buffer_bytes_max =	(128*1024), +	.period_bytes_min =	64, +	.period_bytes_max =	(128*1024), +	.periods_min =		1, +	.periods_max =		1024, +	.fifo_size =		0, +}; + +/* + + */ + +static int snd_wss_playback_open(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	struct snd_pcm_runtime *runtime = substream->runtime; +	int err; + +	runtime->hw = snd_wss_playback; + +	/* hardware limitation of older chipsets */ +	if (chip->hardware & WSS_HW_AD1848_MASK) +		runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM | +					 SNDRV_PCM_FMTBIT_S16_BE); + +	/* hardware bug in InterWave chipset */ +	if (chip->hardware == WSS_HW_INTERWAVE && chip->dma1 > 3) +		runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_MU_LAW; + +	/* hardware limitation of cheap chips */ +	if (chip->hardware == WSS_HW_CS4235 || +	    chip->hardware == WSS_HW_CS4239) +		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE; + +	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.buffer_bytes_max); +	snd_pcm_limit_isa_dma_size(chip->dma1, &runtime->hw.period_bytes_max); + +	if (chip->claim_dma) { +		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma1)) < 0) +			return err; +	} + +	err = snd_wss_open(chip, WSS_MODE_PLAY); +	if (err < 0) { +		if (chip->release_dma) +			chip->release_dma(chip, chip->dma_private_data, chip->dma1); +		snd_free_pages(runtime->dma_area, runtime->dma_bytes); +		return err; +	} +	chip->playback_substream = substream; +	snd_pcm_set_sync(substream); +	chip->rate_constraint(runtime); +	return 0; +} + +static int snd_wss_capture_open(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); +	struct snd_pcm_runtime *runtime = substream->runtime; +	int err; + +	runtime->hw = snd_wss_capture; + +	/* hardware limitation of older chipsets */ +	if (chip->hardware & WSS_HW_AD1848_MASK) +		runtime->hw.formats &= ~(SNDRV_PCM_FMTBIT_IMA_ADPCM | +					 SNDRV_PCM_FMTBIT_S16_BE); + +	/* hardware limitation of cheap chips */ +	if (chip->hardware == WSS_HW_CS4235 || +	    chip->hardware == WSS_HW_CS4239 || +	    chip->hardware == WSS_HW_OPTI93X) +		runtime->hw.formats = SNDRV_PCM_FMTBIT_U8 | +				      SNDRV_PCM_FMTBIT_S16_LE; + +	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.buffer_bytes_max); +	snd_pcm_limit_isa_dma_size(chip->dma2, &runtime->hw.period_bytes_max); + +	if (chip->claim_dma) { +		if ((err = chip->claim_dma(chip, chip->dma_private_data, chip->dma2)) < 0) +			return err; +	} + +	err = snd_wss_open(chip, WSS_MODE_RECORD); +	if (err < 0) { +		if (chip->release_dma) +			chip->release_dma(chip, chip->dma_private_data, chip->dma2); +		snd_free_pages(runtime->dma_area, runtime->dma_bytes); +		return err; +	} +	chip->capture_substream = substream; +	snd_pcm_set_sync(substream); +	chip->rate_constraint(runtime); +	return 0; +} + +static int snd_wss_playback_close(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); + +	chip->playback_substream = NULL; +	snd_wss_close(chip, WSS_MODE_PLAY); +	return 0; +} + +static int snd_wss_capture_close(struct snd_pcm_substream *substream) +{ +	struct snd_wss *chip = snd_pcm_substream_chip(substream); + +	chip->capture_substream = NULL; +	snd_wss_close(chip, WSS_MODE_RECORD); +	return 0; +} + +static void snd_wss_thinkpad_twiddle(struct snd_wss *chip, int on) +{ +	int tmp; + +	if (!chip->thinkpad_flag) +		return; + +	outb(0x1c, AD1848_THINKPAD_CTL_PORT1); +	tmp = inb(AD1848_THINKPAD_CTL_PORT2); + +	if (on) +		/* turn it on */ +		tmp |= AD1848_THINKPAD_CS4248_ENABLE_BIT; +	else +		/* turn it off */ +		tmp &= ~AD1848_THINKPAD_CS4248_ENABLE_BIT; + +	outb(tmp, AD1848_THINKPAD_CTL_PORT2); +} + +#ifdef CONFIG_PM + +/* lowlevel suspend callback for CS4231 */ +static void snd_wss_suspend(struct snd_wss *chip) +{ +	int reg; +	unsigned long flags; + +	snd_pcm_suspend_all(chip->pcm); +	spin_lock_irqsave(&chip->reg_lock, flags); +	for (reg = 0; reg < 32; reg++) +		chip->image[reg] = snd_wss_in(chip, reg); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	if (chip->thinkpad_flag) +		snd_wss_thinkpad_twiddle(chip, 0); +} + +/* lowlevel resume callback for CS4231 */ +static void snd_wss_resume(struct snd_wss *chip) +{ +	int reg; +	unsigned long flags; +	/* int timeout; */ + +	if (chip->thinkpad_flag) +		snd_wss_thinkpad_twiddle(chip, 1); +	snd_wss_mce_up(chip); +	spin_lock_irqsave(&chip->reg_lock, flags); +	for (reg = 0; reg < 32; reg++) { +		switch (reg) { +		case CS4231_VERSION: +			break; +		default: +			snd_wss_out(chip, reg, chip->image[reg]); +			break; +		} +	} +	spin_unlock_irqrestore(&chip->reg_lock, flags); +#if 1 +	snd_wss_mce_down(chip); +#else +	/* The following is a workaround to avoid freeze after resume on TP600E. +	   This is the first half of copy of snd_wss_mce_down(), but doesn't +	   include rescheduling.  -- iwai +	   */ +	snd_wss_busy_wait(chip); +	spin_lock_irqsave(&chip->reg_lock, flags); +	chip->mce_bit &= ~CS4231_MCE; +	timeout = wss_inb(chip, CS4231P(REGSEL)); +	wss_outb(chip, CS4231P(REGSEL), chip->mce_bit | (timeout & 0x1f)); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	if (timeout == 0x80) +		snd_printk("down [0x%lx]: serious init problem - codec still busy\n", chip->port); +	if ((timeout & CS4231_MCE) == 0 || +	    !(chip->hardware & (WSS_HW_CS4231_MASK | WSS_HW_CS4232_MASK))) { +		return; +	} +	snd_wss_busy_wait(chip); +#endif +} +#endif /* CONFIG_PM */ + +static int snd_wss_free(struct snd_wss *chip) +{ +	release_and_free_resource(chip->res_port); +	release_and_free_resource(chip->res_cport); +	if (chip->irq >= 0) { +		disable_irq(chip->irq); +		if (!(chip->hwshare & WSS_HWSHARE_IRQ)) +			free_irq(chip->irq, (void *) chip); +	} +	if (!(chip->hwshare & WSS_HWSHARE_DMA1) && chip->dma1 >= 0) { +		snd_dma_disable(chip->dma1); +		free_dma(chip->dma1); +	} +	if (!(chip->hwshare & WSS_HWSHARE_DMA2) && +	    chip->dma2 >= 0 && chip->dma2 != chip->dma1) { +		snd_dma_disable(chip->dma2); +		free_dma(chip->dma2); +	} +	if (chip->timer) +		snd_device_free(chip->card, chip->timer); +	kfree(chip); +	return 0; +} + +static int snd_wss_dev_free(struct snd_device *device) +{ +	struct snd_wss *chip = device->device_data; +	return snd_wss_free(chip); +} + +const char *snd_wss_chip_id(struct snd_wss *chip) +{ +	switch (chip->hardware) { +	case WSS_HW_CS4231: +		return "CS4231"; +	case WSS_HW_CS4231A: +		return "CS4231A"; +	case WSS_HW_CS4232: +		return "CS4232"; +	case WSS_HW_CS4232A: +		return "CS4232A"; +	case WSS_HW_CS4235: +		return "CS4235"; +	case WSS_HW_CS4236: +		return "CS4236"; +	case WSS_HW_CS4236B: +		return "CS4236B"; +	case WSS_HW_CS4237B: +		return "CS4237B"; +	case WSS_HW_CS4238B: +		return "CS4238B"; +	case WSS_HW_CS4239: +		return "CS4239"; +	case WSS_HW_INTERWAVE: +		return "AMD InterWave"; +	case WSS_HW_OPL3SA2: +		return chip->card->shortname; +	case WSS_HW_AD1845: +		return "AD1845"; +	case WSS_HW_OPTI93X: +		return "OPTi 93x"; +	case WSS_HW_AD1847: +		return "AD1847"; +	case WSS_HW_AD1848: +		return "AD1848"; +	case WSS_HW_CS4248: +		return "CS4248"; +	case WSS_HW_CMI8330: +		return "CMI8330/C3D"; +	default: +		return "???"; +	} +} +EXPORT_SYMBOL(snd_wss_chip_id); + +static int snd_wss_new(struct snd_card *card, +			  unsigned short hardware, +			  unsigned short hwshare, +			  struct snd_wss **rchip) +{ +	struct snd_wss *chip; + +	*rchip = NULL; +	chip = kzalloc(sizeof(*chip), GFP_KERNEL); +	if (chip == NULL) +		return -ENOMEM; +	chip->hardware = hardware; +	chip->hwshare = hwshare; + +	spin_lock_init(&chip->reg_lock); +	mutex_init(&chip->mce_mutex); +	mutex_init(&chip->open_mutex); +	chip->card = card; +	chip->rate_constraint = snd_wss_xrate; +	chip->set_playback_format = snd_wss_playback_format; +	chip->set_capture_format = snd_wss_capture_format; +	if (chip->hardware == WSS_HW_OPTI93X) +		memcpy(&chip->image, &snd_opti93x_original_image, +		       sizeof(snd_opti93x_original_image)); +	else +		memcpy(&chip->image, &snd_wss_original_image, +		       sizeof(snd_wss_original_image)); +	if (chip->hardware & WSS_HW_AD1848_MASK) { +		chip->image[CS4231_PIN_CTRL] = 0; +		chip->image[CS4231_TEST_INIT] = 0; +	} + +	*rchip = chip; +	return 0; +} + +int 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) +{ +	static struct snd_device_ops ops = { +		.dev_free =	snd_wss_dev_free, +	}; +	struct snd_wss *chip; +	int err; + +	err = snd_wss_new(card, hardware, hwshare, &chip); +	if (err < 0) +		return err; + +	chip->irq = -1; +	chip->dma1 = -1; +	chip->dma2 = -1; + +	chip->res_port = request_region(port, 4, "WSS"); +	if (!chip->res_port) { +		snd_printk(KERN_ERR "wss: can't grab port 0x%lx\n", port); +		snd_wss_free(chip); +		return -EBUSY; +	} +	chip->port = port; +	if ((long)cport >= 0) { +		chip->res_cport = request_region(cport, 8, "CS4232 Control"); +		if (!chip->res_cport) { +			snd_printk(KERN_ERR +				"wss: can't grab control port 0x%lx\n", cport); +			snd_wss_free(chip); +			return -ENODEV; +		} +	} +	chip->cport = cport; +	if (!(hwshare & WSS_HWSHARE_IRQ)) +		if (request_irq(irq, snd_wss_interrupt, IRQF_DISABLED, +				"WSS", (void *) chip)) { +			snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq); +			snd_wss_free(chip); +			return -EBUSY; +		} +	chip->irq = irq; +	if (!(hwshare & WSS_HWSHARE_DMA1) && request_dma(dma1, "WSS - 1")) { +		snd_printk(KERN_ERR "wss: can't grab DMA1 %d\n", dma1); +		snd_wss_free(chip); +		return -EBUSY; +	} +	chip->dma1 = dma1; +	if (!(hwshare & WSS_HWSHARE_DMA2) && dma1 != dma2 && +	      dma2 >= 0 && request_dma(dma2, "WSS - 2")) { +		snd_printk(KERN_ERR "wss: can't grab DMA2 %d\n", dma2); +		snd_wss_free(chip); +		return -EBUSY; +	} +	if (dma1 == dma2 || dma2 < 0) { +		chip->single_dma = 1; +		chip->dma2 = chip->dma1; +	} else +		chip->dma2 = dma2; + +	if (hardware == WSS_HW_THINKPAD) { +		chip->thinkpad_flag = 1; +		chip->hardware = WSS_HW_DETECT; /* reset */ +		snd_wss_thinkpad_twiddle(chip, 1); +	} + +	/* global setup */ +	if (snd_wss_probe(chip) < 0) { +		snd_wss_free(chip); +		return -ENODEV; +	} +	snd_wss_init(chip); + +#if 0 +	if (chip->hardware & WSS_HW_CS4232_MASK) { +		if (chip->res_cport == NULL) +			snd_printk("CS4232 control port features are not accessible\n"); +	} +#endif + +	/* Register device */ +	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); +	if (err < 0) { +		snd_wss_free(chip); +		return err; +	} + +#ifdef CONFIG_PM +	/* Power Management */ +	chip->suspend = snd_wss_suspend; +	chip->resume = snd_wss_resume; +#endif + +	*rchip = chip; +	return 0; +} +EXPORT_SYMBOL(snd_wss_create); + +static struct snd_pcm_ops snd_wss_playback_ops = { +	.open =		snd_wss_playback_open, +	.close =	snd_wss_playback_close, +	.ioctl =	snd_pcm_lib_ioctl, +	.hw_params =	snd_wss_playback_hw_params, +	.hw_free =	snd_wss_playback_hw_free, +	.prepare =	snd_wss_playback_prepare, +	.trigger =	snd_wss_trigger, +	.pointer =	snd_wss_playback_pointer, +}; + +static struct snd_pcm_ops snd_wss_capture_ops = { +	.open =		snd_wss_capture_open, +	.close =	snd_wss_capture_close, +	.ioctl =	snd_pcm_lib_ioctl, +	.hw_params =	snd_wss_capture_hw_params, +	.hw_free =	snd_wss_capture_hw_free, +	.prepare =	snd_wss_capture_prepare, +	.trigger =	snd_wss_trigger, +	.pointer =	snd_wss_capture_pointer, +}; + +int snd_wss_pcm(struct snd_wss *chip, int device, struct snd_pcm **rpcm) +{ +	struct snd_pcm *pcm; +	int err; + +	err = snd_pcm_new(chip->card, "WSS", device, 1, 1, &pcm); +	if (err < 0) +		return err; + +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_wss_playback_ops); +	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_wss_capture_ops); + +	/* global setup */ +	pcm->private_data = chip; +	pcm->info_flags = 0; +	if (chip->single_dma) +		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; +	if (chip->hardware != WSS_HW_INTERWAVE) +		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX; +	strcpy(pcm->name, snd_wss_chip_id(chip)); + +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, +					      snd_dma_isa_data(), +					      64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); + +	chip->pcm = pcm; +	if (rpcm) +		*rpcm = pcm; +	return 0; +} +EXPORT_SYMBOL(snd_wss_pcm); + +static void snd_wss_timer_free(struct snd_timer *timer) +{ +	struct snd_wss *chip = timer->private_data; +	chip->timer = NULL; +} + +int snd_wss_timer(struct snd_wss *chip, int device, struct snd_timer **rtimer) +{ +	struct snd_timer *timer; +	struct snd_timer_id tid; +	int err; + +	/* Timer initialization */ +	tid.dev_class = SNDRV_TIMER_CLASS_CARD; +	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; +	tid.card = chip->card->number; +	tid.device = device; +	tid.subdevice = 0; +	if ((err = snd_timer_new(chip->card, "CS4231", &tid, &timer)) < 0) +		return err; +	strcpy(timer->name, snd_wss_chip_id(chip)); +	timer->private_data = chip; +	timer->private_free = snd_wss_timer_free; +	timer->hw = snd_wss_timer_table; +	chip->timer = timer; +	if (rtimer) +		*rtimer = timer; +	return 0; +} +EXPORT_SYMBOL(snd_wss_timer); + +/* + *  MIXER part + */ + +static int snd_wss_info_mux(struct snd_kcontrol *kcontrol, +			    struct snd_ctl_elem_info *uinfo) +{ +	static char *texts[4] = { +		"Line", "Aux", "Mic", "Mix" +	}; +	static char *opl3sa_texts[4] = { +		"Line", "CD", "Mic", "Mix" +	}; +	static char *gusmax_texts[4] = { +		"Line", "Synth", "Mic", "Mix" +	}; +	char **ptexts = texts; +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol); + +	if (snd_BUG_ON(!chip->card)) +		return -EINVAL; +	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; +	uinfo->count = 2; +	uinfo->value.enumerated.items = 4; +	if (uinfo->value.enumerated.item > 3) +		uinfo->value.enumerated.item = 3; +	if (!strcmp(chip->card->driver, "GUS MAX")) +		ptexts = gusmax_texts; +	switch (chip->hardware) { +	case WSS_HW_INTERWAVE: +		ptexts = gusmax_texts; +		break; +	case WSS_HW_OPL3SA2: +		ptexts = opl3sa_texts; +		break; +	} +	strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); +	return 0; +} + +static int snd_wss_get_mux(struct snd_kcontrol *kcontrol, +			   struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol); +	unsigned long flags; + +	spin_lock_irqsave(&chip->reg_lock, flags); +	ucontrol->value.enumerated.item[0] = (chip->image[CS4231_LEFT_INPUT] & CS4231_MIXS_ALL) >> 6; +	ucontrol->value.enumerated.item[1] = (chip->image[CS4231_RIGHT_INPUT] & CS4231_MIXS_ALL) >> 6; +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return 0; +} + +static int snd_wss_put_mux(struct snd_kcontrol *kcontrol, +			   struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol); +	unsigned long flags; +	unsigned short left, right; +	int change; + +	if (ucontrol->value.enumerated.item[0] > 3 || +	    ucontrol->value.enumerated.item[1] > 3) +		return -EINVAL; +	left = ucontrol->value.enumerated.item[0] << 6; +	right = ucontrol->value.enumerated.item[1] << 6; +	spin_lock_irqsave(&chip->reg_lock, flags); +	left = (chip->image[CS4231_LEFT_INPUT] & ~CS4231_MIXS_ALL) | left; +	right = (chip->image[CS4231_RIGHT_INPUT] & ~CS4231_MIXS_ALL) | right; +	change = left != chip->image[CS4231_LEFT_INPUT] || +		 right != chip->image[CS4231_RIGHT_INPUT]; +	snd_wss_out(chip, CS4231_LEFT_INPUT, left); +	snd_wss_out(chip, CS4231_RIGHT_INPUT, right); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return change; +} + +int snd_wss_info_single(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_info *uinfo) +{ +	int mask = (kcontrol->private_value >> 16) & 0xff; + +	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; +	uinfo->count = 1; +	uinfo->value.integer.min = 0; +	uinfo->value.integer.max = mask; +	return 0; +} +EXPORT_SYMBOL(snd_wss_info_single); + +int snd_wss_get_single(struct snd_kcontrol *kcontrol, +		       struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol); +	unsigned long flags; +	int reg = kcontrol->private_value & 0xff; +	int shift = (kcontrol->private_value >> 8) & 0xff; +	int mask = (kcontrol->private_value >> 16) & 0xff; +	int invert = (kcontrol->private_value >> 24) & 0xff; + +	spin_lock_irqsave(&chip->reg_lock, flags); +	ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	if (invert) +		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; +	return 0; +} +EXPORT_SYMBOL(snd_wss_get_single); + +int snd_wss_put_single(struct snd_kcontrol *kcontrol, +		       struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol); +	unsigned long flags; +	int reg = kcontrol->private_value & 0xff; +	int shift = (kcontrol->private_value >> 8) & 0xff; +	int mask = (kcontrol->private_value >> 16) & 0xff; +	int invert = (kcontrol->private_value >> 24) & 0xff; +	int change; +	unsigned short val; + +	val = (ucontrol->value.integer.value[0] & mask); +	if (invert) +		val = mask - val; +	val <<= shift; +	spin_lock_irqsave(&chip->reg_lock, flags); +	val = (chip->image[reg] & ~(mask << shift)) | val; +	change = val != chip->image[reg]; +	snd_wss_out(chip, reg, val); +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return change; +} +EXPORT_SYMBOL(snd_wss_put_single); + +int snd_wss_info_double(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_info *uinfo) +{ +	int mask = (kcontrol->private_value >> 24) & 0xff; + +	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; +	uinfo->count = 2; +	uinfo->value.integer.min = 0; +	uinfo->value.integer.max = mask; +	return 0; +} +EXPORT_SYMBOL(snd_wss_info_double); + +int snd_wss_get_double(struct snd_kcontrol *kcontrol, +		       struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol); +	unsigned long flags; +	int left_reg = kcontrol->private_value & 0xff; +	int right_reg = (kcontrol->private_value >> 8) & 0xff; +	int shift_left = (kcontrol->private_value >> 16) & 0x07; +	int shift_right = (kcontrol->private_value >> 19) & 0x07; +	int mask = (kcontrol->private_value >> 24) & 0xff; +	int invert = (kcontrol->private_value >> 22) & 1; + +	spin_lock_irqsave(&chip->reg_lock, flags); +	ucontrol->value.integer.value[0] = (chip->image[left_reg] >> shift_left) & mask; +	ucontrol->value.integer.value[1] = (chip->image[right_reg] >> shift_right) & mask; +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	if (invert) { +		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; +		ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; +	} +	return 0; +} +EXPORT_SYMBOL(snd_wss_get_double); + +int snd_wss_put_double(struct snd_kcontrol *kcontrol, +		       struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_wss *chip = snd_kcontrol_chip(kcontrol); +	unsigned long flags; +	int left_reg = kcontrol->private_value & 0xff; +	int right_reg = (kcontrol->private_value >> 8) & 0xff; +	int shift_left = (kcontrol->private_value >> 16) & 0x07; +	int shift_right = (kcontrol->private_value >> 19) & 0x07; +	int mask = (kcontrol->private_value >> 24) & 0xff; +	int invert = (kcontrol->private_value >> 22) & 1; +	int change; +	unsigned short val1, val2; + +	val1 = ucontrol->value.integer.value[0] & mask; +	val2 = ucontrol->value.integer.value[1] & mask; +	if (invert) { +		val1 = mask - val1; +		val2 = mask - val2; +	} +	val1 <<= shift_left; +	val2 <<= shift_right; +	spin_lock_irqsave(&chip->reg_lock, flags); +	if (left_reg != right_reg) { +		val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; +		val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; +		change = val1 != chip->image[left_reg] || +			 val2 != chip->image[right_reg]; +		snd_wss_out(chip, left_reg, val1); +		snd_wss_out(chip, right_reg, val2); +	} else { +		mask = (mask << shift_left) | (mask << shift_right); +		val1 = (chip->image[left_reg] & ~mask) | val1 | val2; +		change = val1 != chip->image[left_reg]; +		snd_wss_out(chip, left_reg, val1); +	} +	spin_unlock_irqrestore(&chip->reg_lock, flags); +	return change; +} +EXPORT_SYMBOL(snd_wss_put_double); + +static const DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0); + +static struct snd_kcontrol_new snd_ad1848_controls[] = { +WSS_DOUBLE("PCM Playback Switch", 0, CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, +	   7, 7, 1, 1), +WSS_DOUBLE_TLV("PCM Playback Volume", 0, +	       CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1, +	       db_scale_6bit), +WSS_DOUBLE("Aux Playback Switch", 0, +	   CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE_TLV("Aux Playback Volume", 0, +	       CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1, +	       db_scale_5bit_12db_max), +WSS_DOUBLE("Aux Playback Switch", 1, +	   CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE_TLV("Aux Playback Volume", 1, +	       CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1, +	       db_scale_5bit_12db_max), +WSS_DOUBLE_TLV("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, +		0, 0, 15, 0, db_scale_rec_gain), +{ +	.name = "Capture Source", +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	.info = snd_wss_info_mux, +	.get = snd_wss_get_mux, +	.put = snd_wss_put_mux, +}, +WSS_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), +WSS_SINGLE_TLV("Loopback Capture Volume", 0, CS4231_LOOPBACK, 1, 63, 0, +	       db_scale_6bit), +}; + +static struct snd_kcontrol_new snd_wss_controls[] = { +WSS_DOUBLE("PCM Playback Switch", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +WSS_DOUBLE("PCM Playback Volume", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 63, 1), +WSS_DOUBLE("Line Playback Switch", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +WSS_DOUBLE("Line Playback Volume", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 31, 1), +WSS_DOUBLE("Aux Playback Switch", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Aux Playback Volume", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 0, 0, 31, 1), +WSS_DOUBLE("Aux Playback Switch", 1, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Aux Playback Volume", 1, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 0, 0, 31, 1), +WSS_SINGLE("Mono Playback Switch", 0, +		CS4231_MONO_CTRL, 7, 1, 1), +WSS_SINGLE("Mono Playback Volume", 0, +		CS4231_MONO_CTRL, 0, 15, 1), +WSS_SINGLE("Mono Output Playback Switch", 0, +		CS4231_MONO_CTRL, 6, 1, 1), +WSS_SINGLE("Mono Output Playback Bypass", 0, +		CS4231_MONO_CTRL, 5, 1, 0), +WSS_DOUBLE("Capture Volume", 0, +		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +{ +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	.name = "Capture Source", +	.info = snd_wss_info_mux, +	.get = snd_wss_get_mux, +	.put = snd_wss_put_mux, +}, +WSS_DOUBLE("Mic Boost", 0, +		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), +WSS_SINGLE("Loopback Capture Switch", 0, +		CS4231_LOOPBACK, 0, 1, 0), +WSS_SINGLE("Loopback Capture Volume", 0, +		CS4231_LOOPBACK, 2, 63, 1) +}; + +static struct snd_kcontrol_new snd_opti93x_controls[] = { +WSS_DOUBLE("Master Playback Switch", 0, +		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), +WSS_DOUBLE("Master Playback Volume", 0, +		OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), +WSS_DOUBLE("PCM Playback Switch", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +WSS_DOUBLE("PCM Playback Volume", 0, +		CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1), +WSS_DOUBLE("FM Playback Switch", 0, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("FM Playback Volume", 0, +		CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1), +WSS_DOUBLE("Line Playback Switch", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +WSS_DOUBLE("Line Playback Volume", 0, +		CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1), +WSS_DOUBLE("Mic Playback Switch", 0, +		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Mic Playback Volume", 0, +		OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), +WSS_DOUBLE("Mic Boost", 0, +		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), +WSS_DOUBLE("CD Playback Switch", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("CD Playback Volume", 0, +		CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1), +WSS_DOUBLE("Aux Playback Switch", 0, +		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), +WSS_DOUBLE("Aux Playback Volume", 0, +		OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), +WSS_DOUBLE("Capture Volume", 0, +		CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +{ +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	.name = "Capture Source", +	.info = snd_wss_info_mux, +	.get = snd_wss_get_mux, +	.put = snd_wss_put_mux, +} +}; + +int snd_wss_mixer(struct snd_wss *chip) +{ +	struct snd_card *card; +	unsigned int idx; +	int err; + +	if (snd_BUG_ON(!chip || !chip->pcm)) +		return -EINVAL; + +	card = chip->card; + +	strcpy(card->mixername, chip->pcm->name); + +	if (chip->hardware == WSS_HW_OPTI93X) +		for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { +			err = snd_ctl_add(card, +					snd_ctl_new1(&snd_opti93x_controls[idx], +						     chip)); +			if (err < 0) +				return err; +		} +	else if (chip->hardware & WSS_HW_AD1848_MASK) +		for (idx = 0; idx < ARRAY_SIZE(snd_ad1848_controls); idx++) { +			err = snd_ctl_add(card, +					snd_ctl_new1(&snd_ad1848_controls[idx], +						     chip)); +			if (err < 0) +				return err; +		} +	else +		for (idx = 0; idx < ARRAY_SIZE(snd_wss_controls); idx++) { +			err = snd_ctl_add(card, +					snd_ctl_new1(&snd_wss_controls[idx], +						     chip)); +			if (err < 0) +				return err; +		} +	return 0; +} +EXPORT_SYMBOL(snd_wss_mixer); + +const struct snd_pcm_ops *snd_wss_get_pcm_ops(int direction) +{ +	return direction == SNDRV_PCM_STREAM_PLAYBACK ? +		&snd_wss_playback_ops : &snd_wss_capture_ops; +} +EXPORT_SYMBOL(snd_wss_get_pcm_ops); + +/* + *  INIT part + */ + +static int __init alsa_wss_init(void) +{ +	return 0; +} + +static void __exit alsa_wss_exit(void) +{ +} + +module_init(alsa_wss_init); +module_exit(alsa_wss_exit); diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c index fbef38a..1881cec 100644 --- a/sound/mips/au1x00.c +++ b/sound/mips/au1x00.c @@ -190,14 +190,16 @@ au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,  static void  au1000_dma_stop(struct audio_stream *stream)  { -	snd_assert(stream->buffer, return); +	if (snd_BUG_ON(!stream->buffer)) +		return;  	disable_dma(stream->dma);  }  static void  au1000_dma_start(struct audio_stream *stream)  { -	snd_assert(stream->buffer, return); +	if (snd_BUG_ON(!stream->buffer)) +		return;  	init_dma(stream->dma);  	if (get_dma_active_buffer(stream->dma) == 0) { diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index d4fafb6..1ca7427 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -24,13 +24,6 @@ config SOUND_VWSND  	  <file:Documentation/sound/oss/vwsnd> for more info on this driver's  	  capabilities. -config SOUND_HAL2 -	tristate "SGI HAL2 sound (EXPERIMENTAL)" -	depends on SGI_IP22 && EXPERIMENTAL -	help -	  Say Y or M if you have an SGI Indy or Indigo2 system and want to be able to -	  use its on-board A2 audio system. -  config SOUND_AU1550_AC97  	tristate "Au1550/Au1200 AC97 Sound"  	depends on SOC_AU1550 || SOC_AU1200 @@ -546,34 +539,6 @@ config SC6600_CDROMBASE  	  Base I/O port address for the CD-ROM interface of the Audio Excel  	  DSP 16 card. -choice -	prompt "Audio Excel DSP 16" -	optional -	depends on SOUND_AEDSP16 - -config AEDSP16_MSS -	bool "MSS emulation" -	depends on SOUND_MSS -	help -	  Answer Y if you want your audio card to emulate Microsoft Sound -	  System. You should then say Y to "Microsoft Sound System support" -	  and say N to "Audio Excel DSP 16 (SBPro emulation)". - -config AEDSP16_SBPRO -	bool "SBPro emulation" -	depends on SOUND_SB -	help -	  Answer Y if you want your audio card to emulate Sound Blaster Pro. -	  You should then say Y to "100% Sound Blaster compatibles -	  (SB16/32/64, ESS, Jazz16) support" and N to "Audio Excel DSP 16 (MSS -	  emulation)". - -	  If you compile the driver into the kernel, you have to add -	  "aedsp16=<io>,<irq>,<dma>,<mssio>,<mpuio>,<mouirq>" to the kernel -	  command line. - -endchoice -  config SOUND_VIDC  	tristate "VIDC 16-bit sound"  	depends on ARM && (ARCH_ACORN || ARCH_CLPS7500) diff --git a/sound/oss/Makefile b/sound/oss/Makefile index c611514..e0ae4d4 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_SOUND_OSS)		+= sound.o  # Please leave it as is, cause the link order is significant !  obj-$(CONFIG_SOUND_SH_DAC_AUDIO)	+= sh_dac_audio.o -obj-$(CONFIG_SOUND_HAL2)	+= hal2.o  obj-$(CONFIG_SOUND_AEDSP16)	+= aedsp16.o  obj-$(CONFIG_SOUND_PSS)		+= pss.o ad1848.o mpu401.o  obj-$(CONFIG_SOUND_TRIX)	+= trix.o ad1848.o sb_lib.o uart401.o diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c index 51e1fde..a0274f3 100644 --- a/sound/oss/aedsp16.c +++ b/sound/oss/aedsp16.c @@ -29,14 +29,6 @@  #include "sound_config.h"  /* - * Sanity checks - */ - -#if defined(CONFIG_SOUND_AEDSP16_SBPRO) && defined(CONFIG_SOUND_AEDSP16_MSS) -#error You have to enable only one of the MSS and SBPRO emulations. -#endif - -/*     READ THIS diff --git a/sound/oss/dmasound/Kconfig b/sound/oss/dmasound/Kconfig index 3eb7827..f456574 100644 --- a/sound/oss/dmasound/Kconfig +++ b/sound/oss/dmasound/Kconfig @@ -42,3 +42,4 @@ config DMASOUND_Q40  config DMASOUND  	tristate +	select SOUND_OSS_CORE diff --git a/sound/oss/hal2.c b/sound/oss/hal2.c deleted file mode 100644 index a94b9df..0000000 --- a/sound/oss/hal2.c +++ /dev/null @@ -1,1558 +0,0 @@ -/* - *  Driver for A2 audio system used in SGI machines - *  Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org> - *   - *  Based on Ulf Carlsson's code. - * - *  This program is free software; you can redistribute it and/or modify - *  it under the terms of the GNU General Public License version 2 as  - *  published by the Free Software Foundation. - * - *  This program is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU General Public License for more details. - * - *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - *  Supported devices: - *  /dev/dsp    standard dsp device, (mostly) OSS compatible - *  /dev/mixer	standard mixer device, (mostly) OSS compatible - * - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/interrupt.h> -#include <linux/dma-mapping.h> -#include <linux/sound.h> -#include <linux/soundcard.h> -#include <linux/mutex.h> - - -#include <asm/io.h> -#include <asm/sgi/hpc3.h> -#include <asm/sgi/ip22.h> - -#include "hal2.h" - -#if 0 -#define DEBUG(args...)		printk(args) -#else -#define DEBUG(args...) -#endif - -#if 0  -#define DEBUG_MIX(args...)	printk(args) -#else -#define DEBUG_MIX(args...) -#endif - -/* - * Before touching these look how it works. It is a bit unusual I know, - * but it helps to keep things simple. This driver is considered complete - * and I won't add any new features although hardware has many cool - * capabilities. - * (Historical note: HAL2 driver was first written by Ulf Carlsson - ALSA - * 0.3 running with 2.2.x kernel. Then ALSA changed completely and it - * seemed easier to me to write OSS driver from scratch - this one. Now - * when ALSA is official part of 2.6 kernel it's time to write ALSA driver - * using (hopefully) final version of ALSA interface) - */ -#define H2_BLOCK_SIZE	1024 -#define H2_ADC_BUFSIZE	8192 -#define H2_DAC_BUFSIZE	16834 - -struct hal2_pbus { -	struct hpc3_pbus_dmacregs *pbus; -	int pbusnr; -	unsigned int ctrl;		/* Current state of pbus->pbdma_ctrl */ -}; - -struct hal2_desc { -	struct hpc_dma_desc desc; -	u32 cnt;			/* don't touch, it is also padding */ -}; - -struct hal2_codec { -	unsigned char *buffer; -	struct hal2_desc *desc; -	int desc_count; -	int tail, head;			/* tail index, head index */ -	struct hal2_pbus pbus; -	unsigned int format;		/* Audio data format */ -	int voices;			/* mono/stereo */ -	unsigned int sample_rate; -	unsigned int master;		/* Master frequency */ -	unsigned short mod;		/* MOD value */ -	unsigned short inc;		/* INC value */ - -	wait_queue_head_t dma_wait; -	spinlock_t lock; -	struct mutex sem; - -	int usecount;			/* recording and playback are -					 * independent */ -}; - -#define H2_MIX_OUTPUT_ATT	0 -#define H2_MIX_INPUT_GAIN	1 -#define H2_MIXERS		2 -struct hal2_mixer { -	int modcnt; -	unsigned int master; -	unsigned int volume[H2_MIXERS]; -}; - -struct hal2_card { -	int dev_dsp;			/* audio device */ -	int dev_mixer;			/* mixer device */ -	int dev_midi;			/* midi device */ - -	struct hal2_ctl_regs *ctl_regs;	/* HAL2 ctl registers */ -	struct hal2_aes_regs *aes_regs;	/* HAL2 aes registers */ -	struct hal2_vol_regs *vol_regs;	/* HAL2 vol registers */ -	struct hal2_syn_regs *syn_regs;	/* HAL2 syn registers */ - -	struct hal2_codec dac; -	struct hal2_codec adc; -	struct hal2_mixer mixer; -}; - -#define H2_INDIRECT_WAIT(regs)	while (regs->isr & H2_ISR_TSTATUS); - -#define H2_READ_ADDR(addr)	(addr | (1<<7)) -#define H2_WRITE_ADDR(addr)	(addr) - -static char *hal2str = "HAL2"; - -/* - * I doubt anyone has a machine with two HAL2 cards. It's possible to - * have two HPC's, so it is probably possible to have two HAL2 cards. - * Try to deal with it, but note that it is not tested. - */ -#define MAXCARDS	2 -static struct hal2_card* hal2_card[MAXCARDS]; - -static const struct { -	unsigned char idx:4, avail:1; -} mixtable[SOUND_MIXER_NRDEVICES] = { -	[SOUND_MIXER_PCM]	= { H2_MIX_OUTPUT_ATT, 1 },	/* voice */ -	[SOUND_MIXER_MIC]	= { H2_MIX_INPUT_GAIN, 1 },	/* mic */ -}; - -#define H2_SUPPORTED_FORMATS	(AFMT_S16_LE | AFMT_S16_BE) - -static inline void hal2_isr_write(struct hal2_card *hal2, u16 val) -{ -	hal2->ctl_regs->isr = val; -} - -static inline u16 hal2_isr_look(struct hal2_card *hal2) -{ -	return hal2->ctl_regs->isr; -} - -static inline u16 hal2_rev_look(struct hal2_card *hal2) -{ -	return hal2->ctl_regs->rev; -} - -#ifdef HAL2_DUMP_REGS -static u16 hal2_i_look16(struct hal2_card *hal2, u16 addr) -{ -	struct hal2_ctl_regs *regs = hal2->ctl_regs; - -	regs->iar = H2_READ_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -	return regs->idr0; -} -#endif - -static u32 hal2_i_look32(struct hal2_card *hal2, u16 addr) -{ -	u32 ret; -	struct hal2_ctl_regs *regs = hal2->ctl_regs; - -	regs->iar = H2_READ_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -	ret = regs->idr0 & 0xffff; -	regs->iar = H2_READ_ADDR(addr | 0x1); -	H2_INDIRECT_WAIT(regs); -	ret |= (regs->idr0 & 0xffff) << 16; -	return ret; -} - -static void hal2_i_write16(struct hal2_card *hal2, u16 addr, u16 val) -{ -	struct hal2_ctl_regs *regs = hal2->ctl_regs; - -	regs->idr0 = val; -	regs->idr1 = 0; -	regs->idr2 = 0; -	regs->idr3 = 0; -	regs->iar = H2_WRITE_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -} - -static void hal2_i_write32(struct hal2_card *hal2, u16 addr, u32 val) -{ -	struct hal2_ctl_regs *regs = hal2->ctl_regs; - -	regs->idr0 = val & 0xffff; -	regs->idr1 = val >> 16; -	regs->idr2 = 0; -	regs->idr3 = 0; -	regs->iar = H2_WRITE_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -} - -static void hal2_i_setbit16(struct hal2_card *hal2, u16 addr, u16 bit) -{ -	struct hal2_ctl_regs *regs = hal2->ctl_regs; - -	regs->iar = H2_READ_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -	regs->idr0 = (regs->idr0 & 0xffff) | bit; -	regs->idr1 = 0; -	regs->idr2 = 0; -	regs->idr3 = 0; -	regs->iar = H2_WRITE_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -} - -static void hal2_i_setbit32(struct hal2_card *hal2, u16 addr, u32 bit) -{ -	u32 tmp; -	struct hal2_ctl_regs *regs = hal2->ctl_regs; - -	regs->iar = H2_READ_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -	tmp = (regs->idr0 & 0xffff) | (regs->idr1 << 16) | bit; -	regs->idr0 = tmp & 0xffff; -	regs->idr1 = tmp >> 16; -	regs->idr2 = 0; -	regs->idr3 = 0; -	regs->iar = H2_WRITE_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -} - -static void hal2_i_clearbit16(struct hal2_card *hal2, u16 addr, u16 bit) -{ -	struct hal2_ctl_regs *regs = hal2->ctl_regs; - -	regs->iar = H2_READ_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -	regs->idr0 = (regs->idr0 & 0xffff) & ~bit; -	regs->idr1 = 0; -	regs->idr2 = 0; -	regs->idr3 = 0; -	regs->iar = H2_WRITE_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -} - -#if 0 -static void hal2_i_clearbit32(struct hal2_card *hal2, u16 addr, u32 bit) -{ -	u32 tmp; -	hal2_ctl_regs_t *regs = hal2->ctl_regs; - -	regs->iar = H2_READ_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -	tmp = ((regs->idr0 & 0xffff) | (regs->idr1 << 16)) & ~bit; -	regs->idr0 = tmp & 0xffff; -	regs->idr1 = tmp >> 16; -	regs->idr2 = 0; -	regs->idr3 = 0; -	regs->iar = H2_WRITE_ADDR(addr); -	H2_INDIRECT_WAIT(regs); -} -#endif - -#ifdef HAL2_DUMP_REGS -static void hal2_dump_regs(struct hal2_card *hal2) -{ -	DEBUG("isr: %08hx ", hal2_isr_look(hal2)); -	DEBUG("rev: %08hx\n", hal2_rev_look(hal2)); -	DEBUG("relay: %04hx\n", hal2_i_look16(hal2, H2I_RELAY_C)); -	DEBUG("port en: %04hx ", hal2_i_look16(hal2, H2I_DMA_PORT_EN)); -	DEBUG("dma end: %04hx ", hal2_i_look16(hal2, H2I_DMA_END)); -	DEBUG("dma drv: %04hx\n", hal2_i_look16(hal2, H2I_DMA_DRV)); -	DEBUG("syn ctl: %04hx ", hal2_i_look16(hal2, H2I_SYNTH_C)); -	DEBUG("aesrx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESRX_C)); -	DEBUG("aestx ctl: %04hx ", hal2_i_look16(hal2, H2I_AESTX_C)); -	DEBUG("dac ctl1: %04hx ", hal2_i_look16(hal2, H2I_ADC_C1)); -	DEBUG("dac ctl2: %08x ", hal2_i_look32(hal2, H2I_ADC_C2)); -	DEBUG("adc ctl1: %04hx ", hal2_i_look16(hal2, H2I_DAC_C1)); -	DEBUG("adc ctl2: %08x ", hal2_i_look32(hal2, H2I_DAC_C2)); -	DEBUG("syn map: %04hx\n", hal2_i_look16(hal2, H2I_SYNTH_MAP_C)); -	DEBUG("bres1 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES1_C1)); -	DEBUG("bres1 ctl2: %04x ", hal2_i_look32(hal2, H2I_BRES1_C2)); -	DEBUG("bres2 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES2_C1)); -	DEBUG("bres2 ctl2: %04x ", hal2_i_look32(hal2, H2I_BRES2_C2)); -	DEBUG("bres3 ctl1: %04hx ", hal2_i_look16(hal2, H2I_BRES3_C1)); -	DEBUG("bres3 ctl2: %04x\n", hal2_i_look32(hal2, H2I_BRES3_C2)); -} -#endif - -static struct hal2_card* hal2_dsp_find_card(int minor) -{ -	int i; - -	for (i = 0; i < MAXCARDS; i++) -		if (hal2_card[i] != NULL && hal2_card[i]->dev_dsp == minor) -			return hal2_card[i]; -	return NULL; -} - -static struct hal2_card* hal2_mixer_find_card(int minor) -{ -	int i; - -	for (i = 0; i < MAXCARDS; i++) -		if (hal2_card[i] != NULL && hal2_card[i]->dev_mixer == minor) -			return hal2_card[i]; -	return NULL; -} - -static void hal2_inc_head(struct hal2_codec *codec) -{ -	codec->head++; -	if (codec->head == codec->desc_count) -		codec->head = 0; -} - -static void hal2_inc_tail(struct hal2_codec *codec) -{ -	codec->tail++; -	if (codec->tail == codec->desc_count) -		codec->tail = 0; -} - -static void hal2_dac_interrupt(struct hal2_codec *dac) -{ -	int running; - -	spin_lock(&dac->lock); -	/* if tail buffer contains zero samples DMA stream was already -	 * stopped */ -	running = dac->desc[dac->tail].cnt; -	dac->desc[dac->tail].cnt = 0; -	dac->desc[dac->tail].desc.cntinfo = HPCDMA_XIE | HPCDMA_EOX; -	/* we just proccessed empty buffer, don't update tail pointer */ -	if (running) -		hal2_inc_tail(dac); -	spin_unlock(&dac->lock); - -	wake_up(&dac->dma_wait); -} - -static void hal2_adc_interrupt(struct hal2_codec *adc) -{ -	int running; - -	spin_lock(&adc->lock); -	/* if head buffer contains nonzero samples DMA stream was already -	 * stopped */ -	running = !adc->desc[adc->head].cnt; -	adc->desc[adc->head].cnt = H2_BLOCK_SIZE; -	adc->desc[adc->head].desc.cntinfo = HPCDMA_XIE | HPCDMA_EOR; -	/* we just proccessed empty buffer, don't update head pointer */ -	if (running) -		hal2_inc_head(adc); -	spin_unlock(&adc->lock); - -	wake_up(&adc->dma_wait); -} - -static irqreturn_t hal2_interrupt(int irq, void *dev_id) -{ -	struct hal2_card *hal2 = dev_id; -	irqreturn_t ret = IRQ_NONE; - -	/* decide what caused this interrupt */ -	if (hal2->dac.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) { -		hal2_dac_interrupt(&hal2->dac); -		ret = IRQ_HANDLED; -	} -	if (hal2->adc.pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_INT) { -		hal2_adc_interrupt(&hal2->adc); -		ret = IRQ_HANDLED; -	} -	return ret; -} - -static int hal2_compute_rate(struct hal2_codec *codec, unsigned int rate) -{ -	unsigned short mod; -	 -	DEBUG("rate: %d\n", rate); -	 -	if (rate < 4000) rate = 4000; -	else if (rate > 48000) rate = 48000; - -	if (44100 % rate < 48000 % rate) { -		mod = 4 * 44100 / rate; -		codec->master = 44100; -	} else { -		mod = 4 * 48000 / rate; -		codec->master = 48000; -	} - -	codec->inc = 4; -	codec->mod = mod; -	rate = 4 * codec->master / mod; - -	DEBUG("real_rate: %d\n", rate); - -	return rate; -} - -static void hal2_set_dac_rate(struct hal2_card *hal2) -{ -	unsigned int master = hal2->dac.master; -	int inc = hal2->dac.inc; -	int mod = hal2->dac.mod; - -	DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod); -	 -	hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0); -	hal2_i_write32(hal2, H2I_BRES1_C2, ((0xffff & (inc - mod - 1)) << 16) | inc); -} - -static void hal2_set_adc_rate(struct hal2_card *hal2) -{ -	unsigned int master = hal2->adc.master; -	int inc = hal2->adc.inc; -	int mod = hal2->adc.mod; - -	DEBUG("master: %d inc: %d mod: %d\n", master, inc, mod); -	 -	hal2_i_write16(hal2, H2I_BRES2_C1, (master == 44100) ? 1 : 0); -	hal2_i_write32(hal2, H2I_BRES2_C2, ((0xffff & (inc - mod - 1)) << 16) | inc); -} - -static void hal2_setup_dac(struct hal2_card *hal2) -{ -	unsigned int fifobeg, fifoend, highwater, sample_size; -	struct hal2_pbus *pbus = &hal2->dac.pbus; - -	DEBUG("hal2_setup_dac\n"); -	 -	/* Now we set up some PBUS information. The PBUS needs information about -	 * what portion of the fifo it will use. If it's receiving or -	 * transmitting, and finally whether the stream is little endian or big -	 * endian. The information is written later, on the start call. -	 */ -	sample_size = 2 * hal2->dac.voices; -	/* Fifo should be set to hold exactly four samples. Highwater mark -	 * should be set to two samples. */ -	highwater = (sample_size * 2) >> 1;	/* halfwords */ -	fifobeg = 0;				/* playback is first */ -	fifoend = (sample_size * 4) >> 3;	/* doublewords */ -	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_LD | -		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24) | -		     (hal2->dac.format & AFMT_S16_LE ? HPC3_PDMACTRL_SEL : 0); -	/* We disable everything before we do anything at all */ -	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; -	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX); -	/* Setup the HAL2 for playback */ -	hal2_set_dac_rate(hal2); -	/* Set endianess */ -	if (hal2->dac.format & AFMT_S16_LE) -		hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX); -	else -		hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECTX); -	/* Set DMA bus */ -	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr)); -	/* We are using 1st Bresenham clock generator for playback */ -	hal2_i_write16(hal2, H2I_DAC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT) -			| (1 << H2I_C1_CLKID_SHIFT) -			| (hal2->dac.voices << H2I_C1_DATAT_SHIFT)); -} - -static void hal2_setup_adc(struct hal2_card *hal2) -{ -	unsigned int fifobeg, fifoend, highwater, sample_size; -	struct hal2_pbus *pbus = &hal2->adc.pbus; - -	DEBUG("hal2_setup_adc\n"); - -	sample_size = 2 * hal2->adc.voices; -	highwater = (sample_size * 2) >> 1;		/* halfwords */ -	fifobeg = (4 * 4) >> 3;				/* record is second */ -	fifoend = (4 * 4 + sample_size * 4) >> 3;	/* doublewords */ -	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV | HPC3_PDMACTRL_LD |  -		     (highwater << 8) | (fifobeg << 16) | (fifoend << 24) | -		     (hal2->adc.format & AFMT_S16_LE ? HPC3_PDMACTRL_SEL : 0); -	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; -	hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR); -	/* Setup the HAL2 for record */ -	hal2_set_adc_rate(hal2); -	/* Set endianess */ -	if (hal2->adc.format & AFMT_S16_LE) -		hal2_i_setbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR); -	else -		hal2_i_clearbit16(hal2, H2I_DMA_END, H2I_DMA_END_CODECR); -	/* Set DMA bus */ -	hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr)); -	/* We are using 2nd Bresenham clock generator for record */ -	hal2_i_write16(hal2, H2I_ADC_C1, (pbus->pbusnr << H2I_C1_DMA_SHIFT) -			| (2 << H2I_C1_CLKID_SHIFT) -			| (hal2->adc.voices << H2I_C1_DATAT_SHIFT)); -} - -static dma_addr_t hal2_desc_addr(struct hal2_codec *codec, int i) -{ -	if (--i < 0) -		i = codec->desc_count - 1; -	return codec->desc[i].desc.pnext; -} - -static void hal2_start_dac(struct hal2_card *hal2) -{ -	struct hal2_codec *dac = &hal2->dac; -	struct hal2_pbus *pbus = &dac->pbus; - -	pbus->pbus->pbdma_dptr = hal2_desc_addr(dac, dac->tail); -	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT; -	/* enable DAC */ -	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX); -} - -static void hal2_start_adc(struct hal2_card *hal2) -{ -	struct hal2_codec *adc = &hal2->adc; -	struct hal2_pbus *pbus = &adc->pbus; - -	pbus->pbus->pbdma_dptr = hal2_desc_addr(adc, adc->head); -	pbus->pbus->pbdma_ctrl = pbus->ctrl | HPC3_PDMACTRL_ACT; -	/* enable ADC */ -	hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR); -} - -static inline void hal2_stop_dac(struct hal2_card *hal2) -{ -	hal2->dac.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; -	/* The HAL2 itself may remain enabled safely */ -} - -static inline void hal2_stop_adc(struct hal2_card *hal2) -{ -	hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; -} - -static int hal2_alloc_dmabuf(struct hal2_codec *codec, int size, -			     int count, int cntinfo, int dir) -{ -	struct hal2_desc *desc, *dma_addr; -	int i; - -	DEBUG("allocating %dk DMA buffer.\n", size / 1024); - -	codec->buffer = (unsigned char *)__get_free_pages(GFP_KERNEL | GFP_DMA, -							  get_order(size)); -	if (!codec->buffer) -		return -ENOMEM; -	desc = dma_alloc_coherent(NULL, count * sizeof(struct hal2_desc), -				  (dma_addr_t *)&dma_addr, GFP_KERNEL); -	if (!desc) { -		free_pages((unsigned long)codec->buffer, get_order(size)); -		return -ENOMEM; -	} -	codec->desc = desc; -	for (i = 0; i < count; i++) { -		desc->desc.pbuf = dma_map_single(NULL, -			(void *)(codec->buffer + i * H2_BLOCK_SIZE), -			H2_BLOCK_SIZE, dir); -		desc->desc.cntinfo = cntinfo; -		desc->desc.pnext = (i == count - 1) ? -				   (u32)dma_addr : (u32)(dma_addr + i + 1); -		desc->cnt = 0; -		desc++; -	} -	codec->desc_count = count; -	codec->head = codec->tail = 0; -	return 0; -} - -static int hal2_alloc_dac_dmabuf(struct hal2_codec *codec) -{ -	return hal2_alloc_dmabuf(codec, H2_DAC_BUFSIZE, -				 H2_DAC_BUFSIZE / H2_BLOCK_SIZE, -				 HPCDMA_XIE | HPCDMA_EOX, -				 DMA_TO_DEVICE); -} - -static int hal2_alloc_adc_dmabuf(struct hal2_codec *codec) -{ -	return hal2_alloc_dmabuf(codec, H2_ADC_BUFSIZE, -				 H2_ADC_BUFSIZE / H2_BLOCK_SIZE, -				 HPCDMA_XIE | H2_BLOCK_SIZE, -				 DMA_TO_DEVICE); -} - -static void hal2_free_dmabuf(struct hal2_codec *codec, int size, int dir) -{ -	dma_addr_t dma_addr; -	int i; - -	dma_addr = codec->desc[codec->desc_count - 1].desc.pnext; -	for (i = 0; i < codec->desc_count; i++) -		dma_unmap_single(NULL, codec->desc[i].desc.pbuf, -				 H2_BLOCK_SIZE, dir); -	dma_free_coherent(NULL, codec->desc_count * sizeof(struct hal2_desc), -			  (void *)codec->desc, dma_addr); -	free_pages((unsigned long)codec->buffer, get_order(size)); -} - -static void hal2_free_dac_dmabuf(struct hal2_codec *codec) -{ -	return hal2_free_dmabuf(codec, H2_DAC_BUFSIZE, DMA_TO_DEVICE); -} - -static void hal2_free_adc_dmabuf(struct hal2_codec *codec) -{ -	return hal2_free_dmabuf(codec, H2_ADC_BUFSIZE, DMA_FROM_DEVICE); -} - -/*  - * Add 'count' bytes to 'buffer' from DMA ring buffers. Return number of - * bytes added or -EFAULT if copy_from_user failed. - */ -static int hal2_get_buffer(struct hal2_card *hal2, char *buffer, int count) -{ -	unsigned long flags; -	int size, ret = 0; -	unsigned char *buf; -	struct hal2_desc *tail; -	struct hal2_codec *adc = &hal2->adc; - -	DEBUG("getting %d bytes ", count); - -	spin_lock_irqsave(&adc->lock, flags); -	tail = &adc->desc[adc->tail]; -	/* enable DMA stream if there are no data */ -	if (!tail->cnt && !(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT)) -		hal2_start_adc(hal2); -	while (tail->cnt > 0 && count > 0) { -		size = min((int)tail->cnt, count); -		buf = &adc->buffer[(adc->tail + 1) * H2_BLOCK_SIZE - tail->cnt]; -		spin_unlock_irqrestore(&adc->lock, flags); -		dma_sync_single(NULL, tail->desc.pbuf, size, DMA_FROM_DEVICE); -		if (copy_to_user(buffer, buf, size)) { -			ret = -EFAULT; -			goto out; -		} -		spin_lock_irqsave(&adc->lock, flags); -		tail->cnt -= size; -		/* buffer is empty, update tail pointer */ -		if (tail->cnt == 0) { -			tail->desc.cntinfo = HPCDMA_XIE | H2_BLOCK_SIZE; -			hal2_inc_tail(adc); -			tail = &adc->desc[adc->tail]; -			/* enable DMA stream again if needed */ -			if (!(adc->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT)) -				hal2_start_adc(hal2); -		} -		buffer += size; -		ret += size; -		count -= size; - -		DEBUG("(%d) ", size); -	} -	spin_unlock_irqrestore(&adc->lock, flags); -out: -	DEBUG("\n"); - -	return ret; -}  - -/*  - * Add 'count' bytes from 'buffer' to DMA ring buffers. Return number of - * bytes added or -EFAULT if copy_from_user failed. - */ -static int hal2_add_buffer(struct hal2_card *hal2, char *buffer, int count) -{ -	unsigned long flags; -	unsigned char *buf; -	int size, ret = 0; -	struct hal2_desc *head; -	struct hal2_codec *dac = &hal2->dac; - -	DEBUG("adding %d bytes ", count); - -	spin_lock_irqsave(&dac->lock, flags); -	head = &dac->desc[dac->head]; -	while (head->cnt == 0 && count > 0) { -		size = min((int)H2_BLOCK_SIZE, count); -		buf = &dac->buffer[dac->head * H2_BLOCK_SIZE]; -		spin_unlock_irqrestore(&dac->lock, flags); -		if (copy_from_user(buf, buffer, size)) { -			ret = -EFAULT; -			goto out; -		} -		dma_sync_single(NULL, head->desc.pbuf, size, DMA_TO_DEVICE); -		spin_lock_irqsave(&dac->lock, flags); -		head->desc.cntinfo = size | HPCDMA_XIE; -		head->cnt = size; -		buffer += size; -		ret += size; -		count -= size; -		hal2_inc_head(dac); -		head = &dac->desc[dac->head]; - -		DEBUG("(%d) ", size); -	} -	if (!(dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) && ret > 0) -		hal2_start_dac(hal2); -	spin_unlock_irqrestore(&dac->lock, flags); -out: -	DEBUG("\n"); - -	return ret; -} - -#define hal2_reset_dac_pointer(hal2)	hal2_reset_pointer(hal2, 1) -#define hal2_reset_adc_pointer(hal2)	hal2_reset_pointer(hal2, 0) -static void hal2_reset_pointer(struct hal2_card *hal2, int is_dac) -{ -	int i; -	struct hal2_codec *codec = (is_dac) ? &hal2->dac : &hal2->adc; - -	DEBUG("hal2_reset_pointer\n"); - -	for (i = 0; i < codec->desc_count; i++) { -		codec->desc[i].cnt = 0; -		codec->desc[i].desc.cntinfo = HPCDMA_XIE | (is_dac) ? -					      HPCDMA_EOX : H2_BLOCK_SIZE; -	} -	codec->head = codec->tail = 0; -} - -static int hal2_sync_dac(struct hal2_card *hal2) -{ -	DECLARE_WAITQUEUE(wait, current); -	struct hal2_codec *dac = &hal2->dac; -	int ret = 0; -	unsigned long flags; -	signed long timeout = 1000 * H2_BLOCK_SIZE * 2 * dac->voices * -			      HZ / dac->sample_rate / 900; - -	while (dac->pbus.pbus->pbdma_ctrl & HPC3_PDMACTRL_ISACT) { -		add_wait_queue(&dac->dma_wait, &wait); -		set_current_state(TASK_INTERRUPTIBLE); -		schedule_timeout(timeout); -		spin_lock_irqsave(&dac->lock, flags); -		if (dac->desc[dac->tail].cnt) -			ret = -ETIME; -		spin_unlock_irqrestore(&dac->lock, flags); -		if (signal_pending(current)) -			ret = -ERESTARTSYS; -		if (ret) { -			hal2_stop_dac(hal2); -			hal2_reset_dac_pointer(hal2); -		} -		remove_wait_queue(&dac->dma_wait, &wait); -	} - -	return ret; -} - -static int hal2_write_mixer(struct hal2_card *hal2, int index, int vol) -{ -	unsigned int l, r, tmp; - -	DEBUG_MIX("mixer %d write\n", index); - -	if (index >= SOUND_MIXER_NRDEVICES || !mixtable[index].avail) -		return -EINVAL; - -	r = (vol >> 8) & 0xff; -	if (r > 100) -		r = 100; -	l = vol & 0xff; -	if (l > 100) -		l = 100; - -	hal2->mixer.volume[mixtable[index].idx] = l | (r << 8); - -	switch (mixtable[index].idx) { -	case H2_MIX_OUTPUT_ATT: - -		DEBUG_MIX("output attenuator %d,%d\n", l, r); - -		if (r | l) { -			tmp = hal2_i_look32(hal2, H2I_DAC_C2); -			tmp &= ~(H2I_C2_L_ATT_M | H2I_C2_R_ATT_M | H2I_C2_MUTE); - -			/* Attenuator has five bits */ -			l = 31 * (100 - l) / 99; -			r = 31 * (100 - r) / 99; - -			DEBUG_MIX("left: %d, right %d\n", l, r); - -			tmp |= (l << H2I_C2_L_ATT_SHIFT) & H2I_C2_L_ATT_M; -			tmp |= (r << H2I_C2_R_ATT_SHIFT) & H2I_C2_R_ATT_M; -			hal2_i_write32(hal2, H2I_DAC_C2, tmp); -		} else  -			hal2_i_setbit32(hal2, H2I_DAC_C2, H2I_C2_MUTE); -		break; -	case H2_MIX_INPUT_GAIN: - -		DEBUG_MIX("input gain %d,%d\n", l, r); - -		tmp = hal2_i_look32(hal2, H2I_ADC_C2); -		tmp &= ~(H2I_C2_L_GAIN_M | H2I_C2_R_GAIN_M); - -		/* Gain control has four bits */ -		l = 16 * l / 100; -		r = 16 * r / 100; - -		DEBUG_MIX("left: %d, right %d\n", l, r); - -		tmp |= (l << H2I_C2_L_GAIN_SHIFT) & H2I_C2_L_GAIN_M; -		tmp |= (r << H2I_C2_R_GAIN_SHIFT) & H2I_C2_R_GAIN_M; -		hal2_i_write32(hal2, H2I_ADC_C2, tmp); - -		break; -	} - -	return 0; -} - -static void hal2_init_mixer(struct hal2_card *hal2) -{ -	int i; - -	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) -		if (mixtable[i].avail) -			hal2->mixer.volume[mixtable[i].idx] = 100 | (100 << 8); - -	/* disable attenuator */ -	hal2_i_write32(hal2, H2I_DAC_C2, 0); -	/* set max input gain */ -	hal2_i_write32(hal2, H2I_ADC_C2, H2I_C2_MUTE | -			(H2I_C2_L_GAIN_M << H2I_C2_L_GAIN_SHIFT) | -			(H2I_C2_R_GAIN_M << H2I_C2_R_GAIN_SHIFT)); -	/* set max volume */ -	hal2->mixer.master = 0xff; -	hal2->vol_regs->left = 0xff; -	hal2->vol_regs->right = 0xff; -} - -/* - * XXX: later i'll implement mixer for main volume which will be disabled - * by default. enabling it users will be allowed to have master volume level - * control on panel in their favourite X desktop - */ -static void hal2_volume_control(int direction) -{ -	unsigned int master = hal2_card[0]->mixer.master; -	struct hal2_vol_regs *vol = hal2_card[0]->vol_regs; - -	/* volume up */ -	if (direction > 0 && master < 0xff) -		master++; -	/* volume down */ -	else if (direction < 0 && master > 0) -		master--; -	/* TODO: mute/unmute */ -	vol->left = master; -	vol->right = master; -	hal2_card[0]->mixer.master = master; -} - -static int hal2_mixer_ioctl(struct hal2_card *hal2, unsigned int cmd, -			    unsigned long arg) -{ -	int val; - -        if (cmd == SOUND_MIXER_INFO) { -		mixer_info info; - -		memset(&info, 0, sizeof(info)); -		strlcpy(info.id, hal2str, sizeof(info.id)); -		strlcpy(info.name, hal2str, sizeof(info.name)); -		info.modify_counter = hal2->mixer.modcnt; -		if (copy_to_user((void *)arg, &info, sizeof(info))) -			return -EFAULT; -		return 0; -	} -	if (cmd == SOUND_OLD_MIXER_INFO) { -		_old_mixer_info info; - -		memset(&info, 0, sizeof(info)); -		strlcpy(info.id, hal2str, sizeof(info.id)); -		strlcpy(info.name, hal2str, sizeof(info.name)); -		if (copy_to_user((void *)arg, &info, sizeof(info))) -			return -EFAULT; -		return 0; -	} -	if (cmd == OSS_GETVERSION) -		return put_user(SOUND_VERSION, (int *)arg); - -	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) -                return -EINVAL; - -        if (_IOC_DIR(cmd) == _IOC_READ) { -                switch (_IOC_NR(cmd)) { -		/* Give the current record source */ -		case SOUND_MIXER_RECSRC: -			val = 0;	/* FIXME */ -			break; -		/* Give the supported mixers, all of them support stereo */ -                case SOUND_MIXER_DEVMASK: -                case SOUND_MIXER_STEREODEVS: { -			int i; - -			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++) -				if (mixtable[i].avail) -					val |= 1 << i; -			break; -			} -		/* Arg contains a bit for each supported recording source */ -                case SOUND_MIXER_RECMASK: -			val = 0; -			break; -                case SOUND_MIXER_CAPS: -			val = 0; -			break; -		/* Read a specific mixer */ -		default: { -			int i = _IOC_NR(cmd); - -			if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].avail) -				return -EINVAL; -			val = hal2->mixer.volume[mixtable[i].idx]; -			break; -			} -		} -		return put_user(val, (int *)arg); -	} - -        if (_IOC_DIR(cmd) != (_IOC_WRITE|_IOC_READ)) -		return -EINVAL; - -	hal2->mixer.modcnt++; - -	if (get_user(val, (int *)arg)) -		return -EFAULT; - -	switch (_IOC_NR(cmd)) { -	/* Arg contains a bit for each recording source */ -	case SOUND_MIXER_RECSRC: -		return 0;	/* FIXME */ -	default: -		return hal2_write_mixer(hal2, _IOC_NR(cmd), val); -	} - -	return 0; -} - -static int hal2_open_mixdev(struct inode *inode, struct file *file) -{ -	struct hal2_card *hal2 = hal2_mixer_find_card(iminor(inode)); - -	if (hal2) { -		file->private_data = hal2; -		return nonseekable_open(inode, file); -	} -	return -ENODEV; -} - -static int hal2_release_mixdev(struct inode *inode, struct file *file) -{ -	return 0; -} - -static int hal2_ioctl_mixdev(struct inode *inode, struct file *file, -			     unsigned int cmd, unsigned long arg) -{ -	return hal2_mixer_ioctl((struct hal2_card *)file->private_data, cmd, arg); -} - -static int hal2_ioctl(struct inode *inode, struct file *file,  -		      unsigned int cmd, unsigned long arg) -{ -	int val; -	struct hal2_card *hal2 = (struct hal2_card *) file->private_data; - -	switch (cmd) { -	case OSS_GETVERSION: -		return put_user(SOUND_VERSION, (int *)arg); - -	case SNDCTL_DSP_SYNC: -		if (file->f_mode & FMODE_WRITE) -			return hal2_sync_dac(hal2); -		return 0; - -	case SNDCTL_DSP_SETDUPLEX: -		return 0; - -	case SNDCTL_DSP_GETCAPS: -		return put_user(DSP_CAP_DUPLEX | DSP_CAP_MULTI, (int *)arg); - -	case SNDCTL_DSP_RESET: -		if (file->f_mode & FMODE_READ) { -			hal2_stop_adc(hal2); -			hal2_reset_adc_pointer(hal2); -		} -		if (file->f_mode & FMODE_WRITE) { -			hal2_stop_dac(hal2); -			hal2_reset_dac_pointer(hal2); -		} -		return 0; - - 	case SNDCTL_DSP_SPEED: -		if (get_user(val, (int *)arg)) -			return -EFAULT; -		if (file->f_mode & FMODE_READ) { -			hal2_stop_adc(hal2); -			val = hal2_compute_rate(&hal2->adc, val); -			hal2->adc.sample_rate = val; -			hal2_set_adc_rate(hal2); -		} -		if (file->f_mode & FMODE_WRITE) { -			hal2_stop_dac(hal2); -			val = hal2_compute_rate(&hal2->dac, val); -			hal2->dac.sample_rate = val; -			hal2_set_dac_rate(hal2); -		} -		return put_user(val, (int *)arg); - -	case SNDCTL_DSP_STEREO: -		if (get_user(val, (int *)arg)) -			return -EFAULT; -		if (file->f_mode & FMODE_READ) { -			hal2_stop_adc(hal2); -			hal2->adc.voices = (val) ? 2 : 1; -			hal2_setup_adc(hal2); -		} -		if (file->f_mode & FMODE_WRITE) { -			hal2_stop_dac(hal2); -			hal2->dac.voices = (val) ? 2 : 1; -			hal2_setup_dac(hal2); -                } -		return 0; - -	case SNDCTL_DSP_CHANNELS: -		if (get_user(val, (int *)arg)) -			return -EFAULT; -		if (val != 0) { -			if (file->f_mode & FMODE_READ) { -				hal2_stop_adc(hal2); -				hal2->adc.voices = (val == 1) ? 1 : 2; -				hal2_setup_adc(hal2); -			} -			if (file->f_mode & FMODE_WRITE) { -				hal2_stop_dac(hal2); -				hal2->dac.voices = (val == 1) ? 1 : 2; -				hal2_setup_dac(hal2); -			} -		} -		val = -EINVAL; -		if (file->f_mode & FMODE_READ) -			val = hal2->adc.voices; -		if (file->f_mode & FMODE_WRITE) -			val = hal2->dac.voices; -		return put_user(val, (int *)arg); - -	case SNDCTL_DSP_GETFMTS: /* Returns a mask */ -                return put_user(H2_SUPPORTED_FORMATS, (int *)arg); - -	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ -		if (get_user(val, (int *)arg)) -			return -EFAULT; -		if (val != AFMT_QUERY) { -			if (!(val & H2_SUPPORTED_FORMATS)) -				return -EINVAL; -			if (file->f_mode & FMODE_READ) { -				hal2_stop_adc(hal2); -				hal2->adc.format = val; -				hal2_setup_adc(hal2); -			} -			if (file->f_mode & FMODE_WRITE) { -				hal2_stop_dac(hal2); -				hal2->dac.format = val; -				hal2_setup_dac(hal2); -			} -		} else { -			val = -EINVAL; -			if (file->f_mode & FMODE_READ) -				val = hal2->adc.format; -			if (file->f_mode & FMODE_WRITE) -				val = hal2->dac.format; -		} -		return put_user(val, (int *)arg); - -	case SNDCTL_DSP_POST: -		return 0; - -	case SNDCTL_DSP_GETOSPACE: { -		audio_buf_info info; -		int i; -		unsigned long flags; -		struct hal2_codec *dac = &hal2->dac; - -		if (!(file->f_mode & FMODE_WRITE)) -			return -EINVAL; -		info.fragments = 0; -		spin_lock_irqsave(&dac->lock, flags); -		for (i = 0; i < dac->desc_count; i++) -			if (dac->desc[i].cnt == 0) -				info.fragments++; -		spin_unlock_irqrestore(&dac->lock, flags); -		info.fragstotal = dac->desc_count; -		info.fragsize = H2_BLOCK_SIZE; -                info.bytes = info.fragsize * info.fragments; - -		return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; -	} - -	case SNDCTL_DSP_GETISPACE: { -		audio_buf_info info; -		int i; -		unsigned long flags; -		struct hal2_codec *adc = &hal2->adc; - -		if (!(file->f_mode & FMODE_READ)) -			return -EINVAL; -		info.fragments = 0; -		info.bytes = 0; -		spin_lock_irqsave(&adc->lock, flags); -		for (i = 0; i < adc->desc_count; i++) -			if (adc->desc[i].cnt > 0) { -				info.fragments++; -				info.bytes += adc->desc[i].cnt; -			} -		spin_unlock_irqrestore(&adc->lock, flags); -		info.fragstotal = adc->desc_count; -		info.fragsize = H2_BLOCK_SIZE; - -		return copy_to_user((void *)arg, &info, sizeof(info)) ? -EFAULT : 0; -	} - -	case SNDCTL_DSP_NONBLOCK: -		file->f_flags |= O_NONBLOCK; -		return 0; - -	case SNDCTL_DSP_GETBLKSIZE: -		return put_user(H2_BLOCK_SIZE, (int *)arg); - -	case SNDCTL_DSP_SETFRAGMENT: -		return 0; - -	case SOUND_PCM_READ_RATE: -		val = -EINVAL; -		if (file->f_mode & FMODE_READ) -			val = hal2->adc.sample_rate; -		if (file->f_mode & FMODE_WRITE) -			val = hal2->dac.sample_rate; -		return put_user(val, (int *)arg); - -	case SOUND_PCM_READ_CHANNELS: -		val = -EINVAL; -		if (file->f_mode & FMODE_READ) -			val = hal2->adc.voices; -		if (file->f_mode & FMODE_WRITE) -			val = hal2->dac.voices; -		return put_user(val, (int *)arg); - -	case SOUND_PCM_READ_BITS: -		return put_user(16, (int *)arg); -	} - -	return hal2_mixer_ioctl(hal2, cmd, arg); -} - -static ssize_t hal2_read(struct file *file, char *buffer, -			 size_t count, loff_t *ppos) -{ -	ssize_t err; -	struct hal2_card *hal2 = (struct hal2_card *) file->private_data; -	struct hal2_codec *adc = &hal2->adc; - -	if (!count) -		return 0; -	if (mutex_lock_interruptible(&adc->sem)) -		return -EINTR; -	if (file->f_flags & O_NONBLOCK) { -		err = hal2_get_buffer(hal2, buffer, count); -		err = err == 0 ? -EAGAIN : err; -	} else { -		do { -			/* ~10% longer */ -			signed long timeout = 1000 * H2_BLOCK_SIZE * -				2 * adc->voices * HZ / adc->sample_rate / 900; -			unsigned long flags; -			DECLARE_WAITQUEUE(wait, current); -			ssize_t cnt = 0; - -			err = hal2_get_buffer(hal2, buffer, count); -			if (err > 0) { -				count -= err; -				cnt += err; -				buffer += err; -				err = cnt; -			} -			if (count > 0 && err >= 0) { -				add_wait_queue(&adc->dma_wait, &wait); -				set_current_state(TASK_INTERRUPTIBLE); -				schedule_timeout(timeout); -				spin_lock_irqsave(&adc->lock, flags); -				if (!adc->desc[adc->tail].cnt) -					err = -EAGAIN; -				spin_unlock_irqrestore(&adc->lock, flags); -				if (signal_pending(current)) -					err = -ERESTARTSYS; -				remove_wait_queue(&adc->dma_wait, &wait); -				if (err < 0) { -					hal2_stop_adc(hal2); -					hal2_reset_adc_pointer(hal2); -				} -			} -		} while (count > 0 && err >= 0); -	} -	mutex_unlock(&adc->sem); - -	return err; -} - -static ssize_t hal2_write(struct file *file, const char *buffer, -			  size_t count, loff_t *ppos) -{ -	ssize_t err; -	char *buf = (char*) buffer; -	struct hal2_card *hal2 = (struct hal2_card *) file->private_data; -	struct hal2_codec *dac = &hal2->dac; - -	if (!count) -		return 0; -	if (mutex_lock_interruptible(&dac->sem)) -		return -EINTR; -	if (file->f_flags & O_NONBLOCK) { -		err = hal2_add_buffer(hal2, buf, count); -		err = err == 0 ? -EAGAIN : err; -	} else { -		do { -			/* ~10% longer */ -			signed long timeout = 1000 * H2_BLOCK_SIZE * -				2 * dac->voices * HZ / dac->sample_rate / 900; -			unsigned long flags; -			DECLARE_WAITQUEUE(wait, current); -			ssize_t cnt = 0; - -			err = hal2_add_buffer(hal2, buf, count); -			if (err > 0) { -				count -= err; -				cnt += err; -				buf += err; -				err = cnt; -			} -			if (count > 0 && err >= 0) { -				add_wait_queue(&dac->dma_wait, &wait); -				set_current_state(TASK_INTERRUPTIBLE); -				schedule_timeout(timeout); -				spin_lock_irqsave(&dac->lock, flags); -				if (dac->desc[dac->head].cnt) -					err = -EAGAIN; -				spin_unlock_irqrestore(&dac->lock, flags); -				if (signal_pending(current)) -					err = -ERESTARTSYS; -				remove_wait_queue(&dac->dma_wait, &wait); -				if (err < 0) { -					hal2_stop_dac(hal2); -					hal2_reset_dac_pointer(hal2); -				} -			} -		} while (count > 0 && err >= 0); -	} -	mutex_unlock(&dac->sem); - -	return err; -} - -static unsigned int hal2_poll(struct file *file, struct poll_table_struct *wait) -{ -	unsigned long flags; -	unsigned int mask = 0; -	struct hal2_card *hal2 = (struct hal2_card *) file->private_data; - -	if (file->f_mode & FMODE_READ) { -		struct hal2_codec *adc = &hal2->adc; - -		poll_wait(file, &adc->dma_wait, wait); -		spin_lock_irqsave(&adc->lock, flags); -		if (adc->desc[adc->tail].cnt > 0) -			mask |= POLLIN; -		spin_unlock_irqrestore(&adc->lock, flags); -	} - -	if (file->f_mode & FMODE_WRITE) { -		struct hal2_codec *dac = &hal2->dac; - -		poll_wait(file, &dac->dma_wait, wait); -		spin_lock_irqsave(&dac->lock, flags); -		if (dac->desc[dac->head].cnt == 0) -			mask |= POLLOUT; -		spin_unlock_irqrestore(&dac->lock, flags); -	} - -	return mask; -} - -static int hal2_open(struct inode *inode, struct file *file) -{ -	int err; -	struct hal2_card *hal2 = hal2_dsp_find_card(iminor(inode)); - -	if (!hal2) -		return -ENODEV; -	file->private_data = hal2; -	if (file->f_mode & FMODE_READ) { -		struct hal2_codec *adc = &hal2->adc; - -		if (adc->usecount) -			return -EBUSY; -		/* OSS spec wanted us to use 8 bit, 8 kHz mono by default, -		 * but HAL2 can't do 8bit audio */ -		adc->format = AFMT_S16_BE; -		adc->voices = 1; -		adc->sample_rate = hal2_compute_rate(adc, 8000); -		hal2_set_adc_rate(hal2); -		err = hal2_alloc_adc_dmabuf(adc); -		if (err) -			return err; -		hal2_setup_adc(hal2); -		adc->usecount++; -	} -	if (file->f_mode & FMODE_WRITE) { -		struct hal2_codec *dac = &hal2->dac; - -		if (dac->usecount) -			return -EBUSY; -		dac->format = AFMT_S16_BE; -		dac->voices = 1; -		dac->sample_rate = hal2_compute_rate(dac, 8000); -		hal2_set_dac_rate(hal2); -		err = hal2_alloc_dac_dmabuf(dac); -		if (err) -			return err; -		hal2_setup_dac(hal2); -		dac->usecount++; -	} - -	return nonseekable_open(inode, file); -} - -static int hal2_release(struct inode *inode, struct file *file) -{ -	struct hal2_card *hal2 = (struct hal2_card *) file->private_data; - -	if (file->f_mode & FMODE_READ) { -		struct hal2_codec *adc = &hal2->adc; - -		mutex_lock(&adc->sem); -		hal2_stop_adc(hal2); -		hal2_free_adc_dmabuf(adc); -		adc->usecount--; -		mutex_unlock(&adc->sem); -	} -	if (file->f_mode & FMODE_WRITE) { -		struct hal2_codec *dac = &hal2->dac; - -		mutex_lock(&dac->sem); -		hal2_sync_dac(hal2); -		hal2_free_dac_dmabuf(dac); -		dac->usecount--; -		mutex_unlock(&dac->sem); -	} - -	return 0; -} - -static const struct file_operations hal2_audio_fops = { -	.owner		= THIS_MODULE, -	.llseek		= no_llseek, -	.read		= hal2_read, -	.write		= hal2_write, -	.poll		= hal2_poll, -	.ioctl		= hal2_ioctl, -	.open		= hal2_open, -	.release	= hal2_release, -}; - -static const struct file_operations hal2_mixer_fops = { -	.owner		= THIS_MODULE, -	.llseek		= no_llseek, -	.ioctl		= hal2_ioctl_mixdev, -	.open		= hal2_open_mixdev, -	.release	= hal2_release_mixdev, -}; - -static void hal2_init_codec(struct hal2_codec *codec, struct hpc3_regs *hpc3, -			    int index) -{ -	codec->pbus.pbusnr = index; -	codec->pbus.pbus = &hpc3->pbdma[index]; -	init_waitqueue_head(&codec->dma_wait); -	mutex_init(&codec->sem); -	spin_lock_init(&codec->lock); -} - -static int hal2_detect(struct hal2_card *hal2) -{ -	unsigned short board, major, minor; -	unsigned short rev; - -	/* reset HAL2 */ -	hal2_isr_write(hal2, 0); -	/* release reset */ -	hal2_isr_write(hal2, H2_ISR_GLOBAL_RESET_N | H2_ISR_CODEC_RESET_N); - -	hal2_i_write16(hal2, H2I_RELAY_C, H2I_RELAY_C_STATE);  -	if ((rev = hal2_rev_look(hal2)) & H2_REV_AUDIO_PRESENT) -		return -ENODEV; - -	board = (rev & H2_REV_BOARD_M) >> 12; -	major = (rev & H2_REV_MAJOR_CHIP_M) >> 4; -	minor = (rev & H2_REV_MINOR_CHIP_M); - -	printk(KERN_INFO "SGI HAL2 revision %i.%i.%i\n", -	       board, major, minor); - -	return 0; -} - -static int hal2_init_card(struct hal2_card **phal2, struct hpc3_regs *hpc3) -{ -	int ret = 0; -	struct hal2_card *hal2; - -	hal2 = kzalloc(sizeof(struct hal2_card), GFP_KERNEL); -	if (!hal2) -		return -ENOMEM; - -	hal2->ctl_regs = (struct hal2_ctl_regs *)hpc3->pbus_extregs[0]; -	hal2->aes_regs = (struct hal2_aes_regs *)hpc3->pbus_extregs[1]; -	hal2->vol_regs = (struct hal2_vol_regs *)hpc3->pbus_extregs[2]; -	hal2->syn_regs = (struct hal2_syn_regs *)hpc3->pbus_extregs[3]; - -	if (hal2_detect(hal2) < 0) { -		ret = -ENODEV; -		goto free_card; -	} - -	hal2_init_codec(&hal2->dac, hpc3, 0); -	hal2_init_codec(&hal2->adc, hpc3, 1); - -	/* -	 * All DMA channel interfaces in HAL2 are designed to operate with -	 * PBUS programmed for 2 cycles in D3, 2 cycles in D4 and 2 cycles -	 * in D5. HAL2 is a 16-bit device which can accept both big and little -	 * endian format. It assumes that even address bytes are on high -	 * portion of PBUS (15:8) and assumes that HPC3 is programmed to -	 * accept a live (unsynchronized) version of P_DREQ_N from HAL2. -	 */ -#define HAL2_PBUS_DMACFG ((0 << HPC3_DMACFG_D3R_SHIFT) | \ -			  (2 << HPC3_DMACFG_D4R_SHIFT) | \ -			  (2 << HPC3_DMACFG_D5R_SHIFT) | \ -			  (0 << HPC3_DMACFG_D3W_SHIFT) | \ -			  (2 << HPC3_DMACFG_D4W_SHIFT) | \ -			  (2 << HPC3_DMACFG_D5W_SHIFT) | \ -				HPC3_DMACFG_DS16 | \ -				HPC3_DMACFG_EVENHI | \ -				HPC3_DMACFG_RTIME | \ -			  (8 << HPC3_DMACFG_BURST_SHIFT) | \ -				HPC3_DMACFG_DRQLIVE) -	/* -	 * Ignore what's mentioned in the specification and write value which -	 * works in The Real World (TM) -	 */ -	hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844; -	hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844; - -	if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED, -			hal2str, hal2)) { -		printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ); -		ret = -EAGAIN; -		goto free_card; -	} - -	hal2->dev_dsp = register_sound_dsp(&hal2_audio_fops, -1); -	if (hal2->dev_dsp < 0) { -		ret = hal2->dev_dsp; -		goto free_irq; -	} - -	hal2->dev_mixer = register_sound_mixer(&hal2_mixer_fops, -1); -	if (hal2->dev_mixer < 0) { -		ret = hal2->dev_mixer; -		goto unregister_dsp; -	} - -	hal2_init_mixer(hal2); - -	*phal2 = hal2; -	return 0; -unregister_dsp: -	unregister_sound_dsp(hal2->dev_dsp); -free_irq: -	free_irq(SGI_HPCDMA_IRQ, hal2); -free_card: -	kfree(hal2); - -	return ret; -} - -extern void (*indy_volume_button)(int); - -/*  - * Assuming only one HAL2 card. Mail me if you ever meet machine with - * more than one. - */ -static int __init init_hal2(void) -{ -	int i, error; - -	for (i = 0; i < MAXCARDS; i++) -		hal2_card[i] = NULL; - -	error = hal2_init_card(&hal2_card[0], hpc3c0); - -	/* let Indy's volume buttons work */ -	if (!error && !ip22_is_fullhouse()) -		indy_volume_button = hal2_volume_control; - -	return error; - -} - -static void __exit exit_hal2(void) -{ -	int i; - -	/* unregister volume butons callback function */ -	indy_volume_button = NULL; -	 -	for (i = 0; i < MAXCARDS; i++) -		if (hal2_card[i]) { -			free_irq(SGI_HPCDMA_IRQ, hal2_card[i]); -			unregister_sound_dsp(hal2_card[i]->dev_dsp); -			unregister_sound_mixer(hal2_card[i]->dev_mixer); -			kfree(hal2_card[i]); -	} -} - -module_init(init_hal2); -module_exit(exit_hal2); - -MODULE_DESCRIPTION("OSS compatible driver for SGI HAL2 audio"); -MODULE_AUTHOR("Ladislav Michl"); -MODULE_LICENSE("GPL"); diff --git a/sound/oss/hal2.h b/sound/oss/hal2.h deleted file mode 100644 index 2bd3b52..0000000 --- a/sound/oss/hal2.h +++ /dev/null @@ -1,248 +0,0 @@ -#ifndef __HAL2_H -#define __HAL2_H - -/* - *  Driver for HAL2 sound processors - *  Copyright (c) 1999 Ulf Carlsson <ulfc@bun.falkenberg.se> - *  Copyright (c) 2001, 2002, 2003 Ladislav Michl <ladis@linux-mips.org> - * - *  This program is free software; you can redistribute it and/or modify - *  it under the terms of the GNU General Public License version 2 as  - *  published by the Free Software Foundation. - * - *  This program is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU General Public License for more details. - * - *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include <asm/addrspace.h> -#include <asm/sgi/hpc3.h> -#include <linux/spinlock.h> -#include <linux/types.h> - -/* Indirect status register */ - -#define H2_ISR_TSTATUS		0x01	/* RO: transaction status 1=busy */ -#define H2_ISR_USTATUS		0x02	/* RO: utime status bit 1=armed */ -#define H2_ISR_QUAD_MODE	0x04	/* codec mode 0=indigo 1=quad */ -#define H2_ISR_GLOBAL_RESET_N	0x08	/* chip global reset 0=reset */ -#define H2_ISR_CODEC_RESET_N	0x10	/* codec/synth reset 0=reset  */ - -/* Revision register */ - -#define H2_REV_AUDIO_PRESENT	0x8000	/* RO: audio present 0=present */ -#define H2_REV_BOARD_M		0x7000	/* RO: bits 14:12, board revision */ -#define H2_REV_MAJOR_CHIP_M	0x00F0	/* RO: bits 7:4, major chip revision */ -#define H2_REV_MINOR_CHIP_M	0x000F	/* RO: bits 3:0, minor chip revision */ - -/* Indirect address register */ - -/* - * Address of indirect internal register to be accessed. A write to this - * register initiates read or write access to the indirect registers in the - * HAL2. Note that there af four indirect data registers for write access to - * registers larger than 16 byte. - */ - -#define H2_IAR_TYPE_M		0xF000	/* bits 15:12, type of functional */ -					/* block the register resides in */ -					/* 1=DMA Port */ -					/* 9=Global DMA Control */ -					/* 2=Bresenham */ -					/* 3=Unix Timer */ -#define H2_IAR_NUM_M		0x0F00	/* bits 11:8 instance of the */ -					/* blockin which the indirect */ -					/* register resides */ -					/* If IAR_TYPE_M=DMA Port: */ -					/* 1=Synth In */ -					/* 2=AES In */ -					/* 3=AES Out */ -					/* 4=DAC Out */ -					/* 5=ADC Out */ -					/* 6=Synth Control */ -					/* If IAR_TYPE_M=Global DMA Control: */ -					/* 1=Control */ -					/* If IAR_TYPE_M=Bresenham: */ -					/* 1=Bresenham Clock Gen 1 */ -					/* 2=Bresenham Clock Gen 2 */ -					/* 3=Bresenham Clock Gen 3 */ -					/* If IAR_TYPE_M=Unix Timer: */ -					/* 1=Unix Timer */ -#define H2_IAR_ACCESS_SELECT	0x0080	/* 1=read 0=write */ -#define H2_IAR_PARAM		0x000C	/* Parameter Select */ -#define H2_IAR_RB_INDEX_M	0x0003	/* Read Back Index */ -					/* 00:word0 */ -					/* 01:word1 */ -					/* 10:word2 */ -					/* 11:word3 */ -/* - * HAL2 internal addressing - * - * The HAL2 has "indirect registers" (idr) which are accessed by writing to the - * Indirect Data registers. Write the address to the Indirect Address register - * to transfer the data. - * - * We define the H2IR_* to the read address and H2IW_* to the write address and - * H2I_* to be fields in whatever register is referred to. - * - * When we write to indirect registers which are larger than one word (16 bit) - * we have to fill more than one indirect register before writing. When we read - * back however we have to read several times, each time with different Read - * Back Indexes (there are defs for doing this easily). - */ - -/* - * Relay Control - */ -#define H2I_RELAY_C		0x9100 -#define H2I_RELAY_C_STATE	0x01		/* state of RELAY pin signal */ - -/* DMA port enable */ - -#define H2I_DMA_PORT_EN		0x9104 -#define H2I_DMA_PORT_EN_SY_IN	0x01		/* Synth_in DMA port */ -#define H2I_DMA_PORT_EN_AESRX	0x02		/* AES receiver DMA port */ -#define H2I_DMA_PORT_EN_AESTX	0x04		/* AES transmitter DMA port */ -#define H2I_DMA_PORT_EN_CODECTX	0x08		/* CODEC transmit DMA port */ -#define H2I_DMA_PORT_EN_CODECR	0x10		/* CODEC receive DMA port */ - -#define H2I_DMA_END		0x9108 		/* global dma endian select */ -#define H2I_DMA_END_SY_IN	0x01		/* Synth_in DMA port */ -#define H2I_DMA_END_AESRX	0x02		/* AES receiver DMA port */ -#define H2I_DMA_END_AESTX	0x04		/* AES transmitter DMA port */ -#define H2I_DMA_END_CODECTX	0x08		/* CODEC transmit DMA port */ -#define H2I_DMA_END_CODECR	0x10		/* CODEC receive DMA port */ -						/* 0=b_end 1=l_end */ - -#define H2I_DMA_DRV		0x910C  	/* global PBUS DMA enable */ - -#define H2I_SYNTH_C		0x1104		/* Synth DMA control */ - -#define H2I_AESRX_C		0x1204	 	/* AES RX dma control */ - -#define H2I_C_TS_EN		0x20		/* Timestamp enable */ -#define H2I_C_TS_FRMT		0x40		/* Timestamp format */ -#define H2I_C_NAUDIO		0x80		/* Sign extend */ - -/* AESRX CTL, 16 bit */ - -#define H2I_AESTX_C		0x1304		/* AES TX DMA control */ -#define H2I_AESTX_C_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */ -#define H2I_AESTX_C_CLKID_M	0x18 -#define H2I_AESTX_C_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */ -#define H2I_AESTX_C_DATAT_M	0x300 - -/* CODEC registers */ - -#define H2I_DAC_C1		0x1404 		/* DAC DMA control, 16 bit */ -#define H2I_DAC_C2		0x1408		/* DAC DMA control, 32 bit */ -#define H2I_ADC_C1		0x1504 		/* ADC DMA control, 16 bit */ -#define H2I_ADC_C2		0x1508		/* ADC DMA control, 32 bit */ - -/* Bits in CTL1 register */ - -#define H2I_C1_DMA_SHIFT	0		/* DMA channel */ -#define H2I_C1_DMA_M		0x7 -#define H2I_C1_CLKID_SHIFT	3		/* Bresenham Clock Gen 1-3 */ -#define H2I_C1_CLKID_M		0x18 -#define H2I_C1_DATAT_SHIFT	8		/* 1=mono 2=stereo (3=quad) */ -#define H2I_C1_DATAT_M		0x300 - -/* Bits in CTL2 register */ - -#define H2I_C2_R_GAIN_SHIFT	0		/* right a/d input gain */	 -#define H2I_C2_R_GAIN_M		0xf	 -#define H2I_C2_L_GAIN_SHIFT	4		/* left a/d input gain */ -#define H2I_C2_L_GAIN_M		0xf0 -#define H2I_C2_R_SEL		0x100		/* right input select */ -#define H2I_C2_L_SEL		0x200		/* left input select */ -#define H2I_C2_MUTE		0x400		/* mute */ -#define H2I_C2_DO1		0x00010000	/* digital output port bit 0 */ -#define H2I_C2_DO2		0x00020000	/* digital output port bit 1 */ -#define H2I_C2_R_ATT_SHIFT	18		/* right d/a output - */ -#define H2I_C2_R_ATT_M		0x007c0000	/* attenuation */ -#define H2I_C2_L_ATT_SHIFT	23		/* left d/a output - */ -#define H2I_C2_L_ATT_M		0x0f800000	/* attenuation */ - -#define H2I_SYNTH_MAP_C		0x1104		/* synth dma handshake ctrl */ - -/* Clock generator CTL 1, 16 bit */ - -#define H2I_BRES1_C1		0x2104 -#define H2I_BRES2_C1		0x2204 -#define H2I_BRES3_C1		0x2304 - -#define H2I_BRES_C1_SHIFT	0		/* 0=48.0 1=44.1 2=aes_rx */ -#define H2I_BRES_C1_M		0x03 -				 -/* Clock generator CTL 2, 32 bit */ - -#define H2I_BRES1_C2		0x2108 -#define H2I_BRES2_C2		0x2208 -#define H2I_BRES3_C2		0x2308 - -#define H2I_BRES_C2_INC_SHIFT	0		/* increment value */ -#define H2I_BRES_C2_INC_M	0xffff -#define H2I_BRES_C2_MOD_SHIFT	16		/* modcontrol value */ -#define H2I_BRES_C2_MOD_M	0xffff0000	/* modctrl=0xffff&(modinc-1) */ - -/* Unix timer, 64 bit */ - -#define H2I_UTIME		0x3104 -#define H2I_UTIME_0_LD		0xffff		/* microseconds, LSB's */ -#define H2I_UTIME_1_LD0		0x0f		/* microseconds, MSB's */ -#define H2I_UTIME_1_LD1		0xf0		/* tenths of microseconds */ -#define H2I_UTIME_2_LD		0xffff		/* seconds, LSB's */ -#define H2I_UTIME_3_LD		0xffff		/* seconds, MSB's */ - -struct hal2_ctl_regs { -	u32 _unused0[4]; -	volatile u32 isr;		/* 0x10 Status Register */ -	u32 _unused1[3]; -	volatile u32 rev;		/* 0x20 Revision Register */ -	u32 _unused2[3]; -	volatile u32 iar;		/* 0x30 Indirect Address Register */ -	u32 _unused3[3]; -	volatile u32 idr0;		/* 0x40 Indirect Data Register 0 */ -	u32 _unused4[3]; -	volatile u32 idr1;		/* 0x50 Indirect Data Register 1 */ -	u32 _unused5[3]; -	volatile u32 idr2;		/* 0x60 Indirect Data Register 2 */ -	u32 _unused6[3]; -	volatile u32 idr3;		/* 0x70 Indirect Data Register 3 */ -}; - -struct hal2_aes_regs { -	volatile u32 rx_stat[2];	/* Status registers */ -	volatile u32 rx_cr[2];		/* Control registers */ -	volatile u32 rx_ud[4];		/* User data window */ -	volatile u32 rx_st[24];		/* Channel status data */ -	 -	volatile u32 tx_stat[1];	/* Status register */ -	volatile u32 tx_cr[3];		/* Control registers */ -	volatile u32 tx_ud[4];		/* User data window */ -	volatile u32 tx_st[24];		/* Channel status data */ -}; - -struct hal2_vol_regs { -	volatile u32 right;		/* Right volume */ -	volatile u32 left;		/* Left volume */ -}; - -struct hal2_syn_regs { -	u32 _unused0[2]; -	volatile u32 page;		/* DOC Page register */ -	volatile u32 regsel;		/* DOC Register selection */ -	volatile u32 dlow;		/* DOC Data low */ -	volatile u32 dhigh;		/* DOC Data high */ -	volatile u32 irq;		/* IRQ Status */ -	volatile u32 dram;		/* DRAM Access */ -}; - -#endif	/* __HAL2_H */ diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c index a690ca5..6c0a770 100644 --- a/sound/oss/mpu401.c +++ b/sound/oss/mpu401.c @@ -1015,7 +1015,7 @@ int attach_mpu401(struct address_info *hw_config, struct module *owner)  		mpu401_chk_version(m, devc);  		if (devc->version == 0)  			mpu401_chk_version(m, devc); -			spin_unlock_irqrestore(&devc->lock,flags); +		spin_unlock_irqrestore(&devc->lock, flags);  	}  	if (devc->version != 0) diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index 99f5483..41f870f 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -868,7 +868,8 @@ snd_harmony_mixer_init(struct snd_harmony *h)  	struct snd_card *card = h->card;  	int idx, err; -	snd_assert(h != NULL, return -EINVAL); +	if (snd_BUG_ON(!h)) +		return -EINVAL;  	strcpy(card->mixername, "Harmony Gain control interface");  	for (idx = 0; idx < HARMONY_CONTROLS; idx++) { diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 31f52d3..7003711 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -517,6 +517,14 @@ config SND_HDA_HWDEP  	  This interface can be used for out-of-band communication  	  with codecs for debugging purposes. +config SND_HDA_INPUT_BEEP +	bool "Support digital beep via input layer" +	depends on SND_HDA_INTEL +	depends on INPUT=y || INPUT=SND_HDA_INTEL +	help +	  Say Y here to build a digital beep interface for HD-audio +	  driver. This interface is used to generate digital beeps. +  config SND_HDA_CODEC_REALTEK  	bool "Build Realtek HD-audio codec support"  	depends on SND_HDA_INTEL @@ -557,6 +565,14 @@ config SND_HDA_CODEC_ATIHDMI  	  Say Y here to include ATI HDMI HD-audio codec support in  	  snd-hda-intel driver, such as ATI RS600 HDMI. +config SND_HDA_CODEC_NVHDMI +	bool "Build NVIDIA HDMI HD-audio codec support" +	depends on SND_HDA_INTEL +	default y +	help +	  Say Y here to include NVIDIA HDMI HD-audio codec support in +	  snd-hda-intel driver, such as NVIDIA MCP78 HDMI. +  config SND_HDA_CODEC_CONEXANT  	bool "Build Conexant HD-audio codec support"  	depends on SND_HDA_INTEL @@ -649,8 +665,9 @@ config SND_ICE1712  	  Currently supported hardware is: M-Audio Delta 1010(LT),  	  DiO 2496, 66, 44, 410, Audiophile 24/96; Digigram VX442; -	  TerraTec EWX 24/96, EWS 88MT, 88D, DMX 6Fire, Phase 88; -	  Hoontech SoundTrack DSP 24/Value/Media7.1; Event EZ8. +	  TerraTec EWX 24/96, EWS 88MT/D, DMX 6Fire, Phase 88; +	  Hoontech SoundTrack DSP 24/Value/Media7.1; Event EZ8; +	  Lionstracs Mediastation, Terrasoniq TS 88.  	  To compile this driver as a module, choose M here: the module  	  will be called snd-ice1712. @@ -665,9 +682,12 @@ config SND_ICE1724  	  ICE/VT1724/1720 (Envy24HT/PT) chips.  	  Currently supported hardware is: AMP AUDIO2000; M-Audio -	  Revolution 7.1; TerraTec Aureon 5.1 Sky, 7.1 Space/Universe; -	  AudioTrak Prodigy 7.1; Pontis MS300; Albatron K8X800 Pro II; -	  Chaintech ZNF3-150/250. +	  Revolution 5.1, 7.1, Audiophile 192; TerraTec Aureon 5.1 Sky, +	  7.1 Space/Universe, Phase 22/28; Onkyo SE-90PCI, SE-200PCI; +	  AudioTrak Prodigy 192, 7.1 (HIFI/LT/XT), HD2; Hercules +	  Fortissimo IV; ESI Juli@; Pontis MS300; EGO-SYS WaveTerminal +	  192M; Albatron K8X800 Pro II; Chaintech ZNF3-150/250, 9CJS, +	  AV-710; Shuttle SN25P.  	  To compile this driver as a module, choose M here: the module  	  will be called snd-ice1724. @@ -845,7 +865,8 @@ config SND_VIRTUOSO  	select SND_OXYGEN_LIB  	help  	  Say Y here to include support for sound cards based on the -	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2 and D2X. +	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X and +	  HDAV1.3 (Deluxe).  	  To compile this driver as a module, choose M here: the module  	  will be called snd-virtuoso. diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 8c49a00..6704acb 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -67,8 +67,8 @@ struct ac97_codec_id {  };  static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = { -{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL,	NULL },  { 0x41445300, 0xffffff00, "Analog Devices",	NULL,	NULL }, +{ 0x414b4d00, 0xffffff00, "Asahi Kasei",	NULL,	NULL },  { 0x414c4300, 0xffffff00, "Realtek",		NULL,	NULL },  { 0x414c4700, 0xffffff00, "Realtek",		NULL,	NULL },  { 0x434d4900, 0xffffff00, "C-Media Electronics", NULL,	NULL }, @@ -94,11 +94,6 @@ static const struct ac97_codec_id snd_ac97_codec_id_vendors[] = {  };  static const struct ac97_codec_id snd_ac97_codec_ids[] = { -{ 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL }, -{ 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL }, -{ 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL }, -{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL,		NULL }, -{ 0x414b4d07, 0xffffffff, "AK4545",		NULL,		NULL },  { 0x41445303, 0xffffffff, "AD1819",		patch_ad1819,	NULL },  { 0x41445340, 0xffffffff, "AD1881",		patch_ad1881,	NULL },  { 0x41445348, 0xffffffff, "AD1881A",		patch_ad1881,	NULL }, @@ -112,20 +107,25 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {  { 0x41445374, 0xffffffff, "AD1981B",		patch_ad1981b,	NULL },  { 0x41445375, 0xffffffff, "AD1985",		patch_ad1985,	NULL },  { 0x41445378, 0xffffffff, "AD1986",		patch_ad1986,	NULL }, +{ 0x414b4d00, 0xffffffff, "AK4540",		NULL,		NULL }, +{ 0x414b4d01, 0xffffffff, "AK4542",		NULL,		NULL }, +{ 0x414b4d02, 0xffffffff, "AK4543",		NULL,		NULL }, +{ 0x414b4d06, 0xffffffff, "AK4544A",		NULL,		NULL }, +{ 0x414b4d07, 0xffffffff, "AK4545",		NULL,		NULL },  { 0x414c4300, 0xffffff00, "ALC100,100P", 	NULL,		NULL },  { 0x414c4710, 0xfffffff0, "ALC200,200P",	NULL,		NULL },  { 0x414c4721, 0xffffffff, "ALC650D",		NULL,	NULL }, /* already patched */  { 0x414c4722, 0xffffffff, "ALC650E",		NULL,	NULL }, /* already patched */  { 0x414c4723, 0xffffffff, "ALC650F",		NULL,	NULL }, /* already patched */  { 0x414c4720, 0xfffffff0, "ALC650",		patch_alc650,	NULL }, +{ 0x414c4730, 0xffffffff, "ALC101",		NULL,		NULL }, +{ 0x414c4740, 0xfffffff0, "ALC202",		NULL,		NULL }, +{ 0x414c4750, 0xfffffff0, "ALC250",		NULL,		NULL },  { 0x414c4760, 0xfffffff0, "ALC655",		patch_alc655,	NULL }, +{ 0x414c4770, 0xfffffff0, "ALC203",		patch_alc203,	NULL },  { 0x414c4781, 0xffffffff, "ALC658D",		NULL,	NULL }, /* already patched */  { 0x414c4780, 0xfffffff0, "ALC658",		patch_alc655,	NULL },  { 0x414c4790, 0xfffffff0, "ALC850",		patch_alc850,	NULL }, -{ 0x414c4730, 0xffffffff, "ALC101",		NULL,		NULL }, -{ 0x414c4740, 0xfffffff0, "ALC202",		NULL,		NULL }, -{ 0x414c4750, 0xfffffff0, "ALC250",		NULL,		NULL }, -{ 0x414c4770, 0xfffffff0, "ALC203",		NULL,		NULL },  { 0x434d4941, 0xffffffff, "CMI9738",		patch_cm9738,	NULL },  { 0x434d4961, 0xffffffff, "CMI9739",		patch_cm9739,	NULL },  { 0x434d4969, 0xffffffff, "CMI9780",		patch_cm9780,	NULL }, @@ -168,7 +168,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {  { 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL,		NULL },  { 0x56494161, 0xffffffff, "VIA1612A",		NULL,		NULL }, // modified ICE1232 with S/PDIF  { 0x56494170, 0xffffffff, "VIA1617A",		patch_vt1617a,	NULL }, // modified VT1616 with S/PDIF -{ 0x56494182, 0xffffffff, "VIA1618",		NULL,		NULL }, +{ 0x56494182, 0xffffffff, "VIA1618",		patch_vt1618,   NULL },  { 0x57454301, 0xffffffff, "W83971D",		NULL,		NULL },  { 0x574d4c00, 0xffffffff, "WM9701,WM9701A",	NULL,		NULL },  { 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL}, @@ -1890,8 +1890,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,  		.dev_free =	snd_ac97_bus_dev_free,  	}; -	snd_assert(card != NULL, return -EINVAL); -	snd_assert(rbus != NULL, return -EINVAL); +	if (snd_BUG_ON(!card)) +		return -EINVAL;  	bus = kzalloc(sizeof(*bus), GFP_KERNEL);  	if (bus == NULL)  		return -ENOMEM; @@ -1906,7 +1906,8 @@ int snd_ac97_bus(struct snd_card *card, int num, struct snd_ac97_bus_ops *ops,  		snd_ac97_bus_free(bus);  		return err;  	} -	*rbus = bus; +	if (rbus) +		*rbus = bus;  	return 0;  } @@ -1991,10 +1992,14 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,  		.dev_disconnect =	snd_ac97_dev_disconnect,  	}; -	snd_assert(rac97 != NULL, return -EINVAL); -	*rac97 = NULL; -	snd_assert(bus != NULL && template != NULL, return -EINVAL); -	snd_assert(template->num < 4 && bus->codec[template->num] == NULL, return -EINVAL); +	if (rac97) +		*rac97 = NULL; +	if (snd_BUG_ON(!bus || !template)) +		return -EINVAL; +	if (snd_BUG_ON(template->num >= 4)) +		return -EINVAL; +	if (bus->codec[template->num]) +		return -EBUSY;  	card = bus->card;  	ac97 = kzalloc(sizeof(*ac97), GFP_KERNEL); diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index f4fbc79..6ce3cbe 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -2560,6 +2560,14 @@ static int patch_ad1986(struct snd_ac97 * ac97)  	return 0;  } +/* + * realtek ALC203: use mono-out for pin 37 + */ +static int patch_alc203(struct snd_ac97 *ac97) +{ +	snd_ac97_update_bits(ac97, 0x7a, 0x400, 0x400); +	return 0; +}  /*   * realtek ALC65x/850 codecs @@ -3457,7 +3465,7 @@ static int patch_vt1616(struct snd_ac97 * ac97)  /*   * unfortunately, the vt1617a stashes the twiddlers required for - * nooding the i/o jacks on 2 different regs. * thameans that we cant + * noodling the i/o jacks on 2 different regs. that means that we can't   * use the easy way provided by AC97_ENUM_DOUBLE() we have to write   * are own funcs.   * @@ -3490,7 +3498,7 @@ static int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol,  	pac97 = snd_kcontrol_chip(kcontrol); /* grab codec handle */ -	/* grab our desirec bits, then mash them together in a manner +	/* grab our desired bits, then mash them together in a manner  	 * consistent with Table 6 on page 17 in the 1617a docs */  	usSM51 = snd_ac97_read(pac97, 0x7a) >> 14; @@ -3540,7 +3548,7 @@ static const struct snd_kcontrol_new snd_ac97_controls_vt1617a[] = {  	},  }; -int patch_vt1617a(struct snd_ac97 * ac97) +static int patch_vt1617a(struct snd_ac97 * ac97)  {  	int err = 0;  	int val; @@ -3568,6 +3576,200 @@ int patch_vt1617a(struct snd_ac97 * ac97)  	return err;  } +/* VIA VT1618 8 CHANNEL AC97 CODEC + * + * VIA implements 'Smart 5.1' completely differently on the 1618 than + * it does on the 1617a. awesome! They seem to have sourced this + * particular revision of the technology from somebody else, it's + * called Universal Audio Jack and it shows up on some other folk's chips + * as well. + * + * ordering in this list reflects vt1618 docs for Reg 60h and + * the block diagram, DACs are as follows: + * + *        OUT_O -> Front, + *	  OUT_1 -> Surround, + *	  OUT_2 -> C/LFE + * + * Unlike the 1617a, each OUT has a consistent set of mappings + * for all bitpatterns other than 00: + * + *        01       Unmixed Output + *        10       Line In + *        11       Mic  In + * + * Special Case of 00: + * + *        OUT_0    Mixed Output + *        OUT_1    Reserved + *        OUT_2    Reserved + * + * I have no idea what the hell Reserved does, but on an MSI + * CN700T, i have to set it to get 5.1 output - YMMV, bad + * shit may happen. + * + * If other chips use Universal Audio Jack, then this code might be applicable + * to them. + */ + +struct vt1618_uaj_item { +	unsigned short mask; +	unsigned short shift; +	const char *items[4]; +}; + +/* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */ + +static struct vt1618_uaj_item vt1618_uaj[3] = { +	{ +		/* speaker jack */ +		.mask  = 0x03, +		.shift = 0, +		.items = { +			"Speaker Out", "DAC Unmixed Out", "Line In", "Mic In" +		} +	}, +	{ +		/* line jack */ +		.mask  = 0x0c, +		.shift = 2, +		.items = { +			"Surround Out", "DAC Unmixed Out", "Line In", "Mic In" +		} +	}, +	{ +		/* mic jack */ +		.mask  = 0x30, +		.shift = 4, +		.items = { +			"Center LFE Out", "DAC Unmixed Out", "Line In", "Mic In" +		}, +	}, +}; + +static int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol, +				    struct snd_ctl_elem_info *uinfo) +{ +	return ac97_enum_text_info(kcontrol, uinfo, +				   vt1618_uaj[kcontrol->private_value].items, +				   4); +} + +/* All of the vt1618 Universal Audio Jack twiddlers are on + * Vendor Defined Register 0x60, page 0. The bits, and thus + * the mask, are the only thing that changes + */ +static int snd_ac97_vt1618_UAJ_get(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_value *ucontrol) +{ +	unsigned short datpag, uaj; +	struct snd_ac97 *pac97 = snd_kcontrol_chip(kcontrol); + +	mutex_lock(&pac97->page_mutex); + +	datpag = snd_ac97_read(pac97, AC97_INT_PAGING) & AC97_PAGE_MASK; +	snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, 0); + +	uaj = snd_ac97_read(pac97, 0x60) & +		vt1618_uaj[kcontrol->private_value].mask; + +	snd_ac97_update_bits(pac97, AC97_INT_PAGING, AC97_PAGE_MASK, datpag); +	mutex_unlock(&pac97->page_mutex); + +	ucontrol->value.enumerated.item[0] = uaj >> +		vt1618_uaj[kcontrol->private_value].shift; + +	return 0; +} + +static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_value *ucontrol) +{ +	return ac97_update_bits_page(snd_kcontrol_chip(kcontrol), 0x60, +				     vt1618_uaj[kcontrol->private_value].mask, +				     ucontrol->value.enumerated.item[0]<< +				     vt1618_uaj[kcontrol->private_value].shift, +				     0); +} + +/* config aux in jack - not found on 3 jack motherboards or soundcards */ + +static int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol, +				     struct snd_ctl_elem_info *uinfo) +{ +	static const char *txt_aux[] = {"Aux In", "Back Surr Out"}; + +	return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2); +} + +static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_value *ucontrol) +{ +	ucontrol->value.enumerated.item[0] = +		(snd_ac97_read(snd_kcontrol_chip(kcontrol), 0x5c) & 0x0008)>>3; +	return 0; +} + +static int snd_ac97_vt1618_aux_put(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_value *ucontrol) +{ +	/* toggle surround rear dac power */ + +	snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x5c, 0x0008, +			     ucontrol->value.enumerated.item[0] << 3); + +	/* toggle aux in surround rear out jack */ + +	return snd_ac97_update_bits(snd_kcontrol_chip(kcontrol), 0x76, 0x0008, +				    ucontrol->value.enumerated.item[0] << 3); +} + +static const struct snd_kcontrol_new snd_ac97_controls_vt1618[] = { +	AC97_SINGLE("Exchange Center/LFE", 0x5a,  8, 1,     0), +	AC97_SINGLE("DC Offset",           0x5a, 10, 1,     0), +	AC97_SINGLE("Soft Mute",           0x5c,  0, 1,     1), +	AC97_SINGLE("Headphone Amp",       0x5c,  5, 1,     1), +	AC97_DOUBLE("Back Surr Volume",    0x5e,  8, 0, 31, 1), +	AC97_SINGLE("Back Surr Switch",    0x5e, 15, 1,     1), +	{ +		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name          = "Speaker Jack Mode", +		.info          = snd_ac97_vt1618_UAJ_info, +		.get           = snd_ac97_vt1618_UAJ_get, +		.put           = snd_ac97_vt1618_UAJ_put, +		.private_value = 0 +	}, +	{ +		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name          = "Line Jack Mode", +		.info          = snd_ac97_vt1618_UAJ_info, +		.get           = snd_ac97_vt1618_UAJ_get, +		.put           = snd_ac97_vt1618_UAJ_put, +		.private_value = 1 +	}, +	{ +		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name          = "Mic Jack Mode", +		.info          = snd_ac97_vt1618_UAJ_info, +		.get           = snd_ac97_vt1618_UAJ_get, +		.put           = snd_ac97_vt1618_UAJ_put, +		.private_value = 2 +	}, +	{ +		.iface         = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name          = "Aux Jack Mode", +		.info          = snd_ac97_vt1618_aux_info, +		.get           = snd_ac97_vt1618_aux_get, +		.put           = snd_ac97_vt1618_aux_put, +	} +}; + +static int patch_vt1618(struct snd_ac97 *ac97) +{ +	return patch_build_controls(ac97, snd_ac97_controls_vt1618, +				    ARRAY_SIZE(snd_ac97_controls_vt1618)); +} +  /*   */  static void it2646_update_jacks(struct snd_ac97 *ac97) diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 39ec55b..92f3a97 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -549,7 +549,8 @@ snd_ad1889_playback_pointer(struct snd_pcm_substream *ss)  	ptr = ad1889_readl(chip, AD_DMA_WAVCA);  	ptr -= chip->wave.addr; -	snd_assert((ptr >= 0) && (ptr < chip->wave.size), return 0); +	if (snd_BUG_ON(ptr >= chip->wave.size)) +		return 0;  	return bytes_to_frames(ss->runtime, ptr);  } @@ -567,7 +568,8 @@ snd_ad1889_capture_pointer(struct snd_pcm_substream *ss)  	ptr = ad1889_readl(chip, AD_DMA_ADCCA);  	ptr -= chip->ramc.addr; -	snd_assert((ptr >= 0) && (ptr < chip->ramc.size), return 0); +	if (snd_BUG_ON(ptr >= chip->ramc.size)) +		return 0;  	return bytes_to_frames(ss->runtime, ptr);  } diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c index 33d37b1..0f819dd 100644 --- a/sound/pci/ak4531_codec.c +++ b/sound/pci/ak4531_codec.c @@ -392,9 +392,10 @@ int __devinit snd_ak4531_mixer(struct snd_card *card,  		.dev_free =	snd_ak4531_dev_free,  	}; -	snd_assert(rak4531 != NULL, return -EINVAL); -	*rak4531 = NULL; -	snd_assert(card != NULL && _ak4531 != NULL, return -EINVAL); +	if (snd_BUG_ON(!card || !_ak4531)) +		return -EINVAL; +	if (rak4531) +		*rak4531 = NULL;  	ak4531 = kzalloc(sizeof(*ak4531), GFP_KERNEL);  	if (ak4531 == NULL)  		return -ENOMEM; @@ -428,7 +429,8 @@ int __devinit snd_ak4531_mixer(struct snd_card *card,  #if 0  	snd_ak4531_dump(ak4531);  #endif -	*rak4531 = ak4531; +	if (rak4531) +		*rak4531 = ak4531;  	return 0;  } diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 27ce613..ba57005 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -2,7 +2,7 @@   *  card-als4000.c - driver for Avance Logic ALS4000 based soundcards.   *  Copyright (C) 2000 by Bart Hartgers <bart@etpmod.phys.tue.nl>,   *			  Jaroslav Kysela <perex@perex.cz> - *  Copyright (C) 2002 by Andreas Mohr <hw7oshyuv3001@sneakemail.com> + *  Copyright (C) 2002, 2008 by Andreas Mohr <hw7oshyuv3001@sneakemail.com>   *   *  Framework borrowed from Massimo Piccioni's card-als100.c.   * @@ -27,8 +27,10 @@   *  bought an ALS4000 based soundcard, I was forced to base this driver   *  on reverse engineering.   * - *  Note: this is no longer true. Pretty verbose chip docu (ALS4000a.PDF) - *  can be found on the ALSA web site. + *  Note: this is no longer true (thank you!): + *  pretty verbose chip docu (ALS4000a.PDF) can be found on the ALSA web site. + *  Page numbers stated anywhere below with the "SPECS_PAGE:" tag + *  refer to: ALS4000a.PDF specs Ver 1.0, May 28th, 1998.   *   *  The ALS4000 seems to be the PCI-cousin of the ALS100. It contains an   *  ALS100-like SB DSP/mixer, an OPL3 synth, a MPU401 and a gameport  @@ -59,7 +61,7 @@   * - value -> some port 0x0c0d   *   * ToDo: - * - Proper shared IRQ handling? + * - by default, don't enable legacy game and use PCI game I/O   * - power management? (card can do voice wakeup according to datasheet!!)   */ @@ -78,7 +80,7 @@  #include <sound/sb.h>  #include <sound/initval.h> -MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>"); +MODULE_AUTHOR("Bart Hartgers <bart@etpmod.phys.tue.nl>, Andreas Mohr");  MODULE_DESCRIPTION("Avance Logic ALS4000");  MODULE_LICENSE("GPL");  MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS4000}}"); @@ -107,7 +109,7 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0  struct snd_card_als4000 {  	/* most frequent access first */ -	unsigned long gcr; +	unsigned long iobase;  	struct pci_dev *pci;  	struct snd_sb *chip;  #ifdef SUPPORT_JOYSTICK @@ -122,28 +124,168 @@ static struct pci_device_id snd_als4000_ids[] = {  MODULE_DEVICE_TABLE(pci, snd_als4000_ids); -static inline void snd_als4000_gcr_write_addr(unsigned long port, u32 reg, u32 val) +enum als4k_iobase_t { +	/* IOx: B == Byte, W = Word, D = DWord; SPECS_PAGE: 37 */ +	ALS4K_IOD_00_AC97_ACCESS = 0x00, +	ALS4K_IOW_04_AC97_READ = 0x04, +	ALS4K_IOB_06_AC97_STATUS = 0x06, +	ALS4K_IOB_07_IRQSTATUS = 0x07, +	ALS4K_IOD_08_GCR_DATA = 0x08, +	ALS4K_IOB_0C_GCR_INDEX = 0x0c, +	ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU = 0x0e, +	ALS4K_IOB_10_ADLIB_ADDR0 = 0x10, +	ALS4K_IOB_11_ADLIB_ADDR1 = 0x11, +	ALS4K_IOB_12_ADLIB_ADDR2 = 0x12, +	ALS4K_IOB_13_ADLIB_ADDR3 = 0x13, +	ALS4K_IOB_14_MIXER_INDEX = 0x14, +	ALS4K_IOB_15_MIXER_DATA = 0x15, +	ALS4K_IOB_16_ESP_RESET = 0x16, +	ALS4K_IOB_16_ACK_FOR_CR1E = 0x16, /* 2nd function */ +	ALS4K_IOB_18_OPL_ADDR0 = 0x18, +	ALS4K_IOB_19_OPL_ADDR1 = 0x19, +	ALS4K_IOB_1A_ESP_RD_DATA = 0x1a, +	ALS4K_IOB_1C_ESP_CMD_DATA = 0x1c, +	ALS4K_IOB_1C_ESP_WR_STATUS = 0x1c, /* 2nd function */ +	ALS4K_IOB_1E_ESP_RD_STATUS8 = 0x1e, +	ALS4K_IOB_1F_ESP_RD_STATUS16 = 0x1f, +	ALS4K_IOB_20_ESP_GAMEPORT_200 = 0x20, +	ALS4K_IOB_21_ESP_GAMEPORT_201 = 0x21, +	ALS4K_IOB_30_MIDI_DATA = 0x30, +	ALS4K_IOB_31_MIDI_STATUS = 0x31, +	ALS4K_IOB_31_MIDI_COMMAND = 0x31, /* 2nd function */ +}; + +enum als4k_iobase_0e_t { +	ALS4K_IOB_0E_MPU_IRQ = 0x10, +	ALS4K_IOB_0E_CR1E_IRQ = 0x40, +	ALS4K_IOB_0E_SB_DMA_IRQ = 0x80, +}; + +enum als4k_gcr_t { /* all registers 32bit wide; SPECS_PAGE: 38 to 42 */ +	ALS4K_GCR8C_MISC_CTRL = 0x8c, +	ALS4K_GCR90_TEST_MODE_REG = 0x90, +	ALS4K_GCR91_DMA0_ADDR = 0x91, +	ALS4K_GCR92_DMA0_MODE_COUNT = 0x92, +	ALS4K_GCR93_DMA1_ADDR = 0x93, +	ALS4K_GCR94_DMA1_MODE_COUNT = 0x94, +	ALS4K_GCR95_DMA3_ADDR = 0x95, +	ALS4K_GCR96_DMA3_MODE_COUNT = 0x96, +	ALS4K_GCR99_DMA_EMULATION_CTRL = 0x99, +	ALS4K_GCRA0_FIFO1_CURRENT_ADDR = 0xa0, +	ALS4K_GCRA1_FIFO1_STATUS_BYTECOUNT = 0xa1, +	ALS4K_GCRA2_FIFO2_PCIADDR = 0xa2, +	ALS4K_GCRA3_FIFO2_COUNT = 0xa3, +	ALS4K_GCRA4_FIFO2_CURRENT_ADDR = 0xa4, +	ALS4K_GCRA5_FIFO1_STATUS_BYTECOUNT = 0xa5, +	ALS4K_GCRA6_PM_CTRL = 0xa6, +	ALS4K_GCRA7_PCI_ACCESS_STORAGE = 0xa7, +	ALS4K_GCRA8_LEGACY_CFG1 = 0xa8, +	ALS4K_GCRA9_LEGACY_CFG2 = 0xa9, +	ALS4K_GCRFF_DUMMY_SCRATCH = 0xff, +}; + +enum als4k_gcr8c_t { +	ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE = 0x8000, +	ALS4K_GCR8C_CHIP_REV_MASK = 0xf0000 +}; + +static inline void snd_als4k_iobase_writeb(unsigned long iobase, +						enum als4k_iobase_t reg, +						u8 val) +{ +	outb(val, iobase + reg); +} + +static inline void snd_als4k_iobase_writel(unsigned long iobase, +						enum als4k_iobase_t reg, +						u32 val) +{ +	outl(val, iobase + reg); +} + +static inline u8 snd_als4k_iobase_readb(unsigned long iobase, +						enum als4k_iobase_t reg) +{ +	return inb(iobase + reg); +} + +static inline u32 snd_als4k_iobase_readl(unsigned long iobase, +						enum als4k_iobase_t reg) +{ +	return inl(iobase + reg); +} + +static inline void snd_als4k_gcr_write_addr(unsigned long iobase, +						 enum als4k_gcr_t reg, +						 u32 val)  { -	outb(reg, port+0x0c); -	outl(val, port+0x08); +	snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg); +	snd_als4k_iobase_writel(iobase, ALS4K_IOD_08_GCR_DATA, val);  } -static inline void snd_als4000_gcr_write(struct snd_sb *sb, u32 reg, u32 val) +static inline void snd_als4k_gcr_write(struct snd_sb *sb, +					 enum als4k_gcr_t reg, +					 u32 val)  { -	snd_als4000_gcr_write_addr(sb->alt_port, reg, val); +	snd_als4k_gcr_write_addr(sb->alt_port, reg, val);  }	 -static inline u32 snd_als4000_gcr_read_addr(unsigned long port, u32 reg) +static inline u32 snd_als4k_gcr_read_addr(unsigned long iobase, +						 enum als4k_gcr_t reg)  { -	outb(reg, port+0x0c); -	return inl(port+0x08); +	/* SPECS_PAGE: 37/38 */ +	snd_als4k_iobase_writeb(iobase, ALS4K_IOB_0C_GCR_INDEX, reg); +	return snd_als4k_iobase_readl(iobase, ALS4K_IOD_08_GCR_DATA);  } -static inline u32 snd_als4000_gcr_read(struct snd_sb *sb, u32 reg) +static inline u32 snd_als4k_gcr_read(struct snd_sb *sb, enum als4k_gcr_t reg)  { -	return snd_als4000_gcr_read_addr(sb->alt_port, reg); +	return snd_als4k_gcr_read_addr(sb->alt_port, reg);  } +enum als4k_cr_t { /* all registers 8bit wide; SPECS_PAGE: 20 to 23 */ +	ALS4K_CR0_SB_CONFIG = 0x00, +	ALS4K_CR2_MISC_CONTROL = 0x02, +	ALS4K_CR3_CONFIGURATION = 0x03, +	ALS4K_CR17_FIFO_STATUS = 0x17, +	ALS4K_CR18_ESP_MAJOR_VERSION = 0x18, +	ALS4K_CR19_ESP_MINOR_VERSION = 0x19, +	ALS4K_CR1A_MPU401_UART_MODE_CONTROL = 0x1a, +	ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO = 0x1c, +	ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI = 0x1d, +	ALS4K_CR1E_FIFO2_CONTROL = 0x1e, /* secondary PCM FIFO (recording) */ +	ALS4K_CR3A_MISC_CONTROL = 0x3a, +	ALS4K_CR3B_CRC32_BYTE0 = 0x3b, /* for testing, activate via CR3A */ +	ALS4K_CR3C_CRC32_BYTE1 = 0x3c, +	ALS4K_CR3D_CRC32_BYTE2 = 0x3d, +	ALS4K_CR3E_CRC32_BYTE3 = 0x3e, +}; + +enum als4k_cr0_t { +	ALS4K_CR0_DMA_CONTIN_MODE_CTRL = 0x02, /* IRQ/FIFO controlled for 0/1 */ +	ALS4K_CR0_DMA_90H_MODE_CTRL = 0x04, /* IRQ/FIFO controlled for 0/1 */ +	ALS4K_CR0_MX80_81_REG_WRITE_ENABLE = 0x80, +}; + +static inline void snd_als4_cr_write(struct snd_sb *chip, +					enum als4k_cr_t reg, +					u8 data) +{ +	/* Control Register is reg | 0xc0 (bit 7, 6 set) on sbmixer_index +	 * NOTE: assumes chip->mixer_lock to be locked externally already! +	 * SPECS_PAGE: 6 */ +	snd_sbmixer_write(chip, reg | 0xc0, data); +} + +static inline u8 snd_als4_cr_read(struct snd_sb *chip, +					enum als4k_cr_t reg) +{ +	/* NOTE: assumes chip->mixer_lock to be locked externally already! */ +	return snd_sbmixer_read(chip, reg | 0xc0); +} + + +  static void snd_als4000_set_rate(struct snd_sb *chip, unsigned int rate)  {  	if (!(chip->mode & SB_RATE_LOCK)) { @@ -156,15 +298,19 @@ static void snd_als4000_set_rate(struct snd_sb *chip, unsigned int rate)  static inline void snd_als4000_set_capture_dma(struct snd_sb *chip,  					       dma_addr_t addr, unsigned size)  { -	snd_als4000_gcr_write(chip, 0xa2, addr); -	snd_als4000_gcr_write(chip, 0xa3, (size-1)); +	/* SPECS_PAGE: 40 */ +	snd_als4k_gcr_write(chip, ALS4K_GCRA2_FIFO2_PCIADDR, addr); +	snd_als4k_gcr_write(chip, ALS4K_GCRA3_FIFO2_COUNT, (size-1));  }  static inline void snd_als4000_set_playback_dma(struct snd_sb *chip, -						dma_addr_t addr, unsigned size) +						dma_addr_t addr, +						unsigned size)  { -	snd_als4000_gcr_write(chip, 0x91, addr); -	snd_als4000_gcr_write(chip, 0x92, (size-1)|0x180000); +	/* SPECS_PAGE: 38 */ +	snd_als4k_gcr_write(chip, ALS4K_GCR91_DMA0_ADDR, addr); +	snd_als4k_gcr_write(chip, ALS4K_GCR92_DMA0_MODE_COUNT, +							(size-1)|0x180000);  }  #define ALS4000_FORMAT_SIGNED	(1<<0) @@ -248,7 +394,7 @@ static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)  	count = snd_pcm_lib_period_bytes(substream);  	if (chip->capture_format & ALS4000_FORMAT_16BIT) -		count >>=1; +		count >>= 1;  	count--;  	spin_lock_irq(&chip->reg_lock); @@ -256,8 +402,8 @@ static int snd_als4000_capture_prepare(struct snd_pcm_substream *substream)  	snd_als4000_set_capture_dma(chip, runtime->dma_addr, size);  	spin_unlock_irq(&chip->reg_lock);  	spin_lock_irq(&chip->mixer_lock); -	snd_sbmixer_write(chip, 0xdc, count); -	snd_sbmixer_write(chip, 0xdd, count>>8); +	snd_als4_cr_write(chip, ALS4K_CR1C_FIFO2_BLOCK_LENGTH_LO, count & 0xff); +	snd_als4_cr_write(chip, ALS4K_CR1D_FIFO2_BLOCK_LENGTH_HI, count >> 8);  	spin_unlock_irq(&chip->mixer_lock);  	return 0;  } @@ -275,7 +421,7 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)  	count = snd_pcm_lib_period_bytes(substream);  	if (chip->playback_format & ALS4000_FORMAT_16BIT) -		count >>=1; +		count >>= 1;  	count--;  	/* FIXME: from second playback on, there's a lot more clicks and pops @@ -292,8 +438,8 @@ static int snd_als4000_playback_prepare(struct snd_pcm_substream *substream)  	/* snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON); */  	snd_sbdsp_command(chip, playback_cmd(chip).dsp_cmd);  	snd_sbdsp_command(chip, playback_cmd(chip).format); -	snd_sbdsp_command(chip, count); -	snd_sbdsp_command(chip, count>>8); +	snd_sbdsp_command(chip, count & 0xff); +	snd_sbdsp_command(chip, count >> 8);  	snd_sbdsp_command(chip, playback_cmd(chip).dma_off);	  	spin_unlock_irq(&chip->reg_lock); @@ -305,17 +451,25 @@ static int snd_als4000_capture_trigger(struct snd_pcm_substream *substream, int  	struct snd_sb *chip = snd_pcm_substream_chip(substream);  	int result = 0; +	/* FIXME race condition in here!!! +	   chip->mode non-atomic update gets consistently protected +	   by reg_lock always, _except_ for this place!! +	   Probably need to take reg_lock as outer (or inner??) lock, too. +	   (or serialize both lock operations? probably not, though... - racy?) +	*/  	spin_lock(&chip->mixer_lock);  	switch (cmd) {  	case SNDRV_PCM_TRIGGER_START:  	case SNDRV_PCM_TRIGGER_RESUME:  		chip->mode |= SB_RATE_LOCK_CAPTURE; -		snd_sbmixer_write(chip, 0xde, capture_cmd(chip)); +		snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL, +							 capture_cmd(chip));  		break;  	case SNDRV_PCM_TRIGGER_STOP:  	case SNDRV_PCM_TRIGGER_SUSPEND:  		chip->mode &= ~SB_RATE_LOCK_CAPTURE; -		snd_sbmixer_write(chip, 0xde, 0); +		snd_als4_cr_write(chip, ALS4K_CR1E_FIFO2_CONTROL, +							 capture_cmd(chip));  		break;  	default:  		result = -EINVAL; @@ -356,8 +510,9 @@ static snd_pcm_uframes_t snd_als4000_capture_pointer(struct snd_pcm_substream *s  	unsigned int result;  	spin_lock(&chip->reg_lock);	 -	result = snd_als4000_gcr_read(chip, 0xa4) & 0xffff; +	result = snd_als4k_gcr_read(chip, ALS4K_GCRA4_FIFO2_CURRENT_ADDR);  	spin_unlock(&chip->reg_lock); +	result &= 0xffff;  	return bytes_to_frames( substream->runtime, result );  } @@ -367,8 +522,9 @@ static snd_pcm_uframes_t snd_als4000_playback_pointer(struct snd_pcm_substream *  	unsigned result;  	spin_lock(&chip->reg_lock);	 -	result = snd_als4000_gcr_read(chip, 0xa0) & 0xffff; +	result = snd_als4k_gcr_read(chip, ALS4K_GCRA0_FIFO1_CURRENT_ADDR);  	spin_unlock(&chip->reg_lock); +	result &= 0xffff;  	return bytes_to_frames( substream->runtime, result );  } @@ -376,45 +532,63 @@ static snd_pcm_uframes_t snd_als4000_playback_pointer(struct snd_pcm_substream *   * return IRQ_HANDLED no matter whether we actually had an IRQ flag or not).   * ALS4000a.PDF writes that while ACKing IRQ in PCI block will *not* ACK   * the IRQ in the SB core, ACKing IRQ in SB block *will* ACK the PCI IRQ - * register (alt_port + 0x0e). Probably something could be optimized here to - * query/write one register only... + * register (alt_port + ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU). Probably something + * could be optimized here to query/write one register only...   * And even if both registers need to be queried, then there's still the   * question of whether it's actually correct to ACK PCI IRQ before reading   * SB IRQ like we do now, since ALS4000a.PDF mentions that PCI IRQ will *clear*   * SB IRQ status. + * (hmm, SPECS_PAGE: 38 mentions it the other way around!)   * And do we *really* need the lock here for *reading* SB_DSP4_IRQSTATUS??   * */  static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id)  {  	struct snd_sb *chip = dev_id; -	unsigned gcr_status; -	unsigned sb_status; - -	/* find out which bit of the ALS4000 produced the interrupt */ -	gcr_status = inb(chip->alt_port + 0xe); - -	if ((gcr_status & 0x80) && (chip->playback_substream)) /* playback */ +	unsigned pci_irqstatus; +	unsigned sb_irqstatus; + +	/* find out which bit of the ALS4000 PCI block produced the interrupt, +	   SPECS_PAGE: 38, 5 */ +	pci_irqstatus = snd_als4k_iobase_readb(chip->alt_port, +				 ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU); +	if ((pci_irqstatus & ALS4K_IOB_0E_SB_DMA_IRQ) +	 && (chip->playback_substream)) /* playback */  		snd_pcm_period_elapsed(chip->playback_substream); -	if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */ +	if ((pci_irqstatus & ALS4K_IOB_0E_CR1E_IRQ) +	 && (chip->capture_substream)) /* capturing */  		snd_pcm_period_elapsed(chip->capture_substream); -	if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */ +	if ((pci_irqstatus & ALS4K_IOB_0E_MPU_IRQ) +	 && (chip->rmidi)) /* MPU401 interrupt */  		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); -	/* release the gcr */ -	outb(gcr_status, chip->alt_port + 0xe); +	/* ACK the PCI block IRQ */ +	snd_als4k_iobase_writeb(chip->alt_port, +			 ALS4K_IOB_0E_IRQTYPE_SB_CR1E_MPU, pci_irqstatus);  	spin_lock(&chip->mixer_lock); -	sb_status = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS); +	/* SPECS_PAGE: 20 */ +	sb_irqstatus = snd_sbmixer_read(chip, SB_DSP4_IRQSTATUS);  	spin_unlock(&chip->mixer_lock); -	if (sb_status & SB_IRQTYPE_8BIT)  +	if (sb_irqstatus & SB_IRQTYPE_8BIT)  		snd_sb_ack_8bit(chip); -	if (sb_status & SB_IRQTYPE_16BIT)  +	if (sb_irqstatus & SB_IRQTYPE_16BIT)  		snd_sb_ack_16bit(chip); -	if (sb_status & SB_IRQTYPE_MPUIN) +	if (sb_irqstatus & SB_IRQTYPE_MPUIN)  		inb(chip->mpu_port); -	if (sb_status & 0x20) -		inb(SBP(chip, RESET)); -	return IRQ_HANDLED; +	if (sb_irqstatus & ALS4K_IRQTYPE_CR1E_DMA) +		snd_als4k_iobase_readb(chip->alt_port, +					ALS4K_IOB_16_ACK_FOR_CR1E); + +	/* printk(KERN_INFO "als4000: irq 0x%04x 0x%04x\n", +					 pci_irqstatus, sb_irqstatus); */ + +	/* only ack the things we actually handled above */ +	return IRQ_RETVAL( +	     (pci_irqstatus & (ALS4K_IOB_0E_SB_DMA_IRQ|ALS4K_IOB_0E_CR1E_IRQ| +				ALS4K_IOB_0E_MPU_IRQ)) +	  || (sb_irqstatus & (SB_IRQTYPE_8BIT|SB_IRQTYPE_16BIT| +				SB_IRQTYPE_MPUIN|ALS4K_IRQTYPE_CR1E_DMA)) +	);  }  /*****************************************************************/ @@ -526,7 +700,8 @@ static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)  	struct snd_pcm *pcm;  	int err; -	if ((err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm)) < 0) +	err = snd_pcm_new(chip->card, "ALS4000 DSP", device, 1, 1, &pcm); +	if (err < 0)  		return err;  	pcm->private_data = chip;  	pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; @@ -543,48 +718,55 @@ static int __devinit snd_als4000_pcm(struct snd_sb *chip, int device)  /******************************************************************/ -static void snd_als4000_set_addr(unsigned long gcr, -					unsigned int sb, -					unsigned int mpu, -					unsigned int opl, -					unsigned int game) +static void snd_als4000_set_addr(unsigned long iobase, +					unsigned int sb_io, +					unsigned int mpu_io, +					unsigned int opl_io, +					unsigned int game_io)  { -	u32 confA = 0; -	u32 confB = 0; - -	if (mpu > 0) -		confB |= (mpu | 1) << 16; -	if (sb > 0) -		confB |= (sb | 1); -	if (game > 0) -		confA |= (game | 1) << 16; -	if (opl > 0)	 -		confA |= (opl | 1); -	snd_als4000_gcr_write_addr(gcr, 0xa8, confA); -	snd_als4000_gcr_write_addr(gcr, 0xa9, confB); +	u32 cfg1 = 0; +	u32 cfg2 = 0; + +	if (mpu_io > 0) +		cfg2 |= (mpu_io | 1) << 16; +	if (sb_io > 0) +		cfg2 |= (sb_io | 1); +	if (game_io > 0) +		cfg1 |= (game_io | 1) << 16; +	if (opl_io > 0) +		cfg1 |= (opl_io | 1); +	snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA8_LEGACY_CFG1, cfg1); +	snd_als4k_gcr_write_addr(iobase, ALS4K_GCRA9_LEGACY_CFG2, cfg2);  }  static void snd_als4000_configure(struct snd_sb *chip)  { -	unsigned tmp; +	u8 tmp;  	int i;  	/* do some more configuration */  	spin_lock_irq(&chip->mixer_lock); -	tmp = snd_sbmixer_read(chip, 0xc0); -	snd_sbmixer_write(chip, 0xc0, tmp|0x80); -	/* always select DMA channel 0, since we do not actually use DMA */ +	tmp = snd_als4_cr_read(chip, ALS4K_CR0_SB_CONFIG); +	snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG, +				tmp|ALS4K_CR0_MX80_81_REG_WRITE_ENABLE); +	/* always select DMA channel 0, since we do not actually use DMA +	 * SPECS_PAGE: 19/20 */  	snd_sbmixer_write(chip, SB_DSP4_DMASETUP, SB_DMASETUP_DMA0); -	snd_sbmixer_write(chip, 0xc0, tmp&0x7f); +	snd_als4_cr_write(chip, ALS4K_CR0_SB_CONFIG, +				 tmp & ~ALS4K_CR0_MX80_81_REG_WRITE_ENABLE);  	spin_unlock_irq(&chip->mixer_lock);  	spin_lock_irq(&chip->reg_lock); -	/* magic number. Enables interrupts(?) */ -	snd_als4000_gcr_write(chip, 0x8c, 0x28000); -	for(i = 0x91; i <= 0x96; ++i) -		snd_als4000_gcr_write(chip, i, 0); +	/* enable interrupts */ +	snd_als4k_gcr_write(chip, ALS4K_GCR8C_MISC_CTRL, +					ALS4K_GCR8C_IRQ_MASK_CTRL_ENABLE); + +	/* SPECS_PAGE: 39 */ +	for (i = ALS4K_GCR91_DMA0_ADDR; i <= ALS4K_GCR96_DMA3_MODE_COUNT; ++i) +		snd_als4k_gcr_write(chip, i, 0); -	snd_als4000_gcr_write(chip, 0x99, snd_als4000_gcr_read(chip, 0x99)); +	snd_als4k_gcr_write(chip, ALS4K_GCR99_DMA_EMULATION_CTRL, +		snd_als4k_gcr_read(chip, ALS4K_GCR99_DMA_EMULATION_CTRL));  	spin_unlock_irq(&chip->reg_lock);  } @@ -628,7 +810,7 @@ static int __devinit snd_als4000_create_gameport(struct snd_card_als4000 *acard,  	gameport_set_port_data(gp, r);  	/* Enable legacy joystick port */ -	snd_als4000_set_addr(acard->gcr, 0, 0, 0, 1); +	snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);  	gameport_register_port(acard->gameport); @@ -643,7 +825,9 @@ static void snd_als4000_free_gameport(struct snd_card_als4000 *acard)  		gameport_unregister_port(acard->gameport);  		acard->gameport = NULL; -		snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0); /* disable joystick */ +		/* disable joystick */ +		snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0); +  		release_and_free_resource(r);  	}  } @@ -654,10 +838,10 @@ static inline void snd_als4000_free_gameport(struct snd_card_als4000 *acard) { }  static void snd_card_als4000_free( struct snd_card *card )  { -	struct snd_card_als4000 * acard = (struct snd_card_als4000 *)card->private_data; +	struct snd_card_als4000 *acard = card->private_data;  	/* make sure that interrupts are disabled */ -	snd_als4000_gcr_write_addr( acard->gcr, 0x8c, 0); +	snd_als4k_gcr_write_addr(acard->iobase, ALS4K_GCR8C_MISC_CTRL, 0);  	/* free resources */  	snd_als4000_free_gameport(acard);  	pci_release_regions(acard->pci); @@ -670,7 +854,7 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,  	static int dev;  	struct snd_card *card;  	struct snd_card_als4000 *acard; -	unsigned long gcr; +	unsigned long iobase;  	struct snd_sb *chip;  	struct snd_opl3 *opl3;  	unsigned short word; @@ -699,31 +883,32 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,  		pci_disable_device(pci);  		return err;  	} -	gcr = pci_resource_start(pci, 0); +	iobase = pci_resource_start(pci, 0);  	pci_read_config_word(pci, PCI_COMMAND, &word);  	pci_write_config_word(pci, PCI_COMMAND, word | PCI_COMMAND_IO);  	pci_set_master(pci);  	card = snd_card_new(index[dev], id[dev], THIS_MODULE,  -			    sizeof( struct snd_card_als4000 ) ); +			    sizeof(*acard) /* private_data: acard */);  	if (card == NULL) {  		pci_release_regions(pci);  		pci_disable_device(pci);  		return -ENOMEM;  	} -	acard = (struct snd_card_als4000 *)card->private_data; +	acard = card->private_data;  	acard->pci = pci; -	acard->gcr = gcr; +	acard->iobase = iobase;  	card->private_free = snd_card_als4000_free;  	/* disable all legacy ISA stuff */ -	snd_als4000_set_addr(acard->gcr, 0, 0, 0, 0); +	snd_als4000_set_addr(acard->iobase, 0, 0, 0, 0);  	if ((err = snd_sbdsp_create(card, -				    gcr + 0x10, +				    iobase + ALS4K_IOB_10_ADLIB_ADDR0,  				    pci->irq, +		/* internally registered as IRQF_SHARED in case of ALS4000 SB */  				    snd_als4000_interrupt,  				    -1,  				    -1, @@ -734,7 +919,7 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,  	acard->chip = chip;  	chip->pci = pci; -	chip->alt_port = gcr; +	chip->alt_port = iobase;  	snd_card_set_dev(card, &pci->dev);  	snd_als4000_configure(chip); @@ -745,11 +930,18 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,  		card->shortname, chip->alt_port, chip->irq);  	if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000, -				        gcr+0x30, MPU401_INFO_INTEGRATED, +					iobase + ALS4K_IOB_30_MIDI_DATA, +					MPU401_INFO_INTEGRATED,  					pci->irq, 0, &chip->rmidi)) < 0) { -		printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n", gcr+0x30); +		printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n", +				iobase + ALS4K_IOB_30_MIDI_DATA);  		goto out_err;  	} +	/* FIXME: ALS4000 has interesting MPU401 configuration features +	 * at ALS4K_CR1A_MPU401_UART_MODE_CONTROL +	 * (pass-thru / UART switching, fast MIDI clock, etc.), +	 * however there doesn't seem to be an ALSA API for this... +	 * SPECS_PAGE: 21 */  	if ((err = snd_als4000_pcm(chip, 0)) < 0) {  		goto out_err; @@ -758,10 +950,13 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,  		goto out_err;  	}	     -	if (snd_opl3_create(card, gcr+0x10, gcr+0x12, +	if (snd_opl3_create(card, +				iobase + ALS4K_IOB_10_ADLIB_ADDR0, +				iobase + ALS4K_IOB_12_ADLIB_ADDR2,  			    OPL3_HW_AUTO, 1, &opl3) < 0) {  		printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx?\n", -			   gcr+0x10, gcr+0x12 ); +			   iobase + ALS4K_IOB_10_ADLIB_ADDR0, +			   iobase + ALS4K_IOB_12_ADLIB_ADDR2);  	} else {  		if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {  			goto out_err; @@ -831,13 +1026,13 @@ static int snd_als4000_resume(struct pci_dev *pci)  #ifdef SUPPORT_JOYSTICK  	if (acard->gameport) -		snd_als4000_set_addr(acard->gcr, 0, 0, 0, 1); +		snd_als4000_set_addr(acard->iobase, 0, 0, 0, 1);  #endif  	snd_power_change_state(card, SNDRV_CTL_POWER_D0);  	return 0;  } -#endif +#endif /* CONFIG_PM */  static struct pci_driver driver = { diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 457228f..085a52b 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -37,7 +37,7 @@  MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");  MODULE_DESCRIPTION("ATI IXP AC97 controller");  MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400}}"); +MODULE_SUPPORTED_DEVICE("{{ATI,IXP150/200/250/300/400/600}}");  static int index = SNDRV_DEFAULT_IDX1;	/* Index 0-MAX */  static char *id = SNDRV_DEFAULT_STR1;	/* ID for this card */ @@ -290,6 +290,7 @@ static struct pci_device_id snd_atiixp_ids[] = {  	{ 0x1002, 0x4341, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */  	{ 0x1002, 0x4361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB300 */  	{ 0x1002, 0x4370, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */ +	{ 0x1002, 0x4382, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB600 */  	{ 0, }  }; @@ -722,7 +723,9 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)  	struct atiixp_dma *dma = substream->runtime->private_data;  	int err = 0; -	snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL); +	if (snd_BUG_ON(!dma->ops->enable_transfer || +		       !dma->ops->flush_dma)) +		return -EINVAL;  	spin_lock(&chip->reg_lock);  	switch (cmd) { @@ -1032,7 +1035,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,  	struct snd_pcm_runtime *runtime = substream->runtime;  	int err; -	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); +	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) +		return -EINVAL;  	if (dma->opened)  		return -EBUSY; @@ -1064,7 +1068,8 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,  {  	struct atiixp *chip = snd_pcm_substream_chip(substream);  	/* disable DMA bits */ -	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); +	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) +		return -EINVAL;  	spin_lock_irq(&chip->reg_lock);  	dma->ops->enable_dma(chip, 0);  	spin_unlock_irq(&chip->reg_lock); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index d457a32..2f10630 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -674,7 +674,9 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd)  	struct atiixp_dma *dma = substream->runtime->private_data;  	int err = 0; -	snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL); +	if (snd_BUG_ON(!dma->ops->enable_transfer || +		       !dma->ops->flush_dma)) +		return -EINVAL;  	spin_lock(&chip->reg_lock);  	switch(cmd) { @@ -865,7 +867,8 @@ static int snd_atiixp_pcm_open(struct snd_pcm_substream *substream,  		.mask = 0,  	}; -	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); +	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) +		return -EINVAL;  	if (dma->opened)  		return -EBUSY; @@ -895,7 +898,8 @@ static int snd_atiixp_pcm_close(struct snd_pcm_substream *substream,  {  	struct atiixp_modem *chip = snd_pcm_substream_chip(substream);  	/* disable DMA bits */ -	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL); +	if (snd_BUG_ON(!dma->ops || !dma->ops->enable_dma)) +		return -EINVAL;  	spin_lock_irq(&chip->reg_lock);  	dma->ops->enable_dma(chip, 0);  	spin_unlock_irq(&chip->reg_lock); diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index 4aad35b..cf46bba 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -125,7 +125,6 @@ typedef struct {  	/* Virtual page extender stuff */  	int nr_periods;  	int period_bytes; -	struct snd_sg_buf *sgbuf;	/* DMA Scatter Gather struct */  	int period_real;  	int period_virt; @@ -195,16 +194,14 @@ static void vortex_adb_setsrc(vortex_t * vortex, int adbdma,  /* DMA Engines. */  static void vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma, -				     struct snd_sg_buf * sgbuf, int size, -				     int count); +				     int size, int count);  static void vortex_adbdma_setmode(vortex_t * vortex, int adbdma, int ie,  				  int dir, int fmt, int d,  				  u32 offset);  static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb);  #ifndef CHIP_AU8810  static void vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma, -				    struct snd_sg_buf * sgbuf, int size, -				    int count); +				    int size, int count);  static void vortex_wtdma_setmode(vortex_t * vortex, int wtdma, int ie, int fmt, int d,	/*int e, */  				 u32 offset);  static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb); diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 333c62d..b070e57 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -427,7 +427,7 @@ static void vortex_mixer_init(vortex_t * vortex)  	/* Set clipping ceiling (this may be all wrong). */  	/* -	for (x = 0; x > 0x80; x++) { +	for (x = 0; x < 0x80; x++) {  		hwwrite(vortex->mmio, VORTEX_MIXER_CLIP + (x << 2), 0x3ffff);  	}  	*/ @@ -1097,19 +1097,12 @@ static void vortex_adbdma_setstartbuffer(vortex_t * vortex, int adbdma, int sb)  static void  vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma, -			 struct snd_sg_buf * sgbuf, int psize, int count) +			 int psize, int count)  {  	stream_t *dma = &vortex->dma_adb[adbdma]; -	if (sgbuf == NULL) { -		printk(KERN_INFO "vortex: FATAL: sgbuf is NULL!\n"); -		return; -	} -	//printk(KERN_INFO "vortex: page count = %d, tblcount = %d\n", count, sgbuf->tblsize); -  	dma->period_bytes = psize;  	dma->nr_periods = count; -	dma->sgbuf = sgbuf;  	dma->cfg0 = 0;  	dma->cfg1 = 0; @@ -1120,26 +1113,26 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma,  		dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize - 1);  		hwwrite(vortex->mmio,  			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc, -			snd_sgbuf_get_addr(sgbuf, psize * 3)); +			snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));  		/* 3 pages */  	case 3:  		dma->cfg0 |= 0x12000000;  		dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);  		hwwrite(vortex->mmio,  			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8, -			snd_sgbuf_get_addr(sgbuf, psize * 2)); +			snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));  		/* 2 pages */  	case 2:  		dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1);  		hwwrite(vortex->mmio,  			VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4, -			snd_sgbuf_get_addr(sgbuf, psize)); +			snd_pcm_sgbuf_get_addr(dma->substream, psize));  		/* 1 page */  	case 1:  		dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc);  		hwwrite(vortex->mmio,  			VORTEX_ADBDMA_BUFBASE + (adbdma << 4), -			snd_sgbuf_get_addr(sgbuf, 0)); +			snd_pcm_sgbuf_get_addr(dma->substream, 0));  		break;  	}  	//printk("vortex: cfg0 = 0x%x\nvortex: cfg1=0x%x\n", dma->cfg0, dma->cfg1); @@ -1205,7 +1198,7 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma)  			//hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), dma->table[p].addr);  			hwwrite(vortex->mmio,  				VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2), -				snd_sgbuf_get_addr(dma->sgbuf, +				snd_pcm_sgbuf_get_addr(dma->substream,  				dma->period_bytes * p));  			/* Force write thru cache. */  			hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + @@ -1244,7 +1237,10 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) {  			if (pp >= 4)  				pp -= 4;  		} -		hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE+(((adbdma << 2)+pp) << 2), snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p)); +		hwwrite(vortex->mmio, +			VORTEX_ADBDMA_BUFBASE + (((adbdma << 2) + pp) << 2), +			snd_pcm_sgbuf_get_addr(dma->substream, +					       dma->period_bytes * p));  		/* Force write thru cache. */  		hwread(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (((adbdma << 2)+pp) << 2));  	} @@ -1367,13 +1363,12 @@ static void vortex_wtdma_setstartbuffer(vortex_t * vortex, int wtdma, int sb)  static void  vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma, -			struct snd_sg_buf * sgbuf, int psize, int count) +			int psize, int count)  {  	stream_t *dma = &vortex->dma_wt[wtdma];  	dma->period_bytes = psize;  	dma->nr_periods = count; -	dma->sgbuf = sgbuf;  	dma->cfg0 = 0;  	dma->cfg1 = 0; @@ -1383,23 +1378,23 @@ vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma,  	case 4:  		dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1);  		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc, -			snd_sgbuf_get_addr(sgbuf, psize * 3)); +			snd_pcm_sgbuf_get_addr(dma->substream, psize * 3));  		/* 3 pages */  	case 3:  		dma->cfg0 |= 0x12000000;  		dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);  		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4)  + 0x8, -			snd_sgbuf_get_addr(sgbuf, psize * 2)); +			snd_pcm_sgbuf_get_addr(dma->substream, psize * 2));  		/* 2 pages */  	case 2:  		dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1);  		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4, -			snd_sgbuf_get_addr(sgbuf, psize)); +			snd_pcm_sgbuf_get_addr(dma->substream, psize));  		/* 1 page */  	case 1:  		dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc);  		hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4), -			snd_sgbuf_get_addr(sgbuf, 0)); +			snd_pcm_sgbuf_get_addr(dma->substream, 0));  		break;  	}  	hwwrite(vortex->mmio, VORTEX_WTDMA_BUFCFG0 + (wtdma << 3), dma->cfg0); @@ -1465,7 +1460,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)  			hwwrite(vortex->mmio,  				VORTEX_WTDMA_BUFBASE +  				(((wtdma << 2) + pp) << 2), -				snd_sgbuf_get_addr(dma->sgbuf, dma->period_bytes * p)); +				snd_pcm_sgbuf_get_addr(dma->substream, +						       dma->period_bytes * p));  			/* Force write thru cache. */  			hwread(vortex->mmio, VORTEX_WTDMA_BUFBASE +  			       (((wtdma << 2) + pp) << 2)); diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index f9a58b4..b9d2f20 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -189,7 +189,6 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,  {  	vortex_t *chip = snd_pcm_substream_chip(substream);  	stream_t *stream = (stream_t *) (substream->runtime->private_data); -	struct snd_sg_buf *sgbuf;  	int err;  	// Alloc buffer memory. @@ -199,8 +198,6 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,  		printk(KERN_ERR "Vortex: pcm page alloc failed!\n");  		return err;  	} -	//sgbuf = (struct snd_sg_buf *) substream->runtime->dma_private; -	sgbuf = snd_pcm_substream_sgbuf(substream);  	/*  	   printk(KERN_INFO "Vortex: periods %d, period_bytes %d, channels = %d\n", params_periods(hw_params),  	   params_period_bytes(hw_params), params_channels(hw_params)); @@ -226,7 +223,7 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,  		stream = substream->runtime->private_data = &chip->dma_adb[dma];  		stream->substream = substream;  		/* Setup Buffers. */ -		vortex_adbdma_setbuffers(chip, dma, sgbuf, +		vortex_adbdma_setbuffers(chip, dma,  					 params_period_bytes(hw_params),  					 params_periods(hw_params));  	} @@ -240,7 +237,7 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream,  		    &chip->dma_wt[substream->number];  		stream->dma = substream->number;  		stream->substream = substream; -		vortex_wtdma_setbuffers(chip, substream->number, sgbuf, +		vortex_wtdma_setbuffers(chip, substream->number,  					params_period_bytes(hw_params),  					params_periods(hw_params));  	} @@ -392,13 +389,6 @@ static snd_pcm_uframes_t snd_vortex_pcm_pointer(struct snd_pcm_substream *substr  	return (bytes_to_frames(substream->runtime, current_ptr));  } -/* Page callback. */ -/* -static struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset) { -	 -	 -} -*/  /* operators */  static struct snd_pcm_ops snd_vortex_playback_ops = {  	.open = snd_vortex_pcm_open, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 22f18f3..333007c 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -816,7 +816,8 @@ snd_azf3328_mixer_new(struct snd_azf3328 *chip)  	int err;  	snd_azf3328_dbgcallenter(); -	snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip || !chip->card)) +		return -EINVAL;  	card = chip->card; @@ -1471,7 +1472,8 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,  	u8 val;  	unsigned long flags; -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	spin_lock_irqsave(&chip->reg_lock, flags);  	val = snd_azf3328_game_inb(chip, IDX_GAME_LEGACY_COMPATIBLE); diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 4ecdd63..3aa8d97 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -227,7 +227,6 @@ static inline void snd_bt87x_writel(struct snd_bt87x *chip, u32 reg, u32 value)  static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substream *substream,  			       	 unsigned int periods, unsigned int period_bytes)  { -	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);  	unsigned int i, offset;  	u32 *risc; @@ -246,6 +245,7 @@ static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substrea  		rest = period_bytes;  		do {  			u32 cmd, len; +			unsigned int addr;  			len = PAGE_SIZE - (offset % PAGE_SIZE);  			if (len > rest) @@ -260,7 +260,8 @@ static int snd_bt87x_create_risc(struct snd_bt87x *chip, struct snd_pcm_substrea  			if (len == rest)  				cmd |= RISC_EOL | RISC_IRQ;  			*risc++ = cpu_to_le32(cmd); -			*risc++ = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, offset)); +			addr = snd_pcm_sgbuf_get_addr(substream, offset); +			*risc++ = cpu_to_le32(addr);  			offset += len;  			rest -= len;  		} while (rest > 0); diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 6abe8a3..a7d8966 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -254,7 +254,7 @@ static struct snd_ca0106_details ca0106_chip_details[] = {  	   .name   = "MSI K8N Diamond MB",  	   .gpio_type = 2,  	   .i2c_adc = 1, -	   .spi_dac = 2 }, +	   .spi_dac = 2 } ,  	 /* Shuttle XPC SD31P which has an onboard Creative Labs  	  * Sound Blaster Live! 24-bit EAX  	  * high-definition 7.1 audio processor". diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index 893ee4f..c788511 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -125,7 +125,8 @@ static int ca_midi_input_open(struct snd_rawmidi_substream *substream)  	struct snd_ca_midi *midi = substream->rmidi->private_data;  	unsigned long flags; -	snd_assert(midi->dev_id, return -ENXIO); +	if (snd_BUG_ON(!midi->dev_id)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	midi->midi_mode |= CA_MIDI_MODE_INPUT;  	midi->substream_input = substream; @@ -144,7 +145,8 @@ static int ca_midi_output_open(struct snd_rawmidi_substream *substream)  	struct snd_ca_midi *midi = substream->rmidi->private_data;  	unsigned long flags; -	snd_assert(midi->dev_id, return -ENXIO); +	if (snd_BUG_ON(!midi->dev_id)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	midi->midi_mode |= CA_MIDI_MODE_OUTPUT;  	midi->substream_output = substream; @@ -163,7 +165,8 @@ static int ca_midi_input_close(struct snd_rawmidi_substream *substream)  	struct snd_ca_midi *midi = substream->rmidi->private_data;  	unsigned long flags; -	snd_assert(midi->dev_id, return -ENXIO); +	if (snd_BUG_ON(!midi->dev_id)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	midi->interrupt_disable(midi,midi->rx_enable);  	midi->midi_mode &= ~CA_MIDI_MODE_INPUT; @@ -181,7 +184,9 @@ static int ca_midi_output_close(struct snd_rawmidi_substream *substream)  {  	struct snd_ca_midi *midi = substream->rmidi->private_data;  	unsigned long flags; -	snd_assert(midi->dev_id, return -ENXIO); + +	if (snd_BUG_ON(!midi->dev_id)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags); @@ -201,7 +206,9 @@ static int ca_midi_output_close(struct snd_rawmidi_substream *substream)  static void ca_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)  {  	struct snd_ca_midi *midi = substream->rmidi->private_data; -	snd_assert(midi->dev_id, return); + +	if (snd_BUG_ON(!midi->dev_id)) +		return;  	if (up) {  		midi->interrupt_enable(midi,midi->rx_enable); @@ -215,7 +222,8 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int  	struct snd_ca_midi *midi = substream->rmidi->private_data;  	unsigned long flags; -	snd_assert(midi->dev_id, return); +	if (snd_BUG_ON(!midi->dev_id)) +		return;  	if (up) {  		int max = 4; diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 9971b5b..1a74ca6 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2357,7 +2357,8 @@ static int snd_cmipci_uswitch_get(struct snd_kcontrol *kcontrol,  {  	struct cmipci_switch_args *args;  	args = (struct cmipci_switch_args *)kcontrol->private_value; -	snd_assert(args != NULL, return -EINVAL); +	if (snd_BUG_ON(!args)) +		return -EINVAL;  	return _snd_cmipci_uswitch_get(kcontrol, ucontrol, args);  } @@ -2401,7 +2402,8 @@ static int snd_cmipci_uswitch_put(struct snd_kcontrol *kcontrol,  {  	struct cmipci_switch_args *args;  	args = (struct cmipci_switch_args *)kcontrol->private_value; -	snd_assert(args != NULL, return -EINVAL); +	if (snd_BUG_ON(!args)) +		return -EINVAL;  	return _snd_cmipci_uswitch_put(kcontrol, ucontrol, args);  } @@ -2662,7 +2664,8 @@ static int __devinit snd_cmipci_mixer_new(struct cmipci *cm, int pcm_spdif_devic  	unsigned int idx;  	int err; -	snd_assert(cm != NULL && cm->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!cm || !cm->card)) +		return -EINVAL;  	card = cm->card; diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 7556fd9..ef9308f 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -766,13 +766,13 @@ static void snd_cs4281_mode(struct cs4281 *chip, struct cs4281_dma *dma,  	if (!capture) {  		if (dma->left_slot == chip->src_left_play_slot) {  			unsigned int val = snd_cs4281_rate(runtime->rate, NULL); -			snd_assert(dma->right_slot == chip->src_right_play_slot, ); +			snd_BUG_ON(dma->right_slot != chip->src_right_play_slot);  			snd_cs4281_pokeBA0(chip, BA0_DACSR, val);  		}  	} else {  		if (dma->left_slot == chip->src_left_rec_slot) {  			unsigned int val = snd_cs4281_rate(runtime->rate, NULL); -			snd_assert(dma->right_slot == chip->src_right_rec_slot, ); +			snd_BUG_ON(dma->right_slot != chip->src_right_rec_slot);  			snd_cs4281_pokeBA0(chip, BA0_ADCSR, val);  		}  	} @@ -1209,7 +1209,8 @@ static void snd_cs4281_gameport_trigger(struct gameport *gameport)  {  	struct cs4281 *chip = gameport_get_port_data(gameport); -	snd_assert(chip, return); +	if (snd_BUG_ON(!chip)) +		return;  	snd_cs4281_pokeBA0(chip, BA0_JSPT, 0xff);  } @@ -1217,7 +1218,8 @@ static unsigned char snd_cs4281_gameport_read(struct gameport *gameport)  {  	struct cs4281 *chip = gameport_get_port_data(gameport); -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	return snd_cs4281_peekBA0(chip, BA0_JSPT);  } @@ -1228,7 +1230,8 @@ static int snd_cs4281_gameport_cooked_read(struct gameport *gameport,  	struct cs4281 *chip = gameport_get_port_data(gameport);  	unsigned js1, js2, jst; -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	js1 = snd_cs4281_peekBA0(chip, BA0_JSC1);  	js2 = snd_cs4281_peekBA0(chip, BA0_JSC2); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index e214e56..fb6dc39 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -90,9 +90,10 @@ static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip,  	int count;  	unsigned short result,tmp;  	u32 offset = 0; -	snd_assert ( (codec_index == CS46XX_PRIMARY_CODEC_INDEX) || -		     (codec_index == CS46XX_SECONDARY_CODEC_INDEX), -		     return -EINVAL); + +	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX && +		       codec_index != CS46XX_SECONDARY_CODEC_INDEX)) +		return -EINVAL;  	chip->active_ctrl(chip, 1); @@ -212,9 +213,9 @@ static unsigned short snd_cs46xx_ac97_read(struct snd_ac97 * ac97,  	unsigned short val;  	int codec_index = ac97->num; -	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX || -		   codec_index == CS46XX_SECONDARY_CODEC_INDEX, -		   return 0xffff); +	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX && +		       codec_index != CS46XX_SECONDARY_CODEC_INDEX)) +		return 0xffff;  	val = snd_cs46xx_codec_read(chip, reg, codec_index); @@ -229,9 +230,9 @@ static void snd_cs46xx_codec_write(struct snd_cs46xx *chip,  {  	int count; -	snd_assert ((codec_index == CS46XX_PRIMARY_CODEC_INDEX) || -		    (codec_index == CS46XX_SECONDARY_CODEC_INDEX), -		    return); +	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX && +		       codec_index != CS46XX_SECONDARY_CODEC_INDEX)) +		return;  	chip->active_ctrl(chip, 1); @@ -294,9 +295,9 @@ static void snd_cs46xx_ac97_write(struct snd_ac97 *ac97,  	struct snd_cs46xx *chip = ac97->private_data;  	int codec_index = ac97->num; -	snd_assert(codec_index == CS46XX_PRIMARY_CODEC_INDEX || -		   codec_index == CS46XX_SECONDARY_CODEC_INDEX, -		   return); +	if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX && +		       codec_index != CS46XX_SECONDARY_CODEC_INDEX)) +		return;  	snd_cs46xx_codec_write(chip, reg, val, codec_index);  } @@ -315,7 +316,8 @@ int snd_cs46xx_download(struct snd_cs46xx *chip,  	unsigned int bank = offset >> 16;  	offset = offset & 0xffff; -	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL); +	if (snd_BUG_ON((offset & 3) || (len & 3))) +		return -EINVAL;  	dst = chip->region.idx[bank+1].remap_addr + offset;  	len /= sizeof(u32); @@ -343,7 +345,8 @@ int snd_cs46xx_clear_BA1(struct snd_cs46xx *chip,  	unsigned int bank = offset >> 16;  	offset = offset & 0xffff; -	snd_assert(!(offset & 3) && !(len & 3), return -EINVAL); +	if (snd_BUG_ON((offset & 3) || (len & 3))) +		return -EINVAL;  	dst = chip->region.idx[bank+1].remap_addr + offset;  	len /= sizeof(u32); @@ -722,7 +725,9 @@ static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(struct snd_pcm_subst  	struct snd_cs46xx *chip = snd_pcm_substream_chip(substream);  	size_t ptr;  	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data; -	snd_assert (cpcm->pcm_channel,return -ENXIO); + +	if (snd_BUG_ON(!cpcm->pcm_channel)) +		return -ENXIO;  #ifdef CONFIG_SND_CS46XX_NEW_DSP  	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2); @@ -740,7 +745,8 @@ static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(struct snd_pcm_sub  	struct snd_cs46xx_pcm *cpcm = substream->runtime->private_data;  #ifdef CONFIG_SND_CS46XX_NEW_DSP -	snd_assert (cpcm->pcm_channel,return -ENXIO); +	if (snd_BUG_ON(!cpcm->pcm_channel)) +		return -ENXIO;  	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);  #else  	ptr = snd_cs46xx_peek(chip, BA1_PBA); @@ -908,7 +914,8 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,  	cpcm = runtime->private_data;  #ifdef CONFIG_SND_CS46XX_NEW_DSP -	snd_assert (sample_rate != 0, return -ENXIO); +	if (snd_BUG_ON(!sample_rate)) +		return -ENXIO;  	mutex_lock(&chip->spos_mutex); @@ -917,7 +924,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,  		return -ENXIO;  	} -	snd_assert (cpcm->pcm_channel != NULL); +	snd_BUG_ON(!cpcm->pcm_channel);  	if (!cpcm->pcm_channel) {  		mutex_unlock(&chip->spos_mutex);  		return -ENXIO; @@ -952,7 +959,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,  		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {  			substream->ops = &snd_cs46xx_playback_iec958_ops;  		} else { -			snd_assert(0); +			snd_BUG();  		}  #else  		substream->ops = &snd_cs46xx_playback_ops; @@ -981,7 +988,7 @@ static int snd_cs46xx_playback_hw_params(struct snd_pcm_substream *substream,  		} else if (cpcm->pcm_channel_id == DSP_IEC958_CHANNEL) {  			substream->ops = &snd_cs46xx_playback_indirect_iec958_ops;  		} else { -			snd_assert(0); +			snd_BUG();  		}  #else  		substream->ops = &snd_cs46xx_playback_indirect_ops; @@ -1029,7 +1036,8 @@ static int snd_cs46xx_playback_prepare(struct snd_pcm_substream *substream)  	cpcm = runtime->private_data;  #ifdef CONFIG_SND_CS46XX_NEW_DSP -    snd_assert (cpcm->pcm_channel != NULL, return -ENXIO); +	if (snd_BUG_ON(!cpcm->pcm_channel)) +		return -ENXIO;  	pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );  	pfie &= ~0x0000f03f; @@ -1714,9 +1722,9 @@ static void snd_cs46xx_mixer_free_ac97(struct snd_ac97 *ac97)  {  	struct snd_cs46xx *chip = ac97->private_data; -	snd_assert ((ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) || -		    (ac97 == chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]), -		    return); +	if (snd_BUG_ON(ac97 != chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] && +		       ac97 != chip->ac97[CS46XX_SECONDARY_CODEC_INDEX])) +		return;  	if (ac97 == chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]) {  		chip->ac97[CS46XX_PRIMARY_CODEC_INDEX] = NULL; @@ -1864,7 +1872,7 @@ static int snd_cs46xx_iec958_put(struct snd_kcontrol *kcontrol,  		break;  	default:  		res = -EINVAL; -		snd_assert(0, (void)0); +		snd_BUG(); /* should never happen ... */  	}  	return res; @@ -2236,7 +2244,7 @@ static void snd_cs46xx_codec_reset (struct snd_ac97 * ac97)  		snd_printdd("cs46xx: CODOEC2 mode %04x\n",0x3);  		snd_cs46xx_ac97_write(ac97,AC97_CSR_ACMODE,0x3);  	} else { -		snd_assert(0); /* should never happen ... */ +		snd_BUG(); /* should never happen ... */  	}  	udelay(50); @@ -2553,7 +2561,8 @@ static void snd_cs46xx_gameport_trigger(struct gameport *gameport)  {  	struct snd_cs46xx *chip = gameport_get_port_data(gameport); -	snd_assert(chip, return); +	if (snd_BUG_ON(!chip)) +		return;  	snd_cs46xx_pokeBA0(chip, BA0_JSPT, 0xFF);  //outb(gameport->io, 0xFF);  } @@ -2561,7 +2570,8 @@ static unsigned char snd_cs46xx_gameport_read(struct gameport *gameport)  {  	struct snd_cs46xx *chip = gameport_get_port_data(gameport); -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	return snd_cs46xx_peekBA0(chip, BA0_JSPT); //inb(gameport->io);  } @@ -2570,7 +2580,8 @@ static int snd_cs46xx_gameport_cooked_read(struct gameport *gameport, int *axes,  	struct snd_cs46xx *chip = gameport_get_port_data(gameport);  	unsigned js1, js2, jst; -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	js1 = snd_cs46xx_peekBA0(chip, BA0_JSC1);  	js2 = snd_cs46xx_peekBA0(chip, BA0_JSC2); @@ -2754,7 +2765,8 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)  {  	int idx; -	snd_assert(chip != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip)) +		return -EINVAL;  	if (chip->active_ctrl)  		chip->active_ctrl(chip, 1); @@ -3489,8 +3501,9 @@ static struct cs_card_type __devinitdata cards[] = {  		.name = "Mitac MI6020/21",  		.amp = amp_voyetra,  	}, +	/* Hercules Game Theatre XP */  	{ -		.vendor = 0x14AF, +		.vendor = 0x14af, /* Guillemot Corporation */  		.id = 0x0050,  		.name = "Hercules Game Theatre XP",  		.amp = amp_hercules, @@ -3532,9 +3545,25 @@ static struct cs_card_type __devinitdata cards[] = {  		.amp = amp_hercules,  		.mixer_init = hercules_mixer_init,  	}, +	/* Herculess Fortissimo */ +	{ +		.vendor = 0x1681, +		.id = 0xa010, +		.name = "Hercules Gamesurround Fortissimo II", +	}, +	{ +		.vendor = 0x1681, +		.id = 0xa011, +		.name = "Hercules Gamesurround Fortissimo III 7.1", +	},  	/* Teratec */  	{  		.vendor = 0x153b, +		.id = 0x112e, +		.name = "Terratec DMX XFire 1024", +	}, +	{ +		.vendor = 0x153b,  		.id = 0x1136,  		.name = "Terratec SiXPack 5.1",  	}, diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index ccc8bed..f4f0c8f 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -63,7 +63,8 @@ static int shadow_and_reallocate_code (struct snd_cs46xx * chip, u32 * data, u32  	u32 mop_operands,mop_type,wide_op;  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert( ((size % 2) == 0), return -EINVAL); +	if (snd_BUG_ON(size %2)) +		return -EINVAL;  	while (i < size) {  		loval = data[i++]; @@ -289,7 +290,8 @@ void  cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)  	int i;  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert(ins != NULL, return); +	if (snd_BUG_ON(!ins)) +		return;  	mutex_lock(&chip->spos_mutex);  	for (i = 0; i < ins->nscb; ++i) { @@ -404,7 +406,8 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m  		/* if module has a code segment it must have  		   symbol table */ -		snd_assert(module->symbol_table.symbols != NULL ,return -ENOMEM); +		if (snd_BUG_ON(!module->symbol_table.symbols)) +			return -ENOMEM;  		if (add_symbols(chip,module)) {  			snd_printk(KERN_ERR "dsp_spos: failed to load symbol table\n");  			return -ENOMEM; @@ -1369,7 +1372,8 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)  	valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV); -	snd_assert (chip->nr_ac97_codecs == 1 || chip->nr_ac97_codecs == 2); +	if (snd_BUG_ON(chip->nr_ac97_codecs != 1 && chip->nr_ac97_codecs != 2)) +		goto _fail_end;  	if (chip->nr_ac97_codecs == 1) {  		/* output on slot 5 and 11  @@ -1609,11 +1613,14 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,  		spdifo_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFOSCB",(u32 *)&spdifo_scb,SPDIFO_SCB_INST); -		snd_assert(spdifo_scb_desc, return -EIO); +		if (snd_BUG_ON(!spdifo_scb_desc)) +			return -EIO;  		spdifi_scb_desc = cs46xx_dsp_create_scb(chip,"SPDIFISCB",(u32 *)&spdifi_scb,SPDIFI_SCB_INST); -		snd_assert(spdifi_scb_desc, return -EIO); +		if (snd_BUG_ON(!spdifi_scb_desc)) +			return -EIO;  		async_codec_scb_desc = cs46xx_dsp_create_scb(chip,"AsynCodecInputSCB",(u32 *)&async_codec_input_scb, HFG_TREE_SCB); -		snd_assert(async_codec_scb_desc, return -EIO); +		if (snd_BUG_ON(!async_codec_scb_desc)) +			return -EIO;  		async_codec_scb_desc->parent_scb_ptr = NULL;  		async_codec_scb_desc->next_scb_ptr = spdifi_scb_desc; @@ -1698,8 +1705,10 @@ int cs46xx_dsp_enable_spdif_in (struct snd_cs46xx *chip)  	chip->active_ctrl(chip, 1);  	chip->amplifier_ctrl(chip, 1); -	snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL); -	snd_assert (ins->spdif_in_src != NULL,return -EINVAL); +	if (snd_BUG_ON(ins->asynch_rx_scb)) +		return -EINVAL; +	if (snd_BUG_ON(!ins->spdif_in_src)) +		return -EINVAL;  	mutex_lock(&chip->spos_mutex); @@ -1754,8 +1763,10 @@ int cs46xx_dsp_disable_spdif_in (struct snd_cs46xx *chip)  {  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL); -	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);	 +	if (snd_BUG_ON(!ins->asynch_rx_scb)) +		return -EINVAL; +	if (snd_BUG_ON(!ins->spdif_in_src)) +		return -EINVAL;  	mutex_lock(&chip->spos_mutex); @@ -1780,8 +1791,10 @@ int cs46xx_dsp_enable_pcm_capture (struct snd_cs46xx *chip)  {  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert (ins->pcm_input == NULL,return -EINVAL); -	snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL); +	if (snd_BUG_ON(ins->pcm_input)) +		return -EINVAL; +	if (snd_BUG_ON(!ins->ref_snoop_scb)) +		return -EINVAL;  	mutex_lock(&chip->spos_mutex);  	ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR, @@ -1795,7 +1808,8 @@ int cs46xx_dsp_disable_pcm_capture (struct snd_cs46xx *chip)  {  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert (ins->pcm_input != NULL,return -EINVAL); +	if (snd_BUG_ON(!ins->pcm_input)) +		return -EINVAL;  	mutex_lock(&chip->spos_mutex);  	cs46xx_dsp_remove_scb (chip,ins->pcm_input); @@ -1809,8 +1823,10 @@ int cs46xx_dsp_enable_adc_capture (struct snd_cs46xx *chip)  {  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert (ins->adc_input == NULL,return -EINVAL); -	snd_assert (ins->codec_in_scb != NULL,return -EINVAL); +	if (snd_BUG_ON(ins->adc_input)) +		return -EINVAL; +	if (snd_BUG_ON(!ins->codec_in_scb)) +		return -EINVAL;  	mutex_lock(&chip->spos_mutex);  	ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR, @@ -1824,7 +1840,8 @@ int cs46xx_dsp_disable_adc_capture (struct snd_cs46xx *chip)  {  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert (ins->adc_input != NULL,return -EINVAL); +	if (snd_BUG_ON(!ins->adc_input)) +		return -EINVAL;  	mutex_lock(&chip->spos_mutex);  	cs46xx_dsp_remove_scb (chip,ins->adc_input); diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 2873cfe..dd7c41b 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -46,8 +46,11 @@ static void remove_symbol (struct snd_cs46xx * chip, struct dsp_symbol_entry * s  	struct dsp_spos_instance * ins = chip->dsp_spos_instance;  	int symbol_index = (int)(symbol - ins->symbol_table.symbols); -	snd_assert(ins->symbol_table.nsymbols > 0,return); -	snd_assert(symbol_index >= 0 && symbol_index < ins->symbol_table.nsymbols, return); +	if (snd_BUG_ON(ins->symbol_table.nsymbols <= 0)) +		return; +	if (snd_BUG_ON(symbol_index < 0 || +		       symbol_index >= ins->symbol_table.nsymbols)) +		return;  	ins->symbol_table.symbols[symbol_index].deleted = 1; @@ -116,8 +119,9 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor  	if ( scb->parent_scb_ptr ) {  		/* unlink parent SCB */ -		snd_assert ((scb->parent_scb_ptr->sub_list_ptr == scb || -			     scb->parent_scb_ptr->next_scb_ptr == scb),return); +		if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr != scb && +			       scb->parent_scb_ptr->next_scb_ptr != scb)) +			return;  		if (scb->parent_scb_ptr->sub_list_ptr == scb) { @@ -140,7 +144,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor  				scb->next_scb_ptr = ins->the_null_scb;  			}  		} else { -			/* snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return); */  			scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr;  			if (scb->next_scb_ptr != ins->the_null_scb) { @@ -181,16 +184,17 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *  	unsigned long flags;  	/* check integrety */ -	snd_assert ( (scb->index >= 0 &&  -		      scb->index < ins->nscb &&  -		      (ins->scbs + scb->index) == scb), return ); +	if (snd_BUG_ON(scb->index < 0 || +		       scb->index >= ins->nscb || +		       (ins->scbs + scb->index) != scb)) +		return;  #if 0  	/* can't remove a SCB with childs before   	   removing childs first  */ -	snd_assert ( (scb->sub_list_ptr == ins->the_null_scb && -		      scb->next_scb_ptr == ins->the_null_scb), -		     goto _end); +	if (snd_BUG_ON(scb->sub_list_ptr != ins->the_null_scb || +		       scb->next_scb_ptr != ins->the_null_scb)) +		goto _end;  #endif  	spin_lock_irqsave(&scb->lock, flags); @@ -198,7 +202,8 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *  	spin_unlock_irqrestore(&scb->lock, flags);  	cs46xx_dsp_proc_free_scb_desc(scb); -	snd_assert (scb->scb_symbol != NULL, return ); +	if (snd_BUG_ON(!scb->scb_symbol)) +		return;  	remove_symbol (chip,scb->scb_symbol);  	ins->scbs[scb->index].deleted = 1; @@ -234,7 +239,6 @@ void cs46xx_dsp_proc_free_scb_desc (struct dsp_scb_descriptor * scb)  		snd_info_free_entry(scb->proc_info);  		scb->proc_info = NULL; -		snd_assert (scb_info != NULL, return);  		kfree (scb_info);  	}  } @@ -291,7 +295,8 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u  	unsigned long flags; -	snd_assert (ins->the_null_scb != NULL,return NULL); +	if (snd_BUG_ON(!ins->the_null_scb)) +		return NULL;  	/* fill the data that will be wroten to DSP */  	scb_data[SCBsubListPtr] =  @@ -321,18 +326,20 @@ _dsp_create_generic_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u  #endif  		/* link to  parent SCB */  		if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) { -			snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb), -				     return NULL); +			if (snd_BUG_ON(scb->parent_scb_ptr->next_scb_ptr != +				       ins->the_null_scb)) +				return NULL;  			scb->parent_scb_ptr->next_scb_ptr = scb;  		} else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) { -			snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb), -				     return NULL); +			if (snd_BUG_ON(scb->parent_scb_ptr->sub_list_ptr != +				       ins->the_null_scb)) +				return NULL;  			scb->parent_scb_ptr->sub_list_ptr = scb;  		} else { -			snd_assert (0,return NULL); +			snd_BUG();  		}  		spin_lock_irqsave(&chip->reg_lock, flags); @@ -675,7 +682,7 @@ cs46xx_dsp_create_src_task_scb(struct snd_cs46xx * chip, char * scb_name,  		if (pass_through) {  			/* wont work with any other rate than  			   the native DSP rate */ -			snd_assert (rate == 48000); +			snd_BUG_ON(rate != 48000);  			scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,  							    dest,"DMAREADER",parent_scb, @@ -1142,7 +1149,8 @@ find_next_free_scb (struct snd_cs46xx * chip, struct dsp_scb_descriptor * from)  	struct dsp_scb_descriptor * scb = from;  	while (scb->next_scb_ptr != ins->the_null_scb) { -		snd_assert (scb->next_scb_ptr != NULL, return NULL); +		if (snd_BUG_ON(!scb->next_scb_ptr)) +			return NULL;  		scb = scb->next_scb_ptr;  	} @@ -1246,10 +1254,11 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,  		break;  	case DSP_PCM_S71_CHANNEL:  		/* TODO */ -		snd_assert(0); +		snd_BUG();  		break;  	case DSP_IEC958_CHANNEL: -		snd_assert (ins->asynch_tx_scb != NULL, return NULL); +		if (snd_BUG_ON(!ins->asynch_tx_scb)) +			return NULL;  		mixer_scb = ins->asynch_tx_scb;  		/* if sample rate is set to 48khz we pass @@ -1262,7 +1271,7 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,  		}  		break;  	default: -		snd_assert (0); +		snd_BUG();  		return NULL;  	}  	/* default sample rate is 44100 */ @@ -1308,7 +1317,8 @@ cs46xx_dsp_create_pcm_channel (struct snd_cs46xx * chip,  				break;  			}  		} -		snd_assert (src_index != -1,return NULL); +		if (snd_BUG_ON(src_index == -1)) +			return NULL;  		/* we need to create a new SRC SCB */  		if (mixer_scb->sub_list_ptr == ins->the_null_scb) { @@ -1462,9 +1472,10 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,  	struct dsp_spos_instance * ins = chip->dsp_spos_instance;  	unsigned long flags; -	snd_assert(pcm_channel->active, return ); -	snd_assert(ins->npcm_channels > 0, return ); -	snd_assert(pcm_channel->src_scb->ref_count > 0, return ); +	if (snd_BUG_ON(!pcm_channel->active || +		       ins->npcm_channels <= 0 || +		       pcm_channel->src_scb->ref_count <= 0)) +		return;  	spin_lock_irqsave(&chip->reg_lock, flags);  	pcm_channel->unlinked = 1; @@ -1479,8 +1490,9 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,  	if (!pcm_channel->src_scb->ref_count) {  		cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb); -		snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot < DSP_MAX_SRC_NR, -			    return ); +		if (snd_BUG_ON(pcm_channel->src_slot < 0 || +			       pcm_channel->src_slot >= DSP_MAX_SRC_NR)) +			return;  		ins->src_scb_slots[pcm_channel->src_slot] = 0;  		ins->nsrc_scb --; @@ -1490,11 +1502,11 @@ void cs46xx_dsp_destroy_pcm_channel (struct snd_cs46xx * chip,  int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,  			   struct dsp_pcm_channel_descriptor * pcm_channel)  { -	struct dsp_spos_instance * ins = chip->dsp_spos_instance;  	unsigned long flags; -	snd_assert(pcm_channel->active,return -EIO); -	snd_assert(ins->npcm_channels > 0,return -EIO); +	if (snd_BUG_ON(!pcm_channel->active || +		       chip->dsp_spos_instance->npcm_channels <= 0)) +		return -EIO;  	spin_lock(&pcm_channel->src_scb->lock); @@ -1537,7 +1549,7 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,  	src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb; -	snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; ); +	snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr);  	pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;  	spin_lock_irqsave(&chip->reg_lock, flags); @@ -1564,7 +1576,8 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s  	struct dsp_scb_descriptor * pcm_input;  	int insert_point; -	snd_assert (ins->record_mixer_scb != NULL,return NULL); +	if (snd_BUG_ON(!ins->record_mixer_scb)) +		return NULL;  	if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) {  		parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr); @@ -1583,7 +1596,8 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s  int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)  { -	snd_assert (src->parent_scb_ptr != NULL,  return -EINVAL ); +	if (snd_BUG_ON(!src->parent_scb_ptr)) +		return -EINVAL;  	/* mute SCB */  	cs46xx_dsp_scb_set_volume (chip,src,0,0); @@ -1598,8 +1612,10 @@ int cs46xx_src_link(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)  	struct dsp_spos_instance * ins = chip->dsp_spos_instance;  	struct dsp_scb_descriptor * parent_scb; -	snd_assert (src->parent_scb_ptr == NULL,   return -EINVAL ); -	snd_assert(ins->master_mix_scb !=NULL,   return -EINVAL ); +	if (snd_BUG_ON(src->parent_scb_ptr)) +		return -EINVAL; +	if (snd_BUG_ON(!ins->master_mix_scb)) +		return -EINVAL;  	if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {  		parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr); @@ -1635,8 +1651,11 @@ int cs46xx_dsp_enable_spdif_out (struct snd_cs46xx *chip)  		return -EBUSY;  	} -	snd_assert (ins->asynch_tx_scb == NULL, return -EINVAL); -	snd_assert (ins->master_mix_scb->next_scb_ptr == ins->the_null_scb, return -EINVAL); +	if (snd_BUG_ON(ins->asynch_tx_scb)) +		return -EINVAL; +	if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr != +		       ins->the_null_scb)) +		return -EINVAL;  	/* reset output snooper sample buffer pointer */  	snd_cs46xx_poke (chip, (ins->ref_snoop_scb->address + 2) << 2, @@ -1676,10 +1695,15 @@ int  cs46xx_dsp_disable_spdif_out (struct snd_cs46xx *chip)  	}  	/* check integrety */ -	snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL); -	snd_assert (ins->spdif_pcm_input_scb != NULL,return -EINVAL); -	snd_assert (ins->master_mix_scb->next_scb_ptr == ins->asynch_tx_scb, return -EINVAL); -	snd_assert (ins->asynch_tx_scb->parent_scb_ptr == ins->master_mix_scb, return -EINVAL); +	if (snd_BUG_ON(!ins->asynch_tx_scb)) +		return -EINVAL; +	if (snd_BUG_ON(!ins->spdif_pcm_input_scb)) +		return -EINVAL; +	if (snd_BUG_ON(ins->master_mix_scb->next_scb_ptr != ins->asynch_tx_scb)) +		return -EINVAL; +	if (snd_BUG_ON(ins->asynch_tx_scb->parent_scb_ptr != +		       ins->master_mix_scb)) +		return -EINVAL;  	cs46xx_dsp_remove_scb (chip,ins->spdif_pcm_input_scb);  	cs46xx_dsp_remove_scb (chip,ins->asynch_tx_scb); @@ -1734,7 +1758,8 @@ int cs46xx_iec958_post_close (struct snd_cs46xx *chip)  {  	struct dsp_spos_instance * ins = chip->dsp_spos_instance; -	snd_assert (ins->asynch_tx_scb != NULL, return -EINVAL); +	if (snd_BUG_ON(!ins->asynch_tx_scb)) +		return -EINVAL;  	ins->spdif_status_out  &= ~DSP_SPDIF_STATUS_PLAYBACK_OPEN; diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c index 4159e3b..2904330 100644 --- a/sound/pci/echoaudio/darla20_dsp.c +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -34,7 +34,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Darla20\n")); -	snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c index 79938ee..6022873 100644 --- a/sound/pci/echoaudio/darla24_dsp.c +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -34,7 +34,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Darla24\n")); -	snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -148,8 +149,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)  static int set_input_clock(struct echoaudio *chip, u16 clock)  { -	snd_assert(clock == ECHO_CLOCK_INTERNAL || -		   clock == ECHO_CLOCK_ESYNC, return -EINVAL); +	if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL && +		       clock != ECHO_CLOCK_ESYNC)) +		return -EINVAL;  	chip->input_clock = clock;  	return set_sample_rate(chip, chip->sample_rate);  } diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index 48eb7c5..417e25a 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -47,7 +47,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	local_irq_enable();  	DE_INIT(("init_hw() - Echo3G\n")); -	snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -104,9 +105,11 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	if ((err = init_line_levels(chip)) < 0)  		return err;  	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); -	snd_assert(err >= 0, return err); +	if (err < 0) +		return err;  	err = set_phantom_power(chip, 0); -	snd_assert(err >= 0, return err); +	if (err < 0) +		return err;  	err = set_professional_spdif(chip, TRUE);  	DE_INIT(("init_hw done\n")); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index e16dc92..8dbc5c4 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -490,7 +490,6 @@ static int init_engine(struct snd_pcm_substream *substream,  {  	struct echoaudio *chip;  	int err, per, rest, page, edge, offs; -	struct snd_sg_buf *sgbuf;  	struct audiopipe *pipe;  	chip = snd_pcm_substream_chip(substream); @@ -503,7 +502,7 @@ static int init_engine(struct snd_pcm_substream *substream,  	if (pipe->index >= 0) {  		DE_HWP(("hwp_ie free(%d)\n", pipe->index));  		err = free_pipes(chip, pipe); -		snd_assert(!err); +		snd_BUG_ON(err);  		chip->substream[pipe->index] = NULL;  	} @@ -531,10 +530,6 @@ static int init_engine(struct snd_pcm_substream *substream,  		return err;  	} -	sgbuf = snd_pcm_substream_sgbuf(substream); - -	DE_HWP(("pcm_hw_params table size=%d pages=%d\n", -		sgbuf->size, sgbuf->pages));  	sglist_init(chip, pipe);  	edge = PAGE_SIZE;  	for (offs = page = per = 0; offs < params_buffer_bytes(hw_params); @@ -543,16 +538,15 @@ static int init_engine(struct snd_pcm_substream *substream,  		if (offs + rest > params_buffer_bytes(hw_params))  			rest = params_buffer_bytes(hw_params) - offs;  		while (rest) { +			dma_addr_t addr; +			addr = snd_pcm_sgbuf_get_addr(substream, offs);  			if (rest <= edge - offs) { -				sglist_add_mapping(chip, pipe, -						   snd_sgbuf_get_addr(sgbuf, offs), -						   rest); +				sglist_add_mapping(chip, pipe, addr, rest);  				sglist_add_irq(chip, pipe);  				offs += rest;  				rest = 0;  			} else { -				sglist_add_mapping(chip, pipe, -						   snd_sgbuf_get_addr(sgbuf, offs), +				sglist_add_mapping(chip, pipe, addr,  						   edge - offs);  				rest -= edge - offs;  				offs = edge; @@ -690,8 +684,10 @@ static int pcm_prepare(struct snd_pcm_substream *substream)  		return -EINVAL;  	} -	snd_assert(pipe_index < px_num(chip), return -EINVAL); -	snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL); +	if (snd_BUG_ON(pipe_index >= px_num(chip))) +		return -EINVAL; +	if (snd_BUG_ON(!is_pipe_allocated(chip, pipe_index))) +		return -EINVAL;  	set_audio_format(chip, pipe_index, &format);  	return 0;  } diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index 52a9331..c3736bb 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -103,9 +103,11 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)  	int err, i, o;  	/* All audio channels must be closed before changing the digital mode */ -	snd_assert(!chip->pipe_alloc_mask, return -EAGAIN); +	if (snd_BUG_ON(chip->pipe_alloc_mask)) +		return -EAGAIN; -	snd_assert(chip->digital_modes & (1 << mode), return -EINVAL); +	if (snd_BUG_ON(!(chip->digital_modes & (1 << mode)))) +		return -EINVAL;  	previous_mode = chip->digital_mode;  	err = dsp_set_digital_mode(chip, mode); @@ -267,8 +269,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)  		return 0;  	} -	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, -		   return -EINVAL); +	if (snd_BUG_ON(rate >= 50000 && +		       chip->digital_mode == DIGITAL_MODE_ADAT)) +		return -EINVAL;  	clock = 0;  	control_reg = le32_to_cpu(chip->comm_page->control_register); diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index e6c1007..be0e181 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -474,7 +474,8 @@ static int load_firmware(struct echoaudio *chip)  	const struct firmware *fw;  	int box_type, err; -	snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM); +	if (snd_BUG_ON(!chip->dsp_code_to_load || !chip->comm_page)) +		return -EPERM;  	/* See if the ASIC is present and working - only if the DSP is already loaded */  	if (chip->dsp_code) { @@ -512,8 +513,8 @@ static int load_firmware(struct echoaudio *chip)  /* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */  static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)  { -	snd_assert(index < num_busses_out(chip) + num_busses_in(chip), -		   return -EINVAL); +	if (snd_BUG_ON(index >= num_busses_out(chip) + num_busses_in(chip))) +		return -EINVAL;  	/* Wait for the handshake (OK even if ASIC is not loaded) */  	if (wait_handshake(chip)) @@ -536,7 +537,8 @@ static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)  /* Set the gain for a single physical output channel (dB). */  static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)  { -	snd_assert(channel < num_busses_out(chip), return -EINVAL); +	if (snd_BUG_ON(channel >= num_busses_out(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; @@ -554,8 +556,9 @@ static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)  static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input,  			    s8 gain)  { -	snd_assert(output < num_busses_out(chip) && -		   input < num_busses_in(chip), return -EINVAL); +	if (snd_BUG_ON(output >= num_busses_out(chip) || +		    input >= num_busses_in(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; @@ -1065,8 +1068,10 @@ static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe)  	int i;  	DE_ACT(("free_pipes: Pipe %d\n", pipe->index)); -	snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL); -	snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL); +	if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index))) +		return -EINVAL; +	if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED)) +		return -EINVAL;  	for (channel_mask = i = 0; i < pipe->interleave; i++)  		channel_mask |= 1 << (pipe->index + i); diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c index 3aa37e7..afa2733 100644 --- a/sound/pci/echoaudio/echoaudio_gml.c +++ b/sound/pci/echoaudio/echoaudio_gml.c @@ -112,9 +112,11 @@ static int set_digital_mode(struct echoaudio *chip, u8 mode)  		return -EIO;  	/* All audio channels must be closed before changing the digital mode */ -	snd_assert(!chip->pipe_alloc_mask, return -EAGAIN); +	if (snd_BUG_ON(chip->pipe_alloc_mask)) +		return -EAGAIN; -	snd_assert(chip->digital_modes & (1 << mode), return -EINVAL); +	if (snd_BUG_ON(!(chip->digital_modes & (1 << mode)))) +		return -EINVAL;  	previous_mode = chip->digital_mode;  	err = dsp_set_digital_mode(chip, mode); diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c index 2757c89..db6c952 100644 --- a/sound/pci/echoaudio/gina20_dsp.c +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -38,7 +38,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Gina20\n")); -	snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -177,7 +178,8 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)  /* Set input bus gain (one unit is 0.5dB !) */  static int set_input_gain(struct echoaudio *chip, u16 input, int gain)  { -	snd_assert(input < num_busses_in(chip), return -EINVAL); +	if (snd_BUG_ON(input >= num_busses_in(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c index 144fc56..2fef37a 100644 --- a/sound/pci/echoaudio/gina24_dsp.c +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -43,7 +43,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Gina24\n")); -	snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -84,7 +85,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	if ((err = init_line_levels(chip)) < 0)  		return err;  	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); -	snd_assert(err >= 0, return err); +	if (err < 0) +		return err;  	err = set_professional_spdif(chip, TRUE);  	DE_INIT(("init_hw done\n")); @@ -163,8 +165,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)  {  	u32 control_reg, clock; -	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, -		   return -EINVAL); +	if (snd_BUG_ON(rate >= 50000 && +		       chip->digital_mode == DIGITAL_MODE_ADAT)) +		return -EINVAL;  	/* Only set the clock for internal mode. */  	if (chip->input_clock != ECHO_CLOCK_INTERNAL) { diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c index d6ac773..f05e39f 100644 --- a/sound/pci/echoaudio/indigo_dsp.c +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -39,7 +39,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Indigo\n")); -	snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -143,8 +144,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,  {  	int index; -	snd_assert(pipe < num_pipes_out(chip) && -		   output < num_busses_out(chip), return -EINVAL); +	if (snd_BUG_ON(pipe >= num_pipes_out(chip) || +		       output >= num_busses_out(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c index 500e150..90730a5 100644 --- a/sound/pci/echoaudio/indigodj_dsp.c +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -39,7 +39,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Indigo DJ\n")); -	snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -143,8 +144,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,  {  	int index; -	snd_assert(pipe < num_pipes_out(chip) && -		   output < num_busses_out(chip), return -EINVAL); +	if (snd_BUG_ON(pipe >= num_pipes_out(chip) || +		       output >= num_busses_out(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c index f3ad13d..a7e09ec 100644 --- a/sound/pci/echoaudio/indigoio_dsp.c +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -39,7 +39,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Indigo IO\n")); -	snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -114,8 +115,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,  {  	int index; -	snd_assert(pipe < num_pipes_out(chip) && -		   output < num_busses_out(chip), return -EINVAL); +	if (snd_BUG_ON(pipe >= num_pipes_out(chip) || +		       output >= num_busses_out(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c index 990c9a6..ede75c6 100644 --- a/sound/pci/echoaudio/layla20_dsp.c +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -42,7 +42,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Layla20\n")); -	snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -155,7 +156,8 @@ static int load_asic(struct echoaudio *chip)  static int set_sample_rate(struct echoaudio *chip, u32 rate)  { -	snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL); +	if (snd_BUG_ON(rate < 8000 || rate > 50000)) +		return -EINVAL;  	/* Only set the clock for internal mode. Do not return failure,  	   simply treat it as a non-event. */ @@ -252,7 +254,8 @@ static int set_output_clock(struct echoaudio *chip, u16 clock)  /* Set input bus gain (one unit is 0.5dB !) */  static int set_input_gain(struct echoaudio *chip, u16 input, int gain)  { -	snd_assert(input < num_busses_in(chip), return -EINVAL); +	if (snd_BUG_ON(input >= num_busses_in(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index 97e42e1..d61b5cb 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -42,7 +42,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Layla24\n")); -	snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -73,7 +74,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  		return err;  	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); -	snd_assert(err >= 0, return err); +	if (err < 0) +		return err;  	err = set_professional_spdif(chip, TRUE);  	DE_INIT(("init_hw done\n")); @@ -158,8 +160,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)  {  	u32 control_reg, clock, base_rate; -	snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, -		   return -EINVAL); +	if (snd_BUG_ON(rate >= 50000 && +		       chip->digital_mode == DIGITAL_MODE_ADAT)) +		return -EINVAL;  	/* Only set the clock for internal mode. */  	if (chip->input_clock != ECHO_CLOCK_INTERNAL) { diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c index 891c705..2273866 100644 --- a/sound/pci/echoaudio/mia_dsp.c +++ b/sound/pci/echoaudio/mia_dsp.c @@ -42,7 +42,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Mia\n")); -	snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -161,8 +162,9 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)  static int set_input_clock(struct echoaudio *chip, u16 clock)  {  	DE_ACT(("set_input_clock(%d)\n", clock)); -	snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF, -		   return -EINVAL); +	if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL && +		       clock != ECHO_CLOCK_SPDIF)) +		return -EINVAL;  	chip->input_clock = clock;  	return set_sample_rate(chip, chip->sample_rate); @@ -176,8 +178,9 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,  {  	int index; -	snd_assert(pipe < num_pipes_out(chip) && -		   output < num_busses_out(chip), return -EINVAL); +	if (snd_BUG_ON(pipe >= num_pipes_out(chip) || +		       output >= num_busses_out(chip))) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index 91f5bff..77bf2a8 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -59,7 +59,8 @@ static int enable_midi_input(struct echoaudio *chip, char enable)  Returns how many actually written or < 0 on error */  static int write_midi(struct echoaudio *chip, u8 *data, int bytes)  { -	snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL); +	if (snd_BUG_ON(bytes <= 0 || bytes >= MIDI_OUT_BUFFER_SIZE)) +		return -EINVAL;  	if (wait_handshake(chip))  		return -EIO; @@ -119,7 +120,8 @@ static int midi_service_irq(struct echoaudio *chip)  	/* The count is at index 0, followed by actual data */  	count = le16_to_cpu(chip->comm_page->midi_input[0]); -	snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0); +	if (snd_BUG_ON(count >= MIDI_IN_BUFFER_SIZE)) +		return 0;  	/* Get the MIDI data from the comm page */  	i = 1; diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c index c0b4bf0..eaa619b 100644 --- a/sound/pci/echoaudio/mona_dsp.c +++ b/sound/pci/echoaudio/mona_dsp.c @@ -43,7 +43,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  	int err;  	DE_INIT(("init_hw() - Mona\n")); -	snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV); +	if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA)) +		return -ENODEV;  	if ((err = init_dsp_comm_page(chip))) {  		DE_INIT(("init_hw - could not initialize DSP comm page\n")); @@ -79,7 +80,8 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)  		return err;  	err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); -	snd_assert(err >= 0, return err); +	if (err < 0) +		return err;  	err = set_professional_spdif(chip, TRUE);  	DE_INIT(("init_hw done\n")); diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index 45088eb..0e649dc 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -145,7 +145,8 @@ terminate_voice(struct snd_emux_voice *vp)  {  	struct snd_emu10k1 *hw; -	snd_assert(vp, return); +	if (snd_BUG_ON(!vp)) +		return;  	hw = vp->hw;  	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);  	if (vp->block) { @@ -325,7 +326,8 @@ start_voice(struct snd_emux_voice *vp)  	hw = vp->hw;  	ch = vp->ch; -	snd_assert(ch >= 0, return -EINVAL); +	if (snd_BUG_ON(ch < 0)) +		return -EINVAL;  	chan = vp->chan;  	emem = (struct snd_emu10k1_memblk *)vp->block; diff --git a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c index 42bae6f..e10f027 100644 --- a/sound/pci/emu10k1/emu10k1_patch.c +++ b/sound/pci/emu10k1/emu10k1_patch.c @@ -46,8 +46,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,  	struct snd_emu10k1 *emu;  	emu = rec->hw; -	snd_assert(sp != NULL, return -EINVAL); -	snd_assert(hdr != NULL, return -EINVAL); +	if (snd_BUG_ON(!sp || !hdr)) +		return -EINVAL;  	if (sp->v.size == 0) {  		snd_printd("emu: rom font for sample %d\n", sp->v.sample); @@ -104,7 +104,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,  	size = BLANK_HEAD_SIZE;  	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))  		size *= 2; -	snd_assert(offset + size <= blocksize, return -EINVAL); +	if (offset + size > blocksize) +		return -EINVAL;  	snd_emu10k1_synth_bzero(emu, sp->block, offset, size);  	offset += size; @@ -112,7 +113,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,  	size = loopend;  	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))  		size *= 2; -	snd_assert(offset + size <= blocksize, return -EINVAL); +	if (offset + size > blocksize) +		return -EINVAL;  	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {  		snd_emu10k1_synth_free(emu, sp->block);  		sp->block = NULL; @@ -129,12 +131,14 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,  			int woffset;  			unsigned short *wblock = (unsigned short*)block;  			woffset = offset / 2; -			snd_assert(offset + loopsize*2 <= blocksize, return -EINVAL); +			if (offset + loopsize * 2 > blocksize) +				return -EINVAL;  			for (i = 0; i < loopsize; i++)  				wblock[woffset + i] = wblock[woffset - i -1];  			offset += loopsize * 2;  		} else { -			snd_assert(offset + loopsize <= blocksize, return -EINVAL); +			if (offset + loopsize > blocksize) +				return -EINVAL;  			for (i = 0; i < loopsize; i++)  				block[offset + i] = block[offset - i -1];  			offset += loopsize; @@ -154,7 +158,8 @@ snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,  	/* loopend -> sample end */  	size = sp->v.size - loopend; -	snd_assert(size >= 0, return -EINVAL); +	if (size < 0) +		return -EINVAL;  	if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))  		size *= 2;  	if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) { @@ -212,8 +217,8 @@ snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,  	struct snd_emu10k1 *emu;  	emu = rec->hw; -	snd_assert(sp != NULL, return -EINVAL); -	snd_assert(hdr != NULL, return -EINVAL); +	if (snd_BUG_ON(!sp || !hdr)) +		return -EINVAL;  	if (sp->block) {  		snd_emu10k1_synth_free(emu, sp->block); diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 491a4a5..5ff4dbb 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1319,7 +1319,8 @@ static int snd_emu10k1x_midi_input_open(struct snd_rawmidi_substream *substream)  	unsigned long flags;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	midi->midi_mode |= EMU10K1X_MIDI_MODE_INPUT;  	midi->substream_input = substream; @@ -1345,7 +1346,8 @@ static int snd_emu10k1x_midi_output_open(struct snd_rawmidi_substream *substream  	unsigned long flags;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	midi->midi_mode |= EMU10K1X_MIDI_MODE_OUTPUT;  	midi->substream_output = substream; @@ -1372,7 +1374,8 @@ static int snd_emu10k1x_midi_input_close(struct snd_rawmidi_substream *substream  	int err = 0;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	snd_emu10k1x_intr_disable(emu, midi->rx_enable);  	midi->midi_mode &= ~EMU10K1X_MIDI_MODE_INPUT; @@ -1394,7 +1397,8 @@ static int snd_emu10k1x_midi_output_close(struct snd_rawmidi_substream *substrea  	int err = 0;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	snd_emu10k1x_intr_disable(emu, midi->tx_enable);  	midi->midi_mode &= ~EMU10K1X_MIDI_MODE_OUTPUT; @@ -1413,7 +1417,8 @@ static void snd_emu10k1x_midi_input_trigger(struct snd_rawmidi_substream *substr  	struct emu10k1x *emu;  	struct emu10k1x_midi *midi = substream->rmidi->private_data;  	emu = midi->emu; -	snd_assert(emu, return); +	if (snd_BUG_ON(!emu)) +		return;  	if (up)  		snd_emu10k1x_intr_enable(emu, midi->rx_enable); @@ -1428,7 +1433,8 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst  	unsigned long flags;  	emu = midi->emu; -	snd_assert(emu, return); +	if (snd_BUG_ON(!emu)) +		return;  	if (up) {  		int max = 4; diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 71dc4c8..7dba08f 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -487,7 +487,8 @@ static void snd_emu10k1_write_op(struct snd_emu10k1_fx8010_code *icode,  				 u32 op, u32 r, u32 a, u32 x, u32 y)  {  	u_int32_t *code; -	snd_assert(*ptr < 512, return); +	if (snd_BUG_ON(*ptr >= 512)) +		return;  	code = (u_int32_t __force *)icode->code + (*ptr) * 2;  	set_bit(*ptr, icode->code_valid);  	code[0] = ((x & 0x3ff) << 10) | (y & 0x3ff); @@ -503,7 +504,8 @@ static void snd_emu10k1_audigy_write_op(struct snd_emu10k1_fx8010_code *icode,  					u32 op, u32 r, u32 a, u32 x, u32 y)  {  	u_int32_t *code; -	snd_assert(*ptr < 1024, return); +	if (snd_BUG_ON(*ptr >= 1024)) +		return;  	code = (u_int32_t __force *)icode->code + (*ptr) * 2;  	set_bit(*ptr, icode->code_valid);  	code[0] = ((x & 0x7ff) << 12) | (y & 0x7ff); diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index c4d76d1..8578c70 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -157,7 +157,8 @@ static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)  	unsigned long flags;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;  	midi->substream_input = substream; @@ -183,7 +184,8 @@ static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)  	unsigned long flags;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;  	midi->substream_output = substream; @@ -210,7 +212,8 @@ static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)  	int err = 0;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	snd_emu10k1_intr_disable(emu, midi->rx_enable);  	midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT; @@ -232,7 +235,8 @@ static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream  	int err = 0;  	emu = midi->emu; -	snd_assert(emu, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	spin_lock_irqsave(&midi->open_lock, flags);  	snd_emu10k1_intr_disable(emu, midi->tx_enable);  	midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT; @@ -251,7 +255,8 @@ static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substre  	struct snd_emu10k1 *emu;  	struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;  	emu = midi->emu; -	snd_assert(emu, return); +	if (snd_BUG_ON(!emu)) +		return;  	if (up)  		snd_emu10k1_intr_enable(emu, midi->rx_enable); @@ -266,7 +271,8 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr  	unsigned long flags;  	emu = midi->emu; -	snd_assert(emu, return); +	if (snd_BUG_ON(!emu)) +		return;  	if (up) {  		int max = 4; diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 7d379f5..6a47672 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -107,7 +107,8 @@ static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct lis  	list_for_each (pos, &emu->mapped_link_head) {  		struct snd_emu10k1_memblk *blk = get_emu10k1_memblk(pos, mapped_link); -		snd_assert(blk->mapped_page >= 0, continue); +		if (blk->mapped_page < 0) +			continue;  		size = blk->mapped_page - page;  		if (size == npages) {  			*nextp = pos; @@ -295,15 +296,18 @@ struct snd_util_memblk *  snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime = substream->runtime; -	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);  	struct snd_util_memhdr *hdr;  	struct snd_emu10k1_memblk *blk;  	int page, err, idx; -	snd_assert(emu, return NULL); -	snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes < MAXPAGES * EMUPAGESIZE, return NULL); +	if (snd_BUG_ON(!emu)) +		return NULL; +	if (snd_BUG_ON(runtime->dma_bytes <= 0 || +		       runtime->dma_bytes >= MAXPAGES * EMUPAGESIZE)) +		return NULL;  	hdr = emu->memhdr; -	snd_assert(hdr, return NULL); +	if (snd_BUG_ON(!hdr)) +		return NULL;  	mutex_lock(&hdr->block_mutex);  	blk = search_empty(emu, runtime->dma_bytes); @@ -316,16 +320,9 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst  	 */  	idx = 0;  	for (page = blk->first_page; page <= blk->last_page; page++, idx++) { +		unsigned long ofs = idx << PAGE_SHIFT;  		dma_addr_t addr; -#ifdef CONFIG_SND_DEBUG -		if (idx >= sgbuf->pages) { -			printk(KERN_ERR "emu: pages overflow! (%d-%d) for %d\n", -			       blk->first_page, blk->last_page, sgbuf->pages); -			mutex_unlock(&hdr->block_mutex); -			return NULL; -		} -#endif -		addr = sgbuf->table[idx].addr; +		addr = snd_pcm_sgbuf_get_addr(substream, ofs);  		if (! is_valid_page(emu, addr)) {  			printk(KERN_ERR "emu: failure page = %d\n", idx);  			mutex_unlock(&hdr->block_mutex); @@ -353,7 +350,8 @@ snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *subst   */  int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)  { -	snd_assert(emu && blk, return -EINVAL); +	if (snd_BUG_ON(!emu || !blk)) +		return -EINVAL;  	return snd_emu10k1_synth_free(emu, blk);  } @@ -498,7 +496,8 @@ static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *  static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)  {  	char *ptr; -	snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL); +	if (snd_BUG_ON(page < 0 || page >= emu->max_cache_pages)) +		return NULL;  	ptr = emu->page_ptr_table[page];  	if (! ptr) {  		printk(KERN_ERR "emu10k1: access to NULL ptr: page = %d\n", page); diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c index 958cb2a..d7300a1 100644 --- a/sound/pci/emu10k1/voice.c +++ b/sound/pci/emu10k1/voice.c @@ -111,8 +111,10 @@ int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,  	unsigned long flags;  	int result; -	snd_assert(rvoice != NULL, return -EINVAL); -	snd_assert(number, return -EINVAL); +	if (snd_BUG_ON(!rvoice)) +		return -EINVAL; +	if (snd_BUG_ON(!number)) +		return -EINVAL;  	spin_lock_irqsave(&emu->voice_lock, flags);  	for (;;) { @@ -145,7 +147,8 @@ int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,  {  	unsigned long flags; -	snd_assert(pvoice != NULL, return -EINVAL); +	if (snd_BUG_ON(!pvoice)) +		return -EINVAL;  	spin_lock_irqsave(&emu->voice_lock, flags);  	pvoice->interrupt = NULL;  	pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0; diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 84fac1f..4cd9a1f 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -860,7 +860,8 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream,  	struct es1938 *chip = snd_pcm_substream_chip(substream);  	pos <<= chip->dma1_shift;  	count <<= chip->dma1_shift; -	snd_assert(pos + count <= chip->dma1_size, return -EINVAL); +	if (snd_BUG_ON(pos + count > chip->dma1_size)) +		return -EINVAL;  	if (pos + count < chip->dma1_size) {  		if (copy_to_user(dst, runtime->dma_area + pos + 1, count))  			return -EFAULT; diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 1bf298d..20ee759 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -692,7 +692,8 @@ static void apu_data_set(struct es1968 *chip, u16 data)  /* no spinlock */  static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)  { -	snd_assert(channel < NR_APUS, return); +	if (snd_BUG_ON(channel >= NR_APUS)) +		return;  #ifdef CONFIG_PM  	chip->apu_map[channel][reg] = data;  #endif @@ -711,7 +712,8 @@ static void apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)  static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)  { -	snd_assert(channel < NR_APUS, return 0); +	if (snd_BUG_ON(channel >= NR_APUS)) +		return 0;  	reg |= (channel << 4);  	apu_index_set(chip, reg);  	return __maestro_read(chip, IDR0_DATA_PORT); diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index ab0c726..1980c6d 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -5,6 +5,7 @@ snd-hda-intel-y := hda_intel.o  snd-hda-intel-y += hda_codec.o  snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o  snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o +snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o  snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o  snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o  snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o @@ -14,5 +15,6 @@ snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o  snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o  snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o  snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o +snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o  obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c new file mode 100644 index 0000000..9b77b3e --- /dev/null +++ b/sound/pci/hda/hda_beep.c @@ -0,0 +1,134 @@ +/* + * Digital Beep Input Interface for HD-audio codec + * + * Author: Matthew Ranostay <mranostay@embeddedalley.com> + * Copyright (c) 2008 Embedded Alley Solutions Inc + * + *  This driver is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This driver is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + */ + +#include <linux/input.h> +#include <linux/pci.h> +#include <linux/workqueue.h> +#include <sound/core.h> +#include "hda_beep.h" + +enum { +	DIGBEEP_HZ_STEP = 46875,	/* 46.875 Hz */ +	DIGBEEP_HZ_MIN = 93750,		/* 93.750 Hz */ +	DIGBEEP_HZ_MAX = 12000000,	/* 12 KHz */ +}; + +static void snd_hda_generate_beep(struct work_struct *work) +{ +	struct hda_beep *beep = +		container_of(work, struct hda_beep, beep_work); +	struct hda_codec *codec = beep->codec; + +	/* generate tone */ +	snd_hda_codec_write_cache(codec, beep->nid, 0, +			AC_VERB_SET_BEEP_CONTROL, beep->tone); +} + +static int snd_hda_beep_event(struct input_dev *dev, unsigned int type, +				unsigned int code, int hz) +{ +	struct hda_beep *beep = input_get_drvdata(dev); + +	switch (code) { +	case SND_BELL: +		if (hz) +			hz = 1000; +	case SND_TONE: +		hz *= 1000; /* fixed point */ +		hz = hz - DIGBEEP_HZ_MIN; +		if (hz < 0) +			hz = 0; /* turn off PC beep*/ +		else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN)) +			hz = 0xff; +		else { +			hz /= DIGBEEP_HZ_STEP; +			hz++; +		} +		break; +	default: +		return -1; +	} +	beep->tone = hz; + +	/* schedule beep event */ +	schedule_work(&beep->beep_work); +	return 0; +} + +int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) +{ +	struct input_dev *input_dev; +	struct hda_beep *beep; +	int err; + +	beep = kzalloc(sizeof(*beep), GFP_KERNEL); +	if (beep == NULL) +		return -ENOMEM; +	snprintf(beep->phys, sizeof(beep->phys), +		"card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); +	input_dev = input_allocate_device(); + +	/* setup digital beep device */ +	input_dev->name = "HDA Digital PCBeep"; +	input_dev->phys = beep->phys; +	input_dev->id.bustype = BUS_PCI; + +	input_dev->id.vendor = codec->vendor_id >> 16; +	input_dev->id.product = codec->vendor_id & 0xffff; +	input_dev->id.version = 0x01; + +	input_dev->evbit[0] = BIT_MASK(EV_SND); +	input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); +	input_dev->event = snd_hda_beep_event; +	input_dev->dev.parent = &codec->bus->pci->dev; +	input_set_drvdata(input_dev, beep); + +	err = input_register_device(input_dev); +	if (err < 0) { +		input_free_device(input_dev); +		kfree(beep); +		return err; +	} + +	/* enable linear scale */ +	snd_hda_codec_write(codec, nid, 0, +		AC_VERB_SET_DIGI_CONVERT_2, 0x01); + +	beep->nid = nid; +	beep->dev = input_dev; +	beep->codec = codec; +	codec->beep = beep; + +	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); +	return 0; +} + +void snd_hda_detach_beep_device(struct hda_codec *codec) +{ +	struct hda_beep *beep = codec->beep; +	if (beep) { +		cancel_work_sync(&beep->beep_work); +		flush_scheduled_work(); + +		input_unregister_device(beep->dev); +		kfree(beep); +	} +} diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h new file mode 100644 index 0000000..de4036e --- /dev/null +++ b/sound/pci/hda/hda_beep.h @@ -0,0 +1,44 @@ +/* + * Digital Beep Input Interface for HD-audio codec + * + * Author: Matthew Ranostay <mranostay@embeddedalley.com> + * Copyright (c) 2008 Embedded Alley Solutions Inc + * + *  This driver is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This driver is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + */ + +#ifndef __SOUND_HDA_BEEP_H +#define __SOUND_HDA_BEEP_H + +#include "hda_codec.h" + +/* beep information */ +struct hda_beep { +	struct input_dev *dev; +	struct hda_codec *codec; +	char phys[32]; +	int tone; +	int nid; +	struct work_struct beep_work; /* scheduled task for beep event */ +}; + +#ifdef CONFIG_SND_HDA_INPUT_BEEP +int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); +void snd_hda_detach_beep_device(struct hda_codec *codec); +#else +#define snd_hda_attach_beep_device(...) +#define snd_hda_detach_beep_device(...) +#endif +#endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index d2e1093..6447754 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -94,6 +94,9 @@ static const struct hda_codec_preset *hda_preset_tables[] = {  #ifdef CONFIG_SND_HDA_CODEC_VIA  	snd_hda_preset_via,  #endif +#ifdef CONFIG_SND_HDA_CODEC_NVHDMI +	snd_hda_preset_nvhdmi, +#endif  	NULL  }; @@ -211,7 +214,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,  	unsigned int shift, num_elems, mask;  	hda_nid_t prev_nid; -	snd_assert(conn_list && max_conns > 0, return -EINVAL); +	if (snd_BUG_ON(!conn_list || max_conns <= 0)) +		return -EINVAL;  	parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);  	if (parm & AC_CLIST_LONG) { @@ -313,7 +317,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)  }  /* - * process queueud unsolicited events + * process queued unsolicited events   */  static void process_unsol_events(struct work_struct *work)  { @@ -407,8 +411,10 @@ int __devinit snd_hda_bus_new(struct snd_card *card,  		.dev_free = snd_hda_bus_dev_free,  	}; -	snd_assert(temp, return -EINVAL); -	snd_assert(temp->ops.command && temp->ops.get_response, return -EINVAL); +	if (snd_BUG_ON(!temp)) +		return -EINVAL; +	if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response)) +		return -EINVAL;  	if (busp)  		*busp = NULL; @@ -585,11 +591,13 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,  				struct hda_codec **codecp)  {  	struct hda_codec *codec; -	char component[13]; +	char component[31];  	int err; -	snd_assert(bus, return -EINVAL); -	snd_assert(codec_addr <= HDA_MAX_CODEC_ADDRESS, return -EINVAL); +	if (snd_BUG_ON(!bus)) +		return -EINVAL; +	if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) +		return -EINVAL;  	if (bus->caddr_tbl[codec_addr]) {  		snd_printk(KERN_ERR "hda_codec: " @@ -688,7 +696,7 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,  	snd_hda_create_hwdep(codec);  #endif -	sprintf(component, "HDA:%08x", codec->vendor_id); +	sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);  	snd_component_add(codec->bus->card, component);  	if (codecp) @@ -956,15 +964,6 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)  }  #endif /* SND_HDA_NEEDS_RESUME */ -/* - * AMP control callbacks - */ -/* retrieve parameters from private_value */ -#define get_amp_nid(kc)		((kc)->private_value & 0xffff) -#define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3) -#define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1) -#define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf) -  /* volume */  int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,  				  struct snd_ctl_elem_info *uinfo) @@ -1430,6 +1429,29 @@ static unsigned int convert_to_spdif_status(unsigned short val)  	return sbits;  } +/* set digital convert verbs both for the given NID and its slaves */ +static void set_dig_out(struct hda_codec *codec, hda_nid_t nid, +			int verb, int val) +{ +	hda_nid_t *d; + +	snd_hda_codec_write(codec, nid, 0, verb, val); +	d = codec->slave_dig_outs; +	if (!d) +		return; +	for (; *d; d++) +		snd_hda_codec_write(codec, *d, 0, verb, val); +} + +static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid, +				       int dig1, int dig2) +{ +	if (dig1 != -1) +		set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1); +	if (dig2 != -1) +		set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2); +} +  static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,  				     struct snd_ctl_elem_value *ucontrol)  { @@ -1448,14 +1470,8 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,  	change = codec->spdif_ctls != val;  	codec->spdif_ctls = val; -	if (change) { -		snd_hda_codec_write_cache(codec, nid, 0, -					  AC_VERB_SET_DIGI_CONVERT_1, -					  val & 0xff); -		snd_hda_codec_write_cache(codec, nid, 0, -					  AC_VERB_SET_DIGI_CONVERT_2, -					  val >> 8); -	} +	if (change) +		set_dig_out_convert(codec, nid, val & 0xff, (val >> 8) & 0xff);  	mutex_unlock(&codec->spdif_mutex);  	return change; @@ -1487,9 +1503,7 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,  	change = codec->spdif_ctls != val;  	if (change) {  		codec->spdif_ctls = val; -		snd_hda_codec_write_cache(codec, nid, 0, -					  AC_VERB_SET_DIGI_CONVERT_1, -					  val & 0xff); +		set_dig_out_convert(codec, nid, val & 0xff, -1);  		/* unmute amp switch (if any) */  		if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&  		    (val & AC_DIG1_ENABLE)) @@ -2236,11 +2250,13 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,  	if (info->ops.close == NULL)  		info->ops.close = hda_pcm_default_open_close;  	if (info->ops.prepare == NULL) { -		snd_assert(info->nid, return -EINVAL); +		if (snd_BUG_ON(!info->nid)) +			return -EINVAL;  		info->ops.prepare = hda_pcm_default_prepare;  	}  	if (info->ops.cleanup == NULL) { -		snd_assert(info->nid, return -EINVAL); +		if (snd_BUG_ON(!info->nid)) +			return -EINVAL;  		info->ops.cleanup = hda_pcm_default_cleanup;  	}  	return 0; @@ -2583,14 +2599,31 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,  				 unsigned int stream_tag, unsigned int format)  {  	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ -	if (codec->spdif_ctls & AC_DIG1_ENABLE) -		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, -				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); +	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) +		set_dig_out_convert(codec, nid,  +				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff, +				    -1);  	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); +	if (codec->slave_dig_outs) { +		hda_nid_t *d; +		for (d = codec->slave_dig_outs; *d; d++) +			snd_hda_codec_setup_stream(codec, *d, stream_tag, 0, +						   format); +	}  	/* turn on again (if needed) */ -	if (codec->spdif_ctls & AC_DIG1_ENABLE) -		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, -				    codec->spdif_ctls & 0xff); +	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) +		set_dig_out_convert(codec, nid, +				    codec->spdif_ctls & 0xff, -1); +} + +static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) +{ +	snd_hda_codec_cleanup_stream(codec, nid); +	if (codec->slave_dig_outs) { +		hda_nid_t *d; +		for (d = codec->slave_dig_outs; *d; d++) +			snd_hda_codec_cleanup_stream(codec, *d); +	}  }  /* @@ -2602,7 +2635,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,  	mutex_lock(&codec->spdif_mutex);  	if (mout->dig_out_used == HDA_DIG_ANALOG_DUP)  		/* already opened as analog dup; reset it once */ -		snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); +		cleanup_dig_out_stream(codec, mout->dig_out_nid);  	mout->dig_out_used = HDA_DIG_EXCLUSIVE;  	mutex_unlock(&codec->spdif_mutex);  	return 0; @@ -2697,7 +2730,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,  					     stream_tag, format);  		} else {  			mout->dig_out_used = 0; -			snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); +			cleanup_dig_out_stream(codec, mout->dig_out_nid);  		}  	}  	mutex_unlock(&codec->spdif_mutex); @@ -2748,7 +2781,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,  						     mout->extra_out_nid[i]);  	mutex_lock(&codec->spdif_mutex);  	if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) { -		snd_hda_codec_cleanup_stream(codec, mout->dig_out_nid); +		cleanup_dig_out_stream(codec, mout->dig_out_nid);  		mout->dig_out_used = 0;  	}  	mutex_unlock(&codec->spdif_mutex); @@ -2756,7 +2789,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,  }  /* - * Helper for automatic ping configuration + * Helper for automatic pin configuration   */  static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index efc6828..60468f5 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -90,6 +90,14 @@ enum {  #define AC_VERB_GET_CONFIG_DEFAULT		0x0f1c  /* f20: AFG/MFG */  #define AC_VERB_GET_SUBSYSTEM_ID		0x0f20 +#define AC_VERB_GET_CVT_CHAN_COUNT		0x0f2d +#define AC_VERB_GET_HDMI_DIP_SIZE		0x0f2e +#define AC_VERB_GET_HDMI_ELDD			0x0f2f +#define AC_VERB_GET_HDMI_DIP_INDEX		0x0f30 +#define AC_VERB_GET_HDMI_DIP_DATA		0x0f31 +#define AC_VERB_GET_HDMI_DIP_XMIT		0x0f32 +#define AC_VERB_GET_HDMI_CP_CTRL		0x0f33 +#define AC_VERB_GET_HDMI_CHAN_SLOT		0x0f34  /*   * SET verbs @@ -121,7 +129,14 @@ enum {  #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1	0x71d  #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2	0x71e  #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3	0x71f +#define AC_VERB_SET_EAPD				0x788  #define AC_VERB_SET_CODEC_RESET			0x7ff +#define AC_VERB_SET_CVT_CHAN_COUNT		0x72d +#define AC_VERB_SET_HDMI_DIP_INDEX		0x730 +#define AC_VERB_SET_HDMI_DIP_DATA		0x731 +#define AC_VERB_SET_HDMI_DIP_XMIT		0x732 +#define AC_VERB_SET_HDMI_CP_CTRL		0x733 +#define AC_VERB_SET_HDMI_CHAN_SLOT		0x734  /*   * Parameter IDs @@ -143,6 +158,7 @@ enum {  #define AC_PAR_GPIO_CAP			0x11  #define AC_PAR_AMP_OUT_CAP		0x12  #define AC_PAR_VOL_KNB_CAP		0x13 +#define AC_PAR_HDMI_LPCM_CAP		0x20  /*   * AC_VERB_PARAMETERS results (32bit) @@ -171,6 +187,8 @@ enum {  #define AC_WCAP_DIGITAL			(1<<9)	/* digital I/O */  #define AC_WCAP_POWER			(1<<10)	/* power control */  #define AC_WCAP_LR_SWAP			(1<<11)	/* L/R swap */ +#define AC_WCAP_CP_CAPS			(1<<12) /* content protection */ +#define AC_WCAP_CHAN_CNT_EXT		(7<<13)	/* channel count ext */  #define AC_WCAP_DELAY			(0xf<<16)  #define AC_WCAP_DELAY_SHIFT		16  #define AC_WCAP_TYPE			(0xf<<20) @@ -206,9 +224,20 @@ enum {  /* Input converter SDI select */  #define AC_SDI_SELECT			(0xf<<0) -/* Unsolicited response */ +/* Unsolicited response control */  #define AC_UNSOL_TAG			(0x3f<<0)  #define AC_UNSOL_ENABLED		(1<<7) +#define AC_USRSP_EN			AC_UNSOL_ENABLED + +/* Unsolicited responses */ +#define AC_UNSOL_RES_TAG		(0x3f<<26) +#define AC_UNSOL_RES_TAG_SHIFT		26 +#define AC_UNSOL_RES_SUBTAG		(0x1f<<21) +#define AC_UNSOL_RES_SUBTAG_SHIFT	21 +#define AC_UNSOL_RES_ELDV		(1<<1)	/* ELD Data valid (for HDMI) */ +#define AC_UNSOL_RES_PD			(1<<0)	/* pinsense detect */ +#define AC_UNSOL_RES_CP_STATE		(1<<1)	/* content protection */ +#define AC_UNSOL_RES_CP_READY		(1<<0)	/* content protection */  /* Pin widget capabilies */  #define AC_PINCAP_IMP_SENSE		(1<<0)	/* impedance sense capable */ @@ -222,6 +251,10 @@ enum {   *       but is marked reserved in the Intel HDA specification.   */  #define AC_PINCAP_LR_SWAP		(1<<7)	/* L/R swap */ +/* Note: The same bit as LR_SWAP is newly defined as HDMI capability + *       in HD-audio specification + */ +#define AC_PINCAP_HDMI			(1<<7)	/* HDMI pin */  #define AC_PINCAP_VREF			(0x37<<8)  #define AC_PINCAP_VREF_SHIFT		8  #define AC_PINCAP_EAPD			(1<<16)	/* EAPD capable */ @@ -272,6 +305,22 @@ enum {  #define AC_KNBCAP_NUM_STEPS		(0x7f<<0)  #define AC_KNBCAP_DELTA			(1<<7) +/* HDMI LPCM capabilities */ +#define AC_LPCMCAP_48K_CP_CHNS		(0x0f<<0) /* max channels w/ CP-on */	 +#define AC_LPCMCAP_48K_NO_CHNS		(0x0f<<4) /* max channels w/o CP-on */ +#define AC_LPCMCAP_48K_20BIT		(1<<8)	/* 20b bitrate supported */ +#define AC_LPCMCAP_48K_24BIT		(1<<9)	/* 24b bitrate supported */ +#define AC_LPCMCAP_96K_CP_CHNS		(0x0f<<10) /* max channels w/ CP-on */	 +#define AC_LPCMCAP_96K_NO_CHNS		(0x0f<<14) /* max channels w/o CP-on */ +#define AC_LPCMCAP_96K_20BIT		(1<<18)	/* 20b bitrate supported */ +#define AC_LPCMCAP_96K_24BIT		(1<<19)	/* 24b bitrate supported */ +#define AC_LPCMCAP_192K_CP_CHNS		(0x0f<<20) /* max channels w/ CP-on */	 +#define AC_LPCMCAP_192K_NO_CHNS		(0x0f<<24) /* max channels w/o CP-on */ +#define AC_LPCMCAP_192K_20BIT		(1<<28)	/* 20b bitrate supported */ +#define AC_LPCMCAP_192K_24BIT		(1<<29)	/* 24b bitrate supported */ +#define AC_LPCMCAP_44K			(1<<30)	/* 44.1kHz support */ +#define AC_LPCMCAP_44K_MS		(1<<31)	/* 44.1kHz-multiplies support */ +  /*   * Control Parameters   */ @@ -317,18 +366,44 @@ enum {  #define AC_PINCTL_OUT_EN		(1<<6)  #define AC_PINCTL_HP_EN			(1<<7) -/* Unsolicited response - 8bit */ -#define AC_USRSP_EN			(1<<7) -  /* Pin sense - 32bit */  #define AC_PINSENSE_IMPEDANCE_MASK	(0x7fffffff)  #define AC_PINSENSE_PRESENCE		(1<<31) +#define AC_PINSENSE_ELDV		(1<<30)	/* ELD valid (HDMI) */  /* EAPD/BTL enable - 32bit */  #define AC_EAPDBTL_BALANCED		(1<<0)  #define AC_EAPDBTL_EAPD			(1<<1)  #define AC_EAPDBTL_LR_SWAP		(1<<2) +/* HDMI ELD data */ +#define AC_ELDD_ELD_VALID		(1<<31) +#define AC_ELDD_ELD_DATA		0xff + +/* HDMI DIP size */ +#define AC_DIPSIZE_ELD_BUF		(1<<3) /* ELD buf size of packet size */ +#define AC_DIPSIZE_PACK_IDX		(0x07<<0) /* packet index */ + +/* HDMI DIP index */ +#define AC_DIPIDX_PACK_IDX		(0x07<<5) /* packet idnex */ +#define AC_DIPIDX_BYTE_IDX		(0x1f<<0) /* byte index */ + +/* HDMI DIP xmit (transmit) control */ +#define AC_DIPXMIT_MASK			(0x3<<6) +#define AC_DIPXMIT_DISABLE		(0x0<<6) /* disable xmit */ +#define AC_DIPXMIT_ONCE			(0x2<<6) /* xmit once then disable */ +#define AC_DIPXMIT_BEST			(0x3<<6) /* best effort */ + +/* HDMI content protection (CP) control */ +#define AC_CPCTRL_CES			(1<<9) /* current encryption state */ +#define AC_CPCTRL_READY			(1<<8) /* ready bit */ +#define AC_CPCTRL_SUBTAG		(0x1f<<3) /* subtag for unsol-resp */ +#define AC_CPCTRL_STATE			(3<<0) /* current CP request state */ + +/* Converter channel <-> HDMI slot mapping */ +#define AC_CVTMAP_HDMI_SLOT		(0xf<<0) /* HDMI slot number */ +#define AC_CVTMAP_CHAN			(0xf<<4) /* converter channel number */ +  /* configuration default - 32bit */  #define AC_DEFCFG_SEQUENCE		(0xf<<0)  #define AC_DEFCFG_DEF_ASSOC		(0xf<<4) @@ -449,6 +524,7 @@ enum {   */  struct hda_bus; +struct hda_beep;  struct hda_codec;  struct hda_pcm;  struct hda_pcm_stream; @@ -634,6 +710,9 @@ struct hda_codec {  	/* codec specific info */  	void *spec; +	/* beep device */ +	struct hda_beep *beep; +  	/* widget capabilities cache */  	unsigned int num_nodes;  	hda_nid_t start_nid; @@ -646,9 +725,15 @@ struct hda_codec {  	unsigned int spdif_status;	/* IEC958 status bits */  	unsigned short spdif_ctls;	/* SPDIF control bits */  	unsigned int spdif_in_enable;	/* SPDIF input enable? */ +	hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */  	struct snd_hwdep *hwdep;	/* assigned hwdep device */ +	/* misc flags */ +	unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each +					     * status change +					     * (e.g. Realtek codecs) +					     */  #ifdef CONFIG_SND_HDA_POWER_SAVE  	unsigned int power_on :1;	/* current (global) power-state */  	unsigned int power_transition :1; /* power-state in transition */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 59e4389..0ca3089 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -174,7 +174,8 @@ static int build_afg_tree(struct hda_codec *codec)  	int i, nodes, err;  	hda_nid_t nid; -	snd_assert(spec, return -EINVAL); +	if (snd_BUG_ON(!spec)) +		return -EINVAL;  	spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP);  	spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 1c53e33..9f316c1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -222,9 +222,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };  #define RIRB_INT_OVERRUN	0x04  #define RIRB_INT_MASK		0x05 -/* STATESTS int mask: SD2,SD1,SD0 */ -#define AZX_MAX_CODECS		3 -#define STATESTS_INT_MASK	0x07 +/* STATESTS int mask: S3,SD2,SD1,SD0 */ +#define AZX_MAX_CODECS		4 +#define STATESTS_INT_MASK	0x0f  /* SD_CTL bits */  #define SD_CTL_STREAM_RESET	0x01	/* stream reset bit */ @@ -286,6 +286,11 @@ enum {  #define INTEL_SCH_HDA_DEVC      0x78  #define INTEL_SCH_HDA_DEVC_NOSNOOP       (0x1<<11) +/* Define IN stream 0 FIFO size offset in VIA controller */ +#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET	0x90 +/* Define VIA HD Audio Device ID*/ +#define VIA_HDAC_DEVICE_ID		0x3288 +  /*   */ @@ -317,6 +322,12 @@ struct azx_dev {  	unsigned int running :1;  	unsigned int irq_pending :1;  	unsigned int irq_ignore :1; +	/* +	 * For VIA: +	 *  A flag to ensure DMA position is 0 +	 *  when link position is not greater than FIFO size +	 */ +	unsigned int insufficient :1;  };  /* CORB/RIRB */ @@ -379,6 +390,7 @@ struct azx {  	unsigned int polling_mode :1;  	unsigned int msi :1;  	unsigned int irq_pending_warned :1; +	unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */  	/* for debugging */  	unsigned int last_cmd;	/* last issued command (to sync) */ @@ -398,6 +410,7 @@ enum {  	AZX_DRIVER_ULI,  	AZX_DRIVER_NVIDIA,  	AZX_DRIVER_TERA, +	AZX_NUM_DRIVERS, /* keep this as last entry */  };  static char *driver_short_names[] __devinitdata = { @@ -818,6 +831,11 @@ static void azx_int_clear(struct azx *chip)  /* start a stream */  static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev)  { +	/* +	 * Before stream start, initialize parameter +	 */ +	azx_dev->insufficient = 1; +  	/* enable SIE */  	azx_writeb(chip, INTCTL,  		   azx_readb(chip, INTCTL) | (1 << azx_dev->index)); @@ -998,7 +1016,6 @@ static int setup_bdle(struct snd_pcm_substream *substream,  		      struct azx_dev *azx_dev, u32 **bdlp,  		      int ofs, int size, int with_ioc)  { -	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);  	u32 *bdl = *bdlp;  	while (size > 0) { @@ -1008,14 +1025,12 @@ static int setup_bdle(struct snd_pcm_substream *substream,  		if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)  			return -EINVAL; -		addr = snd_pcm_sgbuf_get_addr(sgbuf, ofs); +		addr = snd_pcm_sgbuf_get_addr(substream, ofs);  		/* program the address field of the BDL entry */  		bdl[0] = cpu_to_le32((u32)addr);  		bdl[1] = cpu_to_le32(upper_32_bits(addr));  		/* program the size field of the BDL entry */ -		chunk = PAGE_SIZE - (ofs % PAGE_SIZE); -		if (size < chunk) -			chunk = size; +		chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);  		bdl[2] = cpu_to_le32(chunk);  		/* program the IOC to enable interrupt  		 * only when the whole fragment is processed @@ -1151,7 +1166,8 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)  	/* enable the position buffer */  	if (chip->position_fix == POS_FIX_POSBUF || -	    chip->position_fix == POS_FIX_AUTO) { +	    chip->position_fix == POS_FIX_AUTO || +	    chip->via_dmapos_patch) {  		if (!(azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))  			azx_writel(chip, DPLBASE,  				(u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE); @@ -1169,23 +1185,26 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)   * Codec initialization   */ -static unsigned int azx_max_codecs[] __devinitdata = { -	[AZX_DRIVER_ICH] = 4,		/* Some ICH9 boards use SD3 */ -	[AZX_DRIVER_SCH] = 3, -	[AZX_DRIVER_ATI] = 4, -	[AZX_DRIVER_ATIHDMI] = 4, -	[AZX_DRIVER_VIA] = 3,		/* FIXME: correct? */ -	[AZX_DRIVER_SIS] = 3,		/* FIXME: correct? */ -	[AZX_DRIVER_ULI] = 3,		/* FIXME: correct? */ -	[AZX_DRIVER_NVIDIA] = 3,	/* FIXME: correct? */ +/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ +static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {  	[AZX_DRIVER_TERA] = 1,  }; +/* number of slots to probe as default + * this can be different from azx_max_codecs[] -- e.g. some boards + * report wrongly the non-existing 4th slot availability + */ +static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = { +	[AZX_DRIVER_ICH] = 3, +	[AZX_DRIVER_ATI] = 3, +}; +  static int __devinit azx_codec_create(struct azx *chip, const char *model,  				      unsigned int codec_probe_mask)  {  	struct hda_bus_template bus_temp;  	int c, codecs, audio_codecs, err; +	int def_slots, max_slots;  	memset(&bus_temp, 0, sizeof(bus_temp));  	bus_temp.private_data = chip; @@ -1201,8 +1220,17 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,  	if (err < 0)  		return err; +	if (chip->driver_type == AZX_DRIVER_NVIDIA) +		chip->bus->needs_damn_long_delay = 1; +  	codecs = audio_codecs = 0; -	for (c = 0; c < AZX_MAX_CODECS; c++) { +	max_slots = azx_max_codecs[chip->driver_type]; +	if (!max_slots) +		max_slots = AZX_MAX_CODECS; +	def_slots = azx_default_codecs[chip->driver_type]; +	if (!def_slots) +		def_slots = max_slots; +	for (c = 0; c < def_slots; c++) {  		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {  			struct hda_codec *codec;  			err = snd_hda_codec_new(chip->bus, c, &codec); @@ -1215,7 +1243,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,  	}  	if (!audio_codecs) {  		/* probe additional slots if no codec is found */ -		for (; c < azx_max_codecs[chip->driver_type]; c++) { +		for (; c < max_slots; c++) {  			if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {  				err = snd_hda_codec_new(chip->bus, c, NULL);  				if (err < 0) @@ -1507,13 +1535,71 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)  	return 0;  } +/* get the current DMA position with correction on VIA chips */ +static unsigned int azx_via_get_position(struct azx *chip, +					 struct azx_dev *azx_dev) +{ +	unsigned int link_pos, mini_pos, bound_pos; +	unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; +	unsigned int fifo_size; + +	link_pos = azx_sd_readl(azx_dev, SD_LPIB); +	if (azx_dev->index >= 4) { +		/* Playback, no problem using link position */ +		return link_pos; +	} + +	/* Capture */ +	/* For new chipset, +	 * use mod to get the DMA position just like old chipset +	 */ +	mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); +	mod_dma_pos %= azx_dev->period_bytes; + +	/* azx_dev->fifo_size can't get FIFO size of in stream. +	 * Get from base address + offset. +	 */ +	fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); + +	if (azx_dev->insufficient) { +		/* Link position never gather than FIFO size */ +		if (link_pos <= fifo_size) +			return 0; + +		azx_dev->insufficient = 0; +	} + +	if (link_pos <= fifo_size) +		mini_pos = azx_dev->bufsize + link_pos - fifo_size; +	else +		mini_pos = link_pos - fifo_size; + +	/* Find nearest previous boudary */ +	mod_mini_pos = mini_pos % azx_dev->period_bytes; +	mod_link_pos = link_pos % azx_dev->period_bytes; +	if (mod_link_pos >= fifo_size) +		bound_pos = link_pos - mod_link_pos; +	else if (mod_dma_pos >= mod_mini_pos) +		bound_pos = mini_pos - mod_mini_pos; +	else { +		bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; +		if (bound_pos >= azx_dev->bufsize) +			bound_pos = 0; +	} + +	/* Calculate real DMA position we want */ +	return bound_pos + mod_dma_pos; +} +  static unsigned int azx_get_position(struct azx *chip,  				     struct azx_dev *azx_dev)  {  	unsigned int pos; -	if (chip->position_fix == POS_FIX_POSBUF || -	    chip->position_fix == POS_FIX_AUTO) { +	if (chip->via_dmapos_patch) +		pos = azx_via_get_position(chip, azx_dev); +	else if (chip->position_fix == POS_FIX_POSBUF || +		 chip->position_fix == POS_FIX_AUTO) {  		/* use the position buffer */  		pos = le32_to_cpu(*azx_dev->posbuf);  	} else { @@ -1559,6 +1645,8 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)  			chip->position_fix = POS_FIX_POSBUF;  	} +	if (!bdl_pos_adj[chip->dev_index]) +		return 1; /* no delayed ack */  	if (pos % azx_dev->period_bytes > azx_dev->period_bytes / 2)  		return 0; /* NG - it's below the period boundary */  	return 1; /* OK, it's fine */ @@ -1646,7 +1734,8 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,  	if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)  		return 0; -	snd_assert(cpcm->name, return -EINVAL); +	if (snd_BUG_ON(!cpcm->name)) +		return -EINVAL;  	err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,  			  cpcm->stream[0].substreams, @@ -1670,7 +1759,7 @@ static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,  		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);  	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,  					      snd_dma_pci_data(chip->pci), -					      1024 * 64, 1024 * 1024); +					      1024 * 64, 32 * 1024 * 1024);  	chip->pcm[cpcm->device] = pcm;  	return 0;  } @@ -1946,6 +2035,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)  {  	const struct snd_pci_quirk *q; +	/* Check VIA HD Audio Controller exist */ +	if (chip->pci->vendor == PCI_VENDOR_ID_VIA && +	    chip->pci->device == VIA_HDAC_DEVICE_ID) { +		chip->via_dmapos_patch = 1; +		/* Use link position directly, avoid any transfer problem. */ +		return POS_FIX_LPIB; +	} +	chip->via_dmapos_patch = 0; +  	if (fix == POS_FIX_AUTO) {  		q = snd_pci_quirk_lookup(chip->pci, position_fix_list);  		if (q) { diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 5c9e578..7957fef 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -368,12 +368,15 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,  #define AMP_OUT_UNMUTE	0xb000  #define AMP_OUT_ZERO	0xb000  /* pinctl values */ -#define PIN_IN		0x20 -#define PIN_VREF80	0x24 -#define PIN_VREF50	0x21 -#define PIN_OUT		0x40 -#define PIN_HP		0xc0 -#define PIN_HP_AMP	0x80 +#define PIN_IN			(AC_PINCTL_IN_EN) +#define PIN_VREFHIZ	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ) +#define PIN_VREF50		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_50) +#define PIN_VREFGRD	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD) +#define PIN_VREF80		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_80) +#define PIN_VREF100	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_100) +#define PIN_OUT		(AC_PINCTL_OUT_EN) +#define PIN_HP			(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN) +#define PIN_HP_AMP		(AC_PINCTL_HP_EN)  /*   * get widget capabilities @@ -418,4 +421,13 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,  				 hda_nid_t nid);  #endif /* CONFIG_SND_HDA_POWER_SAVE */ +/* + * AMP control callbacks + */ +/* retrieve parameters from private_value */ +#define get_amp_nid(kc)		((kc)->private_value & 0xffff) +#define get_amp_channels(kc)	(((kc)->private_value >> 16) & 0x3) +#define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1) +#define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf) +  #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index 2fdf235..dfbcfa8 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -18,3 +18,5 @@ extern struct hda_codec_preset snd_hda_preset_atihdmi[];  extern struct hda_codec_preset snd_hda_preset_conexant[];  /* VIA codecs */  extern struct hda_codec_preset snd_hda_preset_via[]; +/* NVIDIA HDMI codecs */ +extern struct hda_codec_preset snd_hda_preset_nvhdmi[]; diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 1e5aff5..743d779 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -216,7 +216,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,  	unsigned int caps, val;  	caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); -	snd_iprintf(buffer, "  Pincap 0x08%x:", caps); +	snd_iprintf(buffer, "  Pincap 0x%08x:", caps);  	if (caps & AC_PINCAP_IN)  		snd_iprintf(buffer, " IN");  	if (caps & AC_PINCAP_OUT) @@ -229,8 +229,13 @@ static void print_pin_caps(struct snd_info_buffer *buffer,  		snd_iprintf(buffer, " Detect");  	if (caps & AC_PINCAP_BALANCE)  		snd_iprintf(buffer, " Balanced"); -	if (caps & AC_PINCAP_LR_SWAP) -		snd_iprintf(buffer, " R/L"); +	if (caps & AC_PINCAP_HDMI) { +		/* Realtek uses this bit as a different meaning */ +		if ((codec->vendor_id >> 16) == 0x10ec) +			snd_iprintf(buffer, " R/L"); +		else +			snd_iprintf(buffer, " HDMI"); +	}  	if (caps & AC_PINCAP_TRIG_REQ)  		snd_iprintf(buffer, " Trigger");  	if (caps & AC_PINCAP_IMP_SENSE) @@ -552,9 +557,15 @@ static void print_codec_info(struct snd_info_entry *entry,  		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,  			    get_wid_type_name(wid_type), wid_caps); -		if (wid_caps & AC_WCAP_STEREO) -			snd_iprintf(buffer, " Stereo"); -		else +		if (wid_caps & AC_WCAP_STEREO) { +			unsigned int chans; +			chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13; +			chans = ((chans << 1) | 1) + 1; +			if (chans == 2) +				snd_iprintf(buffer, " Stereo"); +			else +				snd_iprintf(buffer, " %d-Channels", chans); +		} else  			snd_iprintf(buffer, " Mono");  		if (wid_caps & AC_WCAP_DIGITAL)  			snd_iprintf(buffer, " Digital"); @@ -566,6 +577,8 @@ static void print_codec_info(struct snd_info_entry *entry,  			snd_iprintf(buffer, " Stripe");  		if (wid_caps & AC_WCAP_LR_SWAP)  			snd_iprintf(buffer, " R/L"); +		if (wid_caps & AC_WCAP_CP_CAPS) +			snd_iprintf(buffer, " CP");  		snd_iprintf(buffer, "\n");  		/* volume knob is a special widget that always have connection diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index e8003d9..2b00c4a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -1826,9 +1826,14 @@ static hda_nid_t ad1988_capsrc_nids[3] = {  	0x0c, 0x0d, 0x0e  }; -#define AD1988_SPDIF_OUT	0x02 +#define AD1988_SPDIF_OUT		0x02 +#define AD1988_SPDIF_OUT_HDMI	0x0b  #define AD1988_SPDIF_IN		0x07 +static hda_nid_t ad1989b_slave_dig_outs[2] = { +	AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI +}; +  static struct hda_input_mux ad1988_6stack_capture_source = {  	.num_items = 5,  	.items = { @@ -2143,6 +2148,7 @@ static struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {  static struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {  	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT), +	HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),  	{ } /* end */  }; @@ -2207,6 +2213,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {  	{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},  	/* Analog CD Input */  	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	/* Analog Mix output amp */ +	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */  	{ }  }; @@ -2247,8 +2255,12 @@ static struct hda_verb ad1988_spdif_init_verbs[] = {  /* AD1989 has no ADC -> SPDIF route */  static struct hda_verb ad1989_spdif_init_verbs[] = { -	/* SPDIF out pin */ +	/* SPDIF-1 out pin */ +	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },  	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */ +	/* SPDIF-2/HDMI out pin */ +	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, +	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */  	{ }  }; @@ -2336,6 +2348,8 @@ static struct hda_verb ad1988_3stack_init_verbs[] = {  	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +	/* Analog Mix output amp */ +	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */  	{ }  }; @@ -2409,6 +2423,8 @@ static struct hda_verb ad1988_laptop_init_verbs[] = {  	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +	/* Analog Mix output amp */ +	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */  	{ }  }; @@ -2868,6 +2884,7 @@ static struct snd_pci_quirk ad1988_cfg_tbl[] = {  	SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),  	SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),  	SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG), +	SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),  	{}  }; @@ -2975,6 +2992,7 @@ static int patch_ad1988(struct hda_codec *codec)  				ad1989_spdif_out_mixers;  			spec->init_verbs[spec->num_init_verbs++] =  				ad1989_spdif_init_verbs; +			codec->slave_dig_outs = ad1989b_slave_dig_outs;  		} else {  			spec->mixers[spec->num_mixers++] =  				ad1988_spdif_out_mixers; @@ -3911,7 +3929,7 @@ static int patch_ad1884a(struct hda_codec *codec)  /* - * AD1882 + * AD1882 / AD1882A   *   * port-A - front hp-out   * port-B - front mic-in @@ -3948,6 +3966,18 @@ static struct hda_input_mux ad1882_capture_source = {  	},  }; +/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */ +static struct hda_input_mux ad1882a_capture_source = { +	.num_items = 5, +	.items = { +		{ "Front Mic", 0x1 }, +		{ "Mic", 0x4}, +		{ "Line", 0x2 }, +		{ "Digital Mic", 0x06 }, +		{ "Mix", 0x7 }, +	}, +}; +  static struct snd_kcontrol_new ad1882_base_mixers[] = {  	HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), @@ -3957,16 +3987,7 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {  	HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), -	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), -	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), -	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), -	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT), -	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), -	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), -	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), -	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT), -	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT), +  	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT), @@ -3999,6 +4020,35 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {  	{ } /* end */  }; +static struct snd_kcontrol_new ad1882_loopback_mixers[] = { +	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), +	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT), +	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT), +	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT), +	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), +	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), +	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT), +	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT), +	{ } /* end */ +}; + +static struct snd_kcontrol_new ad1882a_loopback_mixers[] = { +	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT), +	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT), +	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT), +	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT), +	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT), +	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT), +	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT), +	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT), +	HDA_CODEC_VOLUME("Digital Mic Boost", 0x1f, 0x0, HDA_INPUT), +	{ } /* end */ +}; +  static struct snd_kcontrol_new ad1882_3stack_mixers[] = {  	HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT), @@ -4168,9 +4218,16 @@ static int patch_ad1882(struct hda_codec *codec)  	spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);  	spec->adc_nids = ad1882_adc_nids;  	spec->capsrc_nids = ad1882_capsrc_nids; -	spec->input_mux = &ad1882_capture_source; -	spec->num_mixers = 1; +	if (codec->vendor_id == 0x11d1882) +		spec->input_mux = &ad1882_capture_source; +	else +		spec->input_mux = &ad1882a_capture_source; +	spec->num_mixers = 2;  	spec->mixers[0] = ad1882_base_mixers; +	if (codec->vendor_id == 0x11d1882) +		spec->mixers[1] = ad1882_loopback_mixers; +	else +		spec->mixers[1] = ad1882a_loopback_mixers;  	spec->num_init_verbs = 1;  	spec->init_verbs[0] = ad1882_init_verbs;  	spec->spdif_route = 0; @@ -4187,8 +4244,8 @@ static int patch_ad1882(struct hda_codec *codec)  	switch (board_config) {  	default:  	case AD1882_3STACK: -		spec->num_mixers = 2; -		spec->mixers[1] = ad1882_3stack_mixers; +		spec->num_mixers = 3; +		spec->mixers[2] = ad1882_3stack_mixers;  		spec->channel_mode = ad1882_modes;  		spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);  		spec->need_dac_fix = 1; @@ -4196,8 +4253,8 @@ static int patch_ad1882(struct hda_codec *codec)  		spec->multiout.num_dacs = 1;  		break;  	case AD1882_6STACK: -		spec->num_mixers = 2; -		spec->mixers[1] = ad1882_6stack_mixers; +		spec->num_mixers = 3; +		spec->mixers[2] = ad1882_6stack_mixers;  		break;  	}  	return 0; @@ -4220,6 +4277,7 @@ struct hda_codec_preset snd_hda_preset_analog[] = {  	{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },  	{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },  	{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 }, +	{ .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },  	{ .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },  	{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },  	{} /* terminator */ diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c index 1227250..ba61575 100644 --- a/sound/pci/hda/patch_atihdmi.c +++ b/sound/pci/hda/patch_atihdmi.c @@ -35,6 +35,9 @@ struct atihdmi_spec {  	struct hda_pcm pcm_rec;  }; +#define CVT_NID		0x02	/* audio converter */ +#define PIN_NID		0x03	/* HDMI output pin */ +  static struct hda_verb atihdmi_basic_init[] = {  	/* enable digital output on pin widget */  	{ 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, @@ -60,8 +63,9 @@ static int atihdmi_init(struct hda_codec *codec)  {  	snd_hda_sequence_write(codec, atihdmi_basic_init);  	/* SI codec requires to unmute the pin */ -	if (get_wcaps(codec, 0x03) & AC_WCAP_OUT_AMP) -		snd_hda_codec_write(codec, 0x03, 0, AC_VERB_SET_AMP_GAIN_MUTE, +	if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP) +		snd_hda_codec_write(codec, PIN_NID, 0, +				    AC_VERB_SET_AMP_GAIN_MUTE,  				    AMP_OUT_UNMUTE);  	return 0;  } @@ -92,15 +96,29 @@ static int atihdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,  					    struct snd_pcm_substream *substream)  {  	struct atihdmi_spec *spec = codec->spec; -	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, -					     format, substream); +	int chans = substream->runtime->channels; +	int i, err; + +	err = snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, +					    format, substream); +	if (err < 0) +		return err; +	snd_hda_codec_write(codec, CVT_NID, 0, AC_VERB_SET_CVT_CHAN_COUNT, +			    chans - 1); +	/* FIXME: XXX */ +	for (i = 0; i < chans; i++) { +		snd_hda_codec_write(codec, CVT_NID, 0, +				    AC_VERB_SET_HDMI_CHAN_SLOT, +				    (i << 4) | i); +	} +	return 0;  }  static struct hda_pcm_stream atihdmi_pcm_digital_playback = {  	.substreams = 1,  	.channels_min = 2,  	.channels_max = 2, -	.nid = 0x2, /* NID to query formats and rates and setup streams */ +	.nid = CVT_NID, /* NID to query formats and rates and setup streams */  	.ops = {  		.open = atihdmi_dig_playback_pcm_open,  		.close = atihdmi_dig_playback_pcm_close, @@ -112,6 +130,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec)  {  	struct atihdmi_spec *spec = codec->spec;  	struct hda_pcm *info = &spec->pcm_rec; +	unsigned int chans;  	codec->num_pcms = 1;  	codec->pcm_info = info; @@ -120,6 +139,13 @@ static int atihdmi_build_pcms(struct hda_codec *codec)  	info->pcm_type = HDA_PCM_TYPE_HDMI;  	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; +	/* FIXME: we must check ELD and change the PCM parameters dynamically +	 */ +	chans = get_wcaps(codec, CVT_NID); +	chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13; +	chans = ((chans << 1) | 1) + 1; +	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans; +  	return 0;  } @@ -147,9 +173,11 @@ static int patch_atihdmi(struct hda_codec *codec)  	spec->multiout.num_dacs = 0;	  /* no analog */  	spec->multiout.max_channels = 2; -	spec->multiout.dig_out_nid = 0x2; /* NID for copying analog to digital, -					   * seems to be unused in pure-digital -					   * case. */ +	/* NID for copying analog to digital, +	 * seems to be unused in pure-digital +	 * case. +	 */ +	spec->multiout.dig_out_nid = CVT_NID;  	codec->patch_ops = atihdmi_patch_ops; @@ -164,6 +192,7 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {  	{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },  	{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },  	{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi }, +	{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },  	{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },  	{ .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },  	{} /* terminator */ diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c new file mode 100644 index 0000000..1a65775 --- /dev/null +++ b/sound/pci/hda/patch_nvhdmi.c @@ -0,0 +1,164 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for NVIDIA HDMI codecs + * + * Copyright (c) 2008 NVIDIA Corp.  All rights reserved. + * Copyright (c) 2008 Wei Ni <wni@nvidia.com> + * + * + *  This driver is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This driver is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +struct nvhdmi_spec { +	struct hda_multi_out multiout; + +	struct hda_pcm pcm_rec; +}; + +static struct hda_verb nvhdmi_basic_init[] = { +	/* enable digital output on pin widget */ +	{ 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, +	{} /* terminator */ +}; + +/* + * Controls + */ +static int nvhdmi_build_controls(struct hda_codec *codec) +{ +	struct nvhdmi_spec *spec = codec->spec; +	int err; + +	err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); +	if (err < 0) +		return err; + +	return 0; +} + +static int nvhdmi_init(struct hda_codec *codec) +{ +	snd_hda_sequence_write(codec, nvhdmi_basic_init); +	return 0; +} + +/* + * Digital out + */ +static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, +				     struct hda_codec *codec, +				     struct snd_pcm_substream *substream) +{ +	struct nvhdmi_spec *spec = codec->spec; +	return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, +				      struct hda_codec *codec, +				      struct snd_pcm_substream *substream) +{ +	struct nvhdmi_spec *spec = codec->spec; +	return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, +					    struct hda_codec *codec, +					    unsigned int stream_tag, +					    unsigned int format, +					    struct snd_pcm_substream *substream) +{ +	struct nvhdmi_spec *spec = codec->spec; +	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag, +					     format, substream); +} + +static struct hda_pcm_stream nvhdmi_pcm_digital_playback = { +	.substreams = 1, +	.channels_min = 2, +	.channels_max = 2, +	.nid = 0x4, /* NID to query formats and rates and setup streams */ +	.rates = SNDRV_PCM_RATE_48000, +	.maxbps = 16, +	.formats = SNDRV_PCM_FMTBIT_S16_LE, +	.ops = { +		.open = nvhdmi_dig_playback_pcm_open, +		.close = nvhdmi_dig_playback_pcm_close, +		.prepare = nvhdmi_dig_playback_pcm_prepare +	}, +}; + +static int nvhdmi_build_pcms(struct hda_codec *codec) +{ +	struct nvhdmi_spec *spec = codec->spec; +	struct hda_pcm *info = &spec->pcm_rec; + +	codec->num_pcms = 1; +	codec->pcm_info = info; + +	info->name = "NVIDIA HDMI"; +	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback; + +	return 0; +} + +static void nvhdmi_free(struct hda_codec *codec) +{ +	kfree(codec->spec); +} + +static struct hda_codec_ops nvhdmi_patch_ops = { +	.build_controls = nvhdmi_build_controls, +	.build_pcms = nvhdmi_build_pcms, +	.init = nvhdmi_init, +	.free = nvhdmi_free, +}; + +static int patch_nvhdmi(struct hda_codec *codec) +{ +	struct nvhdmi_spec *spec; + +	spec = kzalloc(sizeof(*spec), GFP_KERNEL); +	if (spec == NULL) +		return -ENOMEM; + +	codec->spec = spec; + +	spec->multiout.num_dacs = 0;	  /* no analog */ +	spec->multiout.max_channels = 2; +	spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital, +					   * seems to be unused in pure-digital +					   * case. */ + +	codec->patch_ops = nvhdmi_patch_ops; + +	return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_nvhdmi[] = { +	{ .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi }, +	{ .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi }, +	{} /* terminator */ +}; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6602516..0b6e682 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -72,6 +72,7 @@ enum {  enum {  	ALC260_BASIC,  	ALC260_HP, +	ALC260_HP_DC7600,  	ALC260_HP_3013,  	ALC260_FUJITSU_S702X,  	ALC260_ACER, @@ -100,6 +101,9 @@ enum {  	ALC262_BENQ_T31,  	ALC262_ULTRA,  	ALC262_LENOVO_3000, +	ALC262_NEC, +	ALC262_TOSHIBA_S06, +	ALC262_TOSHIBA_RX1,  	ALC262_AUTO,  	ALC262_MODEL_LAST /* last tag */  }; @@ -110,6 +114,7 @@ enum {  	ALC268_3ST,  	ALC268_TOSHIBA,  	ALC268_ACER, +	ALC268_ACER_ASPIRE_ONE,  	ALC268_DELL,  	ALC268_ZEPTO,  #ifdef CONFIG_SND_DEBUG @@ -122,6 +127,7 @@ enum {  /* ALC269 models */  enum {  	ALC269_BASIC, +	ALC269_QUANTA_FL1,  	ALC269_ASUS_EEEPC_P703,  	ALC269_ASUS_EEEPC_P901,  	ALC269_AUTO, @@ -169,6 +175,13 @@ enum {  	ALC663_ASUS_G71V,  	ALC663_ASUS_H13,  	ALC663_ASUS_G50V, +	ALC662_ECS, +	ALC663_ASUS_MODE1, +	ALC662_ASUS_MODE2, +	ALC663_ASUS_MODE3, +	ALC663_ASUS_MODE4, +	ALC663_ASUS_MODE5, +	ALC663_ASUS_MODE6,  	ALC662_AUTO,  	ALC662_MODEL_LAST,  }; @@ -200,18 +213,21 @@ enum {  	ALC883_ACER,  	ALC883_ACER_ASPIRE,  	ALC883_MEDION, -	ALC883_MEDION_MD2,	 +	ALC883_MEDION_MD2,  	ALC883_LAPTOP_EAPD,  	ALC883_LENOVO_101E_2ch,  	ALC883_LENOVO_NB0763,  	ALC888_LENOVO_MS7195_DIG, -	ALC883_HAIER_W66,		 +	ALC888_LENOVO_SKY, +	ALC883_HAIER_W66,  	ALC888_3ST_HP,  	ALC888_6ST_DELL,  	ALC883_MITAC,  	ALC883_CLEVO_M720,  	ALC883_FUJITSU_PI2515,  	ALC883_3ST_6ch_INTEL, +	ALC888_ASUS_M90V, +	ALC888_ASUS_EEE1601,  	ALC883_AUTO,  	ALC883_MODEL_LAST,  }; @@ -398,7 +414,7 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,  /*   * Control the mode of pin widget settings via the mixer.  "pc" is used - * instead of "%" to avoid consequences of accidently treating the % as  + * instead of "%" to avoid consequences of accidently treating the % as   * being part of a format specifier.  Maximum allowed length of a value is   * 63 characters plus NULL terminator.   * @@ -429,7 +445,7 @@ static unsigned char alc_pin_mode_values[] = {  #define ALC_PIN_DIR_IN_NOMICBIAS    0x03  #define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04 -/* Info about the pin modes supported by the different pin direction modes.  +/* Info about the pin modes supported by the different pin direction modes.   * For each direction the minimum and maximum values are given.   */  static signed char alc_pin_mode_dir_info[5][2] = { @@ -502,7 +518,7 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,  					  AC_VERB_SET_PIN_WIDGET_CONTROL,  					  alc_pin_mode_values[val]); -		/* Also enable the retasking pin's input/output as required  +		/* Also enable the retasking pin's input/output as required  		 * for the requested pin mode.  Enum values of 2 or less are  		 * input modes.  		 * @@ -707,7 +723,7 @@ static void setup_preset(struct alc_spec *spec,  	     i++)  		spec->init_verbs[spec->num_init_verbs++] =  			preset->init_verbs[i]; -	 +  	spec->channel_mode = preset->channel_mode;  	spec->num_channel_mode = preset->num_channel_mode;  	spec->need_dac_fix = preset->need_dac_fix; @@ -718,7 +734,7 @@ static void setup_preset(struct alc_spec *spec,  	spec->multiout.dac_nids = preset->dac_nids;  	spec->multiout.dig_out_nid = preset->dig_out_nid;  	spec->multiout.hp_nid = preset->hp_nid; -	 +  	spec->num_mux_defs = preset->num_mux_defs;  	if (!spec->num_mux_defs)  		spec->num_mux_defs = 1; @@ -855,7 +871,7 @@ static void alc_subsystem_id(struct hda_codec *codec,  	if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))  		goto do_sku; -	/*	 +	/*  	 * 31~30	: port conetcivity  	 * 29~21	: reserve  	 * 20		: PCBEEP input @@ -946,7 +962,7 @@ do_sku:  			tmp = snd_hda_codec_read(codec, 0x20, 0,  						 AC_VERB_GET_PROC_COEF, 0);  			snd_hda_codec_write(codec, 0x20, 0, -					    AC_VERB_SET_COEF_INDEX, 7);	 +					    AC_VERB_SET_COEF_INDEX, 7);  			snd_hda_codec_write(codec, 0x20, 0,  					    AC_VERB_SET_PROC_COEF,  					    tmp | 0x2010); @@ -961,7 +977,7 @@ do_sku:  			tmp = snd_hda_codec_read(codec, 0x20, 0,  						 AC_VERB_GET_PROC_COEF, 0);  			snd_hda_codec_write(codec, 0x20, 0, -					    AC_VERB_SET_COEF_INDEX, 7);	 +					    AC_VERB_SET_COEF_INDEX, 7);  			snd_hda_codec_write(codec, 0x20, 0,  					    AC_VERB_SET_PROC_COEF,  					    tmp | 0x3000); @@ -970,7 +986,7 @@ do_sku:  	default:  		break;  	} -	 +  	/* is laptop or Desktop and enable the function "Mute internal speaker  	 * when the external headphone out jack is plugged"  	 */ @@ -1006,6 +1022,7 @@ do_sku:  	snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0,  			    AC_VERB_SET_UNSOLICITED_ENABLE,  			    AC_USRSP_EN | ALC880_HP_EVENT); +  	spec->unsol_event = alc_sku_unsol_event;  } @@ -1296,7 +1313,7 @@ static struct snd_kcontrol_new alc880_six_stack_mixer[] = {   *   * The system also has a pair of internal speakers, and a headphone jack.   * These are both connected to Line2 on the codec, hence to DAC 02. - *  + *   * There is a variable resistor to control the speaker or headphone   * volume. This is a hardware-only device without a software API.   * @@ -1824,7 +1841,7 @@ static struct hda_verb alc880_pin_6stack_init_verbs[] = {  	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},  	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -	 +  	{ }  }; @@ -1869,7 +1886,7 @@ static struct hda_verb alc880_uniwill_init_verbs[] = {  /*  * Uniwill P53 -* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,  +* HP = 0x14, InternalSpeaker = 0x15, mic = 0x19,   */  static struct hda_verb alc880_uniwill_p53_init_verbs[] = {  	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ @@ -1968,7 +1985,7 @@ static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec)  static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)  {  	unsigned int present; -	 +  	present = snd_hda_codec_read(codec, 0x21, 0,  				     AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);  	present &= HDA_AMP_VOLMASK; @@ -2050,7 +2067,7 @@ static struct hda_verb alc880_pin_asus_init_verbs[] = {  	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},  	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -	 +  	{ }  }; @@ -2632,12 +2649,14 @@ static int alc_build_pcms(struct hda_codec *codec)  	info->name = spec->stream_name_analog;  	if (spec->stream_analog_playback) { -		snd_assert(spec->multiout.dac_nids, return -EINVAL); +		if (snd_BUG_ON(!spec->multiout.dac_nids)) +			return -EINVAL;  		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);  		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];  	}  	if (spec->stream_analog_capture) { -		snd_assert(spec->adc_nids, return -EINVAL); +		if (snd_BUG_ON(!spec->adc_nids)) +			return -EINVAL;  		info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);  		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];  	} @@ -2667,6 +2686,8 @@ static int alc_build_pcms(struct hda_codec *codec)  			info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);  			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;  		} +		/* FIXME: do we need this for all Realtek codec models? */ +		codec->spdif_status_reset = 1;  	}  	/* If the use of more than one ADC is requested for the current @@ -3683,7 +3704,7 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec)  {  	struct alc_spec *spec = codec->spec;  	int i; -	 +  	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);  	for (i = 0; i < spec->autocfg.line_outs; i++) {  		hda_nid_t nid = spec->autocfg.line_out_pins[i]; @@ -4124,6 +4145,33 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {  	{ } /* end */  }; +static struct hda_bind_ctls alc260_dc7600_bind_master_vol = { +	.ops = &snd_hda_bind_vol, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x0a, 3, 0, HDA_OUTPUT), +		0 +	}, +}; + +static struct hda_bind_ctls alc260_dc7600_bind_switch = { +	.ops = &snd_hda_bind_sw, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x11, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), +		0 +	}, +}; + +static struct snd_kcontrol_new alc260_hp_dc7600_mixer[] = { +	HDA_BIND_VOL("Master Playback Volume", &alc260_dc7600_bind_master_vol), +	HDA_BIND_SW("LineOut Playback Switch", &alc260_dc7600_bind_switch), +	HDA_CODEC_MUTE("Speaker Playback Switch", 0x0f, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT), +	{ } /* end */ +}; +  static struct hda_verb alc260_hp_3013_unsol_verbs[] = {  	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},  	{}, @@ -4147,7 +4195,30 @@ static void alc260_hp_3013_unsol_event(struct hda_codec *codec,  		alc260_hp_3013_automute(codec);  } -/* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12,  +static void alc260_hp_3012_automute(struct hda_codec *codec) +{ +	unsigned int present, bits; + +	present = snd_hda_codec_read(codec, 0x10, 0, +			AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE; + +	bits = present ? 0 : PIN_OUT; +	snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    bits); +	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    bits); +	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    bits); +} + +static void alc260_hp_3012_unsol_event(struct hda_codec *codec, +				       unsigned int res) +{ +	if ((res >> 26) == ALC880_HP_EVENT) +		alc260_hp_3012_automute(codec); +} + +/* Fujitsu S702x series laptops.  ALC260 pin usage: Mic/Line jack = 0x12,   * HP jack = 0x14, CD audio =  0x16, internal speaker = 0x10.   */  static struct snd_kcontrol_new alc260_fujitsu_mixer[] = { @@ -4478,7 +4549,7 @@ static struct hda_verb alc260_fujitsu_init_verbs[] = {  	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},  	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, -	/* Ensure Line1 pin widget takes its input from the OUT1 sum bus  +	/* Ensure Line1 pin widget takes its input from the OUT1 sum bus  	 * when acting as an output.  	 */  	{0x0d, AC_VERB_SET_CONNECT_SEL, 0}, @@ -4503,14 +4574,14 @@ static struct hda_verb alc260_fujitsu_init_verbs[] = {  	 * stage.  	 */  	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	/* Unmute input buffer of pin widget used for Line-in (no equiv  +	/* Unmute input buffer of pin widget used for Line-in (no equiv  	 * mixer ctrl)  	 */  	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	/* Mute capture amp left and right */  	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, -	/* Set ADC connection select to match default mixer setting - line  +	/* Set ADC connection select to match default mixer setting - line  	 * in (on mic1 pin)  	 */  	{0x04, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -4564,7 +4635,7 @@ static struct hda_verb alc260_acer_init_verbs[] = {  	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},  	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, -	/* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum  +	/* Ensure Mic1 and Line1 pin widgets take input from the OUT1 sum  	 * bus when acting as outputs.  	 */  	{0x0b, AC_VERB_SET_CONNECT_SEL, 0}, @@ -4675,6 +4746,20 @@ static void alc260_replacer_672v_unsol_event(struct hda_codec *codec,                  alc260_replacer_672v_automute(codec);  } +static struct hda_verb alc260_hp_dc7600_verbs[] = { +	{0x05, AC_VERB_SET_CONNECT_SEL, 0x01}, +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, +	{0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, +	{0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; +  /* Test configuration for debugging, modelled after the ALC880 test   * configuration.   */ @@ -4686,7 +4771,7 @@ static hda_nid_t alc260_test_adc_nids[2] = {  	0x04, 0x05,  };  /* For testing the ALC260, each input MUX needs its own definition since - * the signal assignments are different.  This assumes that the first ADC  + * the signal assignments are different.  This assumes that the first ADC   * is NID 0x04.   */  static struct hda_input_mux alc260_test_capture_sources[2] = { @@ -4769,7 +4854,7 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {  	/* Switches to allow the digital IO pins to be enabled.  The datasheet  	 * is ambigious as to which NID is which; testing on laptops which -	 * make this output available should provide clarification.  +	 * make this output available should provide clarification.  	 */  	ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),  	ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01), @@ -4805,7 +4890,7 @@ static struct hda_verb alc260_test_init_verbs[] = {  	{0x03, AC_VERB_SET_DIGI_CONVERT_1, 0},  	{0x06, AC_VERB_SET_DIGI_CONVERT_1, 0}, -	/* Ensure mic1, mic2, line1 and line2 pin widgets take input from the  +	/* Ensure mic1, mic2, line1 and line2 pin widgets take input from the  	 * OUT1 sum bus when acting as an output.  	 */  	{0x0b, AC_VERB_SET_CONNECT_SEL, 0}, @@ -4897,7 +4982,7 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,  		sw_val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);  	} else  		return 0; /* N/A */ -	 +  	snprintf(name, sizeof(name), "%s Playback Volume", pfx);  	err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val);  	if (err < 0) @@ -5003,7 +5088,7 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec)  		int pin_type = get_pin_type(spec->autocfg.line_out_type);  		alc260_auto_set_output_and_unmute(codec, nid, pin_type, 0);  	} -	 +  	nid = spec->autocfg.speaker_pins[0];  	if (nid)  		alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); @@ -5045,7 +5130,7 @@ static struct hda_verb alc260_volume_init_verbs[] = {  	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},  	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	 +  	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback  	 * mixer widget  	 * Note: PASD motherboards uses the Line In 2 as the input for @@ -5074,7 +5159,7 @@ static struct hda_verb alc260_volume_init_verbs[] = {  	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	 +  	{ }  }; @@ -5155,6 +5240,7 @@ static const char *alc260_models[ALC260_MODEL_LAST] = {  	[ALC260_BASIC]		= "basic",  	[ALC260_HP]		= "hp",  	[ALC260_HP_3013]	= "hp-3013", +	[ALC260_HP_DC7600]	= "hp-dc7600",  	[ALC260_FUJITSU_S702X]	= "fujitsu",  	[ALC260_ACER]		= "acer",  	[ALC260_WILL]		= "will", @@ -5172,7 +5258,7 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {  	SND_PCI_QUIRK(0x103c, 0x280a, "HP d5750", ALC260_HP_3013),  	SND_PCI_QUIRK(0x103c, 0x3010, "HP", ALC260_HP_3013),  	SND_PCI_QUIRK(0x103c, 0x3011, "HP", ALC260_HP_3013), -	SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_3013), +	SND_PCI_QUIRK(0x103c, 0x3012, "HP", ALC260_HP_DC7600),  	SND_PCI_QUIRK(0x103c, 0x3013, "HP", ALC260_HP_3013),  	SND_PCI_QUIRK(0x103c, 0x3014, "HP", ALC260_HP),  	SND_PCI_QUIRK(0x103c, 0x3015, "HP", ALC260_HP), @@ -5218,6 +5304,22 @@ static struct alc_config_preset alc260_presets[] = {  		.unsol_event = alc260_hp_unsol_event,  		.init_hook = alc260_hp_automute,  	}, +	[ALC260_HP_DC7600] = { +		.mixers = { alc260_hp_dc7600_mixer, +			    alc260_input_mixer, +			    alc260_capture_alt_mixer }, +		.init_verbs = { alc260_init_verbs, +				alc260_hp_dc7600_verbs }, +		.num_dacs = ARRAY_SIZE(alc260_dac_nids), +		.dac_nids = alc260_dac_nids, +		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), +		.adc_nids = alc260_hp_adc_nids, +		.num_channel_mode = ARRAY_SIZE(alc260_modes), +		.channel_mode = alc260_modes, +		.input_mux = &alc260_capture_source, +		.unsol_event = alc260_hp_3012_unsol_event, +		.init_hook = alc260_hp_3012_automute, +	},  	[ALC260_HP_3013] = {  		.mixers = { alc260_hp_3013_mixer,  			    alc260_input_mixer, @@ -5933,7 +6035,7 @@ static struct hda_verb alc882_targa_verbs[] = {  	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},  	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, -	 +  	{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */  	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */  	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ @@ -5949,7 +6051,7 @@ static struct hda_verb alc882_targa_verbs[] = {  static void alc882_targa_automute(struct hda_codec *codec)  {   	unsigned int present; -  +   	present = snd_hda_codec_read(codec, 0x14, 0,  				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, @@ -5975,7 +6077,7 @@ static struct hda_verb alc882_asus_a7j_verbs[] = {  	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},  	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},  	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, -	 +  	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */  	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */  	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ @@ -5993,7 +6095,7 @@ static struct hda_verb alc882_asus_a7m_verbs[] = {  	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},  	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},  	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, -         +  	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */  	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */  	{0x16, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front */ @@ -6319,7 +6421,7 @@ static struct alc_config_preset alc882_presets[] = {  		.channel_mode = alc882_3ST_6ch_modes,  		.need_dac_fix = 1,  		.input_mux = &alc882_capture_source, -	},	 +	},  	[ALC882_ASUS_A7M] = {  		.mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },  		.init_verbs = { alc882_init_verbs, alc882_eapd_verbs, @@ -6332,14 +6434,14 @@ static struct alc_config_preset alc882_presets[] = {  		.channel_mode = alc880_threestack_modes,  		.need_dac_fix = 1,  		.input_mux = &alc882_capture_source, -	},	 +	},  };  /*   * Pin config fixes   */ -enum {  +enum {  	PINFIX_ABIT_AW9D_MAX  }; @@ -6554,16 +6656,19 @@ static int patch_alc882(struct hda_codec *codec)  			board_config = ALC885_MACPRO;  			break;  		case 0x106b1000: /* iMac 24 */ +		case 0x106b2800: /* AppleTV */  			board_config = ALC885_IMAC24;  			break;  		case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */ +		case 0x106b00a4: /* MacbookPro4,1 */  		case 0x106b2c00: /* Macbook Pro rev3 */  		case 0x106b3600: /* Macbook 3.1 */  			board_config = ALC885_MBP3;  			break;  		default:  			/* ALC889A is handled better as ALC888-compatible */ -			if (codec->revision_id == 0x100103) { +			if (codec->revision_id == 0x100101 || +			    codec->revision_id == 0x100103) {  				alc_free(codec);  				return patch_alc883(codec);  			} @@ -6718,6 +6823,23 @@ static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {  	},  }; +static struct hda_input_mux alc883_lenovo_sky_capture_source = { +	.num_items = 3, +	.items = { +		{ "Mic", 0x0 }, +		{ "Front Mic", 0x1 }, +		{ "Line", 0x4 }, +	}, +}; + +static struct hda_input_mux alc883_asus_eee1601_capture_source = { +	.num_items = 2, +	.items = { +		{ "Mic", 0x0 }, +		{ "Line", 0x2 }, +	}, +}; +  #define alc883_mux_enum_info alc_mux_enum_info  #define alc883_mux_enum_get alc_mux_enum_get  /* ALC883 has the ALC882-type input selection */ @@ -7032,13 +7154,11 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {  	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),  	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), -	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), -	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		/* .name = "Capture Source", */  		.name = "Input Source", -		.count = 2, +		.count = 1,  		.info = alc883_mux_enum_info,  		.get = alc883_mux_enum_get,  		.put = alc883_mux_enum_put, @@ -7256,7 +7376,7 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {  		.put = alc883_mux_enum_put,  	},  	{ } /* end */ -};	 +};  static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {  	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), @@ -7283,6 +7403,87 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {  	{ } /* end */  }; +static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = { +	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), +	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), +	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT), +	HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT), +	HDA_CODEC_VOLUME_MONO("Center Playback Volume", +						0x0d, 1, 0x0, HDA_OUTPUT), +	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT), +	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT), +	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT), +	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), +	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), +	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x1a, 0x0, HDA_OUTPUT), +	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), +	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), +	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), +	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), +	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), +	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		/* .name = "Capture Source", */ +		.name = "Input Source", +		.count = 2, +		.info = alc883_mux_enum_info, +		.get = alc883_mux_enum_get, +		.put = alc883_mux_enum_put, +	}, +	{ } /* end */ +}; + +static struct hda_bind_ctls alc883_bind_cap_vol = { +	.ops = &snd_hda_bind_vol, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), +		HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), +		0 +	}, +}; + +static struct hda_bind_ctls alc883_bind_cap_switch = { +	.ops = &snd_hda_bind_sw, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x08, 3, 0, HDA_INPUT), +		HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT), +		0 +	}, +}; + +static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = { +	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), +	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), +	HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT), +	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), +	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol), +	HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		/* .name = "Capture Source", */ +		.name = "Input Source", +		.count = 1, +		.info = alc883_mux_enum_info, +		.get = alc883_mux_enum_get, +		.put = alc883_mux_enum_put, +	}, +	{ } /* end */ +}; +  static struct snd_kcontrol_new alc883_chmode_mixer[] = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -7296,7 +7497,7 @@ static struct snd_kcontrol_new alc883_chmode_mixer[] = {  static struct hda_verb alc883_init_verbs[] = {  	/* ADC1: mute amp left and right */ -	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},  	/* ADC2: mute amp left and right */  	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, @@ -7361,14 +7562,14 @@ static struct hda_verb alc883_init_verbs[] = {  	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */  	/* Input mixer2 */  	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, -	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},  	/* Input mixer3 */  	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, -	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},  	{ }  }; @@ -7468,7 +7669,7 @@ static struct hda_verb alc883_tagra_verbs[] = {  	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},  	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, -	 +  	{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */  	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */  	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ @@ -7518,6 +7719,18 @@ static struct hda_verb alc883_haier_w66_verbs[] = {  	{ } /* end */  }; +static struct hda_verb alc888_lenovo_sky_verbs[] = { +	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x00}, +	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +	{ } /* end */ +}; +  static struct hda_verb alc888_3st_hp_verbs[] = {  	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Front: output 0 (0x0c) */  	{0x16, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Rear : output 1 (0x0d) */ @@ -7555,7 +7768,7 @@ static struct hda_channel_mode alc888_3st_hp_modes[2] = {  static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)  {   	unsigned int present; -  +   	present = snd_hda_codec_read(codec, 0x1b, 0,  				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, @@ -7568,7 +7781,7 @@ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)  static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)  {   	unsigned int present; -  +   	present = snd_hda_codec_read(codec, 0x14, 0,  				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, @@ -7598,7 +7811,7 @@ static struct hda_verb alc883_medion_md2_verbs[] = {  static void alc883_medion_md2_automute(struct hda_codec *codec)  {   	unsigned int present; -  +   	present = snd_hda_codec_read(codec, 0x14, 0,  				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, @@ -7753,7 +7966,7 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,  static void alc883_acer_aspire_automute(struct hda_codec *codec)  {   	unsigned int present; -  +   	present = snd_hda_codec_read(codec, 0x14, 0,  				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, @@ -7790,7 +8003,7 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {  static void alc888_6st_dell_front_automute(struct hda_codec *codec)  {   	unsigned int present; -  +   	present = snd_hda_codec_read(codec, 0x1b, 0,  				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, @@ -7814,6 +8027,50 @@ static void alc888_6st_dell_unsol_event(struct hda_codec *codec,  	}  } +static void alc888_lenovo_sky_front_automute(struct hda_codec *codec) +{ +	unsigned int mute; +	unsigned int present; + +	snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0); +	present = snd_hda_codec_read(codec, 0x1b, 0, +				     AC_VERB_GET_PIN_SENSE, 0); +	present = (present & 0x80000000) != 0; +	if (present) { +		/* mute internal speaker */ +		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, HDA_AMP_MUTE); +		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, HDA_AMP_MUTE); +		snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, HDA_AMP_MUTE); +		snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, HDA_AMP_MUTE); +		snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, HDA_AMP_MUTE); +	} else { +		/* unmute internal speaker if necessary */ +		mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0); +		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, mute); +		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, mute); +		snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, mute); +		snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, mute); +		snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0, +					 HDA_AMP_MUTE, mute); +	} +} + +static void alc883_lenovo_sky_unsol_event(struct hda_codec *codec, +					     unsigned int res) +{ +	if ((res >> 26) == ALC880_HP_EVENT) +		alc888_lenovo_sky_front_automute(codec); +} +  /*   * generic initialization of ADC, input mixers and output mixers   */ @@ -7898,6 +8155,105 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {  	{ } /* end */  }; +static struct hda_verb alc888_asus_m90v_verbs[] = { +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	/* enable unsolicited event */ +	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, +	{ } /* end */ +}; + +static void alc883_nb_mic_automute(struct hda_codec *codec) +{ +	unsigned int present; + +	present = snd_hda_codec_read(codec, 0x18, 0, +				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, +			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); +	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, +			    0x7000 | (0x01 << 8) | (present ? 0x80 : 0)); +} + +static void alc883_M90V_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; + +	present = snd_hda_codec_read(codec, 0x1b, 0, +				     AC_VERB_GET_PIN_SENSE, 0) +		& AC_PINSENSE_PRESENCE; +	bits = present ? 0 : PIN_OUT; +	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    bits); +	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    bits); +	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    bits); +} + +static void alc883_mode2_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc883_M90V_speaker_automute(codec); +		break; +	case ALC880_MIC_EVENT: +		alc883_nb_mic_automute(codec); +		break; +	} +} + +static void alc883_mode2_inithook(struct hda_codec *codec) +{ +	alc883_M90V_speaker_automute(codec); +	alc883_nb_mic_automute(codec); +} + +static struct hda_verb alc888_asus_eee1601_verbs[] = { +	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, +	{0x20, AC_VERB_SET_COEF_INDEX, 0x0b}, +	{0x20, AC_VERB_SET_PROC_COEF,  0x0838}, +	/* enable unsolicited event */ +	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +	{ } /* end */ +}; + +static void alc883_eee1601_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; + +	present = snd_hda_codec_read(codec, 0x14, 0, +				     AC_VERB_GET_PIN_SENSE, 0) +		& AC_PINSENSE_PRESENCE; +	bits = present ? 0 : PIN_OUT; +	snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    bits); +} + +static void alc883_eee1601_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc883_eee1601_speaker_automute(codec); +		break; +	} +} + +static void alc883_eee1601_inithook(struct hda_codec *codec) +{ +	alc883_eee1601_speaker_automute(codec); +} +  #ifdef CONFIG_SND_HDA_POWER_SAVE  #define alc883_loopbacks	alc880_loopbacks  #endif @@ -7927,6 +8283,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {  	[ALC883_LENOVO_101E_2ch] = "lenovo-101e",  	[ALC883_LENOVO_NB0763]	= "lenovo-nb0763",  	[ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig", +	[ALC888_LENOVO_SKY] = "lenovo-sky",  	[ALC883_HAIER_W66] 	= "haier-w66",  	[ALC888_3ST_HP]		= "3stack-hp",  	[ALC888_6ST_DELL]	= "6stack-dell", @@ -7942,7 +8299,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {  	SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),  	SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),  	SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE), -	SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),  +	SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),  	SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */  	SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),  	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG), @@ -7950,10 +8307,13 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {  	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),  	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),  	SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), +	SND_PCI_QUIRK(0x1043, 0x8317, "Asus M90V", ALC888_ASUS_M90V), +	SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),  	SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),  	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),  	SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),  	SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), +	SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),  	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),  	SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),  	SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG), @@ -7989,6 +8349,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {  	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),  	SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),  	SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763), +	SND_PCI_QUIRK(0x17aa, 0x101d, "Lenovo Sky", ALC888_LENOVO_SKY),  	SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),  	SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),  	SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66), @@ -8128,7 +8489,7 @@ static struct alc_config_preset alc883_presets[] = {  		.input_mux = &alc883_capture_source,  		.unsol_event = alc883_medion_md2_unsol_event,  		.init_hook = alc883_medion_md2_automute, -	},	 +	},  	[ALC883_LAPTOP_EAPD] = {  		.mixers = { alc883_base_mixer },  		.init_verbs = { alc883_init_verbs, alc882_eapd_verbs }, @@ -8245,6 +8606,49 @@ static struct alc_config_preset alc883_presets[] = {  		.unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,  		.init_hook = alc883_2ch_fujitsu_pi2515_automute,  	}, +	[ALC888_LENOVO_SKY] = { +		.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer }, +		.init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs}, +		.num_dacs = ARRAY_SIZE(alc883_dac_nids), +		.dac_nids = alc883_dac_nids, +		.dig_out_nid = ALC883_DIGOUT_NID, +		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids), +		.adc_nids = alc883_adc_nids, +		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), +		.channel_mode = alc883_sixstack_modes, +		.need_dac_fix = 1, +		.input_mux = &alc883_lenovo_sky_capture_source, +		.unsol_event = alc883_lenovo_sky_unsol_event, +		.init_hook = alc888_lenovo_sky_front_automute, +	}, +	[ALC888_ASUS_M90V] = { +		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, +		.init_verbs = { alc883_init_verbs, alc888_asus_m90v_verbs }, +		.num_dacs = ARRAY_SIZE(alc883_dac_nids), +		.dac_nids = alc883_dac_nids, +		.dig_out_nid = ALC883_DIGOUT_NID, +		.dig_in_nid = ALC883_DIGIN_NID, +		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), +		.channel_mode = alc883_3ST_6ch_modes, +		.need_dac_fix = 1, +		.input_mux = &alc883_fujitsu_pi2515_capture_source, +		.unsol_event = alc883_mode2_unsol_event, +		.init_hook = alc883_mode2_inithook, +	}, +	[ALC888_ASUS_EEE1601] = { +		.mixers = { alc883_asus_eee1601_mixer }, +		.init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs }, +		.num_dacs = ARRAY_SIZE(alc883_dac_nids), +		.dac_nids = alc883_dac_nids, +		.dig_out_nid = ALC883_DIGOUT_NID, +		.dig_in_nid = ALC883_DIGIN_NID, +		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), +		.channel_mode = alc883_3ST_2ch_modes, +		.need_dac_fix = 1, +		.input_mux = &alc883_asus_eee1601_capture_source, +		.unsol_event = alc883_eee1601_unsol_event, +		.init_hook = alc883_eee1601_inithook, +	},  }; @@ -8452,6 +8856,13 @@ static int patch_alc883(struct hda_codec *codec)  #define alc262_modes		alc260_modes  #define alc262_capture_source	alc882_capture_source +static hda_nid_t alc262_dmic_adc_nids[1] = { +	/* ADC0 */ +	0x09 +}; + +static hda_nid_t alc262_dmic_capsrc_nids[1] = { 0x22 }; +  static struct snd_kcontrol_new alc262_base_mixer[] = {  	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT), @@ -8833,10 +9244,10 @@ static struct hda_verb alc262_init_verbs[] = {  	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},  	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},  	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000}, -	 +  	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},  	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, -	 +  	/* FIXME: use matrix-type input source selection */  	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */  	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ @@ -8858,6 +9269,12 @@ static struct hda_verb alc262_init_verbs[] = {  	{ }  }; +static struct hda_verb alc262_eapd_verbs[] = { +	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, +	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, +	{ } +}; +  static struct hda_verb alc262_hippo_unsol_verbs[] = {  	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},  	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, @@ -8884,6 +9301,91 @@ static struct hda_verb alc262_sony_unsol_verbs[] = {  	{}  }; +static struct hda_input_mux alc262_dmic_capture_source = { +	.num_items = 2, +	.items = { +		{ "Int DMic", 0x9 }, +		{ "Mic", 0x0 }, +	}, +}; + +static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = { +	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		/* The multiple "Capture Source" controls confuse alsamixer +		 * So call somewhat different.. +		 */ +		/* .name = "Capture Source", */ +		.name = "Input Source", +		.count = 1, +		.info = alc_mux_enum_info, +		.get = alc_mux_enum_get, +		.put = alc_mux_enum_put, +	}, +	{ } /* end */ +}; + +static struct hda_verb alc262_toshiba_s06_verbs[] = { +	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +	{0x22, AC_VERB_SET_CONNECT_SEL, 0x09}, +	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; + +static void alc262_dmic_automute(struct hda_codec *codec) +{ +	unsigned int present; + +	present = snd_hda_codec_read(codec, 0x18, 0, +					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	snd_hda_codec_write(codec, 0x22, 0, +				AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09); +} + +/* toggle speaker-output according to the hp-jack state */ +static void alc262_toshiba_s06_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; + +	present = snd_hda_codec_read(codec, 0x15, 0, +					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	bits = present ? 0 : PIN_OUT; +	snd_hda_codec_write(codec, 0x14, 0, +					AC_VERB_SET_PIN_WIDGET_CONTROL, bits); +} + + + +/* unsolicited event for HP jack sensing */ +static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec, +				       unsigned int res) +{ +	if ((res >> 26) == ALC880_HP_EVENT) +		alc262_toshiba_s06_speaker_automute(codec); +	if ((res >> 26) == ALC880_MIC_EVENT) +		alc262_dmic_automute(codec); + +} + +static void alc262_toshiba_s06_init_hook(struct hda_codec *codec) +{ +	alc262_toshiba_s06_speaker_automute(codec); +	alc262_dmic_automute(codec); +} +  /* mute/unmute internal speaker according to the hp jack and mute state */  static void alc262_hippo_automute(struct hda_codec *codec)  { @@ -8948,6 +9450,41 @@ static void alc262_hippo1_unsol_event(struct hda_codec *codec,  }  /* + * nec model + *  0x15 = headphone + *  0x16 = internal speaker + *  0x18 = external mic + */ + +static struct snd_kcontrol_new alc262_nec_mixer[] = { +	HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 0, 0x0, HDA_OUTPUT), + +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + +	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), +	{ } /* end */ +}; + +static struct hda_verb alc262_nec_verbs[] = { +	/* Unmute Speaker */ +	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + +	/* Headphone */ +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + +	/* External mic to headphone */ +	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	/* External mic to speaker */ +	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{} +}; + +/*   * fujitsu model   *  0x14 = headphone/spdif-out, 0x15 = internal speaker,   *  0x1b = port replicator headphone out @@ -9179,6 +9716,25 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {  	{ } /* end */  }; +static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = { +	HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Master Playback Switch", +		.info = snd_hda_mixer_amp_switch_info, +		.get = snd_hda_mixer_amp_switch_get, +		.put = alc262_sony_master_sw_put, +		.private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), +	}, +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), +	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), +	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), +	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT), +	{ } /* end */ +}; +  /* additional init verbs for Benq laptops */  static struct hda_verb alc262_EAPD_verbs[] = {  	{0x20, AC_VERB_SET_COEF_INDEX, 0x07}, @@ -9427,7 +9983,7 @@ static struct hda_verb alc262_volume_init_verbs[] = {  	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},  	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},  	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, -	 +  	/* set up input amps for analog loopback */  	/* Amp Indices: DAC = 0, mixer = 1 */  	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -9482,7 +10038,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {  	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},  	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},          {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)}, -	 +  	/*  	 * Set up output mixers (0x0c - 0x0e)  	 */ @@ -9643,6 +10199,24 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {  	{ }  }; +static struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = { + +	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },	/* Front Speaker */ +	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, +	{0x14, AC_VERB_SET_CONNECT_SEL, 0x01}, + +	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },	/* MIC jack */ +	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },	/* Front MIC */ +	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, +	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) }, + +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },	/* HP  jack */ +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; + +  #ifdef CONFIG_SND_HDA_POWER_SAVE  #define alc262_loopbacks	alc880_loopbacks  #endif @@ -9729,13 +10303,17 @@ static const char *alc262_models[ALC262_MODEL_LAST] = {  	[ALC262_BENQ_ED8]	= "benq",  	[ALC262_BENQ_T31]	= "benq-t31",  	[ALC262_SONY_ASSAMD]	= "sony-assamd", +	[ALC262_TOSHIBA_S06]	= "toshiba-s06", +	[ALC262_TOSHIBA_RX1]	= "toshiba-rx1",  	[ALC262_ULTRA]		= "ultra",  	[ALC262_LENOVO_3000]	= "lenovo-3000", +	[ALC262_NEC]		= "nec",  	[ALC262_AUTO]		= "auto",  };  static struct snd_pci_quirk alc262_cfg_tbl[] = {  	SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO), +	SND_PCI_QUIRK(0x1033, 0x8895, "NEC Versa S9100", ALC262_NEC),  	SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),  	SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),  	SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC), @@ -9764,7 +10342,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {  	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),  	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),  	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1", -		      ALC262_SONY_ASSAMD), +		      ALC262_TOSHIBA_RX1), +	SND_PCI_QUIRK(0x1179, 0x0268, "Toshiba S06", ALC262_TOSHIBA_S06),  	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),  	SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FUJITSU),  	SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA), @@ -9918,7 +10497,7 @@ static struct alc_config_preset alc262_presets[] = {  		.input_mux = &alc262_capture_source,  		.unsol_event = alc262_hippo_unsol_event,  		.init_hook = alc262_hippo_automute, -	},	 +	},  	[ALC262_ULTRA] = {  		.mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },  		.init_verbs = { alc262_ultra_verbs }, @@ -9946,6 +10525,43 @@ static struct alc_config_preset alc262_presets[] = {  		.input_mux = &alc262_fujitsu_capture_source,  		.unsol_event = alc262_lenovo_3000_unsol_event,  	}, +	[ALC262_NEC] = { +		.mixers = { alc262_nec_mixer }, +		.init_verbs = { alc262_nec_verbs }, +		.num_dacs = ARRAY_SIZE(alc262_dac_nids), +		.dac_nids = alc262_dac_nids, +		.hp_nid = 0x03, +		.num_channel_mode = ARRAY_SIZE(alc262_modes), +		.channel_mode = alc262_modes, +		.input_mux = &alc262_capture_source, +	}, +	[ALC262_TOSHIBA_S06] = { +		.mixers = { alc262_toshiba_s06_mixer }, +		.init_verbs = { alc262_init_verbs, alc262_toshiba_s06_verbs, +							alc262_eapd_verbs }, +		.num_dacs = ARRAY_SIZE(alc262_dac_nids), +		.capsrc_nids = alc262_dmic_capsrc_nids, +		.dac_nids = alc262_dac_nids, +		.adc_nids = alc262_dmic_adc_nids, /* ADC0 */ +		.dig_out_nid = ALC262_DIGOUT_NID, +		.num_channel_mode = ARRAY_SIZE(alc262_modes), +		.channel_mode = alc262_modes, +		.input_mux = &alc262_dmic_capture_source, +		.unsol_event = alc262_toshiba_s06_unsol_event, +		.init_hook = alc262_toshiba_s06_init_hook, +	}, +	[ALC262_TOSHIBA_RX1] = { +		.mixers = { alc262_toshiba_rx1_mixer }, +		.init_verbs = { alc262_init_verbs, alc262_toshiba_rx1_unsol_verbs }, +		.num_dacs = ARRAY_SIZE(alc262_dac_nids), +		.dac_nids = alc262_dac_nids, +		.hp_nid = 0x03, +		.num_channel_mode = ARRAY_SIZE(alc262_modes), +		.channel_mode = alc262_modes, +		.input_mux = &alc262_capture_source, +		.unsol_event = alc262_hippo_unsol_event, +		.init_hook = alc262_hippo_automute, +	},  };  static int patch_alc262(struct hda_codec *codec) @@ -10004,7 +10620,7 @@ static int patch_alc262(struct hda_codec *codec)  	spec->stream_name_analog = "ALC262 Analog";  	spec->stream_analog_playback = &alc262_pcm_analog_playback;  	spec->stream_analog_capture = &alc262_pcm_analog_capture; -		 +  	spec->stream_name_digital = "ALC262 Digital";  	spec->stream_digital_playback = &alc262_pcm_digital_playback;  	spec->stream_digital_capture = &alc262_pcm_digital_capture; @@ -10040,7 +10656,7 @@ static int patch_alc262(struct hda_codec *codec)  	if (!spec->loopback.amplist)  		spec->loopback.amplist = alc262_loopbacks;  #endif -		 +  	return 0;  } @@ -10049,7 +10665,7 @@ static int patch_alc262(struct hda_codec *codec)   */  #define ALC268_DIGOUT_NID	ALC880_DIGOUT_NID  #define alc268_modes		alc260_modes -	 +  static hda_nid_t alc268_dac_nids[2] = {  	/* front, hp */  	0x02, 0x03 @@ -10109,6 +10725,14 @@ static struct hda_verb alc268_toshiba_verbs[] = {  	{ } /* end */  }; +static struct hda_input_mux alc268_acer_lc_capture_source = { +	.num_items = 2, +	.items = { +		{ "i-Mic", 0x6 }, +		{ "E-Mic", 0x0 }, +	}, +}; +  /* Acer specific */  /* bind volumes of both NID 0x02 and 0x03 */  static struct hda_bind_ctls alc268_acer_bind_master_vol = { @@ -10161,6 +10785,21 @@ static int alc268_acer_master_sw_put(struct snd_kcontrol *kcontrol,  	return change;  } +static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = { +	/* output mixer control */ +	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Master Playback Switch", +		.info = snd_hda_mixer_amp_switch_info, +		.get = snd_hda_mixer_amp_switch_get, +		.put = alc268_acer_master_sw_put, +		.private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +	}, +	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x18, 0, HDA_INPUT), +	{ } +}; +  static struct snd_kcontrol_new alc268_acer_mixer[] = {  	/* output mixer control */  	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), @@ -10178,6 +10817,16 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {  	{ }  }; +static struct hda_verb alc268_acer_aspire_one_verbs[] = { +	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x23, AC_VERB_SET_CONNECT_SEL, 0x06}, +	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, 0xa017}, +	{ } +}; +  static struct hda_verb alc268_acer_verbs[] = {  	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* internal dmic? */  	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, @@ -10185,7 +10834,6 @@ static struct hda_verb alc268_acer_verbs[] = {  	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},  	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},  	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, -  	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},  	{ }  }; @@ -10212,6 +10860,47 @@ static void alc268_acer_init_hook(struct hda_codec *codec)  	alc268_acer_automute(codec, 1);  } +/* toggle speaker-output according to the hp-jack state */ +static void alc268_aspire_one_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; + +	present = snd_hda_codec_read(codec, 0x15, 0, +				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	bits = present ? AMP_IN_MUTE(0) : 0; +	snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 1, +				AMP_IN_MUTE(0), bits); +} + + +static void alc268_acer_mic_automute(struct hda_codec *codec) +{ +	unsigned int present; + +	present = snd_hda_codec_read(codec, 0x18, 0, +				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL, +			    present ? 0x0 : 0x6); +} + +static void alc268_acer_lc_unsol_event(struct hda_codec *codec, +				    unsigned int res) +{ +	if ((res >> 26) == ALC880_HP_EVENT) +		alc268_aspire_one_speaker_automute(codec); +	if ((res >> 26) == ALC880_MIC_EVENT) +		alc268_acer_mic_automute(codec); +} + +static void alc268_acer_lc_init_hook(struct hda_codec *codec) +{ +	alc268_aspire_one_speaker_automute(codec); +	alc268_acer_mic_automute(codec); +} +  static struct snd_kcontrol_new alc268_dell_mixer[] = {  	/* output mixer control */  	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT), @@ -10360,7 +11049,7 @@ static struct hda_verb alc268_base_init_verbs[] = {  	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},  	/* Unmute Selector 23h,24h and set the default input to mic-in */ -	 +  	{0x23, AC_VERB_SET_CONNECT_SEL, 0x00},  	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x24, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -10559,7 +11248,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,  	nid = cfg->line_out_pins[0];  	if (nid) -		alc268_new_analog_output(spec, nid, "Front", 0);	 +		alc268_new_analog_output(spec, nid, "Front", 0);  	nid = cfg->speaker_pins[0];  	if (nid == 0x1d) { @@ -10581,7 +11270,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,  		if (err < 0)  			return err;  	} -	return 0;	 +	return 0;  }  /* create playback/capture controls for input pins */ @@ -10602,7 +11291,7 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,  		case 0x1a:  			idx1 = 2;	/* Line In */  			break; -		case 0x1c:	 +		case 0x1c:  			idx1 = 3;	/* CD */  			break;  		case 0x12: @@ -10614,7 +11303,7 @@ static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,  		}  		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];  		imux->items[imux->num_items].index = idx1; -		imux->num_items++;	 +		imux->num_items++;  	}  	return 0;  } @@ -10644,11 +11333,11 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)  	}  	dac_vol1 = dac_vol2 = 0xb000 | 0x40;	/* set max volume  */ -	if (line_nid == 0x14)	 +	if (line_nid == 0x14)  		dac_vol2 = AMP_OUT_ZERO;  	else if (line_nid == 0x15)  		dac_vol1 = AMP_OUT_ZERO; -	if (hp_nid == 0x14)	 +	if (hp_nid == 0x14)  		dac_vol2 = AMP_OUT_ZERO;  	else if (hp_nid == 0x15)  		dac_vol1 = AMP_OUT_ZERO; @@ -10739,6 +11428,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {  	[ALC268_3ST]		= "3stack",  	[ALC268_TOSHIBA]	= "toshiba",  	[ALC268_ACER]		= "acer", +	[ALC268_ACER_ASPIRE_ONE]	= "acer-aspire",  	[ALC268_DELL]		= "dell",  	[ALC268_ZEPTO]		= "zepto",  #ifdef CONFIG_SND_DEBUG @@ -10753,11 +11443,14 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {  	SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),  	SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),  	SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER), +	SND_PCI_QUIRK(0x1025, 0x015b, "Acer Aspire One", +						ALC268_ACER_ASPIRE_ONE),  	SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),  	SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),  	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),  	SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),  	SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA), +	SND_PCI_QUIRK(0x1179, 0xff64, "TOSHIBA L305", ALC268_TOSHIBA),  	SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),  	SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),  	SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1), @@ -10830,6 +11523,23 @@ static struct alc_config_preset alc268_presets[] = {  		.unsol_event = alc268_acer_unsol_event,  		.init_hook = alc268_acer_init_hook,  	}, +	[ALC268_ACER_ASPIRE_ONE] = { +		.mixers = { alc268_acer_aspire_one_mixer, +				alc268_capture_alt_mixer }, +		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, +				alc268_acer_aspire_one_verbs }, +		.num_dacs = ARRAY_SIZE(alc268_dac_nids), +		.dac_nids = alc268_dac_nids, +		.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt), +		.adc_nids = alc268_adc_nids_alt, +		.capsrc_nids = alc268_capsrc_nids, +		.hp_nid = 0x03, +		.num_channel_mode = ARRAY_SIZE(alc268_modes), +		.channel_mode = alc268_modes, +		.input_mux = &alc268_acer_lc_capture_source, +		.unsol_event = alc268_acer_lc_unsol_event, +		.init_hook = alc268_acer_lc_init_hook, +	},  	[ALC268_DELL] = {  		.mixers = { alc268_dell_mixer, alc268_beep_mixer },  		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs, @@ -10974,7 +11684,7 @@ static int patch_alc268(struct hda_codec *codec)  	codec->patch_ops = alc_patch_ops;  	if (board_config == ALC268_AUTO)  		spec->init_hook = alc268_auto_init; -		 +  	return 0;  } @@ -10990,6 +11700,14 @@ static hda_nid_t alc269_adc_nids[1] = {  	0x08,  }; +static hda_nid_t alc269_capsrc_nids[1] = { +	0x23, +}; + +/* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24), + *       not a mux! + */ +  static struct hda_input_mux alc269_eeepc_dmic_capture_source = {  	.num_items = 2,  	.items = { @@ -11016,6 +11734,8 @@ static struct snd_kcontrol_new alc269_base_mixer[] = {  	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),  	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT), +	HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT),  	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),  	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),  	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), @@ -11025,6 +11745,28 @@ static struct snd_kcontrol_new alc269_base_mixer[] = {  	{ } /* end */  }; +static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = { +	/* output mixer control */ +	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Master Playback Switch", +		.info = snd_hda_mixer_amp_switch_info, +		.get = snd_hda_mixer_amp_switch_get, +		.put = alc268_acer_master_sw_put, +		.private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +	}, +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), +	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), +	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), +	HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT), +	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT), +	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT), +	{ } +}; +  /* bind volumes of both NID 0x0c and 0x0d */  static struct hda_bind_ctls alc269_epc_bind_vol = {  	.ops = &snd_hda_bind_vol, @@ -11068,75 +11810,72 @@ static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {  	{ } /* end */  }; -/* - * generic initialization of ADC, input mixers and output mixers - */ -static struct hda_verb alc269_init_verbs[] = { -	/* -	 * Unmute ADC0 and set the default input to mic-in -	 */ -	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +/* beep control */ +static struct snd_kcontrol_new alc269_beep_mixer[] = { +	HDA_CODEC_VOLUME("Beep Playback Volume", 0x0b, 0x4, HDA_INPUT), +	HDA_CODEC_MUTE("Beep Playback Switch", 0x0b, 0x4, HDA_INPUT), +	{ } /* end */ +}; -	/* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the -	 * analog-loopback mixer widget -	 * Note: PASD motherboards uses the Line In 2 as the input for -	 * front panel mic (mic 2) -	 */ -	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ -	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, -	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, -	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, -	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, -	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, +static struct hda_verb alc269_quanta_fl1_verbs[] = { +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, +	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{ } +}; -	/* -	 * Set up output mixers (0x0c - 0x0e) -	 */ -	/* set vol=0 to output mixers */ -	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, -	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, +/* toggle speaker-output according to the hp-jack state */ +static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; -	/* set up input amps for analog loopback */ -	/* Amp Indices: DAC = 0, mixer = 1 */ -	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	present = snd_hda_codec_read(codec, 0x15, 0, +			AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	bits = present ? AMP_IN_MUTE(0) : 0; +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, +			AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, +			AMP_IN_MUTE(0), bits); -	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, -	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, -	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, -	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, -	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, -	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, -	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	snd_hda_codec_write(codec, 0x20, 0, +			AC_VERB_SET_COEF_INDEX, 0x0c); +	snd_hda_codec_write(codec, 0x20, 0, +			AC_VERB_SET_PROC_COEF, 0x680); -	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, -	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, -	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, -	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, -	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +	snd_hda_codec_write(codec, 0x20, 0, +			AC_VERB_SET_COEF_INDEX, 0x0c); +	snd_hda_codec_write(codec, 0x20, 0, +			AC_VERB_SET_PROC_COEF, 0x480); +} -	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, -	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, +static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec) +{ +	unsigned int present; -	/* FIXME: use matrix-type input source selection */ -	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ -	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ -	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, -	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, -	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, +	present = snd_hda_codec_read(codec, 0x18, 0, +				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	snd_hda_codec_write(codec, 0x23, 0, +			    AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1); +} -	/* set EAPD */ -	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, -	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, -	{ } -}; +static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec, +				    unsigned int res) +{ +	if ((res >> 26) == ALC880_HP_EVENT) +		alc269_quanta_fl1_speaker_automute(codec); +	if ((res >> 26) == ALC880_MIC_EVENT) +		alc269_quanta_fl1_mic_automute(codec); +} + +static void alc269_quanta_fl1_init_hook(struct hda_codec *codec) +{ +	alc269_quanta_fl1_speaker_automute(codec); +	alc269_quanta_fl1_mic_automute(codec); +}  static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {  	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, @@ -11163,42 +11902,42 @@ static struct hda_verb alc269_eeepc_amic_init_verbs[] = {  static void alc269_speaker_automute(struct hda_codec *codec)  {  	unsigned int present; -	unsigned int bits; +	unsigned char bits;  	present = snd_hda_codec_read(codec, 0x15, 0, -				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	bits = present ? AMP_IN_MUTE(0) : 0;  	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, -				 AMP_IN_MUTE(0), bits); +				AMP_IN_MUTE(0), bits);  	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, -				 AMP_IN_MUTE(0), bits); +				AMP_IN_MUTE(0), bits);  }  static void alc269_eeepc_dmic_automute(struct hda_codec *codec)  {  	unsigned int present; -	present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0) -		& AC_PINSENSE_PRESENCE; -	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL, -			    present ? 0 : 5); +	present = snd_hda_codec_read(codec, 0x18, 0, +				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	snd_hda_codec_write(codec, 0x23, 0, +				AC_VERB_SET_CONNECT_SEL,  (present ? 0 : 5));  }  static void alc269_eeepc_amic_automute(struct hda_codec *codec)  {  	unsigned int present; -	present = snd_hda_codec_read(codec, 0x18, 0, AC_VERB_GET_PIN_SENSE, 0) -		& AC_PINSENSE_PRESENCE; +	present = snd_hda_codec_read(codec, 0x18, 0, +				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;  	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE, -			    present ? AMP_IN_UNMUTE(0) : AMP_IN_MUTE(0)); +				0x7000 | (0x00 << 8) | (present ? 0 : 0x80));  	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE, -			    present ? AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1)); +				0x7000 | (0x01 << 8) | (present ? 0x80 : 0));  }  /* unsolicited event for HP jack sensing */  static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec, -					  unsigned int res) +				     unsigned int res)  {  	if ((res >> 26) == ALC880_HP_EVENT)  		alc269_speaker_automute(codec); @@ -11215,7 +11954,7 @@ static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)  /* unsolicited event for HP jack sensing */  static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec, -					  unsigned int res) +				     unsigned int res)  {  	if ((res >> 26) == ALC880_HP_EVENT)  		alc269_speaker_automute(codec); @@ -11230,6 +11969,76 @@ static void alc269_eeepc_amic_inithook(struct hda_codec *codec)  	alc269_eeepc_amic_automute(codec);  } +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc269_init_verbs[] = { +	/* +	 * Unmute ADC0 and set the default input to mic-in +	 */ +	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + +	/* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the +	 * analog-loopback mixer widget +	 * Note: PASD motherboards uses the Line In 2 as the input for +	 * front panel mic (mic 2) +	 */ +	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ +	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, +	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, +	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, +	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + +	/* +	 * Set up output mixers (0x0c - 0x0e) +	 */ +	/* set vol=0 to output mixers */ +	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, +	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + +	/* set up input amps for analog loopback */ +	/* Amp Indices: DAC = 0, mixer = 1 */ +	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + +	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, +	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, +	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, +	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + +	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, +	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + +	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + +	/* FIXME: use matrix-type input source selection */ +	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */ +	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ +	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, +	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + +	/* set EAPD */ +	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2}, +	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2}, +	{ } +}; +  /* add playback controls from the parsed DAC table */  static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,  					     const struct auto_pin_cfg *cfg) @@ -11330,7 +12139,7 @@ static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,  static int alc269_parse_auto_config(struct hda_codec *codec)  {  	struct alc_spec *spec = codec->spec; -	int err; +	int i, err;  	static hda_nid_t alc269_ignore[] = { 0x1d, 0 };  	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, @@ -11353,9 +12162,20 @@ static int alc269_parse_auto_config(struct hda_codec *codec)  	if (spec->kctl_alloc)  		spec->mixers[spec->num_mixers++] = spec->kctl_alloc; +	/* create a beep mixer control if the pin 0x1d isn't assigned */ +	for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++) +		if (spec->autocfg.input_pins[i] == 0x1d) +			break; +	if (i >= ARRAY_SIZE(spec->autocfg.input_pins)) +		spec->mixers[spec->num_mixers++] = alc269_beep_mixer; +  	spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;  	spec->num_mux_defs = 1;  	spec->input_mux = &spec->private_imux; +	/* set default input source */ +	snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0], +				  0, AC_VERB_SET_CONNECT_SEL, +				  spec->input_mux->items[0].index);  	err = alc_auto_add_mic_boost(codec);  	if (err < 0) @@ -11387,14 +12207,20 @@ static void alc269_auto_init(struct hda_codec *codec)   * configuration and preset   */  static const char *alc269_models[ALC269_MODEL_LAST] = { -	[ALC269_BASIC]		= "basic", +	[ALC269_BASIC]			= "basic", +	[ALC269_QUANTA_FL1]		= "quanta", +	[ALC269_ASUS_EEEPC_P703]	= "eeepc-p703", +	[ALC269_ASUS_EEEPC_P901]	= "eeepc-p901"  };  static struct snd_pci_quirk alc269_cfg_tbl[] = { +	SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),  	SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",  		      ALC269_ASUS_EEEPC_P703),  	SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",  		      ALC269_ASUS_EEEPC_P901), +	SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101", +		      ALC269_ASUS_EEEPC_P901),  	{}  }; @@ -11409,6 +12235,18 @@ static struct alc_config_preset alc269_presets[] = {  		.channel_mode = alc269_modes,  		.input_mux = &alc269_capture_source,  	}, +	[ALC269_QUANTA_FL1] = { +		.mixers = { alc269_quanta_fl1_mixer }, +		.init_verbs = { alc269_init_verbs, alc269_quanta_fl1_verbs }, +		.num_dacs = ARRAY_SIZE(alc269_dac_nids), +		.dac_nids = alc269_dac_nids, +		.hp_nid = 0x03, +		.num_channel_mode = ARRAY_SIZE(alc269_modes), +		.channel_mode = alc269_modes, +		.input_mux = &alc269_capture_source, +		.unsol_event = alc269_quanta_fl1_unsol_event, +		.init_hook = alc269_quanta_fl1_init_hook, +	},  	[ALC269_ASUS_EEEPC_P703] = {  		.mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },  		.init_verbs = { alc269_init_verbs, @@ -11488,6 +12326,7 @@ static int patch_alc269(struct hda_codec *codec)  	spec->adc_nids = alc269_adc_nids;  	spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids); +	spec->capsrc_nids = alc269_capsrc_nids;  	codec->patch_ops = alc_patch_ops;  	if (board_config == ALC269_AUTO) @@ -11689,7 +12528,7 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = {  	HDA_CODEC_MUTE("Master Playback Switch", 0x03, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),  	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT), -	 +          /*Capture mixer control */  	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), @@ -11832,20 +12671,20 @@ static struct hda_verb alc861_base_init_verbs[] = {  	/* route front mic to ADC1*/  	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},  	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	 +  	/* Unmute DAC0~3 & spdif out*/  	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	 +  	/* Unmute Mixer 14 (mic) 1c (Line in)*/  	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},          {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},          {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	 +  	/* Unmute Stereo Mixer 15 */  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -11901,13 +12740,13 @@ static struct hda_verb alc861_threestack_init_verbs[] = {  	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	 +  	/* Unmute Mixer 14 (mic) 1c (Line in)*/  	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},          {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},          {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	 +  	/* Unmute Stereo Mixer 15 */  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -11963,13 +12802,13 @@ static struct hda_verb alc861_uniwill_m31_init_verbs[] = {  	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	 +  	/* Unmute Mixer 14 (mic) 1c (Line in)*/  	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},          {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},          {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	 +  	/* Unmute Stereo Mixer 15 */  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -12034,7 +12873,7 @@ static struct hda_verb alc861_asus_init_verbs[] = {          {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},          {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	 +  	/* Unmute Stereo Mixer 15 */  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -12071,20 +12910,20 @@ static struct hda_verb alc861_auto_init_verbs[] = {  	 */  	/* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */  	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, -	 +  	/* Unmute DAC0~3 & spdif out*/  	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},  	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	 +  	/* Unmute Mixer 14 (mic) 1c (Line in)*/  	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, -	 +  	/* Unmute Stereo Mixer 15 */  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},  	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, @@ -12659,7 +13498,7 @@ static int patch_alc861(struct hda_codec *codec)  	if (!spec->loopback.amplist)  		spec->loopback.amplist = alc861_loopbacks;  #endif -		 +  	return 0;  } @@ -12913,7 +13752,7 @@ static struct snd_kcontrol_new alc861vd_hp_mixer[] = {  	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),  	HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),  	HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), -	 +  	{ } /* end */  }; @@ -13058,7 +13897,7 @@ static struct hda_verb alc861vd_lenovo_unsol_verbs[] = {  	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},  	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, -	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},	 +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},  	{}  }; @@ -13120,7 +13959,7 @@ static struct hda_verb alc861vd_dallas_verbs[] = {  	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},  	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},  	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, -	 +  	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},  	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},  	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, @@ -13145,7 +13984,7 @@ static struct hda_verb alc861vd_dallas_verbs[] = {  	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},  	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, -	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},	 +	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},  	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},  	{ } /* end */ @@ -13304,7 +14143,7 @@ static struct alc_config_preset alc861vd_presets[] = {  		.input_mux = &alc861vd_hp_capture_source,  		.unsol_event = alc861vd_dallas_unsol_event,  		.init_hook = alc861vd_dallas_automute, -	},		 +	},  };  /* @@ -13883,13 +14722,120 @@ static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {  	{ } /* end */  }; +static struct hda_bind_ctls alc663_asus_bind_master_vol = { +	.ops = &snd_hda_bind_vol, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x03, 3, 0, HDA_OUTPUT), +		0 +	}, +}; + +static struct hda_bind_ctls alc663_asus_one_bind_switch = { +	.ops = &snd_hda_bind_sw, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), +		0 +	}, +}; +  static struct snd_kcontrol_new alc663_m51va_mixer[] = { +	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), +	HDA_BIND_SW("Master Playback Switch", &alc663_asus_one_bind_switch), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	{ } /* end */ +}; + +static struct hda_bind_ctls alc663_asus_tree_bind_switch = { +	.ops = &snd_hda_bind_sw, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), +		0 +	}, +}; + +static struct snd_kcontrol_new alc663_two_hp_m1_mixer[] = { +	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), +	HDA_BIND_SW("Master Playback Switch", &alc663_asus_tree_bind_switch), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), +	HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + +	{ } /* end */ +}; + +static struct hda_bind_ctls alc663_asus_four_bind_switch = { +	.ops = &snd_hda_bind_sw, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), +		0 +	}, +}; + +static struct snd_kcontrol_new alc663_two_hp_m2_mixer[] = { +	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), +	HDA_BIND_SW("Master Playback Switch", &alc663_asus_four_bind_switch), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), +	HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +	{ } /* end */ +}; + +static struct snd_kcontrol_new alc662_1bjd_mixer[] = {  	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("F-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), +	HDA_CODEC_MUTE("F-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +	{ } /* end */ +}; + +static struct hda_bind_ctls alc663_asus_two_bind_master_vol = { +	.ops = &snd_hda_bind_vol, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x04, 3, 0, HDA_OUTPUT), +		0 +	}, +}; + +static struct hda_bind_ctls alc663_asus_two_bind_switch = { +	.ops = &snd_hda_bind_sw, +	.values = { +		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), +		HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT), +		0 +	}, +}; + +static struct snd_kcontrol_new alc663_asus_21jd_clfe_mixer[] = { +	HDA_BIND_VOL("Master Playback Volume", +				&alc663_asus_two_bind_master_vol), +	HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), +	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), -	HDA_CODEC_MUTE("DMic Playback Switch", 0x23, 0x9, HDA_INPUT), +	{ } /* end */ +}; + +static struct snd_kcontrol_new alc663_asus_15jd_clfe_mixer[] = { +	HDA_BIND_VOL("Master Playback Volume", &alc663_asus_bind_master_vol), +	HDA_BIND_SW("Master Playback Switch", &alc663_asus_two_bind_switch), +	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), +	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),  	{ } /* end */  }; @@ -14074,14 +15020,81 @@ static struct hda_verb alc663_auto_init_verbs[] = {  };  static struct hda_verb alc663_m51va_init_verbs[] = { +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},  	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},  	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, -	{0x21, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Headphone */ +	{0x21, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */ +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; -	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(9)}, +static struct hda_verb alc663_21jd_amic_init_verbs[] = { +	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x21, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */ +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; +static struct hda_verb alc662_1bjd_amic_init_verbs[] = { +	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Headphone */ +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; + +static struct hda_verb alc663_15jd_amic_init_verbs[] = { +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */ +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; + +static struct hda_verb alc663_two_hp_amic_m1_init_verbs[] = { +	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x21, AC_VERB_SET_CONNECT_SEL, 0x0},	/* Headphone */ +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x0},	/* Headphone */ +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},  	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},  	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; + +static struct hda_verb alc663_two_hp_amic_m2_init_verbs[] = { +	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, +	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */ +	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, +	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, +	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Headphone */ +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},  	{}  }; @@ -14110,6 +15123,14 @@ static struct hda_verb alc663_g50v_init_verbs[] = {  	{}  }; +static struct hda_verb alc662_ecs_init_verbs[] = { +	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, +	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, +	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT}, +	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, +	{} +}; +  /* capture mixer elements */  static struct snd_kcontrol_new alc662_capture_mixer[] = {  	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), @@ -14129,6 +15150,12 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = {  	{ } /* end */  }; +static struct snd_kcontrol_new alc662_auto_capture_mixer[] = { +	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), +	{ } /* end */ +}; +  static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)  {  	unsigned int present; @@ -14209,12 +15236,12 @@ static void alc662_eeepc_ep20_automute(struct hda_codec *codec)  	if (present) {  		/* mute internal speaker */  		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, -					 HDA_AMP_MUTE, HDA_AMP_MUTE); +					HDA_AMP_MUTE, HDA_AMP_MUTE);  	} else {  		/* unmute internal speaker if necessary */  		mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);  		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, -					 HDA_AMP_MUTE, mute); +					HDA_AMP_MUTE, mute);  	}  } @@ -14237,11 +15264,108 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec)  	unsigned char bits;  	present = snd_hda_codec_read(codec, 0x21, 0, -				     AC_VERB_GET_PIN_SENSE, 0) -		& AC_PINSENSE_PRESENCE; +			AC_VERB_GET_PIN_SENSE, 0) +			& AC_PINSENSE_PRESENCE;  	bits = present ? HDA_AMP_MUTE : 0; -	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0, -				 HDA_AMP_MUTE, bits); +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, +				AMP_IN_MUTE(0), bits); +} + +static void alc663_21jd_two_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; + +	present = snd_hda_codec_read(codec, 0x21, 0, +			AC_VERB_GET_PIN_SENSE, 0) +			& AC_PINSENSE_PRESENCE; +	bits = present ? HDA_AMP_MUTE : 0; +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1, +				AMP_IN_MUTE(0), bits); +} + +static void alc663_15jd_two_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; + +	present = snd_hda_codec_read(codec, 0x15, 0, +			AC_VERB_GET_PIN_SENSE, 0) +			& AC_PINSENSE_PRESENCE; +	bits = present ? HDA_AMP_MUTE : 0; +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 0, +				AMP_IN_MUTE(0), bits); +	snd_hda_codec_amp_stereo(codec, 0x0e, HDA_INPUT, 1, +				AMP_IN_MUTE(0), bits); +} + +static void alc662_f5z_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present; +	unsigned char bits; + +	present = snd_hda_codec_read(codec, 0x1b, 0, +			AC_VERB_GET_PIN_SENSE, 0) +			& AC_PINSENSE_PRESENCE; +	bits = present ? 0 : PIN_OUT; +	snd_hda_codec_write(codec, 0x14, 0, +			 AC_VERB_SET_PIN_WIDGET_CONTROL, bits); +} + +static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present1, present2; + +	present1 = snd_hda_codec_read(codec, 0x21, 0, +			AC_VERB_GET_PIN_SENSE, 0) +			& AC_PINSENSE_PRESENCE; +	present2 = snd_hda_codec_read(codec, 0x15, 0, +			AC_VERB_GET_PIN_SENSE, 0) +			& AC_PINSENSE_PRESENCE; + +	if (present1 || present2) { +		snd_hda_codec_write_cache(codec, 0x14, 0, +			AC_VERB_SET_PIN_WIDGET_CONTROL, 0); +	} else { +		snd_hda_codec_write_cache(codec, 0x14, 0, +			AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); +	} +} + +static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec) +{ +	unsigned int present1, present2; + +	present1 = snd_hda_codec_read(codec, 0x1b, 0, +				AC_VERB_GET_PIN_SENSE, 0) +				& AC_PINSENSE_PRESENCE; +	present2 = snd_hda_codec_read(codec, 0x15, 0, +				AC_VERB_GET_PIN_SENSE, 0) +				& AC_PINSENSE_PRESENCE; + +	if (present1 || present2) { +		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, +				AMP_IN_MUTE(0), AMP_IN_MUTE(0)); +		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, +				AMP_IN_MUTE(0), AMP_IN_MUTE(0)); +	} else { +		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0, +				AMP_IN_MUTE(0), 0); +		snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1, +				AMP_IN_MUTE(0), 0); +	}  }  static void alc663_m51va_mic_automute(struct hda_codec *codec) @@ -14249,16 +15373,16 @@ static void alc663_m51va_mic_automute(struct hda_codec *codec)  	unsigned int present;  	present = snd_hda_codec_read(codec, 0x18, 0, -				     AC_VERB_GET_PIN_SENSE, 0) -		& AC_PINSENSE_PRESENCE; +			AC_VERB_GET_PIN_SENSE, 0) +			& AC_PINSENSE_PRESENCE;  	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, -			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); +			0x7000 | (0x00 << 8) | (present ? 0 : 0x80));  	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, -			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80)); +			0x7000 | (0x00 << 8) | (present ? 0 : 0x80));  	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE, -			    0x7000 | (0x09 << 8) | (present ? 0x80 : 0)); +			0x7000 | (0x09 << 8) | (present ? 0x80 : 0));  	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE, -			    0x7000 | (0x09 << 8) | (present ? 0x80 : 0)); +			0x7000 | (0x09 << 8) | (present ? 0x80 : 0));  }  static void alc663_m51va_unsol_event(struct hda_codec *codec, @@ -14280,6 +15404,121 @@ static void alc663_m51va_inithook(struct hda_codec *codec)  	alc663_m51va_mic_automute(codec);  } +/* ***************** Mode1 ******************************/ +static void alc663_mode1_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc663_m51va_speaker_automute(codec); +		break; +	case ALC880_MIC_EVENT: +		alc662_eeepc_mic_automute(codec); +		break; +	} +} + +static void alc663_mode1_inithook(struct hda_codec *codec) +{ +	alc663_m51va_speaker_automute(codec); +	alc662_eeepc_mic_automute(codec); +} +/* ***************** Mode2 ******************************/ +static void alc662_mode2_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc662_f5z_speaker_automute(codec); +		break; +	case ALC880_MIC_EVENT: +		alc662_eeepc_mic_automute(codec); +		break; +	} +} + +static void alc662_mode2_inithook(struct hda_codec *codec) +{ +	alc662_f5z_speaker_automute(codec); +	alc662_eeepc_mic_automute(codec); +} +/* ***************** Mode3 ******************************/ +static void alc663_mode3_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc663_two_hp_m1_speaker_automute(codec); +		break; +	case ALC880_MIC_EVENT: +		alc662_eeepc_mic_automute(codec); +		break; +	} +} + +static void alc663_mode3_inithook(struct hda_codec *codec) +{ +	alc663_two_hp_m1_speaker_automute(codec); +	alc662_eeepc_mic_automute(codec); +} +/* ***************** Mode4 ******************************/ +static void alc663_mode4_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc663_21jd_two_speaker_automute(codec); +		break; +	case ALC880_MIC_EVENT: +		alc662_eeepc_mic_automute(codec); +		break; +	} +} + +static void alc663_mode4_inithook(struct hda_codec *codec) +{ +	alc663_21jd_two_speaker_automute(codec); +	alc662_eeepc_mic_automute(codec); +} +/* ***************** Mode5 ******************************/ +static void alc663_mode5_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc663_15jd_two_speaker_automute(codec); +		break; +	case ALC880_MIC_EVENT: +		alc662_eeepc_mic_automute(codec); +		break; +	} +} + +static void alc663_mode5_inithook(struct hda_codec *codec) +{ +	alc663_15jd_two_speaker_automute(codec); +	alc662_eeepc_mic_automute(codec); +} +/* ***************** Mode6 ******************************/ +static void alc663_mode6_unsol_event(struct hda_codec *codec, +					   unsigned int res) +{ +	switch (res >> 26) { +	case ALC880_HP_EVENT: +		alc663_two_hp_m2_speaker_automute(codec); +		break; +	case ALC880_MIC_EVENT: +		alc662_eeepc_mic_automute(codec); +		break; +	} +} + +static void alc663_mode6_inithook(struct hda_codec *codec) +{ +	alc663_two_hp_m2_speaker_automute(codec); +	alc662_eeepc_mic_automute(codec); +} +  static void alc663_g71v_hp_automute(struct hda_codec *codec)  {  	unsigned int present; @@ -14350,6 +15589,46 @@ static void alc663_g50v_inithook(struct hda_codec *codec)  	alc662_eeepc_mic_automute(codec);  } +/* bind hp and internal speaker mute (with plug check) */ +static int alc662_ecs_master_sw_put(struct snd_kcontrol *kcontrol, +				     struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	long *valp = ucontrol->value.integer.value; +	int change; + +	change = snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0, +					  HDA_AMP_MUTE, +					  valp[0] ? 0 : HDA_AMP_MUTE); +	change |= snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0, +					   HDA_AMP_MUTE, +					   valp[1] ? 0 : HDA_AMP_MUTE); +	if (change) +		alc262_hippo1_automute(codec); +	return change; +} + +static struct snd_kcontrol_new alc662_ecs_mixer[] = { +	HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Master Playback Switch", +		.info = snd_hda_mixer_amp_switch_info, +		.get = snd_hda_mixer_amp_switch_get, +		.put = alc662_ecs_master_sw_put, +		.private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT), +	}, + +	HDA_CODEC_VOLUME("e-Mic/LineIn Boost", 0x18, 0, HDA_INPUT), +	HDA_CODEC_VOLUME("e-Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("e-Mic/LineIn Playback Switch", 0x0b, 0x0, HDA_INPUT), + +	HDA_CODEC_VOLUME("i-Mic Boost", 0x19, 0, HDA_INPUT), +	HDA_CODEC_VOLUME("i-Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), +	HDA_CODEC_MUTE("i-Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), +	{ } /* end */ +}; +  #ifdef CONFIG_SND_HDA_POWER_SAVE  #define alc662_loopbacks	alc880_loopbacks  #endif @@ -14372,21 +15651,67 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {  	[ALC662_LENOVO_101E]	= "lenovo-101e",  	[ALC662_ASUS_EEEPC_P701] = "eeepc-p701",  	[ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20", +	[ALC662_ECS] = "ecs",  	[ALC663_ASUS_M51VA] = "m51va",  	[ALC663_ASUS_G71V] = "g71v",  	[ALC663_ASUS_H13] = "h13",  	[ALC663_ASUS_G50V] = "g50v", +	[ALC663_ASUS_MODE1] = "asus-mode1", +	[ALC662_ASUS_MODE2] = "asus-mode2", +	[ALC663_ASUS_MODE3] = "asus-mode3", +	[ALC663_ASUS_MODE4] = "asus-mode4", +	[ALC663_ASUS_MODE5] = "asus-mode5", +	[ALC663_ASUS_MODE6] = "asus-mode6",  	[ALC662_AUTO]		= "auto",  };  static struct snd_pci_quirk alc662_cfg_tbl[] = { -	SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS G71V", ALC663_ASUS_G71V),  	SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M51VA", ALC663_ASUS_M51VA),  	SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS M51VA", ALC663_ASUS_G50V),  	SND_PCI_QUIRK(0x1043, 0x8290, "ASUS P5GC-MX", ALC662_3ST_6ch_DIG),  	SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),  	SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20), +	SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x1878, "ASUS M50Vr", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x19b3, "ASUS F7Z", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x1953, "ASUS NB", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x19a3, "ASUS NB", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x1203, "ASUS NB", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x19e3, "ASUS NB", ALC663_ASUS_MODE1), +	SND_PCI_QUIRK(0x1043, 0x19c3, "ASUS F5Z/F6x", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1339, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1913, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1843, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1813, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1876, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1864, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1783, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1753, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x16c3, "ASUS NB", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1933, "ASUS F80Q", ALC662_ASUS_MODE2), +	SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3), +	SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3), +	SND_PCI_QUIRK(0x1043, 0x1963, "ASUS X71C", ALC663_ASUS_MODE3), +	SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3), +	SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3), +	SND_PCI_QUIRK(0x1043, 0x19f3, "ASUS NB", ALC663_ASUS_MODE4), +	SND_PCI_QUIRK(0x1043, 0x1823, "ASUS NB", ALC663_ASUS_MODE5), +	SND_PCI_QUIRK(0x1043, 0x1833, "ASUS NB", ALC663_ASUS_MODE6), +	SND_PCI_QUIRK(0x1043, 0x1763, "ASUS NB", ALC663_ASUS_MODE6), +	SND_PCI_QUIRK(0x1043, 0x1765, "ASUS NB", ALC663_ASUS_MODE6), +	SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K", +		      ALC662_3ST_6ch_DIG),  	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), +	SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_ECS), +	SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS), +	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L", +		      ALC662_3ST_6ch_DIG), +	SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG), +	SND_PCI_QUIRK(0x1849, 0x3662, "ASROCK K10N78FullHD-hSLI R3.0", +					ALC662_3ST_6ch_DIG),  	SND_PCI_QUIRK(0x1854, 0x2000, "ASUS H13-2000", ALC663_ASUS_H13),  	SND_PCI_QUIRK(0x1854, 0x2001, "ASUS H13-2001", ALC663_ASUS_H13),  	SND_PCI_QUIRK(0x1854, 0x2002, "ASUS H13-2002", ALC663_ASUS_H13), @@ -14477,6 +15802,18 @@ static struct alc_config_preset alc662_presets[] = {  		.unsol_event = alc662_eeepc_ep20_unsol_event,  		.init_hook = alc662_eeepc_ep20_inithook,  	}, +	[ALC662_ECS] = { +		.mixers = { alc662_ecs_mixer, alc662_capture_mixer }, +		.init_verbs = { alc662_init_verbs, +				alc662_ecs_init_verbs }, +		.num_dacs = ARRAY_SIZE(alc662_dac_nids), +		.dac_nids = alc662_dac_nids, +		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), +		.channel_mode = alc662_3ST_2ch_modes, +		.input_mux = &alc662_eeepc_capture_source, +		.unsol_event = alc662_eeepc_unsol_event, +		.init_hook = alc662_eeepc_inithook, +	},  	[ALC663_ASUS_M51VA] = {  		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},  		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs }, @@ -14524,6 +15861,91 @@ static struct alc_config_preset alc662_presets[] = {  		.unsol_event = alc663_g50v_unsol_event,  		.init_hook = alc663_g50v_inithook,  	}, +	[ALC663_ASUS_MODE1] = { +		.mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer }, +		.init_verbs = { alc662_init_verbs, +				alc663_21jd_amic_init_verbs }, +		.num_dacs = ARRAY_SIZE(alc662_dac_nids), +		.hp_nid = 0x03, +		.dac_nids = alc662_dac_nids, +		.dig_out_nid = ALC662_DIGOUT_NID, +		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), +		.channel_mode = alc662_3ST_2ch_modes, +		.input_mux = &alc662_eeepc_capture_source, +		.unsol_event = alc663_mode1_unsol_event, +		.init_hook = alc663_mode1_inithook, +	}, +	[ALC662_ASUS_MODE2] = { +		.mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer }, +		.init_verbs = { alc662_init_verbs, +				alc662_1bjd_amic_init_verbs }, +		.num_dacs = ARRAY_SIZE(alc662_dac_nids), +		.dac_nids = alc662_dac_nids, +		.dig_out_nid = ALC662_DIGOUT_NID, +		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), +		.channel_mode = alc662_3ST_2ch_modes, +		.input_mux = &alc662_eeepc_capture_source, +		.unsol_event = alc662_mode2_unsol_event, +		.init_hook = alc662_mode2_inithook, +	}, +	[ALC663_ASUS_MODE3] = { +		.mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer }, +		.init_verbs = { alc662_init_verbs, +				alc663_two_hp_amic_m1_init_verbs }, +		.num_dacs = ARRAY_SIZE(alc662_dac_nids), +		.hp_nid = 0x03, +		.dac_nids = alc662_dac_nids, +		.dig_out_nid = ALC662_DIGOUT_NID, +		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), +		.channel_mode = alc662_3ST_2ch_modes, +		.input_mux = &alc662_eeepc_capture_source, +		.unsol_event = alc663_mode3_unsol_event, +		.init_hook = alc663_mode3_inithook, +	}, +	[ALC663_ASUS_MODE4] = { +		.mixers = { alc663_asus_21jd_clfe_mixer, +				alc662_auto_capture_mixer}, +		.init_verbs = { alc662_init_verbs, +				alc663_21jd_amic_init_verbs}, +		.num_dacs = ARRAY_SIZE(alc662_dac_nids), +		.hp_nid = 0x03, +		.dac_nids = alc662_dac_nids, +		.dig_out_nid = ALC662_DIGOUT_NID, +		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), +		.channel_mode = alc662_3ST_2ch_modes, +		.input_mux = &alc662_eeepc_capture_source, +		.unsol_event = alc663_mode4_unsol_event, +		.init_hook = alc663_mode4_inithook, +	}, +	[ALC663_ASUS_MODE5] = { +		.mixers = { alc663_asus_15jd_clfe_mixer, +				alc662_auto_capture_mixer }, +		.init_verbs = { alc662_init_verbs, +				alc663_15jd_amic_init_verbs }, +		.num_dacs = ARRAY_SIZE(alc662_dac_nids), +		.hp_nid = 0x03, +		.dac_nids = alc662_dac_nids, +		.dig_out_nid = ALC662_DIGOUT_NID, +		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), +		.channel_mode = alc662_3ST_2ch_modes, +		.input_mux = &alc662_eeepc_capture_source, +		.unsol_event = alc663_mode5_unsol_event, +		.init_hook = alc663_mode5_inithook, +	}, +	[ALC663_ASUS_MODE6] = { +		.mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer }, +		.init_verbs = { alc662_init_verbs, +				alc663_two_hp_amic_m2_init_verbs }, +		.num_dacs = ARRAY_SIZE(alc662_dac_nids), +		.hp_nid = 0x03, +		.dac_nids = alc662_dac_nids, +		.dig_out_nid = ALC662_DIGOUT_NID, +		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), +		.channel_mode = alc662_3ST_2ch_modes, +		.input_mux = &alc662_eeepc_capture_source, +		.unsol_event = alc663_mode6_unsol_event, +		.init_hook = alc663_mode6_inithook, +	},  }; @@ -14560,15 +15982,15 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,  							      HDA_OUTPUT));  			if (err < 0)  				return err; -			err = add_control(spec, ALC_CTL_BIND_MUTE, +			err = add_control(spec, ALC_CTL_WIDGET_MUTE,  					  "Center Playback Switch", -					  HDA_COMPOSE_AMP_VAL(nid, 1, 2, +					  HDA_COMPOSE_AMP_VAL(0x0e, 1, 0,  							      HDA_INPUT));  			if (err < 0)  				return err; -			err = add_control(spec, ALC_CTL_BIND_MUTE, +			err = add_control(spec, ALC_CTL_WIDGET_MUTE,  					  "LFE Playback Switch", -					  HDA_COMPOSE_AMP_VAL(nid, 2, 2, +					  HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,  							      HDA_INPUT));  			if (err < 0)  				return err; @@ -14580,9 +16002,9 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,  			if (err < 0)  				return err;  			sprintf(name, "%s Playback Switch", chname[i]); -			err = add_control(spec, ALC_CTL_BIND_MUTE, name, -					  HDA_COMPOSE_AMP_VAL(nid, 3, 2, -							      HDA_INPUT)); +			err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, +				HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i), +						    3, 0, HDA_INPUT));  			if (err < 0)  				return err;  		} @@ -14777,7 +16199,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec)  	spec->num_mux_defs = 1;  	spec->input_mux = &spec->private_imux; -	 +  	spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;  	if (codec->vendor_id == 0x10ec0663)  		spec->init_verbs[spec->num_init_verbs++] = @@ -14896,6 +16318,8 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {  	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },  	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },  	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, +	{ .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A", +	  .patch = patch_alc882 }, /* should be patch_alc883() in future */  	{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",  	  .patch = patch_alc882 }, /* should be patch_alc883() in future */  	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index ad994fc..c461baa 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -33,10 +33,12 @@  #include "hda_codec.h"  #include "hda_local.h"  #include "hda_patch.h" +#include "hda_beep.h"  #define NUM_CONTROL_ALLOC	32  #define STAC_PWR_EVENT		0x20  #define STAC_HP_EVENT		0x30 +#define STAC_VREF_EVENT		0x40  enum {  	STAC_REF, @@ -71,9 +73,15 @@ enum {  };  enum { +	STAC_92HD83XXX_REF, +	STAC_92HD83XXX_MODELS +}; + +enum {  	STAC_92HD71BXX_REF,  	STAC_DELL_M4_1,  	STAC_DELL_M4_2, +	STAC_HP_M4,  	STAC_92HD71BXX_MODELS  }; @@ -104,6 +112,7 @@ enum {  	STAC_MACBOOK_PRO_V2,  	STAC_IMAC_INTEL,  	STAC_IMAC_INTEL_20, +	STAC_ECS_202,  	STAC_922X_DELL_D81,  	STAC_922X_DELL_D82,  	STAC_922X_DELL_M81, @@ -130,6 +139,7 @@ struct sigmatel_spec {  	unsigned int mic_switch: 1;  	unsigned int alt_switch: 1;  	unsigned int hp_detect: 1; +	unsigned int spdif_mute: 1;  	/* gpio lines */  	unsigned int eapd_mask; @@ -138,17 +148,22 @@ struct sigmatel_spec {  	unsigned int gpio_data;  	unsigned int gpio_mute; +	/* stream */ +	unsigned int stream_delay; +  	/* analog loopback */  	unsigned char aloopback_mask;  	unsigned char aloopback_shift;  	/* power management */  	unsigned int num_pwrs; +	unsigned int *pwr_mapping;  	hda_nid_t *pwr_nids;  	hda_nid_t *dac_list;  	/* playback */  	struct hda_input_mux *mono_mux; +	struct hda_input_mux *amp_mux;  	unsigned int cur_mmux;  	struct hda_multi_out multiout;  	hda_nid_t dac_nids[5]; @@ -162,8 +177,14 @@ struct sigmatel_spec {  	unsigned int num_dmics;  	hda_nid_t *dmux_nids;  	unsigned int num_dmuxes; +	hda_nid_t *smux_nids; +	unsigned int num_smuxes; +	const char **spdif_labels; +  	hda_nid_t dig_in_nid;  	hda_nid_t mono_nid; +	hda_nid_t anabeep_nid; +	hda_nid_t digbeep_nid;  	/* pin widgets */  	hda_nid_t *pin_nids; @@ -180,6 +201,12 @@ struct sigmatel_spec {  	unsigned int cur_dmux[2];  	struct hda_input_mux *input_mux;  	unsigned int cur_mux[3]; +	struct hda_input_mux *sinput_mux; +	unsigned int cur_smux[2]; +	unsigned int cur_amux; +	hda_nid_t *amp_nids; +	unsigned int num_amps; +	unsigned int powerdown_adcs;  	/* i/o switches */  	unsigned int io_switch[2]; @@ -195,6 +222,8 @@ struct sigmatel_spec {  	struct snd_kcontrol_new *kctl_alloc;  	struct hda_input_mux private_dimux;  	struct hda_input_mux private_imux; +	struct hda_input_mux private_smux; +	struct hda_input_mux private_amp_mux;  	struct hda_input_mux private_mono_mux;  }; @@ -215,10 +244,19 @@ static hda_nid_t stac92hd73xx_pwr_nids[8] = {  	0x0f, 0x10, 0x11  }; +static hda_nid_t stac92hd73xx_slave_dig_outs[2] = { +	0x26, 0, +}; +  static hda_nid_t stac92hd73xx_adc_nids[2] = {  	0x1a, 0x1b  }; +#define DELL_M6_AMP 2 +static hda_nid_t stac92hd73xx_amp_nids[3] = { +	0x0b, 0x0c, 0x0e +}; +  #define STAC92HD73XX_NUM_DMICS	2  static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {  	0x13, 0x14, 0 @@ -237,6 +275,41 @@ static hda_nid_t stac92hd73xx_dmux_nids[2] = {  	0x20, 0x21,  }; +static hda_nid_t stac92hd73xx_smux_nids[2] = { +	0x22, 0x23, +}; + +#define STAC92HD83XXX_NUM_DMICS	2 +static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = { +	0x11, 0x12, 0 +}; + +#define STAC92HD81_DAC_COUNT 2 +#define STAC92HD83_DAC_COUNT 3 +static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = { +	0x13, 0x14, 0x22, +}; + +static hda_nid_t stac92hd83xxx_dmux_nids[2] = { +	0x17, 0x18, +}; + +static hda_nid_t stac92hd83xxx_adc_nids[2] = { +	0x15, 0x16, +}; + +static hda_nid_t stac92hd83xxx_pwr_nids[4] = { +	0xa, 0xb, 0xd, 0xe, +}; + +static hda_nid_t stac92hd83xxx_slave_dig_outs[2] = { +	0x1e, 0, +}; + +static unsigned int stac92hd83xxx_pwr_mapping[4] = { +	0x03, 0x0c, 0x10, 0x40, +}; +  static hda_nid_t stac92hd71bxx_pwr_nids[3] = {  	0x0a, 0x0d, 0x0f  }; @@ -253,6 +326,10 @@ static hda_nid_t stac92hd71bxx_dmux_nids[1] = {  	0x1c,  }; +static hda_nid_t stac92hd71bxx_smux_nids[2] = { +	0x24, 0x25, +}; +  static hda_nid_t stac92hd71bxx_dac_nids[1] = {  	0x10, /*0x11, */  }; @@ -262,6 +339,10 @@ static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {  	0x18, 0x19, 0  }; +static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = { +	0x22, 0 +}; +  static hda_nid_t stac925x_adc_nids[1] = {          0x03,  }; @@ -299,6 +380,10 @@ static hda_nid_t stac927x_mux_nids[3] = {          0x15, 0x16, 0x17  }; +static hda_nid_t stac927x_smux_nids[1] = { +	0x21, +}; +  static hda_nid_t stac927x_dac_nids[6] = {  	0x02, 0x03, 0x04, 0x05, 0x06, 0  }; @@ -312,6 +397,11 @@ static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {  	0x13, 0x14, 0  }; +static const char *stac927x_spdif_labels[5] = { +	"Digital Playback", "ADAT", "Analog Mux 1", +	"Analog Mux 2", "Analog Mux 3" +}; +  static hda_nid_t stac9205_adc_nids[2] = {          0x12, 0x13  }; @@ -324,6 +414,10 @@ static hda_nid_t stac9205_dmux_nids[1] = {  	0x1d,  }; +static hda_nid_t stac9205_smux_nids[1] = { +	0x21, +}; +  #define STAC9205_NUM_DMICS	2  static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {          0x17, 0x18, 0 @@ -347,12 +441,18 @@ static hda_nid_t stac922x_pin_nids[10] = {  static hda_nid_t stac92hd73xx_pin_nids[13] = {  	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,  	0x0f, 0x10, 0x11, 0x12, 0x13, -	0x14, 0x1e, 0x22 +	0x14, 0x22, 0x23  }; -static hda_nid_t stac92hd71bxx_pin_nids[10] = { +static hda_nid_t stac92hd83xxx_pin_nids[14] = { +	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, +	0x0f, 0x10, 0x11, 0x12, 0x13, +	0x1d, 0x1e, 0x1f, 0x20 +}; +static hda_nid_t stac92hd71bxx_pin_nids[11] = {  	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,  	0x0f, 0x14, 0x18, 0x19, 0x1e, +	0x1f,  };  static hda_nid_t stac927x_pin_nids[14] = { @@ -367,6 +467,34 @@ static hda_nid_t stac9205_pin_nids[12] = {  	0x21, 0x22,  }; +#define stac92xx_amp_volume_info snd_hda_mixer_amp_volume_info + +static int stac92xx_amp_volume_get(struct snd_kcontrol *kcontrol, +				 struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; +	hda_nid_t nid = spec->amp_nids[spec->cur_amux]; + +	kcontrol->private_value ^= get_amp_nid(kcontrol); +	kcontrol->private_value |= nid; + +	return snd_hda_mixer_amp_volume_get(kcontrol, ucontrol); +} + +static int stac92xx_amp_volume_put(struct snd_kcontrol *kcontrol, +				 struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; +	hda_nid_t nid = spec->amp_nids[spec->cur_amux]; + +	kcontrol->private_value ^= get_amp_nid(kcontrol); +	kcontrol->private_value |= nid; + +	return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol); +} +  static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,  				   struct snd_ctl_elem_info *uinfo)  { @@ -397,6 +525,58 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol,  			spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]);  } +static int stac92xx_smux_enum_info(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_info *uinfo) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; +	return snd_hda_input_mux_info(spec->sinput_mux, uinfo); +} + +static int stac92xx_smux_enum_get(struct snd_kcontrol *kcontrol, +				  struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; +	unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + +	ucontrol->value.enumerated.item[0] = spec->cur_smux[smux_idx]; +	return 0; +} + +static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol, +				  struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; +	struct hda_input_mux *smux = &spec->private_smux; +	unsigned int smux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); +	int err, val; +	hda_nid_t nid; + +	err = snd_hda_input_mux_put(codec, spec->sinput_mux, ucontrol, +			spec->smux_nids[smux_idx], &spec->cur_smux[smux_idx]); +	if (err < 0) +		return err; + +	if (spec->spdif_mute) { +		if (smux_idx == 0) +			nid = spec->multiout.dig_out_nid; +		else +			nid = codec->slave_dig_outs[smux_idx - 1]; +		if (spec->cur_smux[smux_idx] == smux->num_items - 1) +			val = AMP_OUT_MUTE; +		if (smux_idx == 0) +			nid = spec->multiout.dig_out_nid; +		else +			nid = codec->slave_dig_outs[smux_idx - 1]; +		/* un/mute SPDIF out */ +		snd_hda_codec_write_cache(codec, nid, 0, +			AC_VERB_SET_AMP_GAIN_MUTE, val); +	} +	return 0; +} +  static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)  {  	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); @@ -452,6 +632,41 @@ static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,  				     spec->mono_nid, &spec->cur_mmux);  } +static int stac92xx_amp_mux_enum_info(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_info *uinfo) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; +	return snd_hda_input_mux_info(spec->amp_mux, uinfo); +} + +static int stac92xx_amp_mux_enum_get(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; + +	ucontrol->value.enumerated.item[0] = spec->cur_amux; +	return 0; +} + +static int stac92xx_amp_mux_enum_put(struct snd_kcontrol *kcontrol, +	struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct sigmatel_spec *spec = codec->spec; +	struct snd_kcontrol *ctl = +		snd_hda_find_mixer_ctl(codec, "Amp Capture Volume"); +	if (!ctl) +		return -EINVAL; + +	snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE | +		SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); + +	return snd_hda_input_mux_put(codec, spec->amp_mux, ucontrol, +				     0, &spec->cur_amux); +} +  #define stac92xx_aloopback_info snd_ctl_boolean_mono_info  static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, @@ -546,8 +761,8 @@ static struct hda_verb dell_eq_core_init[] = {  	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},  	/* setup audio connections */  	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, -	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, -	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02}, +	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02}, +	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01},  	/* setup adcs to point to mixer */  	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},  	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, @@ -628,6 +843,19 @@ static struct hda_verb stac92hd73xx_10ch_core_init[] = {  	{}  }; +static struct hda_verb stac92hd83xxx_core_init[] = { +	/* start of config #1 */ +	{ 0xe, AC_VERB_SET_CONNECT_SEL, 0x3}, + +	/* start of config #2 */ +	{ 0xa, AC_VERB_SET_CONNECT_SEL, 0x0}, +	{ 0xb, AC_VERB_SET_CONNECT_SEL, 0x0}, +	{ 0xd, AC_VERB_SET_CONNECT_SEL, 0x1}, + +	/* power state controls amps */ +	{ 0x01, AC_VERB_SET_EAPD, 1 << 2}, +}; +  static struct hda_verb stac92hd71bxx_core_init[] = {  	/* set master volume and direct control */  	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -690,12 +918,16 @@ static struct hda_verb d965_core_init[] = {  static struct hda_verb stac927x_core_init[] = {  	/* set master volume and direct control */	  	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +	/* enable analog pc beep path */ +	{ 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},  	{}  };  static struct hda_verb stac9205_core_init[] = {  	/* set master volume and direct control */	  	{ 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, +	/* enable analog pc beep path */ +	{ 0x01, AC_VERB_SET_DIGI_CONVERT_2, 1 << 5},  	{}  }; @@ -709,6 +941,31 @@ static struct hda_verb stac9205_core_init[] = {  		.put = stac92xx_mono_mux_enum_put, \  	} +#define STAC_AMP_MUX \ +	{ \ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ +		.name = "Amp Selector Capture Switch", \ +		.count = 1, \ +		.info = stac92xx_amp_mux_enum_info, \ +		.get = stac92xx_amp_mux_enum_get, \ +		.put = stac92xx_amp_mux_enum_put, \ +	} + +#define STAC_AMP_VOL(xname, nid, chs, idx, dir) \ +	{ \ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ +		.name = xname, \ +		.index = 0, \ +		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ +			SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ +			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ +		.info = stac92xx_amp_volume_info, \ +		.get = stac92xx_amp_volume_get, \ +		.put = stac92xx_amp_volume_put, \ +		.tlv = { .c = snd_hda_mixer_amp_tlv }, \ +		.private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \ +	} +  #define STAC_INPUT_SOURCE(cnt) \  	{ \  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -736,33 +993,36 @@ static struct snd_kcontrol_new stac9200_mixer[] = {  	STAC_INPUT_SOURCE(1),  	HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),  	HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), -	HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT),  	{ } /* end */  }; +#define DELL_M6_MIXER 6  static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { -	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), - -	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), -	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), - -	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), -	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), - +	/* start of config #1 */  	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),  	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), -	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), -	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), -  	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),  	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), +	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), +	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + +	/* start of config #2 */ +	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), +  	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),  	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), -	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), -	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), +	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3), + +	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + +	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), +  	{ } /* end */  }; @@ -818,19 +1078,46 @@ static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {  	{ } /* end */  }; + +static struct snd_kcontrol_new stac92hd83xxx_mixer[] = { +	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_OUTPUT), + +	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT), +	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT), + +	HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT), +	HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT), + +	HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x1, HDA_INPUT), +	HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, HDA_INPUT), + +	HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x2, HDA_INPUT), +	HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, HDA_INPUT), + +	HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x3, HDA_INPUT), +	HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, HDA_INPUT), + +	/* +	HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x4, HDA_INPUT), +	HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT), +	*/ +	{ } /* end */ +}; +  static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {  	STAC_INPUT_SOURCE(2),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), - +	/* analog pc-beep replaced with digital beep support */ +	/*  	HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT),  	HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT), +	*/  	HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT),  	HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), @@ -843,11 +1130,9 @@ static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT),  	{ } /* end */  }; @@ -855,7 +1140,6 @@ static struct snd_kcontrol_new stac925x_mixer[] = {  	STAC_INPUT_SOURCE(1),  	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),  	HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT), -	HDA_CODEC_VOLUME("Capture Mux Volume", 0x0f, 0, HDA_OUTPUT),  	{ } /* end */  }; @@ -865,12 +1149,9 @@ static struct snd_kcontrol_new stac9205_mixer[] = {  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x19, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x1A, 0x0, HDA_OUTPUT), -  	{ } /* end */  }; @@ -879,11 +1160,9 @@ static struct snd_kcontrol_new stac922x_mixer[] = {  	STAC_INPUT_SOURCE(2),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT), -	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x12, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT), -	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x13, 0x0, HDA_OUTPUT),  	{ } /* end */  }; @@ -894,15 +1173,12 @@ static struct snd_kcontrol_new stac927x_mixer[] = {  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x0, 0x15, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x1, 0x16, 0x0, HDA_OUTPUT),  	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),  	HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT), -	HDA_CODEC_VOLUME_IDX("Mux Capture Volume", 0x2, 0x17, 0x0, HDA_OUTPUT),  	{ } /* end */  }; @@ -915,6 +1191,15 @@ static struct snd_kcontrol_new stac_dmux_mixer = {  	.put = stac92xx_dmux_enum_put,  }; +static struct snd_kcontrol_new stac_smux_mixer = { +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	.name = "IEC958 Playback Source", +	/* count set later */ +	.info = stac92xx_smux_enum_info, +	.get = stac92xx_smux_enum_get, +	.put = stac92xx_smux_enum_put, +}; +  static const char *slave_vols[] = {  	"Front Playback Volume",  	"Surround Playback Volume", @@ -966,6 +1251,22 @@ static int stac92xx_build_controls(struct hda_codec *codec)  		if (err < 0)  			return err;  	} +	if (spec->num_smuxes > 0) { +		int wcaps = get_wcaps(codec, spec->multiout.dig_out_nid); +		struct hda_input_mux *smux = &spec->private_smux; +		/* check for mute support on SPDIF out */ +		if (wcaps & AC_WCAP_OUT_AMP) { +			smux->items[smux->num_items].label = "Off"; +			smux->items[smux->num_items].index = 0; +			smux->num_items++; +			spec->spdif_mute = 1; +		} +		stac_smux_mixer.count = spec->num_smuxes; +		err = snd_ctl_add(codec->bus->card, +				  snd_ctl_new1(&stac_smux_mixer, codec)); +		if (err < 0) +			return err; +	}  	if (spec->multiout.dig_out_nid) {  		err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); @@ -977,7 +1278,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)  			return err;  		spec->multiout.share_spdif = 1;  	} -	if (spec->dig_in_nid) { +	if (spec->dig_in_nid && (!spec->gpio_dir & 0x01)) {  		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);  		if (err < 0)  			return err; @@ -1325,40 +1626,65 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {  	{} /* terminator */  }; -static unsigned int ref92hd71bxx_pin_configs[10] = { +static unsigned int ref92hd83xxx_pin_configs[14] = { +	0x02214030, 0x02211010, 0x02a19020, 0x02170130, +	0x01014050, 0x01819040, 0x01014020, 0x90a3014e, +	0x40f000f0, 0x40f000f0, 0x40f000f0, 0x40f000f0, +	0x01451160, 0x98560170, +}; + +static unsigned int *stac92hd83xxx_brd_tbl[STAC_92HD83XXX_MODELS] = { +	[STAC_92HD83XXX_REF] = ref92hd83xxx_pin_configs, +}; + +static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = { +	[STAC_92HD83XXX_REF] = "ref", +}; + +static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { +	/* SigmaTel reference board */ +	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, +		      "DFI LanParty", STAC_92HD71BXX_REF), +}; + +static unsigned int ref92hd71bxx_pin_configs[11] = {  	0x02214030, 0x02a19040, 0x01a19020, 0x01014010,  	0x0181302e, 0x01114010, 0x01019020, 0x90a000f0, -	0x90a000f0, 0x01452050, +	0x90a000f0, 0x01452050, 0x01452050,  }; -static unsigned int dell_m4_1_pin_configs[10] = { +static unsigned int dell_m4_1_pin_configs[11] = {  	0x0421101f, 0x04a11221, 0x40f000f0, 0x90170110,  	0x23a1902e, 0x23014250, 0x40f000f0, 0x90a000f0, -	0x40f000f0, 0x4f0000f0, +	0x40f000f0, 0x4f0000f0, 0x4f0000f0,  }; -static unsigned int dell_m4_2_pin_configs[10] = { +static unsigned int dell_m4_2_pin_configs[11] = {  	0x0421101f, 0x04a11221, 0x90a70330, 0x90170110,  	0x23a1902e, 0x23014250, 0x40f000f0, 0x40f000f0, -	0x40f000f0, 0x044413b0, +	0x40f000f0, 0x044413b0, 0x044413b0,  };  static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = {  	[STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs,  	[STAC_DELL_M4_1]	= dell_m4_1_pin_configs,  	[STAC_DELL_M4_2]	= dell_m4_2_pin_configs, +	[STAC_HP_M4]		= NULL,  };  static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {  	[STAC_92HD71BXX_REF] = "ref",  	[STAC_DELL_M4_1] = "dell-m4-1",  	[STAC_DELL_M4_2] = "dell-m4-2", +	[STAC_HP_M4] = "hp-m4",  };  static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {  	/* SigmaTel reference board */  	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,  		      "DFI LanParty", STAC_92HD71BXX_REF), +	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a, +				"unknown HP", STAC_HP_M4),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,  				"unknown Dell", STAC_DELL_M4_1),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234, @@ -1477,6 +1803,11 @@ static unsigned int intel_mac_v5_pin_configs[10] = {  	0x400000fc, 0x400000fb,  }; +static unsigned int ecs202_pin_configs[10] = { +	0x0221401f, 0x02a19020, 0x01a19020, 0x01114010, +	0x408000f0, 0x01813022, 0x074510a0, 0x40c400f1, +	0x9037012e, 0x40e000f2, +};  static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {  	[STAC_D945_REF] = ref922x_pin_configs, @@ -1495,6 +1826,7 @@ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {  	[STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,  	[STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,  	[STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs, +	[STAC_ECS_202] = ecs202_pin_configs,  	[STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,  	[STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,	  	[STAC_922X_DELL_M81] = dell_922x_m81_pin_configs, @@ -1518,6 +1850,7 @@ static const char *stac922x_models[STAC_922X_MODELS] = {  	[STAC_MACBOOK_PRO_V2]	= "macbook-pro",  	[STAC_IMAC_INTEL] = "imac-intel",  	[STAC_IMAC_INTEL_20] = "imac-intel-20", +	[STAC_ECS_202] = "ecs202",  	[STAC_922X_DELL_D81] = "dell-d81",  	[STAC_922X_DELL_D82] = "dell-d82",  	[STAC_922X_DELL_M81] = "dell-m81", @@ -1604,6 +1937,33 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {  		      "unknown Dell", STAC_922X_DELL_D81),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,  		      "Dell XPS M1210", STAC_922X_DELL_M82), +	/* ECS/PC Chips boards */ +	SND_PCI_QUIRK(0x1019, 0x2144, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2608, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2633, +		      "ECS/PC chips P17G/1333", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2811, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2812, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2813, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2814, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2815, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2816, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2817, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2818, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2819, +		      "ECS/PC chips", STAC_ECS_202), +	SND_PCI_QUIRK(0x1019, 0x2820, +		      "ECS/PC chips", STAC_ECS_202),  	{} /* terminator */  }; @@ -1683,8 +2043,8 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {  	/* Dell 3 stack systems with verb table in BIOS */  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0227, "Dell Vostro 1400  ", STAC_DELL_BIOS), -	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022f, "Dell     ", STAC_DELL_BIOS),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022e, "Dell     ", STAC_DELL_BIOS), +	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x022f, "Dell Inspiron 1525", STAC_DELL_3ST),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0242, "Dell     ", STAC_DELL_BIOS),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x0243, "Dell     ", STAC_DELL_BIOS),  	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL,  0x02ff, "Dell     ", STAC_DELL_BIOS), @@ -1867,6 +2227,8 @@ static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,  				      struct snd_pcm_substream *substream)  {  	struct sigmatel_spec *spec = codec->spec; +	if (spec->stream_delay) +		msleep(spec->stream_delay);  	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,  					     hinfo);  } @@ -1930,9 +2292,14 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,  					struct snd_pcm_substream *substream)  {  	struct sigmatel_spec *spec = codec->spec; +	hda_nid_t nid = spec->adc_nids[substream->number]; -	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], -                                   stream_tag, 0, format); +	if (spec->powerdown_adcs) { +		msleep(40); +		snd_hda_codec_write_cache(codec, nid, 0, +			AC_VERB_SET_POWER_STATE, AC_PWRST_D0); +	} +	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);  	return 0;  } @@ -1941,8 +2308,12 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,  					struct snd_pcm_substream *substream)  {  	struct sigmatel_spec *spec = codec->spec; +	hda_nid_t nid = spec->adc_nids[substream->number]; -	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); +	snd_hda_codec_cleanup_stream(codec, nid); +	if (spec->powerdown_adcs) +		snd_hda_codec_write_cache(codec, nid, 0, +			AC_VERB_SET_POWER_STATE, AC_PWRST_D3);  	return 0;  } @@ -2193,6 +2564,8 @@ enum {  	STAC_CTL_WIDGET_VOL,  	STAC_CTL_WIDGET_MUTE,  	STAC_CTL_WIDGET_MONO_MUX, +	STAC_CTL_WIDGET_AMP_MUX, +	STAC_CTL_WIDGET_AMP_VOL,  	STAC_CTL_WIDGET_HP_SWITCH,  	STAC_CTL_WIDGET_IO_SWITCH,  	STAC_CTL_WIDGET_CLFE_SWITCH @@ -2202,13 +2575,16 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {  	HDA_CODEC_VOLUME(NULL, 0, 0, 0),  	HDA_CODEC_MUTE(NULL, 0, 0, 0),  	STAC_MONO_MUX, +	STAC_AMP_MUX, +	STAC_AMP_VOL(NULL, 0, 0, 0, 0),  	STAC_CODEC_HP_SWITCH(NULL),  	STAC_CODEC_IO_SWITCH(NULL, 0),  	STAC_CODEC_CLFE_SWITCH(NULL, 0),  };  /* add dynamic controls */ -static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val) +static int stac92xx_add_control_idx(struct sigmatel_spec *spec, int type, +		int idx, const char *name, unsigned long val)  {  	struct snd_kcontrol_new *knew; @@ -2228,6 +2604,7 @@ static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char  	knew = &spec->kctl_alloc[spec->num_kctl_used];  	*knew = stac92xx_control_templates[type]; +	knew->index = idx;  	knew->name = kstrdup(name, GFP_KERNEL);  	if (! knew->name)  		return -ENOMEM; @@ -2236,6 +2613,14 @@ static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char  	return 0;  } + +/* add dynamic controls */ +static int stac92xx_add_control(struct sigmatel_spec *spec, int type, +		const char *name, unsigned long val) +{ +	return stac92xx_add_control_idx(spec, type, 0, name, val); +} +  /* flag inputs as additional dynamic lineouts */  static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)  { @@ -2467,6 +2852,10 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,  		}  	} +	if ((spec->multiout.num_dacs - cfg->line_outs) > 0 && +			cfg->hp_outs && !spec->multiout.hp_nid) +		spec->multiout.hp_nid = nid; +  	if (cfg->hp_outs > 1) {  		err = stac92xx_add_control(spec,  			STAC_CTL_WIDGET_HP_SWITCH, @@ -2579,8 +2968,8 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,  }  /* labels for mono mux outputs */ -static const char *stac92xx_mono_labels[3] = { -	"DAC0", "DAC1", "Mixer" +static const char *stac92xx_mono_labels[4] = { +	"DAC0", "DAC1", "Mixer", "DAC2"  };  /* create mono mux for mono out on capable codecs */ @@ -2609,6 +2998,116 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)  				"Mono Mux", spec->mono_nid);  } +/* labels for amp mux outputs */ +static const char *stac92xx_amp_labels[3] = { +	"Front Microphone", "Microphone", "Line In" +}; + +/* create amp out controls mux on capable codecs */ +static int stac92xx_auto_create_amp_output_ctls(struct hda_codec *codec) +{ +	struct sigmatel_spec *spec = codec->spec; +	struct hda_input_mux *amp_mux = &spec->private_amp_mux; +	int i, err; + +	for (i = 0; i < spec->num_amps; i++) { +		amp_mux->items[amp_mux->num_items].label = +					stac92xx_amp_labels[i]; +		amp_mux->items[amp_mux->num_items].index = i; +		amp_mux->num_items++; +	} + +	if (spec->num_amps > 1) { +		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_MUX, +			"Amp Selector Capture Switch", 0); +		if (err < 0) +			return err; +	} +	return stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_VOL, +		"Amp Capture Volume", +		HDA_COMPOSE_AMP_VAL(spec->amp_nids[0], 3, 0, HDA_INPUT)); +} + + +/* create PC beep volume controls */ +static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec, +						hda_nid_t nid) +{ +	struct sigmatel_spec *spec = codec->spec; +	u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT); +	int err; + +	/* check for mute support for the the amp */ +	if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) { +		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, +			"PC Beep Playback Switch", +			HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); +			if (err < 0) +				return err; +	} + +	/* check to see if there is volume support for the amp */ +	if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { +		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, +			"PC Beep Playback Volume", +			HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); +			if (err < 0) +				return err; +	} +	return 0; +} + +static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec) +{ +	struct sigmatel_spec *spec = codec->spec; +	int wcaps, nid, i, err = 0; + +	for (i = 0; i < spec->num_muxes; i++) { +		nid = spec->mux_nids[i]; +		wcaps = get_wcaps(codec, nid); + +		if (wcaps & AC_WCAP_OUT_AMP) { +			err = stac92xx_add_control_idx(spec, +				STAC_CTL_WIDGET_VOL, i, "Mux Capture Volume", +				HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); +			if (err < 0) +				return err; +		} +	} +	return 0; +}; + +static const char *stac92xx_spdif_labels[3] = { +	"Digital Playback", "Analog Mux 1", "Analog Mux 2", +}; + +static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec) +{ +	struct sigmatel_spec *spec = codec->spec; +	struct hda_input_mux *spdif_mux = &spec->private_smux; +	const char **labels = spec->spdif_labels; +	int i, num_cons; +	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; + +	num_cons = snd_hda_get_connections(codec, +				spec->smux_nids[0], +				con_lst, +				HDA_MAX_NUM_INPUTS); +	if (!num_cons) +		return -EINVAL; + +	if (!labels) +		labels = stac92xx_spdif_labels; + +	for (i = 0; i < num_cons; i++) { +		spdif_mux->items[spdif_mux->num_items].label = labels[i]; +		spdif_mux->items[spdif_mux->num_items].index = i; +		spdif_mux->num_items++; +	} + +	return 0; +} +  /* labels for dmic mux inputs */  static const char *stac92xx_dmic_labels[5] = {  	"Analog Inputs", "Digital Mic 1", "Digital Mic 2", @@ -2656,16 +3155,19 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,  			}  		continue;  found: -		wcaps = get_wcaps(codec, nid); +		wcaps = get_wcaps(codec, nid) & +			(AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); -		if (wcaps & AC_WCAP_OUT_AMP) { +		if (wcaps) {  			sprintf(name, "%s Capture Volume",  				stac92xx_dmic_labels[dimux->num_items]);  			err = stac92xx_add_control(spec,  				STAC_CTL_WIDGET_VOL,  				name, -				HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); +				HDA_COMPOSE_AMP_VAL(nid, 3, 0, +				(wcaps & AC_WCAP_OUT_AMP) ? +				HDA_OUTPUT : HDA_INPUT));  			if (err < 0)  				return err;  		} @@ -2789,8 +3291,8 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out  		hp_speaker_swap = 1;  	}  	if (spec->autocfg.mono_out_pin) { -		int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin) -				& AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; +		int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) & +			(AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);  		u32 caps = query_amp_caps(codec,  				spec->autocfg.mono_out_pin, dir);  		hda_nid_t conn_list[1]; @@ -2812,21 +3314,26 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out  						!(wcaps & AC_WCAP_LR_SWAP))  					spec->mono_nid = conn_list[0];  		} -		/* all mono outs have a least a mute/unmute switch */ -		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, -			"Mono Playback Switch", -			HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, -					1, 0, dir)); -		if (err < 0) -			return err; -		/* check to see if there is volume support for the amp */ -		if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { -			err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, -				"Mono Playback Volume", -				HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, -					1, 0, dir)); +		if (dir) { +			hda_nid_t nid = spec->autocfg.mono_out_pin; + +			/* most mono outs have a least a mute/unmute switch */ +			dir = (dir & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; +			err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, +				"Mono Playback Switch", +				HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir));  			if (err < 0)  				return err; +			/* check for volume support for the amp */ +			if ((caps & AC_AMPCAP_NUM_STEPS) +					>> AC_AMPCAP_NUM_STEPS_SHIFT) { +				err = stac92xx_add_control(spec, +					STAC_CTL_WIDGET_VOL, +					"Mono Playback Volume", +				HDA_COMPOSE_AMP_VAL(nid, 1, 0, dir)); +				if (err < 0) +					return err; +			}  		}  		stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, @@ -2844,6 +3351,28 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out  	if (err < 0)  		return err; +	/* setup analog beep controls */ +	if (spec->anabeep_nid > 0) { +		err = stac92xx_auto_create_beep_ctls(codec, +			spec->anabeep_nid); +		if (err < 0) +			return err; +	} + +	/* setup digital beep controls and input device */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP +	if (spec->digbeep_nid > 0) { +		hda_nid_t nid = spec->digbeep_nid; + +		err = stac92xx_auto_create_beep_ctls(codec, nid); +		if (err < 0) +			return err; +		err = snd_hda_attach_beep_device(codec, nid); +		if (err < 0) +			return err; +	} +#endif +  	if (hp_speaker_swap == 1) {  		/* Restore the hp_outs and line_outs */  		memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, @@ -2872,11 +3401,25 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out  		if (err < 0)  			return err;  	} - -	if (spec->num_dmics > 0) +	if (spec->num_amps > 0) { +		err = stac92xx_auto_create_amp_output_ctls(codec); +		if (err < 0) +			return err; +	} +	if (spec->num_dmics > 0 && !spec->dinput_mux)  		if ((err = stac92xx_auto_create_dmic_input_ctls(codec,  						&spec->autocfg)) < 0)  			return err; +	if (spec->num_muxes > 0) { +		err = stac92xx_auto_create_mux_input_ctls(codec); +		if (err < 0) +			return err; +	} +	if (spec->num_smuxes > 0) { +		err = stac92xx_auto_create_spdif_mux_ctls(codec); +		if (err < 0) +			return err; +	}  	spec->multiout.max_channels = spec->multiout.num_dacs * 2;  	if (spec->multiout.max_channels > 2) @@ -2884,17 +3427,17 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out  	if (spec->autocfg.dig_out_pin)  		spec->multiout.dig_out_nid = dig_out; -	if (spec->autocfg.dig_in_pin) +	if (dig_in && spec->autocfg.dig_in_pin)  		spec->dig_in_nid = dig_in;  	if (spec->kctl_alloc)  		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;  	spec->input_mux = &spec->private_imux; -	if (!spec->dinput_mux) -		spec->dinput_mux = &spec->private_dimux; +	spec->dinput_mux = &spec->private_dimux; +	spec->sinput_mux = &spec->private_smux;  	spec->mono_mux = &spec->private_mono_mux; - +	spec->amp_mux = &spec->private_amp_mux;  	return 1;  } @@ -3074,6 +3617,12 @@ static int stac92xx_init(struct hda_codec *codec)  	snd_hda_sequence_write(codec, spec->init); +	/* power down adcs initially */ +	if (spec->powerdown_adcs) +		for (i = 0; i < spec->num_adcs; i++) +			snd_hda_codec_write_cache(codec, +				spec->adc_nids[i], 0, +				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);  	/* set up pins */  	if (spec->hp_detect) {  		/* Enable unsolicited responses on the HP widget */ @@ -3095,7 +3644,12 @@ static int stac92xx_init(struct hda_codec *codec)  	for (i = 0; i < AUTO_PIN_LAST; i++) {  		hda_nid_t nid = cfg->input_pins[i];  		if (nid) { -			unsigned int pinctl = AC_PINCTL_IN_EN; +			unsigned int pinctl = snd_hda_codec_read(codec, nid, +				0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); +			/* if PINCTL already set then skip */ +			if (pinctl & AC_PINCAP_IN) +				continue; +			pinctl = AC_PINCTL_IN_EN;  			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)  				pinctl |= stac92xx_get_vref(codec, nid);  			stac92xx_auto_set_pinctl(codec, nid, pinctl); @@ -3158,6 +3712,7 @@ static void stac92xx_free(struct hda_codec *codec)  		kfree(spec->bios_pin_configs);  	kfree(spec); +	snd_hda_detach_beep_device(codec);  }  static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, @@ -3279,7 +3834,12 @@ static void stac92xx_pin_sense(struct hda_codec *codec, int idx)  	val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)  							& 0x000000ff;  	presence = get_hp_pin_presence(codec, nid); -	idx = 1 << idx; + +	/* several codecs have two power down bits */ +	if (spec->pwr_mapping) +		idx = spec->pwr_mapping[idx]; +	else +		idx = 1 << idx;  	if (presence)  		val &= ~idx; @@ -3295,13 +3855,22 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)  	struct sigmatel_spec *spec = codec->spec;  	int idx = res >> 26 & 0x0f; -	switch ((res >> 26) & 0x30) { +	switch ((res >> 26) & 0x70) {  	case STAC_HP_EVENT:  		stac92xx_hp_detect(codec, res);  		/* fallthru */  	case STAC_PWR_EVENT:  		if (spec->num_pwrs > 0)  			stac92xx_pin_sense(codec, idx); +		break; +	case STAC_VREF_EVENT: { +		int data = snd_hda_codec_read(codec, codec->afg, 0, +			AC_VERB_GET_GPIO_DATA, 0); +		/* toggle VREF state based on GPIOx status */ +		snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, +			!!(data & (1 << idx))); +		break; +		}  	}  } @@ -3478,9 +4047,9 @@ static struct hda_input_mux stac92hd73xx_dmux = {  	.num_items = 4,  	.items = {  		{ "Analog Inputs", 0x0b }, -		{ "CD", 0x08 },  		{ "Digital Mic 1", 0x09 },  		{ "Digital Mic 2", 0x0a }, +		{ "CD", 0x08 },  	}  }; @@ -3495,6 +4064,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)  		return -ENOMEM;  	codec->spec = spec; +	codec->slave_dig_outs = stac92hd73xx_slave_dig_outs;  	spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids);  	spec->pin_nids = stac92hd73xx_pin_nids;  	spec->board_config = snd_hda_check_board_config(codec, @@ -3527,17 +4097,14 @@ again:  	switch (spec->multiout.num_dacs) {  	case 0x3: /* 6 Channel */ -		spec->multiout.hp_nid = 0x17;  		spec->mixer = stac92hd73xx_6ch_mixer;  		spec->init = stac92hd73xx_6ch_core_init;  		break;  	case 0x4: /* 8 Channel */ -		spec->multiout.hp_nid = 0x18;  		spec->mixer = stac92hd73xx_8ch_mixer;  		spec->init = stac92hd73xx_8ch_core_init;  		break;  	case 0x5: /* 10 Channel */ -		spec->multiout.hp_nid = 0x19;  		spec->mixer = stac92hd73xx_10ch_mixer;  		spec->init = stac92hd73xx_10ch_core_init;  	}; @@ -3546,27 +4113,34 @@ again:  	spec->aloopback_mask = 0x01;  	spec->aloopback_shift = 8; +	spec->digbeep_nid = 0x1c;  	spec->mux_nids = stac92hd73xx_mux_nids;  	spec->adc_nids = stac92hd73xx_adc_nids;  	spec->dmic_nids = stac92hd73xx_dmic_nids;  	spec->dmux_nids = stac92hd73xx_dmux_nids; +	spec->smux_nids = stac92hd73xx_smux_nids; +	spec->amp_nids = stac92hd73xx_amp_nids; +	spec->num_amps = ARRAY_SIZE(stac92hd73xx_amp_nids);  	spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);  	spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);  	spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); -	spec->dinput_mux = &stac92hd73xx_dmux; -	/* GPIO0 High = Enable EAPD */ -	spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; -	spec->gpio_data = 0x01; +	memcpy(&spec->private_dimux, &stac92hd73xx_dmux, +			sizeof(stac92hd73xx_dmux));  	switch (spec->board_config) {  	case STAC_DELL_M6:  		spec->init = dell_eq_core_init; +		spec->num_smuxes = 0; +		spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER]; +		spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP]; +		spec->num_amps = 1;  		switch (codec->subsystem_id) {  		case 0x1028025e: /* Analog Mics */  		case 0x1028025f:  			stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);  			spec->num_dmics = 0; +			spec->private_dimux.num_items = 1;  			break;  		case 0x10280271: /* Digital Mics */  		case 0x10280272: @@ -3576,23 +4150,32 @@ again:  		case 0x10280255:  			stac92xx_set_config_reg(codec, 0x13, 0x90A60160);  			spec->num_dmics = 1; +			spec->private_dimux.num_items = 2;  			break;  		case 0x10280256: /* Both */  		case 0x10280057:  			stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);  			stac92xx_set_config_reg(codec, 0x13, 0x90A60160);  			spec->num_dmics = 1; +			spec->private_dimux.num_items = 2;  			break;  		}  		break;  	default:  		spec->num_dmics = STAC92HD73XX_NUM_DMICS; +		spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);  	} +	if (spec->board_config > STAC_92HD73XX_REF) { +		/* GPIO0 High = Enable EAPD */ +		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; +		spec->gpio_data = 0x01; +	} +	spec->dinput_mux = &spec->private_dimux;  	spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);  	spec->pwr_nids = stac92hd73xx_pwr_nids; -	err = stac92xx_parse_auto_config(codec, 0x22, 0x24); +	err = stac92xx_parse_auto_config(codec, 0x25, 0x27);  	if (!err) {  		if (spec->board_config < 0) { @@ -3614,6 +4197,136 @@ again:  	return 0;  } +static struct hda_input_mux stac92hd83xxx_dmux = { +	.num_items = 3, +	.items = { +		{ "Analog Inputs", 0x03 }, +		{ "Digital Mic 1", 0x04 }, +		{ "Digital Mic 2", 0x05 }, +	} +}; + +static int patch_stac92hd83xxx(struct hda_codec *codec) +{ +	struct sigmatel_spec *spec; +	int err; + +	spec  = kzalloc(sizeof(*spec), GFP_KERNEL); +	if (spec == NULL) +		return -ENOMEM; + +	codec->spec = spec; +	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs; +	spec->mono_nid = 0x19; +	spec->digbeep_nid = 0x21; +	spec->dmic_nids = stac92hd83xxx_dmic_nids; +	spec->dmux_nids = stac92hd83xxx_dmux_nids; +	spec->adc_nids = stac92hd83xxx_adc_nids; +	spec->pwr_nids = stac92hd83xxx_pwr_nids; +	spec->pwr_mapping = stac92hd83xxx_pwr_mapping; +	spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids); +	spec->multiout.dac_nids = stac92hd83xxx_dac_nids; + +	spec->init = stac92hd83xxx_core_init; +	switch (codec->vendor_id) { +	case 0x111d7605: +		spec->multiout.num_dacs = STAC92HD81_DAC_COUNT; +		break; +	default: +		spec->num_pwrs--; +		spec->init++; /* switch to config #2 */ +		spec->multiout.num_dacs = STAC92HD83_DAC_COUNT; +	} + +	spec->mixer = stac92hd83xxx_mixer; +	spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids); +	spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids); +	spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids); +	spec->num_dmics = STAC92HD83XXX_NUM_DMICS; +	spec->dinput_mux = &stac92hd83xxx_dmux; +	spec->pin_nids = stac92hd83xxx_pin_nids; +	spec->board_config = snd_hda_check_board_config(codec, +							STAC_92HD83XXX_MODELS, +							stac92hd83xxx_models, +							stac92hd83xxx_cfg_tbl); +again: +	if (spec->board_config < 0) { +		snd_printdd(KERN_INFO "hda_codec: Unknown model for" +			" STAC92HD83XXX, using BIOS defaults\n"); +		err = stac92xx_save_bios_config_regs(codec); +		if (err < 0) { +			stac92xx_free(codec); +			return err; +		} +		spec->pin_configs = spec->bios_pin_configs; +	} else { +		spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config]; +		stac92xx_set_config_regs(codec); +	} + +	err = stac92xx_parse_auto_config(codec, 0x1d, 0); +	if (!err) { +		if (spec->board_config < 0) { +			printk(KERN_WARNING "hda_codec: No auto-config is " +			       "available, default to model=ref\n"); +			spec->board_config = STAC_92HD83XXX_REF; +			goto again; +		} +		err = -EINVAL; +	} + +	if (err < 0) { +		stac92xx_free(codec); +		return err; +	} + +	codec->patch_ops = stac92xx_patch_ops; + +	return 0; +} + +#ifdef SND_HDA_NEEDS_RESUME +static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr) +{ +	struct sigmatel_spec *spec = codec->spec; +	int i; +	snd_hda_codec_write_cache(codec, codec->afg, 0, +		AC_VERB_SET_POWER_STATE, pwr); + +	msleep(1); +	for (i = 0; i < spec->num_adcs; i++) { +		snd_hda_codec_write_cache(codec, +			spec->adc_nids[i], 0, +			AC_VERB_SET_POWER_STATE, pwr); +	} +}; + +static int stac92hd71xx_resume(struct hda_codec *codec) +{ +	stac92hd71xx_set_power_state(codec, AC_PWRST_D0); +	return stac92xx_resume(codec); +} + +static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state) +{ +	stac92hd71xx_set_power_state(codec, AC_PWRST_D3); +	return 0; +}; + +#endif + +static struct hda_codec_ops stac92hd71bxx_patch_ops = { +	.build_controls = stac92xx_build_controls, +	.build_pcms = stac92xx_build_pcms, +	.init = stac92xx_init, +	.free = stac92xx_free, +	.unsol_event = stac92xx_unsol_event, +#ifdef SND_HDA_NEEDS_RESUME +	.resume = stac92hd71xx_resume, +	.suspend = stac92hd71xx_suspend, +#endif +}; +  static int patch_stac92hd71bxx(struct hda_codec *codec)  {  	struct sigmatel_spec *spec; @@ -3624,6 +4337,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)  		return -ENOMEM;  	codec->spec = spec; +	codec->patch_ops = stac92xx_patch_ops;  	spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids);  	spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);  	spec->pin_nids = stac92hd71bxx_pin_nids; @@ -3653,8 +4367,28 @@ again:  	case 0x111d76b5:  		spec->mixer = stac92hd71bxx_mixer;  		spec->init = stac92hd71bxx_core_init; +		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;  		break;  	case 0x111d7608: /* 5 Port with Analog Mixer */ +		switch (codec->subsystem_id) { +		case 0x103c361a: +			/* Enable VREF power saving on GPIO1 detect */ +			snd_hda_codec_write(codec, codec->afg, 0, +				AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); +			snd_hda_codec_write_cache(codec, codec->afg, 0, +					AC_VERB_SET_UNSOLICITED_ENABLE, +					(AC_USRSP_EN | STAC_VREF_EVENT | 0x01)); +			spec->gpio_mask |= 0x02; +			break; +		} +		if ((codec->revision_id & 0xf) == 0 || +				(codec->revision_id & 0xf) == 1) { +#ifdef SND_HDA_NEEDS_RESUME +			codec->patch_ops = stac92hd71bxx_patch_ops; +#endif +			spec->stream_delay = 40; /* 40 milliseconds */ +		} +  		/* no output amps */  		spec->num_pwrs = 0;  		spec->mixer = stac92hd71bxx_analog_mixer; @@ -3664,32 +4398,60 @@ again:  		stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);  		break;  	case 0x111d7603: /* 6 Port with Analog Mixer */ +		if ((codec->revision_id & 0xf) == 1) { +#ifdef SND_HDA_NEEDS_RESUME +			codec->patch_ops = stac92hd71bxx_patch_ops; +#endif +			spec->stream_delay = 40; /* 40 milliseconds */ +		} +  		/* no output amps */  		spec->num_pwrs = 0;  		/* fallthru */  	default:  		spec->mixer = stac92hd71bxx_analog_mixer;  		spec->init = stac92hd71bxx_analog_core_init; +		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;  	}  	spec->aloopback_mask = 0x20;  	spec->aloopback_shift = 0; -	/* GPIO0 High = EAPD */ -	spec->gpio_mask = 0x01; -	spec->gpio_dir = 0x01; -	spec->gpio_data = 0x01; +	if (spec->board_config > STAC_92HD71BXX_REF) { +		/* GPIO0 = EAPD */ +		spec->gpio_mask = 0x01; +		spec->gpio_dir = 0x01; +		spec->gpio_data = 0x01; +	} +	spec->powerdown_adcs = 1; +	spec->digbeep_nid = 0x26;  	spec->mux_nids = stac92hd71bxx_mux_nids;  	spec->adc_nids = stac92hd71bxx_adc_nids;  	spec->dmic_nids = stac92hd71bxx_dmic_nids;  	spec->dmux_nids = stac92hd71bxx_dmux_nids; +	spec->smux_nids = stac92hd71bxx_smux_nids;  	spec->pwr_nids = stac92hd71bxx_pwr_nids;  	spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);  	spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); -	spec->num_dmics = STAC92HD71BXX_NUM_DMICS; -	spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); + +	switch (spec->board_config) { +	case STAC_HP_M4: +		spec->num_dmics = 0; +		spec->num_smuxes = 0; +		spec->num_dmuxes = 0; + +		/* enable internal microphone */ +		stac92xx_set_config_reg(codec, 0x0e, 0x01813040); +		stac92xx_auto_set_pinctl(codec, 0x0e, +			AC_PINCTL_IN_EN | AC_PINCTL_VREF_80); +		break; +	default: +		spec->num_dmics = STAC92HD71BXX_NUM_DMICS; +		spec->num_smuxes = ARRAY_SIZE(stac92hd71bxx_smux_nids); +		spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); +	};  	spec->multiout.num_dacs = 1;  	spec->multiout.hp_nid = 0x11; @@ -3711,8 +4473,6 @@ again:  		return err;  	} -	codec->patch_ops = stac92xx_patch_ops; -  	return 0;  }; @@ -3854,10 +4614,14 @@ static int patch_stac927x(struct hda_codec *codec)  		stac92xx_set_config_regs(codec);  	} +	spec->digbeep_nid = 0x23;  	spec->adc_nids = stac927x_adc_nids;  	spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids);  	spec->mux_nids = stac927x_mux_nids;  	spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); +	spec->smux_nids = stac927x_smux_nids; +	spec->num_smuxes = ARRAY_SIZE(stac927x_smux_nids); +	spec->spdif_labels = stac927x_spdif_labels;  	spec->dac_list = stac927x_dac_nids;  	spec->multiout.dac_nids = spec->dac_nids; @@ -3900,9 +4664,11 @@ static int patch_stac927x(struct hda_codec *codec)  		spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);  		break;  	default: -		/* GPIO0 High = Enable EAPD */ -		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; -		spec->gpio_data = 0x01; +		if (spec->board_config > STAC_D965_REF) { +			/* GPIO0 High = Enable EAPD */ +			spec->eapd_mask = spec->gpio_mask = 0x01; +			spec->gpio_dir = spec->gpio_data = 0x01; +		}  		spec->num_dmics = 0;  		spec->init = stac927x_core_init; @@ -3974,10 +4740,13 @@ static int patch_stac9205(struct hda_codec *codec)  		stac92xx_set_config_regs(codec);  	} +	spec->digbeep_nid = 0x23;  	spec->adc_nids = stac9205_adc_nids;  	spec->num_adcs = ARRAY_SIZE(stac9205_adc_nids);  	spec->mux_nids = stac9205_mux_nids;  	spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids); +	spec->smux_nids = stac9205_smux_nids; +	spec->num_smuxes = ARRAY_SIZE(stac9205_smux_nids);  	spec->dmic_nids = stac9205_dmic_nids;  	spec->num_dmics = STAC9205_NUM_DMICS;  	spec->dmux_nids = stac9205_dmux_nids; @@ -4013,6 +4782,9 @@ static int patch_stac9205(struct hda_codec *codec)  		 */  		spec->gpio_data = 0x01;  		break; +	case STAC_9205_REF: +		/* SPDIF-In enabled */ +		break;  	default:  		/* GPIO0 High = EAPD */  		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1; @@ -4332,6 +5104,8 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {   	{ .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },   	{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },  	{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx}, +	{ .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx}, +	{ .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},  	{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},  	{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },  	{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index e7e4352..63e4871 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1,10 +1,10 @@  /*   * Universal Interface for Intel High Definition Audio Codec   * - * HD audio interface patch for VIA VT1708 codec + * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec   * - * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com> - *                    Takashi Iwai <tiwai@suse.de> + * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com> + *			   Takashi Iwai <tiwai@suse.de>   *   *  This driver is free software; you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -29,6 +29,13 @@  /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */  /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization      */  /* 2007-09-17  Lydia Wang  Add VT1708B codec support                        */ +/* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */ +/* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */ +/* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support        */ +/* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin             */ +/* 2008-04-09  Lydia Wang  Add Independent HP feature                        */ +/* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */ +/* 2008-09-15  Logan Li    Add VT1708S Mic Boost workaround/backdoor	     */  /*                                                                           */  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -37,6 +44,7 @@  #include <linux/delay.h>  #include <linux/slab.h>  #include <sound/core.h> +#include <sound/asoundef.h>  #include "hda_codec.h"  #include "hda_local.h"  #include "hda_patch.h" @@ -53,6 +61,8 @@  #define VT1708_DIGOUT_NID	0x14  #define VT1708_DIGIN_NID	0x16  #define VT1708_DIGIN_PIN	0x26 +#define VT1708_HP_PIN_NID	0x20 +#define VT1708_CD_PIN_NID	0x24  #define VT1709_HP_DAC_NID	0x28  #define VT1709_DIGOUT_NID	0x13 @@ -64,12 +74,64 @@  #define VT1708B_DIGIN_NID	0x15  #define VT1708B_DIGIN_PIN	0x21 +#define VT1708S_HP_NID		0x25 +#define VT1708S_DIGOUT_NID	0x12 + +#define VT1702_HP_NID		0x17 +#define VT1702_DIGOUT_NID	0x11 +  #define IS_VT1708_VENDORID(x)		((x) >= 0x11061708 && (x) <= 0x1106170b)  #define IS_VT1709_10CH_VENDORID(x)	((x) >= 0x1106e710 && (x) <= 0x1106e713)  #define IS_VT1709_6CH_VENDORID(x)	((x) >= 0x1106e714 && (x) <= 0x1106e717)  #define IS_VT1708B_8CH_VENDORID(x)	((x) >= 0x1106e720 && (x) <= 0x1106e723)  #define IS_VT1708B_4CH_VENDORID(x)	((x) >= 0x1106e724 && (x) <= 0x1106e727) +#define IS_VT1708S_VENDORID(x)		((x) >= 0x11060397 && (x) <= 0x11067397) +#define IS_VT1702_VENDORID(x)		((x) >= 0x11060398 && (x) <= 0x11067398) + +enum VIA_HDA_CODEC { +	UNKNOWN = -1, +	VT1708, +	VT1709_10CH, +	VT1709_6CH, +	VT1708B_8CH, +	VT1708B_4CH, +	VT1708S, +	VT1702, +	CODEC_TYPES, +}; + +static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) +{ +	u16 ven_id = vendor_id >> 16; +	u16 dev_id = vendor_id & 0xffff; +	enum VIA_HDA_CODEC codec_type; + +	/* get codec type */ +	if (ven_id != 0x1106) +		codec_type = UNKNOWN; +	else if (dev_id >= 0x1708 && dev_id <= 0x170b) +		codec_type = VT1708; +	else if (dev_id >= 0xe710 && dev_id <= 0xe713) +		codec_type = VT1709_10CH; +	else if (dev_id >= 0xe714 && dev_id <= 0xe717) +		codec_type = VT1709_6CH; +	else if (dev_id >= 0xe720 && dev_id <= 0xe723) +		codec_type = VT1708B_8CH; +	else if (dev_id >= 0xe724 && dev_id <= 0xe727) +		codec_type = VT1708B_4CH; +	else if ((dev_id & 0xfff) == 0x397 +		 && (dev_id >> 12) < 8) +		codec_type = VT1708S; +	else if ((dev_id & 0xfff) == 0x398 +		 && (dev_id >> 12) < 8) +		codec_type = VT1702; +	else +		codec_type = UNKNOWN; +	return codec_type; +}; +#define VIA_HP_EVENT		0x01 +#define VIA_GPIO_EVENT		0x02  enum {  	VIA_CTL_WIDGET_VOL, @@ -77,12 +139,54 @@ enum {  };  enum { -	AUTO_SEQ_FRONT, +	AUTO_SEQ_FRONT = 0,  	AUTO_SEQ_SURROUND,  	AUTO_SEQ_CENLFE,  	AUTO_SEQ_SIDE  }; +#define get_amp_nid(kc)	((kc)->private_value & 0xffff) + +/* Some VT1708S based boards gets the micboost setting wrong, so we have + * to apply some brute-force and re-write the TLV's by software. */ +static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, +			 unsigned int size, unsigned int __user *_tlv) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	hda_nid_t nid = get_amp_nid(kcontrol); + +	if (get_codec_type(codec->vendor_id) == VT1708S +	    && (nid == 0x1a || nid == 0x1e)) { +		if (size < 4 * sizeof(unsigned int)) +			return -ENOMEM; +		if (put_user(1, _tlv))	/* SNDRV_CTL_TLVT_DB_SCALE */ +			return -EFAULT; +		if (put_user(2 * sizeof(unsigned int), _tlv + 1)) +			return -EFAULT; +		if (put_user(0, _tlv + 2)) /* offset = 0 */ +			return -EFAULT; +		if (put_user(1000, _tlv + 3)) /* step size = 10 dB */ +			return -EFAULT; +	} +	return 0; +} + +static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, +				 struct snd_ctl_elem_info *uinfo) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	hda_nid_t nid = get_amp_nid(kcontrol); + +	if (get_codec_type(codec->vendor_id) == VT1708S +	    && (nid == 0x1a || nid == 0x1e)) { +		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; +		uinfo->count = 2; +		uinfo->value.integer.min = 0; +		uinfo->value.integer.max = 3; +	} +	return 0; +} +  static struct snd_kcontrol_new vt1708_control_templates[] = {  	HDA_CODEC_VOLUME(NULL, 0, 0, 0),  	HDA_CODEC_MUTE(NULL, 0, 0, 0), @@ -94,7 +198,8 @@ struct via_spec {  	struct snd_kcontrol_new *mixers[3];  	unsigned int num_mixers; -	struct hda_verb *init_verbs; +	struct hda_verb *init_verbs[5]; +	unsigned int num_iverbs;  	char *stream_name_analog;  	struct hda_pcm_stream *stream_analog_playback; @@ -106,6 +211,7 @@ struct via_spec {  	/* playback */  	struct hda_multi_out multiout; +	hda_nid_t extra_dig_out_nid;  	/* capture */  	unsigned int num_adc_nids; @@ -117,15 +223,19 @@ struct via_spec {  	unsigned int cur_mux[3];  	/* PCM information */ -	struct hda_pcm pcm_rec[2]; +	struct hda_pcm pcm_rec[3];  	/* dynamic controls, init_verbs and input_mux */  	struct auto_pin_cfg autocfg;  	unsigned int num_kctl_alloc, num_kctl_used;  	struct snd_kcontrol_new *kctl_alloc; -	struct hda_input_mux private_imux; +	struct hda_input_mux private_imux[2];  	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; +	/* HP mode source */ +	const struct hda_input_mux *hp_mux; +	unsigned int hp_independent_mode; +  #ifdef CONFIG_SND_HDA_POWER_SAVE  	struct hda_loopback_check loopback;  #endif @@ -146,6 +256,16 @@ static hda_nid_t vt1708B_adc_nids[2] = {  	0x13, 0x14  }; +static hda_nid_t vt1708S_adc_nids[2] = { +	/* ADC1-2 */ +	0x13, 0x14 +}; + +static hda_nid_t vt1702_adc_nids[3] = { +	/* ADC1-2 */ +	0x12, 0x20, 0x1F +}; +  /* add dynamic controls */  static int via_add_control(struct via_spec *spec, int type, const char *name,  			   unsigned long val) @@ -283,19 +403,108 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,  		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,  					     0x18, &spec->cur_mux[adc_idx]);  	else if ((IS_VT1709_10CH_VENDORID(vendor_id) || -		  IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0) +		  IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0))  		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,  					     0x19, &spec->cur_mux[adc_idx]);  	else if ((IS_VT1708B_8CH_VENDORID(vendor_id) || -		  IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0) +		  IS_VT1708B_4CH_VENDORID(vendor_id)) && (adc_idx == 0))  		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,  					     0x17, &spec->cur_mux[adc_idx]); +	else if (IS_VT1702_VENDORID(vendor_id) && (adc_idx == 0)) +		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, +					     0x13, &spec->cur_mux[adc_idx]);  	else  		return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,  					     spec->adc_nids[adc_idx],  					     &spec->cur_mux[adc_idx]);  } +static int via_independent_hp_info(struct snd_kcontrol *kcontrol, +				   struct snd_ctl_elem_info *uinfo) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct via_spec *spec = codec->spec; +	return snd_hda_input_mux_info(spec->hp_mux, uinfo); +} + +static int via_independent_hp_get(struct snd_kcontrol *kcontrol, +				  struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct via_spec *spec = codec->spec; +	hda_nid_t nid = spec->autocfg.hp_pins[0]; +	unsigned int pinsel = snd_hda_codec_read(codec, nid, 0, +						 AC_VERB_GET_CONNECT_SEL, +						 0x00); + +	ucontrol->value.enumerated.item[0] = pinsel; + +	return 0; +} + +static int via_independent_hp_put(struct snd_kcontrol *kcontrol, +				  struct snd_ctl_elem_value *ucontrol) +{ +	struct hda_codec *codec = snd_kcontrol_chip(kcontrol); +	struct via_spec *spec = codec->spec; +	hda_nid_t nid = spec->autocfg.hp_pins[0]; +	unsigned int pinsel = ucontrol->value.enumerated.item[0]; +	unsigned int con_nid = snd_hda_codec_read(codec, nid, 0, +					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff; + +	if (con_nid == spec->multiout.hp_nid) { +		if (pinsel == 0) { +			if (!spec->hp_independent_mode) { +				if (spec->multiout.num_dacs > 1) +					spec->multiout.num_dacs -= 1; +				spec->hp_independent_mode = 1; +			} +		} else if (pinsel == 1) { +		       if (spec->hp_independent_mode) { +				if (spec->multiout.num_dacs > 1) +					spec->multiout.num_dacs += 1; +				spec->hp_independent_mode = 0; +		       } +		} +	} else { +		if (pinsel == 0) { +			if (spec->hp_independent_mode) { +				if (spec->multiout.num_dacs > 1) +					spec->multiout.num_dacs += 1; +				spec->hp_independent_mode = 0; +			} +		} else if (pinsel == 1) { +		       if (!spec->hp_independent_mode) { +				if (spec->multiout.num_dacs > 1) +					spec->multiout.num_dacs -= 1; +				spec->hp_independent_mode = 1; +		       } +		} +	} +	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, +			    pinsel); + +	if (spec->multiout.hp_nid && +	    spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) +			snd_hda_codec_setup_stream(codec, +						   spec->multiout.hp_nid, +						   0, 0, 0); + +	return 0; +} + +static struct snd_kcontrol_new via_hp_mixer[] = { +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Independent HP", +		.count = 1, +		.info = via_independent_hp_info, +		.get = via_independent_hp_get, +		.put = via_independent_hp_put, +	}, +	{ } /* end */ +}; +  /* capture mixer elements */  static struct snd_kcontrol_new vt1708_capture_mixer[] = {  	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), @@ -380,6 +589,138 @@ static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,  	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);  } + +static void playback_multi_pcm_prep_0(struct hda_codec *codec, +				      unsigned int stream_tag, +				      unsigned int format, +				      struct snd_pcm_substream *substream) +{ +	struct via_spec *spec = codec->spec; +	struct hda_multi_out *mout = &spec->multiout; +	hda_nid_t *nids = mout->dac_nids; +	int chs = substream->runtime->channels; +	int i; + +	mutex_lock(&codec->spdif_mutex); +	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { +		if (chs == 2 && +		    snd_hda_is_supported_format(codec, mout->dig_out_nid, +						format) && +		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { +			mout->dig_out_used = HDA_DIG_ANALOG_DUP; +			/* turn off SPDIF once; otherwise the IEC958 bits won't +			 * be updated */ +			if (codec->spdif_ctls & AC_DIG1_ENABLE) +				snd_hda_codec_write(codec, mout->dig_out_nid, 0, +						    AC_VERB_SET_DIGI_CONVERT_1, +						    codec->spdif_ctls & +							~AC_DIG1_ENABLE & 0xff); +			snd_hda_codec_setup_stream(codec, mout->dig_out_nid, +						   stream_tag, 0, format); +			/* turn on again (if needed) */ +			if (codec->spdif_ctls & AC_DIG1_ENABLE) +				snd_hda_codec_write(codec, mout->dig_out_nid, 0, +						    AC_VERB_SET_DIGI_CONVERT_1, +						    codec->spdif_ctls & 0xff); +		} else { +			mout->dig_out_used = 0; +			snd_hda_codec_setup_stream(codec, mout->dig_out_nid, +						   0, 0, 0); +		} +	} +	mutex_unlock(&codec->spdif_mutex); + +	/* front */ +	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, +				   0, format); + +	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && +	    !spec->hp_independent_mode) +		/* headphone out will just decode front left/right (stereo) */ +		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, +					   0, format); + +	/* extra outputs copied from front */ +	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) +		if (mout->extra_out_nid[i]) +			snd_hda_codec_setup_stream(codec, +						   mout->extra_out_nid[i], +						   stream_tag, 0, format); + +	/* surrounds */ +	for (i = 1; i < mout->num_dacs; i++) { +		if (chs >= (i + 1) * 2) /* independent out */ +			snd_hda_codec_setup_stream(codec, nids[i], stream_tag, +						   i * 2, format); +		else /* copy front */ +			snd_hda_codec_setup_stream(codec, nids[i], stream_tag, +						   0, format); +	} +} + +static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, +					  struct hda_codec *codec, +					  unsigned int stream_tag, +					  unsigned int format, +					  struct snd_pcm_substream *substream) +{ +	struct via_spec *spec = codec->spec; +	struct hda_multi_out *mout = &spec->multiout; +	hda_nid_t *nids = mout->dac_nids; + +	if (substream->number == 0) +		playback_multi_pcm_prep_0(codec, stream_tag, format, +					  substream); +	else { +		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && +		    spec->hp_independent_mode) +			snd_hda_codec_setup_stream(codec, mout->hp_nid, +						   stream_tag, 0, format); +	} + +	return 0; +} + +static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, +				    struct hda_codec *codec, +				    struct snd_pcm_substream *substream) +{ +	struct via_spec *spec = codec->spec; +	struct hda_multi_out *mout = &spec->multiout; +	hda_nid_t *nids = mout->dac_nids; +	int i; + +	if (substream->number == 0) { +		for (i = 0; i < mout->num_dacs; i++) +			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); + +		if (mout->hp_nid && !spec->hp_independent_mode) +			snd_hda_codec_setup_stream(codec, mout->hp_nid, +						   0, 0, 0); + +		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) +			if (mout->extra_out_nid[i]) +				snd_hda_codec_setup_stream(codec, +							mout->extra_out_nid[i], +							0, 0, 0); +		mutex_lock(&codec->spdif_mutex); +		if (mout->dig_out_nid && +		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) { +			snd_hda_codec_setup_stream(codec, mout->dig_out_nid, +						   0, 0, 0); +			mout->dig_out_used = 0; +		} +		mutex_unlock(&codec->spdif_mutex); +	} else { +		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && +		    spec->hp_independent_mode) +			snd_hda_codec_setup_stream(codec, mout->hp_nid, +						   0, 0, 0); +	} + +	return 0; +} +  /*   * Digital out   */ @@ -399,6 +740,21 @@ static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,  	return snd_hda_multi_out_dig_close(codec, &spec->multiout);  } +/* setup SPDIF output stream */ +static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid, +				 unsigned int stream_tag, unsigned int format) +{ +	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ +	if (codec->spdif_ctls & AC_DIG1_ENABLE) +		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, +				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); +	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); +	/* turn on again (if needed) */ +	if (codec->spdif_ctls & AC_DIG1_ENABLE) +		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, +				    codec->spdif_ctls & 0xff); +} +  static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,  					struct hda_codec *codec,  					unsigned int stream_tag, @@ -406,8 +762,20 @@ static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,  					struct snd_pcm_substream *substream)  {  	struct via_spec *spec = codec->spec; -	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, -					     stream_tag, format, substream); +	hda_nid_t nid; + +	/* 1st or 2nd S/PDIF */ +	if (substream->number == 0) +		nid = spec->multiout.dig_out_nid; +	else if (substream->number == 1) +		nid = spec->extra_dig_out_nid; +	else +		return -1; + +	mutex_lock(&codec->spdif_mutex); +	setup_dig_playback_stream(codec, nid, stream_tag, format); +	mutex_unlock(&codec->spdif_mutex); +	return 0;  }  /* @@ -436,14 +804,14 @@ static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,  }  static struct hda_pcm_stream vt1708_pcm_analog_playback = { -	.substreams = 1, +	.substreams = 2,  	.channels_min = 2,  	.channels_max = 8,  	.nid = 0x10, /* NID to query formats and rates */  	.ops = {  		.open = via_playback_pcm_open, -		.prepare = via_playback_pcm_prepare, -		.cleanup = via_playback_pcm_cleanup +		.prepare = via_playback_multi_pcm_prepare, +		.cleanup = via_playback_multi_pcm_cleanup  	},  }; @@ -515,6 +883,13 @@ static int via_build_controls(struct hda_codec *codec)  		if (err < 0)  			return err;  		spec->multiout.share_spdif = 1; + +		if (spec->extra_dig_out_nid) { +			err = snd_hda_create_spdif_out_ctls(codec, +						    spec->extra_dig_out_nid); +			if (err < 0) +				return err; +		}  	}  	if (spec->dig_in_nid) {  		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); @@ -580,10 +955,89 @@ static void via_free(struct hda_codec *codec)  	kfree(codec->spec);  } +/* mute internal speaker if HP is plugged */ +static void via_hp_automute(struct hda_codec *codec) +{ +	unsigned int present; +	struct via_spec *spec = codec->spec; + +	present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, +				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; +	snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], +				 HDA_OUTPUT, 0, HDA_AMP_MUTE, +				 present ? HDA_AMP_MUTE : 0); +} + +static void via_gpio_control(struct hda_codec *codec) +{ +	unsigned int gpio_data; +	unsigned int vol_counter; +	unsigned int vol; +	unsigned int master_vol; + +	struct via_spec *spec = codec->spec; + +	gpio_data = snd_hda_codec_read(codec, codec->afg, 0, +				       AC_VERB_GET_GPIO_DATA, 0) & 0x03; + +	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, +					  0xF84, 0) & 0x3F0000) >> 16; + +	vol = vol_counter & 0x1F; +	master_vol = snd_hda_codec_read(codec, 0x1A, 0, +					AC_VERB_GET_AMP_GAIN_MUTE, +					AC_AMP_GET_INPUT); + +	if (gpio_data == 0x02) { +		/* unmute line out */ +		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], +					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); + +		if (vol_counter & 0x20) { +			/* decrease volume */ +			if (vol > master_vol) +				vol = master_vol; +			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, +						 0, HDA_AMP_VOLMASK, +						 master_vol-vol); +		} else { +			/* increase volume */ +			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, +					 HDA_AMP_VOLMASK, +					 ((master_vol+vol) > 0x2A) ? 0x2A : +					  (master_vol+vol)); +		} +	} else if (!(gpio_data & 0x02)) { +		/* mute line out */ +		snd_hda_codec_amp_stereo(codec, +					 spec->autocfg.line_out_pins[0], +					 HDA_OUTPUT, 0, HDA_AMP_MUTE, +					 HDA_AMP_MUTE); +	} +} + +/* unsolicited event for jack sensing */ +static void via_unsol_event(struct hda_codec *codec, +				  unsigned int res) +{ +	res >>= 26; +	if (res == VIA_HP_EVENT) +		via_hp_automute(codec); +	else if (res == VIA_GPIO_EVENT) +		via_gpio_control(codec); +} + +static hda_nid_t slave_dig_outs[] = { +	0, +}; +  static int via_init(struct hda_codec *codec)  {  	struct via_spec *spec = codec->spec; -	snd_hda_sequence_write(codec, spec->init_verbs); +	int i; +	for (i = 0; i < spec->num_iverbs; i++) +		snd_hda_sequence_write(codec, spec->init_verbs[i]); +  	/* Lydia Add for EAPD enable */  	if (!spec->dig_in_nid) { /* No Digital In connection */  		if (IS_VT1708_VENDORID(codec->vendor_id)) { @@ -611,6 +1065,9 @@ static int via_init(struct hda_codec *codec)  		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,  				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); +	/* no slave outs */ +	codec->slave_dig_outs = slave_dig_outs; +   	return 0;  } @@ -657,10 +1114,10 @@ static int vt1708_auto_fill_dac_nids(struct via_spec *spec,  				spec->multiout.dac_nids[i] = 0x12;  				break;  			case AUTO_SEQ_SURROUND: -				spec->multiout.dac_nids[i] = 0x13; +				spec->multiout.dac_nids[i] = 0x11;  				break;  			case AUTO_SEQ_SIDE: -				spec->multiout.dac_nids[i] = 0x11; +				spec->multiout.dac_nids[i] = 0x13;  				break;  			}  		} @@ -685,7 +1142,7 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,  			continue;  		if (i != AUTO_SEQ_FRONT) -			nid_vol = 0x1b - i + 1; +			nid_vol = 0x18 + i;  		if (i == AUTO_SEQ_CENLFE) {  			/* Center/LFE */ @@ -760,6 +1217,24 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,  	return 0;  } +static void create_hp_imux(struct via_spec *spec) +{ +	int i; +	struct hda_input_mux *imux = &spec->private_imux[1]; +	static const char *texts[] = { "OFF", "ON", NULL}; + +	/* for hp mode select */ +	i = 0; +	while (texts[i] != NULL) { +		imux->items[imux->num_items].label =  texts[i]; +		imux->items[imux->num_items].index = i; +		imux->num_items++; +		i++; +	} + +	spec->hp_mux = &spec->private_imux[1]; +} +  static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)  {  	int err; @@ -780,6 +1255,8 @@ static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)  	if (err < 0)  		return err; +	create_hp_imux(spec); +  	return 0;  } @@ -790,7 +1267,7 @@ static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,  	static char *labels[] = {  		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL  	}; -	struct hda_input_mux *imux = &spec->private_imux; +	struct hda_input_mux *imux = &spec->private_imux[0];  	int i, err, idx = 0;  	/* for internal loopback recording select */ @@ -840,11 +1317,36 @@ static struct hda_amp_list vt1708_loopbacks[] = {  };  #endif +static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) +{ +	unsigned int def_conf; +	unsigned char seqassoc; + +	def_conf = snd_hda_codec_read(codec, nid, 0, +				      AC_VERB_GET_CONFIG_DEFAULT, 0); +	seqassoc = (unsigned char) get_defcfg_association(def_conf); +	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); +	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { +		if (seqassoc == 0xff) { +			def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); +			snd_hda_codec_write(codec, nid, 0, +					    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, +					    def_conf >> 24); +		} +	} + +	return; +} +  static int vt1708_parse_auto_config(struct hda_codec *codec)  {  	struct via_spec *spec = codec->spec;  	int err; +	/* Add HP and CD pin config connect bit re-config action */ +	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); +	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); +  	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);  	if (err < 0)  		return err; @@ -874,9 +1376,12 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)  	if (spec->kctl_alloc)  		spec->mixers[spec->num_mixers++] = spec->kctl_alloc; -	spec->init_verbs = vt1708_volume_init_verbs;	 +	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; + +	spec->input_mux = &spec->private_imux[0]; -	spec->input_mux = &spec->private_imux; +	if (spec->hp_mux) +		spec->mixers[spec->num_mixers++] = via_hp_mixer;  	return 1;  } @@ -897,7 +1402,7 @@ static int patch_vt1708(struct hda_codec *codec)  	int err;  	/* create a codec specific record */ -	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); +	spec = kzalloc(sizeof(*spec), GFP_KERNEL);  	if (spec == NULL)  		return -ENOMEM; @@ -966,6 +1471,11 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {  	{ } /* end */  }; +static struct hda_verb vt1709_uniwill_init_verbs[] = { +	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, +	{ } +}; +  /*   * generic initialization of ADC, input mixers and output mixers   */ @@ -1090,11 +1600,11 @@ static int vt1709_auto_fill_dac_nids(struct via_spec *spec,  					break;  				case AUTO_SEQ_SURROUND:  					/* AOW3 */ -					spec->multiout.dac_nids[i] = 0x27; +					spec->multiout.dac_nids[i] = 0x11;  					break;  				case AUTO_SEQ_SIDE:  					/* AOW1 */ -					spec->multiout.dac_nids[i] = 0x11; +					spec->multiout.dac_nids[i] = 0x27;  					break;  				default:  					break; @@ -1203,26 +1713,26 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,  		} else if (i == AUTO_SEQ_SURROUND) {  			sprintf(name, "%s Playback Volume", chname[i]);  			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, -					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0, +					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,  								  HDA_OUTPUT));  			if (err < 0)  				return err;  			sprintf(name, "%s Playback Switch", chname[i]);  			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, -					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0, +					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,  								  HDA_OUTPUT));  			if (err < 0)  				return err;  		} else if (i == AUTO_SEQ_SIDE) {  			sprintf(name, "%s Playback Volume", chname[i]);  			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, -					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, +					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,  								  HDA_OUTPUT));  			if (err < 0)  				return err;  			sprintf(name, "%s Playback Switch", chname[i]);  			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, -					      HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, +					      HDA_COMPOSE_AMP_VAL(0x29, 3, 0,  								  HDA_OUTPUT));  			if (err < 0)  				return err; @@ -1265,7 +1775,7 @@ static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,  	static char *labels[] = {  		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL  	}; -	struct hda_input_mux *imux = &spec->private_imux; +	struct hda_input_mux *imux = &spec->private_imux[0];  	int i, err, idx = 0;  	/* for internal loopback recording select */ @@ -1339,7 +1849,10 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)  	if (spec->kctl_alloc)  		spec->mixers[spec->num_mixers++] = spec->kctl_alloc; -	spec->input_mux = &spec->private_imux; +	spec->input_mux = &spec->private_imux[0]; + +	if (spec->hp_mux) +		spec->mixers[spec->num_mixers++] = via_hp_mixer;  	return 1;  } @@ -1360,7 +1873,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec)  	int err;  	/* create a codec specific record */ -	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); +	spec = kzalloc(sizeof(*spec), GFP_KERNEL);  	if (spec == NULL)  		return -ENOMEM; @@ -1375,7 +1888,8 @@ static int patch_vt1709_10ch(struct hda_codec *codec)  		       "Using genenic mode...\n");  	} -	spec->init_verbs = vt1709_10ch_volume_init_verbs;	 +	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;  	spec->stream_name_analog = "VT1709 Analog";  	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; @@ -1396,6 +1910,7 @@ static int patch_vt1709_10ch(struct hda_codec *codec)  	codec->patch_ops = via_patch_ops;  	codec->patch_ops.init = via_auto_init; +	codec->patch_ops.unsol_event = via_unsol_event;  #ifdef CONFIG_SND_HDA_POWER_SAVE  	spec->loopback.amplist = vt1709_loopbacks;  #endif @@ -1451,7 +1966,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec)  	int err;  	/* create a codec specific record */ -	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); +	spec = kzalloc(sizeof(*spec), GFP_KERNEL);  	if (spec == NULL)  		return -ENOMEM; @@ -1466,7 +1981,8 @@ static int patch_vt1709_6ch(struct hda_codec *codec)  		       "Using genenic mode...\n");  	} -	spec->init_verbs = vt1709_6ch_volume_init_verbs;	 +	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;  	spec->stream_name_analog = "VT1709 Analog";  	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; @@ -1487,6 +2003,7 @@ static int patch_vt1709_6ch(struct hda_codec *codec)  	codec->patch_ops = via_patch_ops;  	codec->patch_ops.init = via_auto_init; +	codec->patch_ops.unsol_event = via_unsol_event;  #ifdef CONFIG_SND_HDA_POWER_SAVE  	spec->loopback.amplist = vt1709_loopbacks;  #endif @@ -1586,27 +2103,32 @@ static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {  	{ }  }; +static struct hda_verb vt1708B_uniwill_init_verbs[] = { +	{0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, +	{ } +}; +  static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { -	.substreams = 1, +	.substreams = 2,  	.channels_min = 2,  	.channels_max = 8,  	.nid = 0x10, /* NID to query formats and rates */  	.ops = {  		.open = via_playback_pcm_open, -		.prepare = via_playback_pcm_prepare, -		.cleanup = via_playback_pcm_cleanup +		.prepare = via_playback_multi_pcm_prepare, +		.cleanup = via_playback_multi_pcm_cleanup  	},  };  static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { -	.substreams = 1, +	.substreams = 2,  	.channels_min = 2,  	.channels_max = 4,  	.nid = 0x10, /* NID to query formats and rates */  	.ops = {  		.open = via_playback_pcm_open, -		.prepare = via_playback_pcm_prepare, -		.cleanup = via_playback_pcm_cleanup +		.prepare = via_playback_multi_pcm_prepare, +		.cleanup = via_playback_multi_pcm_cleanup  	},  }; @@ -1662,10 +2184,10 @@ static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,  				spec->multiout.dac_nids[i] = 0x24;  				break;  			case AUTO_SEQ_SURROUND: -				spec->multiout.dac_nids[i] = 0x25; +				spec->multiout.dac_nids[i] = 0x11;  				break;  			case AUTO_SEQ_SIDE: -				spec->multiout.dac_nids[i] = 0x11; +				spec->multiout.dac_nids[i] = 0x25;  				break;  			}  		} @@ -1680,7 +2202,7 @@ static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,  {  	char name[32];  	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; -	hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18}; +	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};  	hda_nid_t nid, nid_vol = 0;  	int i, err; @@ -1785,6 +2307,8 @@ static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)  	if (err < 0)  		return err; +	create_hp_imux(spec); +  	return 0;  } @@ -1795,7 +2319,7 @@ static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,  	static char *labels[] = {  		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL  	}; -	struct hda_input_mux *imux = &spec->private_imux; +	struct hda_input_mux *imux = &spec->private_imux[0];  	int i, err, idx = 0;  	/* for internal loopback recording select */ @@ -1869,7 +2393,10 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)  	if (spec->kctl_alloc)  		spec->mixers[spec->num_mixers++] = spec->kctl_alloc; -	spec->input_mux = &spec->private_imux; +	spec->input_mux = &spec->private_imux[0]; + +	if (spec->hp_mux) +		spec->mixers[spec->num_mixers++] = via_hp_mixer;  	return 1;  } @@ -1890,7 +2417,7 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)  	int err;  	/* create a codec specific record */ -	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); +	spec = kzalloc(sizeof(*spec), GFP_KERNEL);  	if (spec == NULL)  		return -ENOMEM; @@ -1906,7 +2433,8 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)  		       "from BIOS.  Using genenic mode...\n");  	} -	spec->init_verbs = vt1708B_8ch_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;  	spec->stream_name_analog = "VT1708B Analog";  	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; @@ -1926,6 +2454,7 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)  	codec->patch_ops = via_patch_ops;  	codec->patch_ops.init = via_auto_init; +	codec->patch_ops.unsol_event = via_unsol_event;  #ifdef CONFIG_SND_HDA_POWER_SAVE  	spec->loopback.amplist = vt1708B_loopbacks;  #endif @@ -1939,7 +2468,7 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)  	int err;  	/* create a codec specific record */ -	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); +	spec = kzalloc(sizeof(*spec), GFP_KERNEL);  	if (spec == NULL)  		return -ENOMEM; @@ -1955,7 +2484,8 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)  		       "from BIOS.  Using genenic mode...\n");  	} -	spec->init_verbs = vt1708B_4ch_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;  	spec->stream_name_analog = "VT1708B Analog";  	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; @@ -1975,6 +2505,7 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)  	codec->patch_ops = via_patch_ops;  	codec->patch_ops.init = via_auto_init; +	codec->patch_ops.unsol_event = via_unsol_event;  #ifdef CONFIG_SND_HDA_POWER_SAVE  	spec->loopback.amplist = vt1708B_loopbacks;  #endif @@ -1982,6 +2513,752 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)  	return 0;  } +/* Patch for VT1708S */ + +/* VT1708S software backdoor based override for buggy hardware micboost + * setting */ +#define MIC_BOOST_VOLUME(xname, nid) {				\ +	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\ +	.name = xname,					\ +	.index = 0,					\ +	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |	\ +	SNDRV_CTL_ELEM_ACCESS_TLV_READ |		\ +	SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,		\ +	.info = mic_boost_volume_info,			\ +	.get = snd_hda_mixer_amp_volume_get,		\ +	.put = snd_hda_mixer_amp_volume_put,		\ +	.tlv = { .c = mic_boost_tlv },			\ +	.private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) } + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1708S_capture_mixer[] = { +	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), +	MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A), +	MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		/* The multiple "Capture Source" controls confuse alsamixer +		 * So call somewhat different.. +		 */ +		/* .name = "Capture Source", */ +		.name = "Input Source", +		.count = 1, +		.info = via_mux_enum_info, +		.get = via_mux_enum_get, +		.put = via_mux_enum_put, +	}, +	{ } /* end */ +}; + +static struct hda_verb vt1708S_volume_init_verbs[] = { +	/* Unmute ADC0-1 and set the default input to mic-in */ +	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + +	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the +	 * analog-loopback mixer widget */ +	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ +	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, +	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, +	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + +	/* Setup default input of PW4 to MW0 */ +	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, +	/* PW9, PW10  Output enable */ +	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +	/* Enable Mic Boost Volume backdoor */ +	{0x1, 0xf98, 0x1}, +	{ } +}; + +static struct hda_verb vt1708S_uniwill_init_verbs[] = { +	{0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, +	{ } +}; + +static struct hda_pcm_stream vt1708S_pcm_analog_playback = { +	.substreams = 2, +	.channels_min = 2, +	.channels_max = 8, +	.nid = 0x10, /* NID to query formats and rates */ +	.ops = { +		.open = via_playback_pcm_open, +		.prepare = via_playback_pcm_prepare, +		.cleanup = via_playback_pcm_cleanup +	}, +}; + +static struct hda_pcm_stream vt1708S_pcm_analog_capture = { +	.substreams = 2, +	.channels_min = 2, +	.channels_max = 2, +	.nid = 0x13, /* NID to query formats and rates */ +	.ops = { +		.prepare = via_capture_pcm_prepare, +		.cleanup = via_capture_pcm_cleanup +	}, +}; + +static struct hda_pcm_stream vt1708S_pcm_digital_playback = { +	.substreams = 2, +	.channels_min = 2, +	.channels_max = 2, +	/* NID is set in via_build_pcms */ +	.ops = { +		.open = via_dig_playback_pcm_open, +		.close = via_dig_playback_pcm_close, +		.prepare = via_dig_playback_pcm_prepare +	}, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, +				     const struct auto_pin_cfg *cfg) +{ +	int i; +	hda_nid_t nid; + +	spec->multiout.num_dacs = cfg->line_outs; + +	spec->multiout.dac_nids = spec->private_dac_nids; + +	for (i = 0; i < 4; i++) { +		nid = cfg->line_out_pins[i]; +		if (nid) { +			/* config dac list */ +			switch (i) { +			case AUTO_SEQ_FRONT: +				spec->multiout.dac_nids[i] = 0x10; +				break; +			case AUTO_SEQ_CENLFE: +				spec->multiout.dac_nids[i] = 0x24; +				break; +			case AUTO_SEQ_SURROUND: +				spec->multiout.dac_nids[i] = 0x11; +				break; +			case AUTO_SEQ_SIDE: +				spec->multiout.dac_nids[i] = 0x25; +				break; +			} +		} +	} + +	return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, +					     const struct auto_pin_cfg *cfg) +{ +	char name[32]; +	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; +	hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; +	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; +	hda_nid_t nid, nid_vol, nid_mute; +	int i, err; + +	for (i = 0; i <= AUTO_SEQ_SIDE; i++) { +		nid = cfg->line_out_pins[i]; + +		if (!nid) +			continue; + +		nid_vol = nid_vols[i]; +		nid_mute = nid_mutes[i]; + +		if (i == AUTO_SEQ_CENLFE) { +			/* Center/LFE */ +			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, +					      "Center Playback Volume", +					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, +					      "LFE Playback Volume", +					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, +					      "Center Playback Switch", +					      HDA_COMPOSE_AMP_VAL(nid_mute, +								  1, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, +					      "LFE Playback Switch", +					      HDA_COMPOSE_AMP_VAL(nid_mute, +								  2, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +		} else if (i == AUTO_SEQ_FRONT) { +			/* add control to mixer index 0 */ +			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, +					      "Master Front Playback Volume", +					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, +								  HDA_INPUT)); +			if (err < 0) +				return err; +			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, +					      "Master Front Playback Switch", +					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, +								  HDA_INPUT)); +			if (err < 0) +				return err; + +			/* Front */ +			sprintf(name, "%s Playback Volume", chname[i]); +			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, +					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +			sprintf(name, "%s Playback Switch", chname[i]); +			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, +					      HDA_COMPOSE_AMP_VAL(nid_mute, +								  3, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +		} else { +			sprintf(name, "%s Playback Volume", chname[i]); +			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, +					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +			sprintf(name, "%s Playback Switch", chname[i]); +			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, +					      HDA_COMPOSE_AMP_VAL(nid_mute, +								  3, 0, +								  HDA_OUTPUT)); +			if (err < 0) +				return err; +		} +	} + +	return 0; +} + +static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ +	int err; + +	if (!pin) +		return 0; + +	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ + +	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, +			      "Headphone Playback Volume", +			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); +	if (err < 0) +		return err; + +	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, +			      "Headphone Playback Switch", +			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); +	if (err < 0) +		return err; + +	create_hp_imux(spec); + +	return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, +						const struct auto_pin_cfg *cfg) +{ +	static char *labels[] = { +		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL +	}; +	struct hda_input_mux *imux = &spec->private_imux[0]; +	int i, err, idx = 0; + +	/* for internal loopback recording select */ +	imux->items[imux->num_items].label = "Stereo Mixer"; +	imux->items[imux->num_items].index = 5; +	imux->num_items++; + +	for (i = 0; i < AUTO_PIN_LAST; i++) { +		if (!cfg->input_pins[i]) +			continue; + +		switch (cfg->input_pins[i]) { +		case 0x1a: /* Mic */ +			idx = 2; +			break; + +		case 0x1b: /* Line In */ +			idx = 3; +			break; + +		case 0x1e: /* Front Mic */ +			idx = 4; +			break; + +		case 0x1f: /* CD */ +			idx = 1; +			break; +		} +		err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], +					   idx, 0x16); +		if (err < 0) +			return err; +		imux->items[imux->num_items].label = labels[i]; +		imux->items[imux->num_items].index = idx-1; +		imux->num_items++; +	} +	return 0; +} + +static int vt1708S_parse_auto_config(struct hda_codec *codec) +{ +	struct via_spec *spec = codec->spec; +	int err; +	static hda_nid_t vt1708s_ignore[] = {0x21, 0}; + +	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, +					   vt1708s_ignore); +	if (err < 0) +		return err; +	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); +	if (err < 0) +		return err; +	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) +		return 0; /* can't find valid BIOS pin config */ + +	err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); +	if (err < 0) +		return err; +	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); +	if (err < 0) +		return err; +	err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); +	if (err < 0) +		return err; + +	spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +	if (spec->autocfg.dig_out_pin) +		spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID; + +	spec->extra_dig_out_nid = 0x15; + +	if (spec->kctl_alloc) +		spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + +	spec->input_mux = &spec->private_imux[0]; + +	if (spec->hp_mux) +		spec->mixers[spec->num_mixers++] = via_hp_mixer; + +	return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1708S_loopbacks[] = { +	{ 0x16, HDA_INPUT, 1 }, +	{ 0x16, HDA_INPUT, 2 }, +	{ 0x16, HDA_INPUT, 3 }, +	{ 0x16, HDA_INPUT, 4 }, +	{ } /* end */ +}; +#endif + +static int patch_vt1708S(struct hda_codec *codec) +{ +	struct via_spec *spec; +	int err; + +	/* create a codec specific record */ +	spec = kzalloc(sizeof(*spec), GFP_KERNEL); +	if (spec == NULL) +		return -ENOMEM; + +	codec->spec = spec; + +	/* automatic parse from the BIOS config */ +	err = vt1708S_parse_auto_config(codec); +	if (err < 0) { +		via_free(codec); +		return err; +	} else if (!err) { +		printk(KERN_INFO "hda_codec: Cannot set up configuration " +		       "from BIOS.  Using genenic mode...\n"); +	} + +	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; + +	spec->stream_name_analog = "VT1708S Analog"; +	spec->stream_analog_playback = &vt1708S_pcm_analog_playback; +	spec->stream_analog_capture = &vt1708S_pcm_analog_capture; + +	spec->stream_name_digital = "VT1708S Digital"; +	spec->stream_digital_playback = &vt1708S_pcm_digital_playback; + +	if (!spec->adc_nids && spec->input_mux) { +		spec->adc_nids = vt1708S_adc_nids; +		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); +		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; +		spec->num_mixers++; +	} + +	codec->patch_ops = via_patch_ops; + +	codec->patch_ops.init = via_auto_init; +	codec->patch_ops.unsol_event = via_unsol_event; +#ifdef CONFIG_SND_HDA_POWER_SAVE +	spec->loopback.amplist = vt1708S_loopbacks; +#endif + +	return 0; +} + +/* Patch for VT1702 */ + +/* capture mixer elements */ +static struct snd_kcontrol_new vt1702_capture_mixer[] = { +	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), +	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), +	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, +			 HDA_INPUT), +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		/* The multiple "Capture Source" controls confuse alsamixer +		 * So call somewhat different.. +		 */ +		/* .name = "Capture Source", */ +		.name = "Input Source", +		.count = 1, +		.info = via_mux_enum_info, +		.get = via_mux_enum_get, +		.put = via_mux_enum_put, +	}, +	{ } /* end */ +}; + +static struct hda_verb vt1702_volume_init_verbs[] = { +	/* +	 * Unmute ADC0-1 and set the default input to mic-in +	 */ +	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + +	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback +	 * mixer widget +	 */ +	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ +	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, +	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, +	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, +	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + +	/* Setup default input of PW4 to MW0 */ +	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, +	/* PW6 PW7 Output enable */ +	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, +	{ } +}; + +static struct hda_verb vt1702_uniwill_init_verbs[] = { +	{0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT}, +	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, +	{ } +}; + +static struct hda_pcm_stream vt1702_pcm_analog_playback = { +	.substreams = 2, +	.channels_min = 2, +	.channels_max = 2, +	.nid = 0x10, /* NID to query formats and rates */ +	.ops = { +		.open = via_playback_pcm_open, +		.prepare = via_playback_multi_pcm_prepare, +		.cleanup = via_playback_multi_pcm_cleanup +	}, +}; + +static struct hda_pcm_stream vt1702_pcm_analog_capture = { +	.substreams = 3, +	.channels_min = 2, +	.channels_max = 2, +	.nid = 0x12, /* NID to query formats and rates */ +	.ops = { +		.prepare = via_capture_pcm_prepare, +		.cleanup = via_capture_pcm_cleanup +	}, +}; + +static struct hda_pcm_stream vt1702_pcm_digital_playback = { +	.substreams = 2, +	.channels_min = 2, +	.channels_max = 2, +	/* NID is set in via_build_pcms */ +	.ops = { +		.open = via_dig_playback_pcm_open, +		.close = via_dig_playback_pcm_close, +		.prepare = via_dig_playback_pcm_prepare +	}, +}; + +/* fill in the dac_nids table from the parsed pin configuration */ +static int vt1702_auto_fill_dac_nids(struct via_spec *spec, +				     const struct auto_pin_cfg *cfg) +{ +	spec->multiout.num_dacs = 1; +	spec->multiout.dac_nids = spec->private_dac_nids; + +	if (cfg->line_out_pins[0]) { +		/* config dac list */ +		spec->multiout.dac_nids[0] = 0x10; +	} + +	return 0; +} + +/* add playback controls from the parsed DAC table */ +static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, +					     const struct auto_pin_cfg *cfg) +{ +	int err; + +	if (!cfg->line_out_pins[0]) +		return -1; + +	/* add control to mixer index 0 */ +	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, +			      "Master Front Playback Volume", +			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); +	if (err < 0) +		return err; +	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, +			      "Master Front Playback Switch", +			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); +	if (err < 0) +		return err; + +	/* Front */ +	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, +			      "Front Playback Volume", +			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); +	if (err < 0) +		return err; +	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, +			      "Front Playback Switch", +			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); +	if (err < 0) +		return err; + +	return 0; +} + +static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) +{ +	int err; + +	if (!pin) +		return 0; + +	spec->multiout.hp_nid = 0x1D; + +	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, +			      "Headphone Playback Volume", +			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); +	if (err < 0) +		return err; + +	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, +			      "Headphone Playback Switch", +			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); +	if (err < 0) +		return err; + +	create_hp_imux(spec); + +	return 0; +} + +/* create playback/capture controls for input pins */ +static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, +						const struct auto_pin_cfg *cfg) +{ +	static char *labels[] = { +		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL +	}; +	struct hda_input_mux *imux = &spec->private_imux[0]; +	int i, err, idx = 0; + +	/* for internal loopback recording select */ +	imux->items[imux->num_items].label = "Stereo Mixer"; +	imux->items[imux->num_items].index = 3; +	imux->num_items++; + +	for (i = 0; i < AUTO_PIN_LAST; i++) { +		if (!cfg->input_pins[i]) +			continue; + +		switch (cfg->input_pins[i]) { +		case 0x14: /* Mic */ +			idx = 1; +			break; + +		case 0x15: /* Line In */ +			idx = 2; +			break; + +		case 0x18: /* Front Mic */ +			idx = 3; +			break; +		} +		err = via_new_analog_input(spec, cfg->input_pins[i], +					   labels[i], idx, 0x1A); +		if (err < 0) +			return err; +		imux->items[imux->num_items].label = labels[i]; +		imux->items[imux->num_items].index = idx-1; +		imux->num_items++; +	} +	return 0; +} + +static int vt1702_parse_auto_config(struct hda_codec *codec) +{ +	struct via_spec *spec = codec->spec; +	int err; +	static hda_nid_t vt1702_ignore[] = {0x1C, 0}; + +	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, +					   vt1702_ignore); +	if (err < 0) +		return err; +	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); +	if (err < 0) +		return err; +	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) +		return 0; /* can't find valid BIOS pin config */ + +	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); +	if (err < 0) +		return err; +	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); +	if (err < 0) +		return err; +	err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); +	if (err < 0) +		return err; + +	spec->multiout.max_channels = spec->multiout.num_dacs * 2; + +	if (spec->autocfg.dig_out_pin) +		spec->multiout.dig_out_nid = VT1702_DIGOUT_NID; + +	spec->extra_dig_out_nid = 0x1B; + +	if (spec->kctl_alloc) +		spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + +	spec->input_mux = &spec->private_imux[0]; + +	if (spec->hp_mux) +		spec->mixers[spec->num_mixers++] = via_hp_mixer; + +	return 1; +} + +#ifdef CONFIG_SND_HDA_POWER_SAVE +static struct hda_amp_list vt1702_loopbacks[] = { +	{ 0x1A, HDA_INPUT, 1 }, +	{ 0x1A, HDA_INPUT, 2 }, +	{ 0x1A, HDA_INPUT, 3 }, +	{ 0x1A, HDA_INPUT, 4 }, +	{ } /* end */ +}; +#endif + +static int patch_vt1702(struct hda_codec *codec) +{ +	struct via_spec *spec; +	int err; +	unsigned int response; +	unsigned char control; + +	/* create a codec specific record */ +	spec = kzalloc(sizeof(*spec), GFP_KERNEL); +	if (spec == NULL) +		return -ENOMEM; + +	codec->spec = spec; + +	/* automatic parse from the BIOS config */ +	err = vt1702_parse_auto_config(codec); +	if (err < 0) { +		via_free(codec); +		return err; +	} else if (!err) { +		printk(KERN_INFO "hda_codec: Cannot set up configuration " +		       "from BIOS.  Using genenic mode...\n"); +	} + +	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; +	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; + +	spec->stream_name_analog = "VT1702 Analog"; +	spec->stream_analog_playback = &vt1702_pcm_analog_playback; +	spec->stream_analog_capture = &vt1702_pcm_analog_capture; + +	spec->stream_name_digital = "VT1702 Digital"; +	spec->stream_digital_playback = &vt1702_pcm_digital_playback; + +	if (!spec->adc_nids && spec->input_mux) { +		spec->adc_nids = vt1702_adc_nids; +		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); +		spec->mixers[spec->num_mixers] = vt1702_capture_mixer; +		spec->num_mixers++; +	} + +	codec->patch_ops = via_patch_ops; + +	codec->patch_ops.init = via_auto_init; +	codec->patch_ops.unsol_event = via_unsol_event; +#ifdef CONFIG_SND_HDA_POWER_SAVE +	spec->loopback.amplist = vt1702_loopbacks; +#endif + +	/* Open backdoor */ +	response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0); +	control = (unsigned char)(response & 0xff); +	control |= 0x3; +	snd_hda_codec_write(codec,  codec->afg, 0, 0xF88, control); + +	/* Enable GPIO 0&1 for volume&mute control */ +	/* Enable GPIO 2 for DMIC-DATA */ +	response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0); +	control = (unsigned char)((response >> 16) & 0x3f); +	snd_hda_codec_write(codec,  codec->afg, 0, 0xF82, control); + +	return 0; +} +  /*   * patch entries   */ @@ -2022,5 +3299,37 @@ struct hda_codec_preset snd_hda_preset_via[] = {  	  .patch = patch_vt1708B_4ch},  	{ .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",  	  .patch = patch_vt1708B_4ch}, +	{ .id = 0x11060397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11061397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11062397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11063397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11064397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11065397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11066397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11067397, .name = "VIA VT1708S", +	  .patch = patch_vt1708S}, +	{ .id = 0x11060398, .name = "VIA VT1702", +	  .patch = patch_vt1702}, +	{ .id = 0x11061398, .name = "VIA VT1702", +	  .patch = patch_vt1702}, +	{ .id = 0x11062398, .name = "VIA VT1702", +	  .patch = patch_vt1702}, +	{ .id = 0x11063398, .name = "VIA VT1702", +	  .patch = patch_vt1702}, +	{ .id = 0x11064398, .name = "VIA VT1702", +	  .patch = patch_vt1702}, +	{ .id = 0x11065398, .name = "VIA VT1702", +	  .patch = patch_vt1702}, +	{ .id = 0x11066398, .name = "VIA VT1702", +	  .patch = patch_vt1702}, +	{ .id = 0x11067398, .name = "VIA VT1702", +	  .patch = patch_vt1702},  	{} /* terminator */  }; diff --git a/sound/pci/ice1712/ak4xxx.c b/sound/pci/ice1712/ak4xxx.c index dab31b2..03391da 100644 --- a/sound/pci/ice1712/ak4xxx.c +++ b/sound/pci/ice1712/ak4xxx.c @@ -59,7 +59,8 @@ static void snd_ice1712_akm4xxx_write(struct snd_akm4xxx *ak, int chip,  	struct snd_ak4xxx_private *priv = (void *)ak->private_value[0];  	struct snd_ice1712 *ice = ak->private_data[0]; -	snd_assert(chip >= 0 && chip < 4, return); +	if (snd_BUG_ON(chip < 0 || chip >= 4)) +		return;  	tmp = snd_ice1712_gpio_read(ice);  	tmp |= priv->add_flags; diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 868ae29..110d16e 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -44,10 +44,9 @@   *       not working: prety much everything else, at least i could verify that   *                    we have no digital output, no capture, pretty bad clicks and poops   *                    on mixer switch and other coll stuff. - * - */       + */ -#include <asm/io.h> +#include <linux/io.h>  #include <linux/delay.h>  #include <linux/interrupt.h>  #include <linux/init.h> @@ -131,7 +130,7 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,  	snd_ice1712_gpio_write(ice, tmp);  	udelay(50); -	/*  +	/*  	 * send i2c stop condition and start condition  	 * to obtain sane state  	 */ @@ -152,10 +151,16 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,  	 * skipping ack cycles inbetween  	 */  	for (j = 0; j < 3; j++) { -		switch(j) { -		case 0: val = dev; break; -		case 1: val = reg; break; -		case 2: val = data; break; +		switch (j) { +		case 0: +			val = dev; +			break; +		case 1: +			val = reg; +			break; +		case 2: +			val = data; +			break;  		}  		for (i = 7; i >= 0; i--) {  			tmp &= ~AUREON_SPI_CLK; @@ -171,7 +176,7 @@ static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,  			snd_ice1712_gpio_write(ice, tmp);  			udelay(40);  		} -                tmp &= ~AUREON_SPI_CLK; +		tmp &= ~AUREON_SPI_CLK;  		snd_ice1712_gpio_write(ice, tmp);  		udelay(40);  		tmp |= AUREON_SPI_CLK; @@ -203,7 +208,7 @@ static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	uinfo->count = 1;  	uinfo->value.enumerated.items = 3; -	if(uinfo->value.enumerated.item >= uinfo->value.enumerated.items) +	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)  		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;  	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);  	return 0; @@ -231,12 +236,12 @@ static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,  		return -EINVAL;  	snd_ice1712_save_gpio_status(ice);  	oval = spec->pca9554_out; -	if ((change = (oval != nval))) { +	change = (oval != nval); +	if (change) {  		aureon_pca9554_write(ice, PCA9554_OUT, nval);  		spec->pca9554_out = nval;  	}  	snd_ice1712_restore_gpio_status(ice); -	  	return change;  } @@ -256,7 +261,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,  	udelay(10);  	tmp &= ~AUREON_AC97_ADDR;  	snd_ice1712_gpio_write(ice, tmp); -	udelay(10);	 +	udelay(10);  	/* Send low-order byte to XILINX chip */  	tmp &= ~AUREON_AC97_DATA_MASK; @@ -269,7 +274,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,  	tmp &= ~AUREON_AC97_DATA_LOW;  	snd_ice1712_gpio_write(ice, tmp);  	udelay(10); -	 +  	/* Send high-order byte to XILINX chip */  	tmp &= ~AUREON_AC97_DATA_MASK;  	tmp |= (val >> 8) & AUREON_AC97_DATA_MASK; @@ -282,7 +287,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,  	tmp &= ~AUREON_AC97_DATA_HIGH;  	snd_ice1712_gpio_write(ice, tmp);  	udelay(10); -	 +  	/* Instruct XILINX chip to parse the data to the STAC9744 chip */  	tmp |= AUREON_AC97_COMMIT;  	snd_ice1712_gpio_write(ice, tmp); @@ -290,7 +295,7 @@ static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,  	tmp &= ~AUREON_AC97_COMMIT;  	snd_ice1712_gpio_write(ice, tmp);  	udelay(10); -	 +  	/* Store the data in out private buffer */  	spec->stac9744[(reg & 0x7F) >> 1] = val;  } @@ -304,7 +309,7 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r  /*   * Initialize STAC9744 chip   */ -static int aureon_ac97_init (struct snd_ice1712 *ice) +static int aureon_ac97_init(struct snd_ice1712 *ice)  {  	struct aureon_spec *spec = ice->spec;  	int i; @@ -335,20 +340,21 @@ static int aureon_ac97_init (struct snd_ice1712 *ice)  	tmp = (snd_ice1712_gpio_read(ice) | AUREON_AC97_RESET) & ~AUREON_AC97_DATA_MASK;  	snd_ice1712_gpio_write(ice, tmp);  	udelay(3); -	 +  	tmp &= ~AUREON_AC97_RESET;  	snd_ice1712_gpio_write(ice, tmp);  	udelay(3); -	 +  	tmp |= AUREON_AC97_RESET;  	snd_ice1712_gpio_write(ice, tmp);  	udelay(3); -	 +  	memset(&spec->stac9744, 0, sizeof(spec->stac9744)); -	for (i=0; ac97_defaults[i] != (unsigned short)-1; i+=2) +	for (i = 0; ac97_defaults[i] != (unsigned short)-1; i += 2)  		spec->stac9744[(ac97_defaults[i]) >> 1] = ac97_defaults[i+1]; -		 -	aureon_ac97_write(ice, AC97_MASTER, 0x0000); // Unmute AC'97 master volume permanently - muting is done by WM8770 + +	/* Unmute AC'97 master volume permanently - muting is done by WM8770 */ +	aureon_ac97_write(ice, AC97_MASTER, 0x0000);  	return 0;  } @@ -388,7 +394,7 @@ static int aureon_ac97_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned short ovol, nvol;  	int change; -	 +  	snd_ice1712_save_gpio_status(ice);  	ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F); @@ -396,13 +402,14 @@ static int aureon_ac97_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele  	if (kcontrol->private_value & AUREON_AC97_STEREO)  		nvol |= ((0x1F - ucontrol->value.integer.value[1]) << 8) & 0x1F00;  	nvol |= ovol & ~0x1F1F; -	 -	if ((change = (ovol != nvol))) + +	change = (ovol != nvol); +	if (change)  		aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol);  	snd_ice1712_restore_gpio_status(ice); -	return change;		 +	return change;  }  /* @@ -416,7 +423,8 @@ static int aureon_ac97_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_el  	mutex_lock(&ice->gpio_mutex); -	ucontrol->value.integer.value[0] = aureon_ac97_read(ice, kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1; +	ucontrol->value.integer.value[0] = aureon_ac97_read(ice, +			kcontrol->private_value & 0x7F) & 0x8000 ? 0 : 1;  	mutex_unlock(&ice->gpio_mutex);  	return 0; @@ -429,13 +437,14 @@ static int aureon_ac97_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el  	int change;  	snd_ice1712_save_gpio_status(ice); -	 +  	ovol = aureon_ac97_read(ice, kcontrol->private_value & 0x7F); -	nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~	0x8000); -	 -	if ((change = (ovol != nvol))) +	nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x8000) | (ovol & ~0x8000); + +	change = (ovol != nvol); +	if (change)  		aureon_ac97_write(ice, kcontrol->private_value & 0x7F, nvol); -		 +  	snd_ice1712_restore_gpio_status(ice);  	return change; @@ -465,13 +474,14 @@ static int aureon_ac97_micboost_put(struct snd_kcontrol *kcontrol, struct snd_ct  	int change;  	snd_ice1712_save_gpio_status(ice); -	 +  	ovol = aureon_ac97_read(ice, AC97_MIC);  	nvol = (ucontrol->value.integer.value[0] ? 0x0000 : 0x0020) | (ovol & ~0x0020); -	 -	if ((change = (ovol != nvol))) + +	change = (ovol != nvol); +	if (change)  		aureon_ac97_write(ice, AC97_MIC, nvol); -		 +  	snd_ice1712_restore_gpio_status(ice);  	return change; @@ -493,16 +503,15 @@ static void aureon_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned  		snd_ice1712_gpio_set_mask(ice, ~(PRODIGY_SPI_MOSI|PRODIGY_SPI_CLK|PRODIGY_WM_CS));  		mosi = PRODIGY_SPI_MOSI;  		clk = PRODIGY_SPI_CLK; -	} -	else { +	} else {  		snd_ice1712_gpio_set_mask(ice, ~(AUREON_WM_RW|AUREON_SPI_MOSI|AUREON_SPI_CLK|  						 AUREON_WM_CS|AUREON_CS8415_CS));  		mosi = AUREON_SPI_MOSI;  		clk = AUREON_SPI_CLK; -		 +  		tmp |= AUREON_WM_RW;  	} -	 +  	tmp &= ~cs;  	snd_ice1712_gpio_write(ice, tmp);  	udelay(1); @@ -534,7 +543,9 @@ static void aureon_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned  /*   * Read data in SPI mode   */ -static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits, unsigned char *buffer, int size) { +static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, +		unsigned int data, int bits, unsigned char *buffer, int size) +{  	int i, j;  	unsigned int tmp; @@ -544,7 +555,7 @@ static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned i  	snd_ice1712_gpio_write(ice, tmp);  	udelay(1); -	for (i=bits-1; i>=0; i--) { +	for (i = bits-1; i >= 0; i--) {  		if (data & (1 << i))  			tmp |= AUREON_SPI_MOSI;  		else @@ -561,9 +572,9 @@ static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned i  		udelay(1);  	} -	for (j=0; j<size; j++) { +	for (j = 0; j < size; j++) {  		unsigned char outdata = 0; -		for (i=7; i>=0; i--) { +		for (i = 7; i >= 0; i--) {  			tmp = snd_ice1712_gpio_read(ice);  			outdata <<= 1;  			outdata |= (tmp & AUREON_SPI_MISO) ? 1 : 0; @@ -584,19 +595,24 @@ static void aureon_spi_read(struct snd_ice1712 *ice, unsigned int cs, unsigned i  	snd_ice1712_gpio_write(ice, tmp);  } -static unsigned char aureon_cs8415_get(struct snd_ice1712 *ice, int reg) { +static unsigned char aureon_cs8415_get(struct snd_ice1712 *ice, int reg) +{  	unsigned char val;  	aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);  	aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, &val, 1);  	return val;  } -static void aureon_cs8415_read(struct snd_ice1712 *ice, int reg, unsigned char *buffer, int size) { +static void aureon_cs8415_read(struct snd_ice1712 *ice, int reg, +				unsigned char *buffer, int size) +{  	aureon_spi_write(ice, AUREON_CS8415_CS, 0x2000 | reg, 16);  	aureon_spi_read(ice, AUREON_CS8415_CS, 0x21, 8, buffer, size);  } -static void aureon_cs8415_put(struct snd_ice1712 *ice, int reg, unsigned char val) { +static void aureon_cs8415_put(struct snd_ice1712 *ice, int reg, +						unsigned char val) +{  	aureon_spi_write(ice, AUREON_CS8415_CS, 0x200000 | (reg << 8) | val, 24);  } @@ -654,18 +670,20 @@ static int aureon_ac97_mmute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e  	return 0;  } -static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { +static int aureon_ac97_mmute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned short ovol, nvol;  	int change; -	 +  	snd_ice1712_save_gpio_status(ice); -	 +  	ovol = wm_get(ice, WM_OUT_MUX1);  	nvol = (ovol & ~0x02) | (ucontrol->value.integer.value[0] ? 0x02 : 0x00); -	if ((change = (ovol != nvol))) +	change = (ovol != nvol); +	if (change)  		wm_put(ice, WM_OUT_MUX1, nvol); -		 +  	snd_ice1712_restore_gpio_status(ice);  	return change; @@ -702,12 +720,12 @@ static const unsigned char wm_vol[256] = {  static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master)  {  	unsigned char nvol; -	 +  	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))  		nvol = 0;  	else  		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX]; -	 +  	wm_put(ice, index, nvol);  	wm_put_nocache(ice, index, 0x180 | nvol);  } @@ -736,7 +754,8 @@ static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va  	snd_ice1712_save_gpio_status(ice);  	oval = wm_get(ice, WM_MUTE);  	nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10); -	if ((change = (nval != oval))) +	change = (oval != nval); +	if (change)  		wm_put(ice, WM_MUTE, nval);  	snd_ice1712_restore_gpio_status(ice); @@ -760,7 +779,7 @@ static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct aureon_spec *spec = ice->spec;  	int i; -	for (i=0; i<2; i++) +	for (i = 0; i < 2; i++)  		ucontrol->value.integer.value[i] =  			spec->master[i] & ~WM_VOL_MUTE;  	return 0; @@ -849,7 +868,8 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *  /*   * WM8770 mute control   */ -static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { +static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;  	uinfo->count = kcontrol->private_value >> 8;  	uinfo->value.integer.min = 0; @@ -862,7 +882,7 @@ static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct aureon_spec *spec = ice->spec;  	int voices, ofs, i; -	 +  	voices = kcontrol->private_value >> 8;  	ofs = kcontrol->private_value & 0xFF; @@ -907,7 +927,7 @@ static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct aureon_spec *spec = ice->spec; -	 +  	ucontrol->value.integer.value[0] =  		(spec->master[0] & WM_VOL_MUTE) ? 0 : 1;  	ucontrol->value.integer.value[1] = @@ -1083,21 +1103,21 @@ static int wm_adc_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val  static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)  {  	static const char * const texts[] = { -		"CD",		//AIN1 -		"Aux",		//AIN2 -		"Line",		//AIN3 -		"Mic",		//AIN4 -		"AC97"		//AIN5 +		"CD",		/* AIN1 */ +		"Aux",		/* AIN2 */ +		"Line",		/* AIN3 */ +		"Mic",		/* AIN4 */ +		"AC97"		/* AIN5 */  	};  	static const char * const universe_texts[] = { -		"Aux1",		//AIN1 -		"CD",		//AIN2 -		"Phono",	//AIN3 -		"Line",		//AIN4 -		"Aux2",		//AIN5 -		"Mic",		//AIN6 -		"Aux3",		//AIN7 -		"AC97"		//AIN8 +		"Aux1",		/* AIN1 */ +		"CD",		/* AIN2 */ +		"Phono",	/* AIN3 */ +		"Line",		/* AIN4 */ +		"Aux2",		/* AIN5 */ +		"Mic",		/* AIN6 */ +		"Aux3",		/* AIN7 */ +		"AC97"		/* AIN8 */  	};  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); @@ -1108,8 +1128,7 @@ static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in  		if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)  			uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;  		strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]); -	} -	else { +	} else {  		uinfo->value.enumerated.items = 5;  		if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)  			uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; @@ -1156,8 +1175,8 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	static const char * const aureon_texts[] = { -		"CD",		//RXP0 -		"Optical"	//RXP1 +		"CD",		/* RXP0 */ +		"Optical"	/* RXP1 */  	};  	static const char * const prodigy_texts[] = {  		"CD", @@ -1180,10 +1199,10 @@ static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct aureon_spec *spec = ice->spec; -	//snd_ice1712_save_gpio_status(ice); -	//val = aureon_cs8415_get(ice, CS8415_CTRL2); +	/* snd_ice1712_save_gpio_status(ice); */ +	/* val = aureon_cs8415_get(ice, CS8415_CTRL2); */  	ucontrol->value.enumerated.item[0] = spec->cs8415_mux; -	//snd_ice1712_restore_gpio_status(ice); +	/* snd_ice1712_restore_gpio_status(ice); */  	return 0;  } @@ -1206,7 +1225,7 @@ static int aureon_cs8415_mux_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e  	return change;  } -static int aureon_cs8415_rate_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int aureon_cs8415_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)  {  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;  	uinfo->count = 1; @@ -1215,7 +1234,7 @@ static int aureon_cs8415_rate_info (struct snd_kcontrol *kcontrol, struct snd_ct  	return 0;  } -static int aureon_cs8415_rate_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int aureon_cs8415_rate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char ratio; @@ -1229,7 +1248,7 @@ static int aureon_cs8415_rate_get (struct snd_kcontrol *kcontrol, struct snd_ctl   */  #define aureon_cs8415_mute_info		snd_ctl_boolean_mono_info -static int aureon_cs8415_mute_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int aureon_cs8415_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	snd_ice1712_save_gpio_status(ice); @@ -1238,7 +1257,7 @@ static int aureon_cs8415_mute_get (struct snd_kcontrol *kcontrol, struct snd_ctl  	return 0;  } -static int aureon_cs8415_mute_put (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int aureon_cs8415_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char oval, nval; @@ -1249,7 +1268,8 @@ static int aureon_cs8415_mute_put (struct snd_kcontrol *kcontrol, struct snd_ctl  		nval = oval & ~0x20;  	else  		nval = oval | 0x20; -	if ((change = (oval != nval))) +	change = (oval != nval); +	if (change)  		aureon_cs8415_put(ice, CS8415_CTRL1, nval);  	snd_ice1712_restore_gpio_status(ice);  	return change; @@ -1258,15 +1278,17 @@ static int aureon_cs8415_mute_put (struct snd_kcontrol *kcontrol, struct snd_ctl  /*   * CS8415A Q-Sub info   */ -static int aureon_cs8415_qsub_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { +static int aureon_cs8415_qsub_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;  	uinfo->count = 10;  	return 0;  } -static int aureon_cs8415_qsub_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { +static int aureon_cs8415_qsub_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	 +  	snd_ice1712_save_gpio_status(ice);  	aureon_cs8415_read(ice, CS8415_QSUB, ucontrol->value.bytes.data, 10);  	snd_ice1712_restore_gpio_status(ice); @@ -1274,18 +1296,21 @@ static int aureon_cs8415_qsub_get (struct snd_kcontrol *kcontrol, struct snd_ctl  	return 0;  } -static int aureon_cs8415_spdif_info (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { +static int aureon_cs8415_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{  	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;  	uinfo->count = 1;  	return 0;  } -static int aureon_cs8415_mask_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { +static int aureon_cs8415_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{  	memset(ucontrol->value.iec958.status, 0xFF, 24);  	return 0;  } -static int aureon_cs8415_spdif_get (struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { +static int aureon_cs8415_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +{  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	snd_ice1712_save_gpio_status(ice); @@ -1311,9 +1336,9 @@ static int aureon_set_headphone_amp(struct snd_ice1712 *ice, int enable)  	else  		if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&  		    ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) -			tmp &= ~ AUREON_HP_SEL; +			tmp &= ~AUREON_HP_SEL;  		else -			tmp &= ~ PRODIGY_HP_SEL; +			tmp &= ~PRODIGY_HP_SEL;  	if (tmp != tmp2) {  		snd_ice1712_gpio_write(ice, tmp);  		return 1; @@ -1325,7 +1350,7 @@ static int aureon_get_headphone_amp(struct snd_ice1712 *ice)  {  	unsigned int tmp = snd_ice1712_gpio_read(ice); -	return ( tmp & AUREON_HP_SEL )!= 0; +	return (tmp & AUREON_HP_SEL) != 0;  }  #define aureon_hpamp_info	snd_ctl_boolean_mono_info @@ -1343,7 +1368,7 @@ static int aureon_hpamp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	return aureon_set_headphone_amp(ice,ucontrol->value.integer.value[0]); +	return aureon_set_headphone_amp(ice, ucontrol->value.integer.value[0]);  }  /* @@ -1390,7 +1415,7 @@ static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_  		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;  	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); -        return 0; +	return 0;  }  static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1434,7 +1459,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "Master Playback Volume",  		.info = wm_master_vol_info,  		.get = wm_master_vol_get, @@ -1452,7 +1477,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "Front Playback Volume",  		.info = wm_vol_info,  		.get = wm_vol_get, @@ -1471,7 +1496,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "Rear Playback Volume",  		.info = wm_vol_info,  		.get = wm_vol_get, @@ -1490,7 +1515,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "Center Playback Volume",  		.info = wm_vol_info,  		.get = wm_vol_get, @@ -1509,7 +1534,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "LFE Playback Volume",  		.info = wm_vol_info,  		.get = wm_vol_get, @@ -1528,7 +1553,7 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "Side Playback Volume",  		.info = wm_vol_info,  		.get = wm_vol_get, @@ -1539,23 +1564,23 @@ static struct snd_kcontrol_new aureon_dac_controls[] __devinitdata = {  };  static struct snd_kcontrol_new wm_controls[] __devinitdata = { - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.name = "PCM Playback Switch",  		.info = wm_pcm_mute_info,  		.get = wm_pcm_mute_get,  		.put = wm_pcm_mute_put - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "PCM Playback Volume",  		.info = wm_pcm_vol_info,  		.get = wm_pcm_vol_get,  		.put = wm_pcm_vol_put,  		.tlv = { .p = db_scale_wm_pcm } - 	}, +	},  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.name = "Capture Switch", @@ -1566,7 +1591,7 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), +				SNDRV_CTL_ELEM_ACCESS_TLV_READ),  		.name = "Capture Volume",  		.info = wm_adc_vol_info,  		.get = wm_adc_vol_get, @@ -1605,232 +1630,232 @@ static struct snd_kcontrol_new wm_controls[] __devinitdata = {  };  static struct snd_kcontrol_new ac97_controls[] __devinitdata = { - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.name = "AC97 Playback Switch",  		.info = aureon_ac97_mmute_info,  		.get = aureon_ac97_mmute_get,  		.put = aureon_ac97_mmute_put,  		.private_value = AC97_MASTER - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "AC97 Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_MASTER|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "AC97 Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_MASTER|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_master } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "CD Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_CD - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "CD Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_CD +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "CD Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_CD|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "CD Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_CD|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Aux Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_AUX, - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Aux Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_AUX, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "Aux Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_AUX|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "Aux Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_AUX|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Line Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_LINE - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Line Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_LINE +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "Line Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_LINE|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "Line Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_LINE|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Mic Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_MIC - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Mic Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_MIC +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "Mic Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_MIC, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "Mic Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_MIC,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Mic Boost (+20dB)", - 		.info = aureon_ac97_micboost_info, - 		.get = aureon_ac97_micboost_get, - 		.put = aureon_ac97_micboost_put - 	} +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Mic Boost (+20dB)", +		.info = aureon_ac97_micboost_info, +		.get = aureon_ac97_micboost_get, +		.put = aureon_ac97_micboost_put +	}  };  static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = { - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.name = "AC97 Playback Switch",  		.info = aureon_ac97_mmute_info,  		.get = aureon_ac97_mmute_get,  		.put = aureon_ac97_mmute_put,  		.private_value = AC97_MASTER - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "AC97 Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_MASTER|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "AC97 Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_MASTER|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_master } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "CD Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_AUX - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "CD Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_AUX +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "CD Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_AUX|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "CD Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_AUX|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Phono Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_CD - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Phono Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_CD +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "Phono Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_CD|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "Phono Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_CD|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Line Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_LINE - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Line Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_LINE +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "Line Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_LINE|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "Line Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_LINE|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Mic Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_MIC - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Mic Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_MIC +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "Mic Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_MIC, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "Mic Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_MIC,  		.tlv = { .p = db_scale_ac97_gain } - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Mic Boost (+20dB)", - 		.info = aureon_ac97_micboost_info, - 		.get = aureon_ac97_micboost_get, - 		.put = aureon_ac97_micboost_put - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, - 		.name = "Aux Playback Switch", - 		.info = aureon_ac97_mute_info, - 		.get = aureon_ac97_mute_get, - 		.put = aureon_ac97_mute_put, - 		.private_value = AC97_VIDEO, - 	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Mic Boost (+20dB)", +		.info = aureon_ac97_micboost_info, +		.get = aureon_ac97_micboost_get, +		.put = aureon_ac97_micboost_put +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = "Aux Playback Switch", +		.info = aureon_ac97_mute_info, +		.get = aureon_ac97_mute_get, +		.put = aureon_ac97_mute_put, +		.private_value = AC97_VIDEO, +	}, +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | -			   SNDRV_CTL_ELEM_ACCESS_TLV_READ), - 		.name = "Aux Playback Volume", - 		.info = aureon_ac97_vol_info, - 		.get = aureon_ac97_vol_get, - 		.put = aureon_ac97_vol_put, - 		.private_value = AC97_VIDEO|AUREON_AC97_STEREO, +				SNDRV_CTL_ELEM_ACCESS_TLV_READ), +		.name = "Aux Playback Volume", +		.info = aureon_ac97_vol_info, +		.get = aureon_ac97_vol_get, +		.put = aureon_ac97_vol_put, +		.private_value = AC97_VIDEO|AUREON_AC97_STEREO,  		.tlv = { .p = db_scale_ac97_gain } - 	}, +	},  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  		.name = "Aux Source", @@ -1844,43 +1869,43 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {  static struct snd_kcontrol_new cs8415_controls[] __devinitdata = {  	{  		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), +		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),  		.info = aureon_cs8415_mute_info,  		.get = aureon_cs8415_mute_get,  		.put = aureon_cs8415_mute_put  	}, - 	{ - 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Source", +	{ +		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, +		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Source",  		.info = aureon_cs8415_mux_info,  		.get = aureon_cs8415_mux_get,  		.put = aureon_cs8415_mux_put,  	},  	{  		.iface = SNDRV_CTL_ELEM_IFACE_PCM, -		.name = SNDRV_CTL_NAME_IEC958("Q-subcode ",CAPTURE,DEFAULT), +		.name = SNDRV_CTL_NAME_IEC958("Q-subcode ", CAPTURE, DEFAULT),  		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,  		.info = aureon_cs8415_qsub_info,  		.get = aureon_cs8415_qsub_get,  	},  	{  		.iface = SNDRV_CTL_ELEM_IFACE_PCM, -		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK), +		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),  		.access = SNDRV_CTL_ELEM_ACCESS_READ,  		.info = aureon_cs8415_spdif_info,  		.get = aureon_cs8415_mask_get  	},  	{  		.iface = SNDRV_CTL_ELEM_IFACE_PCM, -		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT), +		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),  		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,  		.info = aureon_cs8415_spdif_info,  		.get = aureon_cs8415_spdif_get  	},  	{  		.iface = SNDRV_CTL_ELEM_IFACE_PCM, -		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Rate", -		.access =SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, +		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Rate", +		.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,  		.info = aureon_cs8415_rate_info,  		.get = aureon_cs8415_rate_get  	} @@ -1905,15 +1930,14 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)  		if (err < 0)  			return err;  	} -	 +  	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) {  		for (i = 0; i < ARRAY_SIZE(universe_ac97_controls); i++) {  			err = snd_ctl_add(ice->card, snd_ctl_new1(&universe_ac97_controls[i], ice));  			if (err < 0)  				return err;  		} -	} -	else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT && +	} else if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT &&  		 ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71XT) {  		for (i = 0; i < ARRAY_SIZE(ac97_controls); i++) {  			err = snd_ctl_add(ice->card, snd_ctl_new1(&ac97_controls[i], ice)); @@ -1932,7 +1956,7 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)  		else if ((id & 0x0F) != 0x01)  			snd_printk(KERN_INFO "Detected unsupported CS8415 rev. (%c)\n", (char)((id & 0x0F) + 'A' - 1));  		else { -			for (i = 0; i< ARRAY_SIZE(cs8415_controls); i++) { +			for (i = 0; i < ARRAY_SIZE(cs8415_controls); i++) {  				struct snd_kcontrol *kctl;  				err = snd_ctl_add(ice->card, (kctl = snd_ctl_new1(&cs8415_controls[i], ice)));  				if (err < 0) @@ -1943,7 +1967,7 @@ static int __devinit aureon_add_controls(struct snd_ice1712 *ice)  		}  		snd_ice1712_restore_gpio_status(ice);  	} -	 +  	return 0;  } @@ -2059,11 +2083,12 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)  	/* to remeber the register values of CS8415 */  	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); -	if (! ice->akm) +	if (!ice->akm)  		return -ENOMEM;  	ice->akm_codecs = 1; -	 -	if ((err = aureon_ac97_init(ice)) != 0) + +	err = aureon_ac97_init(ice); +	if (err != 0)  		return err;  	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */ @@ -2086,7 +2111,7 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)  	/* initialize WM8770 codec */  	if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71 ||  		ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71LT || -	        ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT) +		ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71XT)  		p = wm_inits_prodigy;  	else  		p = wm_inits_aureon; @@ -2105,10 +2130,10 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)  	snd_ice1712_restore_gpio_status(ice); -        /* initialize PCA9554 pin directions & set default input*/ +	/* initialize PCA9554 pin directions & set default input */  	aureon_pca9554_write(ice, PCA9554_DIR, 0x00);  	aureon_pca9554_write(ice, PCA9554_OUT, 0x00);   /* internal AUX */ -	 +  	spec->master[0] = WM_VOL_MUTE;  	spec->master[1] = WM_VOL_MUTE;  	for (i = 0; i < ice->num_total_dacs; i++) { @@ -2158,6 +2183,24 @@ static unsigned char aureon71_eeprom[] __devinitdata = {  };  #define prodigy71_eeprom aureon71_eeprom +static unsigned char aureon71_universe_eeprom[] __devinitdata = { +	[ICE_EEP2_SYSCONF]     = 0x2b,	/* clock 512, mpu401, spdif-in/ADC, +					 * 4DACs +					 */ +	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */ +	[ICE_EEP2_I2S]         = 0xfc,	/* vol, 96k, 24bit, 192k */ +	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */ +	[ICE_EEP2_GPIO_DIR]    = 0xff, +	[ICE_EEP2_GPIO_DIR1]   = 0xff, +	[ICE_EEP2_GPIO_DIR2]   = 0x5f, +	[ICE_EEP2_GPIO_MASK]   = 0x00, +	[ICE_EEP2_GPIO_MASK1]  = 0x00, +	[ICE_EEP2_GPIO_MASK2]  = 0x00, +	[ICE_EEP2_GPIO_STATE]  = 0x00, +	[ICE_EEP2_GPIO_STATE1] = 0x00, +	[ICE_EEP2_GPIO_STATE2] = 0x00, +}; +  static unsigned char prodigy71lt_eeprom[] __devinitdata = {  	[ICE_EEP2_SYSCONF]     = 0x4b,	/* clock 384, spdif-in/ADC, 4DACs */  	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */ @@ -2197,14 +2240,14 @@ struct snd_ice1712_card_info snd_vt1724_aureon_cards[] __devinitdata = {  		.eeprom_data = aureon71_eeprom,  		.driver = "Aureon71",  	}, - 	{ - 		.subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE, - 		.name = "Terratec Aureon 7.1-Universe", +	{ +		.subvendor = VT1724_SUBDEVICE_AUREON71_UNIVERSE, +		.name = "Terratec Aureon 7.1-Universe",  		.model = "universe", - 		.chip_init = aureon_init, - 		.build_controls = aureon_add_controls, - 		.eeprom_size = sizeof(aureon71_eeprom), - 		.eeprom_data = aureon71_eeprom, +		.chip_init = aureon_init, +		.build_controls = aureon_add_controls, +		.eeprom_size = sizeof(aureon71_universe_eeprom), +		.eeprom_data = aureon71_universe_eeprom,  		.driver = "Aureon71Univ", /* keep in 15 letters */  	},  	{ diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index 0ed96c1..d216362 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -400,7 +400,7 @@ static void delta_setup_spdif(struct snd_ice1712 *ice, int rate)  static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kcontrol,  			 struct snd_ctl_elem_value *ucontrol)  { -	char reg = 0x10; // cs8427 receiver error register +	char reg = 0x10; /* CS8427 receiver error register */  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	if (snd_i2c_sendbytes(ice->cs8427, ®, 1) != 1) diff --git a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h index ea7116c..f7f14df 100644 --- a/sound/pci/ice1712/delta.h +++ b/sound/pci/ice1712/delta.h @@ -31,6 +31,7 @@  		"{MidiMan M Audio,Delta DiO 2496},"\  		"{MidiMan M Audio,Delta 66},"\  		"{MidiMan M Audio,Delta 44},"\ +		"{MidiMan M Audio,Delta 410},"\  		"{MidiMan M Audio,Audiophile 24/96},"\  		"{Digigram,VX442},"\  		"{Lionstracs,Mediastation}," diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 013fc4f..6fe35b8 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -149,7 +149,8 @@ static int snd_ice1712_ews88mt_chip_select(struct snd_ice1712 *ice, int chip_mas  	struct ews_spec *spec = ice->spec;  	unsigned char data, ndata; -	snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL); +	if (snd_BUG_ON(chip_mask < 0 || chip_mask > 0x0f)) +		return -EINVAL;  	snd_i2c_lock(ice->i2c);  	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF2], &data, 1) != 1)  		goto __error; @@ -685,7 +686,8 @@ static int snd_ice1712_ews88mt_input_sense_get(struct snd_kcontrol *kcontrol, st  	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	unsigned char data; -	snd_assert(channel >= 0 && channel <= 7, return 0); +	if (snd_BUG_ON(channel < 0 || channel > 7)) +		return 0;  	snd_i2c_lock(ice->i2c);  	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {  		snd_i2c_unlock(ice->i2c); @@ -705,7 +707,8 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st  	int channel = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	unsigned char data, ndata; -	snd_assert(channel >= 0 && channel <= 7, return 0); +	if (snd_BUG_ON(channel < 0 || channel > 7)) +		return 0;  	snd_i2c_lock(ice->i2c);  	if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_PCF1], &data, 1) != 1) {  		snd_i2c_unlock(ice->i2c); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 29d449d..5b44238 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -17,7 +17,7 @@   *   along with this program; if not, write to the Free Software   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA   * - */       + */  /*    NOTES: @@ -35,7 +35,7 @@   *   *  2002.11.26	James Stafford <jstafford@ampltd.com>   *	Added support for VT1724 (Envy24HT) - *	I have left out support for 176.4 and 192 KHz for the moment.  + *	I have left out support for 176.4 and 192 KHz for the moment.   *  I also haven't done anything with the internal S/PDIF transmitter or the MPU-401   *   *  2003.02.20  Taksahi Iwai <tiwai@suse.de> @@ -47,7 +47,7 @@   */ -#include <asm/io.h> +#include <linux/io.h>  #include <linux/delay.h>  #include <linux/interrupt.h>  #include <linux/init.h> @@ -123,7 +123,7 @@ static unsigned int PRO_RATE_DEFAULT = 44100;  /*   *  Basic I/O   */ -  +  /* check whether the clock mode is spdif-in */  static inline int is_spdif_master(struct snd_ice1712 *ice)  { @@ -135,13 +135,13 @@ static inline int is_pro_rate_locked(struct snd_ice1712 *ice)  	return is_spdif_master(ice) || PRO_RATE_LOCKED;  } -static inline void snd_ice1712_ds_write(struct snd_ice1712 * ice, u8 channel, u8 addr, u32 data) +static inline void snd_ice1712_ds_write(struct snd_ice1712 *ice, u8 channel, u8 addr, u32 data)  {  	outb((channel << 4) | addr, ICEDS(ice, INDEX));  	outl(data, ICEDS(ice, DATA));  } -static inline u32 snd_ice1712_ds_read(struct snd_ice1712 * ice, u8 channel, u8 addr) +static inline u32 snd_ice1712_ds_read(struct snd_ice1712 *ice, u8 channel, u8 addr)  {  	outb((channel << 4) | addr, ICEDS(ice, INDEX));  	return inl(ICEDS(ice, DATA)); @@ -260,7 +260,7 @@ static unsigned short snd_ice1712_pro_ac97_read(struct snd_ac97 *ac97,  static int snd_ice1712_digmix_route_ac97_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	 +  	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;  	return 0;  } @@ -269,11 +269,12 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char val, nval; -	 +  	spin_lock_irq(&ice->reg_lock);  	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));  	nval = val & ~ICE1712_ROUTE_AC97; -	if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97; +	if (ucontrol->value.integer.value[0]) +		nval |= ICE1712_ROUTE_AC97;  	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));  	spin_unlock_irq(&ice->reg_lock);  	return val != nval; @@ -329,7 +330,7 @@ static int snd_ice1712_cs8427_set_input_clock(struct snd_ice1712 *ice, int spdif  	unsigned char reg[2] = { 0x80 | 4, 0 };   /* CS8427 auto increment | register number 4 + data */  	unsigned char val, nval;  	int res = 0; -	 +  	snd_i2c_lock(ice->i2c);  	if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {  		snd_i2c_unlock(ice->i2c); @@ -381,9 +382,9 @@ int __devinit snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)  {  	int err; -	if ((err = snd_cs8427_create(ice->i2c, addr, -				     (ice->cs8427_timeout * HZ) / 1000, -				     &ice->cs8427)) < 0) { +	err = snd_cs8427_create(ice->i2c, addr, +		(ice->cs8427_timeout * HZ) / 1000, &ice->cs8427); +	if (err < 0) {  		snd_printk(KERN_ERR "CS8427 initialization failed\n");  		return err;  	} @@ -395,9 +396,9 @@ int __devinit snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr)  static void snd_ice1712_set_input_clock_source(struct snd_ice1712 *ice, int spdif_is_master)  { -        /* change CS8427 clock source too */ -        if (ice->cs8427) -                snd_ice1712_cs8427_set_input_clock(ice, spdif_is_master); +	/* change CS8427 clock source too */ +	if (ice->cs8427) +		snd_ice1712_cs8427_set_input_clock(ice, spdif_is_master);  	/* notify ak4524 chip as well */  	if (spdif_is_master) {  		unsigned int i; @@ -457,11 +458,12 @@ static irqreturn_t snd_ice1712_interrupt(int irq, void *dev_id)  			u16 pbkstatus;  			struct snd_pcm_substream *substream;  			pbkstatus = inw(ICEDS(ice, INTSTAT)); -			//printk("pbkstatus = 0x%x\n", pbkstatus); +			/* printk("pbkstatus = 0x%x\n", pbkstatus); */  			for (idx = 0; idx < 6; idx++) {  				if ((pbkstatus & (3 << (idx * 2))) == 0)  					continue; -				if ((substream = ice->playback_con_substream_ds[idx]) != NULL) +				substream = ice->playback_con_substream_ds[idx]; +				if (substream != NULL)  					snd_pcm_period_elapsed(substream);  				outw(3 << (idx * 2), ICEDS(ice, INTSTAT));  			} @@ -507,7 +509,7 @@ static int snd_ice1712_playback_trigger(struct snd_pcm_substream *substream,  	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);  	int result = 0;  	u32 tmp; -	 +  	spin_lock(&ice->reg_lock);  	tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);  	if (cmd == SNDRV_PCM_TRIGGER_START) { @@ -532,7 +534,7 @@ static int snd_ice1712_playback_ds_trigger(struct snd_pcm_substream *substream,  	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);  	int result = 0;  	u32 tmp; -	 +  	spin_lock(&ice->reg_lock);  	tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);  	if (cmd == SNDRV_PCM_TRIGGER_START) { @@ -557,7 +559,7 @@ static int snd_ice1712_capture_trigger(struct snd_pcm_substream *substream,  	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);  	int result = 0;  	u8 tmp; -	 +  	spin_lock(&ice->reg_lock);  	tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);  	if (cmd == SNDRV_PCM_TRIGGER_START) { @@ -711,8 +713,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pointer(struct snd_pcm_substream *s  	return bytes_to_frames(substream->runtime, ptr);  } -static const struct snd_pcm_hardware snd_ice1712_playback = -{ +static const struct snd_pcm_hardware snd_ice1712_playback = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID | @@ -731,8 +732,7 @@ static const struct snd_pcm_hardware snd_ice1712_playback =  	.fifo_size =		0,  }; -static const struct snd_pcm_hardware snd_ice1712_playback_ds = -{ +static const struct snd_pcm_hardware snd_ice1712_playback_ds = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID | @@ -751,8 +751,7 @@ static const struct snd_pcm_hardware snd_ice1712_playback_ds =  	.fifo_size =		0,  }; -static const struct snd_pcm_hardware snd_ice1712_capture = -{ +static const struct snd_pcm_hardware snd_ice1712_capture = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID), @@ -788,7 +787,7 @@ static int snd_ice1712_playback_ds_open(struct snd_pcm_substream *substream)  	ice->playback_con_substream_ds[substream->number] = substream;  	runtime->hw = snd_ice1712_playback_ds; -	spin_lock_irq(&ice->reg_lock);  +	spin_lock_irq(&ice->reg_lock);  	tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));  	outw(tmp, ICEDS(ice, INTMASK));  	spin_unlock_irq(&ice->reg_lock); @@ -821,7 +820,7 @@ static int snd_ice1712_playback_ds_close(struct snd_pcm_substream *substream)  	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);  	u32 tmp; -	spin_lock_irq(&ice->reg_lock);  +	spin_lock_irq(&ice->reg_lock);  	tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));  	outw(tmp, ICEDS(ice, INTMASK));  	spin_unlock_irq(&ice->reg_lock); @@ -870,7 +869,7 @@ static struct snd_pcm_ops snd_ice1712_capture_ops = {  	.pointer =	snd_ice1712_capture_pointer,  }; -static int __devinit snd_ice1712_pcm(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm) +static int __devinit snd_ice1712_pcm(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)  {  	struct snd_pcm *pcm;  	int err; @@ -900,7 +899,7 @@ static int __devinit snd_ice1712_pcm(struct snd_ice1712 * ice, int device, struc  	return 0;  } -static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm) +static int __devinit snd_ice1712_pcm_ds(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)  {  	struct snd_pcm *pcm;  	int err; @@ -1029,14 +1028,14 @@ static void snd_ice1712_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,  	if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|  						 ICE1712_PLAYBACK_PAUSE|  						 ICE1712_PLAYBACK_START)) { -	      __out: +__out:  		spin_unlock_irqrestore(&ice->reg_lock, flags);  		return;  	}  	if (!force && is_pro_rate_locked(ice))  		goto __out; -        old = inb(ICEMT(ice, RATE)); +	old = inb(ICEMT(ice, RATE));  	if (!force && old == val)  		goto __out;  	outb(val, ICEMT(ice, RATE)); @@ -1123,8 +1122,7 @@ static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(struct snd_pcm_substrea  	return bytes_to_frames(substream->runtime, ptr);  } -static const struct snd_pcm_hardware snd_ice1712_playback_pro = -{ +static const struct snd_pcm_hardware snd_ice1712_playback_pro = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID | @@ -1143,8 +1141,7 @@ static const struct snd_pcm_hardware snd_ice1712_playback_pro =  	.fifo_size =		0,  }; -static const struct snd_pcm_hardware snd_ice1712_capture_pro = -{ +static const struct snd_pcm_hardware snd_ice1712_capture_pro = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID | @@ -1238,7 +1235,7 @@ static struct snd_pcm_ops snd_ice1712_capture_pro_ops = {  	.pointer =	snd_ice1712_capture_pro_pointer,  }; -static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 * ice, int device, struct snd_pcm ** rpcm) +static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd_pcm **rpcm)  {  	struct snd_pcm *pcm;  	int err; @@ -1262,7 +1259,7 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 * ice, int device,  	ice->pcm_pro = pcm;  	if (rpcm)  		*rpcm = pcm; -	 +  	if (ice->cs8427) {  		/* assign channels to iec958 */  		err = snd_cs8427_iec958_build(ice->cs8427, @@ -1272,7 +1269,8 @@ static int __devinit snd_ice1712_pcm_profi(struct snd_ice1712 * ice, int device,  			return err;  	} -	if ((err = snd_ice1712_build_pro_mixer(ice)) < 0) +	err = snd_ice1712_build_pro_mixer(ice); +	if (err < 0)  		return err;  	return 0;  } @@ -1299,7 +1297,7 @@ static int snd_ice1712_pro_mixer_switch_get(struct snd_kcontrol *kcontrol, struc  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +  		kcontrol->private_value; -	 +  	spin_lock_irq(&ice->reg_lock);  	ucontrol->value.integer.value[0] =  		!((ice->pro_volumes[priv_idx] >> 15) & 1); @@ -1341,7 +1339,7 @@ static int snd_ice1712_pro_mixer_volume_get(struct snd_kcontrol *kcontrol, struc  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int priv_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id) +  		kcontrol->private_value; -	 +  	spin_lock_irq(&ice->reg_lock);  	ucontrol->value.integer.value[0] =  		(ice->pro_volumes[priv_idx] >> 0) & 127; @@ -1406,7 +1404,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch __devinit  static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch __devinitdata = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,SWITCH), +	.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),  	.info = snd_ice1712_pro_mixer_switch_info,  	.get = snd_ice1712_pro_mixer_switch_get,  	.put = snd_ice1712_pro_mixer_switch_put, @@ -1428,7 +1426,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume __devinit  static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume __devinitdata = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = SNDRV_CTL_NAME_IEC958("Multi ",CAPTURE,VOLUME), +	.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),  	.info = snd_ice1712_pro_mixer_volume_info,  	.get = snd_ice1712_pro_mixer_volume_get,  	.put = snd_ice1712_pro_mixer_volume_put, @@ -1448,7 +1446,7 @@ static int __devinit snd_ice1712_build_pro_mixer(struct snd_ice1712 *ice)  		if (err < 0)  			return err;  	} -	 +  	if (ice->num_total_adcs > 0) {  		struct snd_kcontrol_new tmp = snd_ice1712_multi_capture_analog_switch;  		tmp.count = ice->num_total_adcs; @@ -1495,7 +1493,7 @@ static void snd_ice1712_mixer_free_ac97(struct snd_ac97 *ac97)  	ice->ac97 = NULL;  } -static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 * ice) +static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 *ice)  {  	int err, bus_num = 0;  	struct snd_ac97_template ac97; @@ -1510,27 +1508,32 @@ static int __devinit snd_ice1712_ac97_mixer(struct snd_ice1712 * ice)  	};  	if (ice_has_con_ac97(ice)) { -		if ((err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus)) < 0) +		err = snd_ac97_bus(ice->card, bus_num++, &con_ops, NULL, &pbus); +		if (err < 0)  			return err;  		memset(&ac97, 0, sizeof(ac97));  		ac97.private_data = ice;  		ac97.private_free = snd_ice1712_mixer_free_ac97; -		if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0) +		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97); +		if (err < 0)  			printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");  		else { -			if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0) +			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice)); +			if (err < 0)  				return err;  			return 0;  		}  	} -	if (! (ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) { -		if ((err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus)) < 0) +	if (!(ice->eeprom.data[ICE_EEP1_ACLINK] & ICE1712_CFG_PRO_I2S)) { +		err = snd_ac97_bus(ice->card, bus_num, &pro_ops, NULL, &pbus); +		if (err < 0)  			return err;  		memset(&ac97, 0, sizeof(ac97));  		ac97.private_data = ice;  		ac97.private_free = snd_ice1712_mixer_free_ac97; -		if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0) +		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97); +		if (err < 0)  			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");  		else  			return 0; @@ -1549,7 +1552,7 @@ static inline unsigned int eeprom_double(struct snd_ice1712 *ice, int idx)  	return (unsigned int)ice->eeprom.data[idx] | ((unsigned int)ice->eeprom.data[idx + 1] << 8);  } -static void snd_ice1712_proc_read(struct snd_info_entry *entry,  +static void snd_ice1712_proc_read(struct snd_info_entry *entry,  				  struct snd_info_buffer *buffer)  {  	struct snd_ice1712 *ice = entry->private_data; @@ -1585,15 +1588,15 @@ static void snd_ice1712_proc_read(struct snd_info_entry *entry,  	snd_iprintf(buffer, "  SPDOUT           : 0x%04x\n", (unsigned)inw(ICEMT(ice, ROUTE_SPDOUT)));  	snd_iprintf(buffer, "  RATE             : 0x%02x\n", (unsigned)inb(ICEMT(ice, RATE)));  	snd_iprintf(buffer, "  GPIO_DATA        : 0x%02x\n", (unsigned)snd_ice1712_get_gpio_data(ice)); -        snd_iprintf(buffer, "  GPIO_WRITE_MASK  : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK)); +	snd_iprintf(buffer, "  GPIO_WRITE_MASK  : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK));  	snd_iprintf(buffer, "  GPIO_DIRECTION   : 0x%02x\n", (unsigned)snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION));  } -static void __devinit snd_ice1712_proc_init(struct snd_ice1712 * ice) +static void __devinit snd_ice1712_proc_init(struct snd_ice1712 *ice)  {  	struct snd_info_entry *entry; -	if (! snd_card_proc_new(ice->card, "ice1712", &entry)) +	if (!snd_card_proc_new(ice->card, "ice1712", &entry))  		snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read);  } @@ -1613,7 +1616,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,  				  struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	 +  	memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));  	return 0;  } @@ -1641,7 +1644,7 @@ static int snd_ice1712_spdif_default_get(struct snd_kcontrol *kcontrol,  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	if (ice->spdif.ops.default_get) -		ice->spdif.ops.default_get(ice, ucontrol);  +		ice->spdif.ops.default_get(ice, ucontrol);  	return 0;  } @@ -1657,7 +1660,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,  static struct snd_kcontrol_new snd_ice1712_spdif_default __devinitdata =  {  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, -	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), +	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),  	.info =		snd_ice1712_spdif_info,  	.get =		snd_ice1712_spdif_default_get,  	.put =		snd_ice1712_spdif_default_put @@ -1709,7 +1712,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc __devinitdata =  {  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, -	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), +	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),  	.info =		snd_ice1712_spdif_info,  	.get =		snd_ice1712_spdif_maskc_get,  }; @@ -1718,7 +1721,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskp __devinitdata =  {  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, -	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), +	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),  	.info =		snd_ice1712_spdif_info,  	.get =		snd_ice1712_spdif_maskp_get,  }; @@ -1746,7 +1749,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_stream __devinitdata =  	.access =	(SNDRV_CTL_ELEM_ACCESS_READWRITE |  			 SNDRV_CTL_ELEM_ACCESS_INACTIVE),  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, -	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM), +	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),  	.info =		snd_ice1712_spdif_info,  	.get =		snd_ice1712_spdif_stream_get,  	.put =		snd_ice1712_spdif_stream_put @@ -1758,7 +1761,7 @@ int snd_ice1712_gpio_get(struct snd_kcontrol *kcontrol,  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char mask = kcontrol->private_value & 0xff;  	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0; -	 +  	snd_ice1712_save_gpio_status(ice);  	ucontrol->value.integer.value[0] =  		(snd_ice1712_gpio_read(ice) & mask ? 1 : 0) ^ invert; @@ -1825,7 +1828,7 @@ static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol,  		9, 6, 3, 1, 7, 4, 0, 12, 8, 5, 2, 11, 255, 255, 255, 10  	};  	unsigned char val; -	 +  	spin_lock_irq(&ice->reg_lock);  	if (is_spdif_master(ice)) {  		ucontrol->value.enumerated.item[0] = 13; @@ -1867,7 +1870,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,  	if ((oval & ICE1712_SPDIF_MASTER) !=  	    (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER)) -	        snd_ice1712_set_input_clock_source(ice, is_spdif_master(ice)); +		snd_ice1712_set_input_clock_source(ice, is_spdif_master(ice));  	return change;  } @@ -1897,7 +1900,7 @@ static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcon  		"64000",	/* 10: 15 */  		"88200",	/* 11: 11 */  		"96000",	/* 12: 7 */ -		// "IEC958 Input",	/* 13: -- */ +		/* "IEC958 Input",	13: -- */  	};  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	uinfo->count = 1; @@ -2026,7 +2029,7 @@ static int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol,  		"IEC958 In L", "IEC958 In R", /* 9-10 */  		"Digital Mixer", /* 11 - optional */  	}; -	 +  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	uinfo->count = 1;  	uinfo->value.enumerated.items = @@ -2070,7 +2073,7 @@ static int snd_ice1712_pro_route_analog_put(struct snd_kcontrol *kcontrol,  	int change, shift;  	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	unsigned int val, old_val, nval; -	 +  	/* update PSDOUT */  	if (ucontrol->value.enumerated.item[0] >= 11)  		nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */ @@ -2140,7 +2143,7 @@ static int snd_ice1712_pro_route_spdif_put(struct snd_kcontrol *kcontrol,  	int change, shift;  	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	unsigned int val, old_val, nval; -	 +  	/* update SPDOUT */  	spin_lock_irq(&ice->reg_lock);  	val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT)); @@ -2182,7 +2185,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route __devinitdata  static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route __devinitdata = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", +	.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",  	.info = snd_ice1712_pro_route_info,  	.get = snd_ice1712_pro_route_spdif_get,  	.put = snd_ice1712_pro_route_spdif_put, @@ -2204,7 +2207,7 @@ static int snd_ice1712_pro_volume_rate_get(struct snd_kcontrol *kcontrol,  					   struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	 +  	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));  	return 0;  } @@ -2245,7 +2248,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int idx; -	 +  	spin_lock_irq(&ice->reg_lock);  	for (idx = 0; idx < 22; idx++) {  		outb(idx, ICEMT(ice, MONITOR_PEAKINDEX)); @@ -2296,12 +2299,12 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,  	unsigned int i, size;  	struct snd_ice1712_card_info * const *tbl, *c; -	if (! modelname || ! *modelname) { +	if (!modelname || !*modelname) {  		ice->eeprom.subvendor = 0;  		if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) != 0)  			ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) | -				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) |  -				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) |  +				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | +				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) |  				(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);  		if (ice->eeprom.subvendor == 0 ||  		    ice->eeprom.subvendor == (unsigned int)-1) { @@ -2318,12 +2321,12 @@ static int __devinit snd_ice1712_read_eeprom(struct snd_ice1712 *ice,  	}  	for (tbl = card_tables; *tbl; tbl++) {  		for (c = *tbl; c->subvendor; c++) { -			if (modelname && c->model && ! strcmp(modelname, c->model)) { +			if (modelname && c->model && !strcmp(modelname, c->model)) {  				printk(KERN_INFO "ice1712: Using board model %s\n", c->name);  				ice->eeprom.subvendor = c->subvendor;  			} else if (c->subvendor != ice->eeprom.subvendor)  				continue; -			if (! c->eeprom_size || ! c->eeprom_data) +			if (!c->eeprom_size || !c->eeprom_data)  				goto found;  			/* if the EEPROM is given by the driver, use it */  			snd_printdd("using the defined eeprom..\n"); @@ -2416,7 +2419,8 @@ int __devinit snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice)  	int err;  	struct snd_kcontrol *kctl; -	snd_assert(ice->pcm_pro != NULL, return -EIO); +	if (snd_BUG_ON(!ice->pcm_pro)) +		return -EIO;  	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));  	if (err < 0)  		return err; @@ -2483,13 +2487,13 @@ static int __devinit snd_ice1712_build_controls(struct snd_ice1712 *ice)  static int snd_ice1712_free(struct snd_ice1712 *ice)  { -	if (! ice->port) +	if (!ice->port)  		goto __hw_end;  	/* mask all interrupts */  	outb(0xc0, ICEMT(ice, IRQ));  	outb(0xff, ICEREG(ice, IRQMASK));  	/* --- */ -      __hw_end: +__hw_end:  	if (ice->irq >= 0)  		free_irq(ice->irq, ice); @@ -2514,7 +2518,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card,  					int omni,  					int cs8427_timeout,  					int dxr_enable, -					struct snd_ice1712 ** r_ice1712) +					struct snd_ice1712 **r_ice1712)  {  	struct snd_ice1712 *ice;  	int err; @@ -2524,8 +2528,9 @@ static int __devinit snd_ice1712_create(struct snd_card *card,  	*r_ice1712 = NULL; -        /* enable PCI device */ -	if ((err = pci_enable_device(pci)) < 0) +	/* enable PCI device */ +	err = pci_enable_device(pci); +	if (err < 0)  		return err;  	/* check, if we can restrict PCI DMA transfers to 28 bits */  	if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 || @@ -2569,7 +2574,8 @@ static int __devinit snd_ice1712_create(struct snd_card *card,  	snd_ice1712_proc_init(ice);  	synchronize_irq(pci->irq); -	if ((err = pci_request_regions(pci, "ICE1712")) < 0) { +	err = pci_request_regions(pci, "ICE1712"); +	if (err < 0) {  		kfree(ice);  		pci_disable_device(pci);  		return err; @@ -2585,7 +2591,7 @@ static int __devinit snd_ice1712_create(struct snd_card *card,  		snd_ice1712_free(ice);  		return -EIO;  	} -	 +  	ice->irq = pci->irq;  	if (snd_ice1712_read_eeprom(ice, modelname) < 0) { @@ -2605,9 +2611,10 @@ static int __devinit snd_ice1712_create(struct snd_card *card,  	     ICEREG(ice, IRQMASK));  	outb(0x00, ICEMT(ice, IRQ)); -	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) { +	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops); +	if (err < 0) {  		snd_ice1712_free(ice); - 		return err; +		return err;  	}  	snd_card_set_dev(card, &pci->dev); @@ -2647,10 +2654,10 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,  	strcpy(card->driver, "ICE1712");  	strcpy(card->shortname, "ICEnsemble ICE1712"); -	 -	if ((err = snd_ice1712_create(card, pci, model[dev], omni[dev], -				      cs8427_timeout[dev], dxr_enable[dev], -				      &ice)) < 0) { + +	err = snd_ice1712_create(card, pci, model[dev], omni[dev], +		cs8427_timeout[dev], dxr_enable[dev], &ice); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} @@ -2662,7 +2669,8 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,  				if (c->driver) /* specific driver? */  					strcpy(card->driver, c->driver);  				if (c->chip_init) { -					if ((err = c->chip_init(ice)) < 0) { +					err = c->chip_init(ice); +					if (err < 0) {  						snd_card_free(card);  						return err;  					} @@ -2674,47 +2682,52 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,  	c = &no_matched;   __found: -	if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) { +	err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} -	 +  	if (ice_has_con_ac97(ice)) -		if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) { +		err = snd_ice1712_pcm(ice, pcm_dev++, NULL); +		if (err < 0) {  			snd_card_free(card);  			return err;  		} -	if ((err = snd_ice1712_ac97_mixer(ice)) < 0) { +	err = snd_ice1712_ac97_mixer(ice); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} -	if ((err = snd_ice1712_build_controls(ice)) < 0) { +	err = snd_ice1712_build_controls(ice); +	if (err < 0) {  		snd_card_free(card);  		return err;  	}  	if (c->build_controls) { -		if ((err = c->build_controls(ice)) < 0) { +		err = c->build_controls(ice); +		if (err < 0) {  			snd_card_free(card);  			return err;  		}  	}  	if (ice_has_con_ac97(ice)) -		if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) { +		err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL); +		if (err < 0) {  			snd_card_free(card);  			return err;  		} -	if (! c->no_mpu401) { -		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, -					       ICEREG(ice, MPU1_CTRL), -					       (c->mpu401_1_info_flags | -						MPU401_INFO_INTEGRATED), -					       ice->irq, 0, -					       &ice->rmidi[0])) < 0) { +	if (!c->no_mpu401) { +		err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, +			ICEREG(ice, MPU1_CTRL), +			(c->mpu401_1_info_flags | MPU401_INFO_INTEGRATED), +			ice->irq, 0, &ice->rmidi[0]); +		if (err < 0) {  			snd_card_free(card);  			return err;  		} @@ -2726,12 +2739,12 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,  		if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) {  			/*  2nd port used  */ -			if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712, -						       ICEREG(ice, MPU2_CTRL), -						       (c->mpu401_2_info_flags | -							MPU401_INFO_INTEGRATED), -						       ice->irq, 0, -						       &ice->rmidi[1])) < 0) { +			err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712, +				ICEREG(ice, MPU2_CTRL), +				(c->mpu401_2_info_flags | MPU401_INFO_INTEGRATED), +				ice->irq, 0, &ice->rmidi[1]); + +			if (err < 0) {  				snd_card_free(card);  				return err;  			} @@ -2749,7 +2762,8 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,  	sprintf(card->longname, "%s at 0x%lx, irq %i",  		card->shortname, ice->port, ice->irq); -	if ((err = snd_card_register(card)) < 0) { +	err = snd_card_register(card); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 762fbd7..fdae6de 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -20,7 +20,7 @@   *   along with this program; if not, write to the Free Software   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA   * - */       + */  #include <sound/control.h>  #include <sound/ac97_codec.h> @@ -112,7 +112,7 @@   */  #define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x) -  +  #define ICE1712_DS_INTMASK		0x00	/* word - interrupt mask */  #define ICE1712_DS_INTSTAT		0x02	/* word - interrupt status */  #define ICE1712_DS_DATA			0x04	/* dword - channel data */ @@ -121,7 +121,7 @@  /*   *  Consumer section channel registers   */ -  +  #define ICE1712_DSC_ADDR0		0x00	/* dword - base address 0 */  #define ICE1712_DSC_COUNT0		0x01	/* word - count 0 */  #define ICE1712_DSC_ADDR1		0x02	/* dword - base address 1 */ @@ -138,7 +138,7 @@  #define ICE1712_DSC_RATE		0x05	/* dword - rate */  #define ICE1712_DSC_VOLUME		0x06	/* word - volume control */ -/*  +/*   *  Professional multi-track direct control registers   */ @@ -214,7 +214,7 @@  /* - *   + *   */  struct snd_ice1712; @@ -253,12 +253,12 @@ enum {  	ICE_EEP1_ADC_ID2,  	ICE_EEP1_ADC_ID3  }; -	 +  #define ice_has_con_ac97(ice)	(!((ice)->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_NO_CON_AC97))  struct snd_ak4xxx_private { -	unsigned int cif: 1;		/* CIF mode */ +	unsigned int cif:1;		/* CIF mode */  	unsigned char caddr;		/* C0 and C1 bits */  	unsigned int data_mask;		/* DATA gpio bit */  	unsigned int clk_mask;		/* CLK gpio bit */ @@ -306,11 +306,11 @@ struct snd_ice1712 {  	struct snd_pcm *pcm;  	struct snd_pcm *pcm_ds;  	struct snd_pcm *pcm_pro; -        struct snd_pcm_substream *playback_con_substream; -        struct snd_pcm_substream *playback_con_substream_ds[6]; -        struct snd_pcm_substream *capture_con_substream; -        struct snd_pcm_substream *playback_pro_substream; -        struct snd_pcm_substream *capture_pro_substream; +	struct snd_pcm_substream *playback_con_substream; +	struct snd_pcm_substream *playback_con_substream_ds[6]; +	struct snd_pcm_substream *capture_con_substream; +	struct snd_pcm_substream *playback_pro_substream; +	struct snd_pcm_substream *capture_pro_substream;  	unsigned int playback_pro_size;  	unsigned int capture_pro_size;  	unsigned int playback_con_virt_addr[6]; @@ -326,15 +326,15 @@ struct snd_ice1712 {  	struct snd_ice1712_eeprom eeprom;  	unsigned int pro_volumes[20]; -	unsigned int omni: 1;		/* Delta Omni I/O */ -	unsigned int dxr_enable: 1;	/* Terratec DXR enable for DMX6FIRE */ -	unsigned int vt1724: 1; -	unsigned int vt1720: 1; -	unsigned int has_spdif: 1;	/* VT1720/4 - has SPDIF I/O */ -	unsigned int force_pdma4: 1;	/* VT1720/4 - PDMA4 as non-spdif */ -	unsigned int force_rdma1: 1;	/* VT1720/4 - RDMA1 as non-spdif */ -	unsigned int midi_output: 1;	/* VT1720/4: MIDI output triggered */ -	unsigned int midi_input: 1;	/* VT1720/4: MIDI input triggered */ +	unsigned int omni:1;		/* Delta Omni I/O */ +	unsigned int dxr_enable:1;	/* Terratec DXR enable for DMX6FIRE */ +	unsigned int vt1724:1; +	unsigned int vt1720:1; +	unsigned int has_spdif:1;	/* VT1720/4 - has SPDIF I/O */ +	unsigned int force_pdma4:1;	/* VT1720/4 - PDMA4 as non-spdif */ +	unsigned int force_rdma1:1;	/* VT1720/4 - RDMA1 as non-spdif */ +	unsigned int midi_output:1;	/* VT1720/4: MIDI output triggered */ +	unsigned int midi_input:1;	/* VT1720/4: MIDI input triggered */  	unsigned int num_total_dacs;	/* total DACs */  	unsigned int num_total_adcs;	/* total ADCs */  	unsigned int cur_rate;		/* current rate */ @@ -351,7 +351,7 @@ struct snd_ice1712 {  	struct snd_i2c_bus *i2c;		/* I2C bus */  	struct snd_i2c_device *cs8427;	/* CS8427 I2C device */  	unsigned int cs8427_timeout;	/* CS8427 reset timeout in HZ/100 */ -	 +  	struct ice1712_gpio {  		unsigned int direction;		/* current direction bits */  		unsigned int write_mask;	/* current mask bits */ @@ -455,7 +455,7 @@ static inline int snd_ice1712_gpio_read_bits(struct snd_ice1712 *ice,  {  	ice->gpio.direction &= ~mask;  	snd_ice1712_gpio_set_dir(ice, ice->gpio.direction); -	return  (snd_ice1712_gpio_read(ice) & mask); +	return  snd_ice1712_gpio_read(ice) & mask;  }  int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice); @@ -467,13 +467,13 @@ int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice);  int snd_ice1712_init_cs8427(struct snd_ice1712 *ice, int addr); -static inline void snd_ice1712_write(struct snd_ice1712 * ice, u8 addr, u8 data) +static inline void snd_ice1712_write(struct snd_ice1712 *ice, u8 addr, u8 data)  {  	outb(addr, ICEREG(ice, INDEX));  	outb(data, ICEREG(ice, DATA));  } -static inline u8 snd_ice1712_read(struct snd_ice1712 * ice, u8 addr) +static inline u8 snd_ice1712_read(struct snd_ice1712 *ice, u8 addr)  {  	outb(addr, ICEREG(ice, INDEX));  	return inb(ICEREG(ice, DATA)); @@ -491,7 +491,7 @@ struct snd_ice1712_card_info {  	char *driver;  	int (*chip_init)(struct snd_ice1712 *);  	int (*build_controls)(struct snd_ice1712 *); -	unsigned int no_mpu401: 1; +	unsigned int no_mpu401:1;  	unsigned int mpu401_1_info_flags;  	unsigned int mpu401_2_info_flags;  	const char *mpu401_1_name; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index e596d77..1b3f117 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -20,9 +20,9 @@   *   along with this program; if not, write to the Free Software   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA   * - */       + */ -#include <asm/io.h> +#include <linux/io.h>  #include <linux/delay.h>  #include <linux/interrupt.h>  #include <linux/init.h> @@ -105,7 +105,7 @@ static unsigned int PRO_RATE_DEFAULT = 44100;  /*   *  Basic I/O   */ -  +  /*   *  default rates, default clock routines   */ @@ -198,7 +198,7 @@ static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)  static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)  {  	outw(data, ICEREG1724(ice, GPIO_WRITE_MASK)); -	if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */ +	if (!ice->vt1720) /* VT1720 supports only 16 GPIO bits */  		outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22));  	inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */  } @@ -206,7 +206,7 @@ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)  static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)  {  	outw(data, ICEREG1724(ice, GPIO_DATA)); -	if (! ice->vt1720) +	if (!ice->vt1720)  		outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22));  	inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */  } @@ -214,7 +214,7 @@ static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)  static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)  {  	unsigned int data; -	if (! ice->vt1720) +	if (!ice->vt1720)  		data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22));  	else  		data = 0; @@ -399,7 +399,7 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)  			break;  		}  #endif -		handled = 1;		 +		handled = 1;  		if (status & VT1724_IRQ_MPU_TX) {  			spin_lock(&ice->reg_lock);  			if (ice->midi_output) @@ -468,8 +468,8 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)  			/* ought to really handle this properly */  			if (mtstat & VT1724_MULTI_FIFO_ERR) {  				unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR)); -				outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));	 -				outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));	 +				outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR)); +				outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));  				/* If I don't do this, I get machine lockup due to continual interrupts */  			} @@ -733,17 +733,17 @@ static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)  	outl(substream->runtime->dma_addr, ICEMT1724(ice, PLAYBACK_ADDR));  	size = (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1; -	// outl(size, ICEMT1724(ice, PLAYBACK_SIZE)); +	/* outl(size, ICEMT1724(ice, PLAYBACK_SIZE)); */  	outw(size, ICEMT1724(ice, PLAYBACK_SIZE));  	outb(size >> 16, ICEMT1724(ice, PLAYBACK_SIZE) + 2);  	size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1; -	// outl(size, ICEMT1724(ice, PLAYBACK_COUNT)); +	/* outl(size, ICEMT1724(ice, PLAYBACK_COUNT)); */  	outw(size, ICEMT1724(ice, PLAYBACK_COUNT));  	outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2);  	spin_unlock_irq(&ice->reg_lock); -	// printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream)); +	/* printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream)); */  	return 0;  } @@ -771,7 +771,7 @@ static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substrea  	ptr = inl(ICEMT1724(ice, PLAYBACK_SIZE)) & 0xffffff;  	ptr = (ptr + 1) << 2;  	ptr = bytes_to_frames(substream->runtime, ptr); -	if (! ptr) +	if (!ptr)  		;  	else if (ptr <= substream->runtime->buffer_size)  		ptr = substream->runtime->buffer_size - ptr; @@ -815,7 +815,7 @@ static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substr  	ptr = inw(ice->profi_port + reg->size);  	ptr = (ptr + 1) << 2;  	ptr = bytes_to_frames(substream->runtime, ptr); -	if (! ptr) +	if (!ptr)  		;  	else if (ptr <= substream->runtime->buffer_size)  		ptr = substream->runtime->buffer_size - ptr; @@ -842,8 +842,7 @@ static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {  	.start = VT1724_RDMA0_START,  }; -static const struct snd_pcm_hardware snd_vt1724_playback_pro = -{ +static const struct snd_pcm_hardware snd_vt1724_playback_pro = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID | @@ -861,8 +860,7 @@ static const struct snd_pcm_hardware snd_vt1724_playback_pro =  	.periods_max =		1024,  }; -static const struct snd_pcm_hardware snd_vt1724_spdif = -{ +static const struct snd_pcm_hardware snd_vt1724_spdif = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID | @@ -883,8 +881,7 @@ static const struct snd_pcm_hardware snd_vt1724_spdif =  	.periods_max =		1024,  }; -static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = -{ +static const struct snd_pcm_hardware snd_vt1724_2ch_stereo = {  	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |  				 SNDRV_PCM_INFO_BLOCK_TRANSFER |  				 SNDRV_PCM_INFO_MMAP_VALID | @@ -942,7 +939,7 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)  {  	struct snd_pcm_runtime *runtime = substream->runtime;  	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); -	int chs; +	int chs, num_indeps;  	runtime->private_data = (void *)&vt1724_playback_pro_reg;  	ice->playback_pro_substream = substream; @@ -952,7 +949,8 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)  	set_rate_constraints(ice, substream);  	mutex_lock(&ice->open_mutex);  	/* calculate the currently available channels */ -	for (chs = 0; chs < 3; chs++) { +	num_indeps = ice->num_total_dacs / 2 - 1; +	for (chs = 0; chs < num_indeps; chs++) {  		if (ice->pcm_reserved[chs])  			break;  	} @@ -1029,7 +1027,7 @@ static struct snd_pcm_ops snd_vt1724_capture_pro_ops = {  	.pointer =	snd_vt1724_pcm_pointer,  }; -static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 * ice, int device) +static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 *ice, int device)  {  	struct snd_pcm *pcm;  	int err; @@ -1114,7 +1112,7 @@ static void update_spdif_rate(struct snd_ice1712 *ice, unsigned int rate)  static int snd_vt1724_playback_spdif_prepare(struct snd_pcm_substream *substream)  {  	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); -	if (! ice->force_pdma4) +	if (!ice->force_pdma4)  		update_spdif_rate(ice, substream->runtime->rate);  	return snd_vt1724_pcm_prepare(substream);  } @@ -1214,7 +1212,7 @@ static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {  }; -static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device) +static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 *ice, int device)  {  	char *name;  	struct snd_pcm *pcm; @@ -1233,7 +1231,7 @@ static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device)  		ice->has_spdif = 1;  	} else  		capt = 0; -	if (! play && ! capt) +	if (!play && !capt)  		return 0; /* no spdif device */  	if (ice->force_pdma4 || ice->force_rdma1) @@ -1348,7 +1346,7 @@ static struct snd_pcm_ops snd_vt1724_playback_indep_ops = {  }; -static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 * ice, int device) +static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 *ice, int device)  {  	struct snd_pcm *pcm;  	int play; @@ -1383,11 +1381,11 @@ static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 * ice, int device)   *  Mixer section   */ -static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 * ice) +static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 *ice)  {  	int err; -	if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) { +	if (!(ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {  		struct snd_ac97_bus *pbus;  		struct snd_ac97_template ac97;  		static struct snd_ac97_bus_ops ops = { @@ -1400,11 +1398,13 @@ static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 * ice)  		mdelay(5); /* FIXME */  		outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD)); -		if ((err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus)) < 0) +		err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus); +		if (err < 0)  			return err;  		memset(&ac97, 0, sizeof(ac97));  		ac97.private_data = ice; -		if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0) +		err = snd_ac97_mixer(pbus, &ac97, &ice->ac97); +		if (err < 0)  			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");  		else  			return 0; @@ -1425,7 +1425,7 @@ static inline unsigned int eeprom_triple(struct snd_ice1712 *ice, int idx)  		((unsigned int)ice->eeprom.data[idx + 2] << 16);  } -static void snd_vt1724_proc_read(struct snd_info_entry *entry,  +static void snd_vt1724_proc_read(struct snd_info_entry *entry,  				 struct snd_info_buffer *buffer)  {  	struct snd_ice1712 *ice = entry->private_data; @@ -1467,11 +1467,11 @@ static void snd_vt1724_proc_read(struct snd_info_entry *entry,  			    idx, inb(ice->profi_port+idx));  } -static void __devinit snd_vt1724_proc_init(struct snd_ice1712 * ice) +static void __devinit snd_vt1724_proc_init(struct snd_ice1712 *ice)  {  	struct snd_info_entry *entry; -	if (! snd_card_proc_new(ice->card, "ice1724", &entry)) +	if (!snd_card_proc_new(ice->card, "ice1724", &entry))  		snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read);  } @@ -1491,7 +1491,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,  				 struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	 +  	memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));  	return 0;  } @@ -1606,13 +1606,13 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,  	if (val != old)  		update_spdif_bits(ice, val);  	spin_unlock_irq(&ice->reg_lock); -	return (val != old); +	return val != old;  }  static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =  {  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, -	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), +	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),  	.info =		snd_vt1724_spdif_info,  	.get =		snd_vt1724_spdif_default_get,  	.put =		snd_vt1724_spdif_default_put @@ -1645,7 +1645,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =  {  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, -	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), +	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),  	.info =		snd_vt1724_spdif_info,  	.get =		snd_vt1724_spdif_maskc_get,  }; @@ -1654,7 +1654,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata =  {  	.access =	SNDRV_CTL_ELEM_ACCESS_READ,  	.iface =	SNDRV_CTL_ELEM_IFACE_PCM, -	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), +	.name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),  	.info =		snd_vt1724_spdif_info,  	.get =		snd_vt1724_spdif_maskp_get,  }; @@ -1691,8 +1691,8 @@ static struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata =  {  	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,  	/* FIXME: the following conflict with IEC958 Playback Route */ -	// .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), -	.name =         SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), +	/* .name =         SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), */ +	.name =         SNDRV_CTL_NAME_IEC958("Output ", NONE, SWITCH),  	.info =		snd_vt1724_spdif_sw_info,  	.get =		snd_vt1724_spdif_sw_get,  	.put =		snd_vt1724_spdif_sw_put @@ -1712,7 +1712,7 @@ int snd_vt1724_gpio_get(struct snd_kcontrol *kcontrol,  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int shift = kcontrol->private_value & 0xff;  	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0; -	 +  	snd_ice1712_save_gpio_status(ice);  	ucontrol->value.integer.value[0] =  		(snd_ice1712_gpio_read(ice) & (1 << shift) ? 1 : 0) ^ invert; @@ -1767,7 +1767,7 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned int i, rate; -	 +  	spin_lock_irq(&ice->reg_lock);  	if (ice->is_spdif_master(ice)) {  		ucontrol->value.enumerated.item[0] = ice->hw_rates->count; @@ -1923,7 +1923,7 @@ static int snd_vt1724_pro_route_info(struct snd_kcontrol *kcontrol,  		"H/W In 0", "H/W In 1", /* 1-2 */  		"IEC958 In L", "IEC958 In R", /* 3-4 */  	}; -	 +  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	uinfo->count = 1;  	uinfo->value.enumerated.items = 5; @@ -1953,7 +1953,7 @@ static int get_route_val(struct snd_ice1712 *ice, int shift)  	val = inl(ICEMT1724(ice, ROUTE_PLAYBACK));  	val >>= shift; -	val &= 7;	//we now have 3 bits per output +	val &= 7; /* we now have 3 bits per output */  	eitem = xlate[val];  	if (eitem == 255) {  		snd_BUG(); @@ -2032,7 +2032,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =  static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, -	.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", +	.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",  	.info = snd_vt1724_pro_route_info,  	.get = snd_vt1724_pro_route_spdif_get,  	.put = snd_vt1724_pro_route_spdif_put, @@ -2055,7 +2055,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int idx; -	 +  	spin_lock_irq(&ice->reg_lock);  	for (idx = 0; idx < 22; idx++) {  		outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX)); @@ -2082,7 +2082,7 @@ static struct snd_ice1712_card_info no_matched __devinitdata;  static struct snd_ice1712_card_info *card_tables[] __devinitdata = {  	snd_vt1724_revo_cards, -	snd_vt1724_amp_cards,  +	snd_vt1724_amp_cards,  	snd_vt1724_aureon_cards,  	snd_vt1720_mobo_cards,  	snd_vt1720_pontis_cards, @@ -2120,7 +2120,7 @@ unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,  	wait_i2c_busy(ice);  	val = inb(ICEREG1724(ice, I2C_DATA));  	mutex_unlock(&ice->i2c_mutex); -	//printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val); +	/* printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val); */  	return val;  } @@ -2129,7 +2129,7 @@ void snd_vt1724_write_i2c(struct snd_ice1712 *ice,  {  	mutex_lock(&ice->i2c_mutex);  	wait_i2c_busy(ice); -	//printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data); +	/* printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data); */  	outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));  	outb(data, ICEREG1724(ice, I2C_DATA));  	outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR)); @@ -2144,13 +2144,13 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,  	unsigned int i, size;  	struct snd_ice1712_card_info * const *tbl, *c; -	if (! modelname || ! *modelname) { +	if (!modelname || !*modelname) {  		ice->eeprom.subvendor = 0;  		if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) != 0)  			ice->eeprom.subvendor =  				(snd_vt1724_read_i2c(ice, dev, 0x00) << 0) | -				(snd_vt1724_read_i2c(ice, dev, 0x01) << 8) |  -				(snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |  +				(snd_vt1724_read_i2c(ice, dev, 0x01) << 8) | +				(snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |  				(snd_vt1724_read_i2c(ice, dev, 0x03) << 24);  		if (ice->eeprom.subvendor == 0 ||  		    ice->eeprom.subvendor == (unsigned int)-1) { @@ -2173,13 +2173,13 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,  	for (tbl = card_tables; *tbl; tbl++) {  		for (c = *tbl; c->subvendor; c++) {  			if (modelname && c->model && -			    ! strcmp(modelname, c->model)) { +			    !strcmp(modelname, c->model)) {  				printk(KERN_INFO "ice1724: Using board model %s\n",  				       c->name);  				ice->eeprom.subvendor = c->subvendor;  			} else if (c->subvendor != ice->eeprom.subvendor)  				continue; -			if (! c->eeprom_size || ! c->eeprom_data) +			if (!c->eeprom_size || !c->eeprom_data)  				goto found;  			/* if the EEPROM is given by the driver, use it */  			snd_printdd("using the defined eeprom..\n"); @@ -2250,7 +2250,8 @@ static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)  	int err;  	struct snd_kcontrol *kctl; -	snd_assert(ice->pcm != NULL, return -EIO); +	if (snd_BUG_ON(!ice->pcm)) +		return -EIO;  	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));  	if (err < 0) @@ -2320,13 +2321,13 @@ static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)  static int snd_vt1724_free(struct snd_ice1712 *ice)  { -	if (! ice->port) +	if (!ice->port)  		goto __hw_end;  	/* mask all interrupts */  	outb(0xff, ICEMT1724(ice, DMA_INT_MASK));  	outb(0xff, ICEREG1724(ice, IRQMASK));  	/* --- */ -      __hw_end: +__hw_end:  	if (ice->irq >= 0)  		free_irq(ice->irq, ice);  	pci_release_regions(ice->pci); @@ -2346,7 +2347,7 @@ static int snd_vt1724_dev_free(struct snd_device *device)  static int __devinit snd_vt1724_create(struct snd_card *card,  				       struct pci_dev *pci,  				       const char *modelname, -				       struct snd_ice1712 ** r_ice1712) +				       struct snd_ice1712 **r_ice1712)  {  	struct snd_ice1712 *ice;  	int err; @@ -2357,8 +2358,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,  	*r_ice1712 = NULL; -        /* enable PCI device */ -	if ((err = pci_enable_device(pci)) < 0) +	/* enable PCI device */ +	err = pci_enable_device(pci); +	if (err < 0)  		return err;  	ice = kzalloc(sizeof(*ice), GFP_KERNEL); @@ -2382,7 +2384,8 @@ static int __devinit snd_vt1724_create(struct snd_card *card,  	snd_vt1724_proc_init(ice);  	synchronize_irq(pci->irq); -	if ((err = pci_request_regions(pci, "ICE1724")) < 0) { +	err = pci_request_regions(pci, "ICE1724"); +	if (err < 0) {  		kfree(ice);  		pci_disable_device(pci);  		return err; @@ -2417,9 +2420,10 @@ static int __devinit snd_vt1724_create(struct snd_card *card,  	 */  	outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK)); -	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) { +	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops); +	if (err < 0) {  		snd_vt1724_free(ice); - 		return err; +		return err;  	}  	snd_card_set_dev(card, &pci->dev); @@ -2457,8 +2461,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,  	strcpy(card->driver, "ICE1724");  	strcpy(card->shortname, "ICEnsemble ICE1724"); -	 -	if ((err = snd_vt1724_create(card, pci, model[dev], &ice)) < 0) { + +	err = snd_vt1724_create(card, pci, model[dev], &ice); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} @@ -2470,7 +2475,8 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,  				if (c->driver) /* specific driver? */  					strcpy(card->driver, c->driver);  				if (c->chip_init) { -					if ((err = c->chip_init(ice)) < 0) { +					err = c->chip_init(ice); +					if (err < 0) {  						snd_card_free(card);  						return err;  					} @@ -2480,15 +2486,15 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,  		}  	}  	c = &no_matched; - __found: -       /* -        * VT1724 has separate DMAs for the analog and the SPDIF streams while -        * ICE1712 has only one for both (mixed up). -        * -        * Confusingly the analog PCM is named "professional" here because it -        * was called so in ice1712 driver, and vt1724 driver is derived from -        * ice1712 driver. -        */ +__found: +	/* +	* VT1724 has separate DMAs for the analog and the SPDIF streams while +	* ICE1712 has only one for both (mixed up). +	* +	* Confusingly the analog PCM is named "professional" here because it +	* was called so in ice1712 driver, and vt1724 driver is derived from +	* ice1712 driver. +	*/  	ice->pro_rate_default = PRO_RATE_DEFAULT;  	if (!ice->is_spdif_master)  		ice->is_spdif_master = stdclock_is_spdif_master; @@ -2503,46 +2509,53 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,  	if (!ice->hw_rates)  		set_std_hw_rates(ice); -	if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) { +	err = snd_vt1724_pcm_profi(ice, pcm_dev++); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} -	 -	if ((err = snd_vt1724_pcm_spdif(ice, pcm_dev++)) < 0) { + +	err = snd_vt1724_pcm_spdif(ice, pcm_dev++); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} -	 -	if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) { + +	err = snd_vt1724_pcm_indep(ice, pcm_dev++); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} -	if ((err = snd_vt1724_ac97_mixer(ice)) < 0) { +	err = snd_vt1724_ac97_mixer(ice); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} -	if ((err = snd_vt1724_build_controls(ice)) < 0) { +	err = snd_vt1724_build_controls(ice); +	if (err < 0) {  		snd_card_free(card);  		return err;  	}  	if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */ -		if ((err = snd_vt1724_spdif_build_controls(ice)) < 0) { +		err = snd_vt1724_spdif_build_controls(ice); +		if (err < 0) {  			snd_card_free(card);  			return err;  		}  	}  	if (c->build_controls) { -		if ((err = c->build_controls(ice)) < 0) { +		err = c->build_controls(ice); +		if (err < 0) {  			snd_card_free(card);  			return err;  		}  	} -	if (! c->no_mpu401) { +	if (!c->no_mpu401) {  		if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {  			struct snd_rawmidi *rmidi; @@ -2574,7 +2587,8 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,  	sprintf(card->longname, "%s at 0x%lx, irq %i",  		card->shortname, ice->port, ice->irq); -	if ((err = snd_card_register(card)) < 0) { +	err = snd_card_register(card); +	if (err < 0) {  		snd_card_free(card);  		return err;  	} diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index b4e0c16..c51659b 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -21,7 +21,7 @@   *   along with this program; if not, write to the Free Software   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA   * - */       + */  #include <asm/io.h>  #include <linux/delay.h> @@ -34,9 +34,10 @@  #include "ice1712.h"  #include "envy24ht.h"  #include "juli.h" +  struct juli_spec {  	struct ak4114 *ak4114; -	unsigned int analog: 1; +	unsigned int analog:1;  };  /* @@ -160,14 +161,17 @@ static int get_gpio_val(int rate)  	return 0;  } -static void juli_ak4114_write(void *private_data, unsigned char reg, unsigned char val) +static void juli_ak4114_write(void *private_data, unsigned char reg, +				unsigned char val)  { -	snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg, val); +	snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, +				reg, val);  } -         +  static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)  { -	return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, AK4114_ADDR, reg); +	return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data, +					AK4114_ADDR, reg);  }  /* @@ -175,7 +179,7 @@ static unsigned char juli_ak4114_read(void *private_data, unsigned char reg)   * to the external rate   */  static void juli_spdif_in_open(struct snd_ice1712 *ice, -			       struct snd_pcm_substream *substream) +				struct snd_pcm_substream *substream)  {  	struct juli_spec *spec = ice->spec;  	struct snd_pcm_runtime *runtime = substream->runtime; @@ -208,7 +212,8 @@ static void juli_akm_write(struct snd_akm4xxx *ak, int chip,  {  	struct snd_ice1712 *ice = ak->private_data[0]; -	snd_assert(chip == 0, return); +	if (snd_BUG_ON(chip)) +		return;  	snd_vt1724_write_i2c(ice, AK4358_ADDR, addr, data);  } @@ -571,10 +576,12 @@ static void juli_ak4114_change(struct ak4114 *ak4114, unsigned char c0,  static int __devinit juli_init(struct snd_ice1712 *ice)  {  	static const unsigned char ak4114_init_vals[] = { -		/* AK4117_REG_PWRDN */	AK4114_RST | AK4114_PWN | AK4114_OCKS0 | AK4114_OCKS1, +		/* AK4117_REG_PWRDN */	AK4114_RST | AK4114_PWN | +					AK4114_OCKS0 | AK4114_OCKS1,  		/* AK4114_REQ_FORMAT */	AK4114_DIF_I24I2S,  		/* AK4114_REG_IO0 */	AK4114_TX1E, -		/* AK4114_REG_IO1 */	AK4114_EFH_1024 | AK4114_DIT | AK4114_IPS(1), +		/* AK4114_REG_IO1 */	AK4114_EFH_1024 | AK4114_DIT | +					AK4114_IPS(1),  		/* AK4114_REG_INT0_MASK */ 0,  		/* AK4114_REG_INT1_MASK */ 0  	}; @@ -604,12 +611,14 @@ static int __devinit juli_init(struct snd_ice1712 *ice)  	spec->ak4114->check_flags = 0;  #if 0 -        /* it seems that the analog doughter board detection does not work -           reliably, so force the analog flag; it should be very rare -           to use Juli@ without the analog doughter board */ +/* + * it seems that the analog doughter board detection does not work reliably, so + * force the analog flag; it should be very rare (if ever) to come at Juli@ + * used without the analog daughter board + */  	spec->analog = (ice->gpio.get_data(ice) & GPIO_ANALOG_PRESENT) ? 0 : 1;  #else -        spec->analog = 1; +	spec->analog = 1;  #endif  	if (spec->analog) { @@ -617,14 +626,16 @@ static int __devinit juli_init(struct snd_ice1712 *ice)  		ice->num_total_dacs = 2;  		ice->num_total_adcs = 2; -		ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); -		if (! ak) +		ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); +		ak = ice->akm; +		if (!ak)  			return -ENOMEM;  		ice->akm_codecs = 1; -		if ((err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice)) < 0) +		err = snd_ice1712_akm4xxx_init(ak, &akm_juli_dac, NULL, ice); +		if (err < 0)  			return err;  	} -	 +  	/* juli is clocked by Xilinx array */  	ice->hw_rates = &juli_rates_info;  	ice->is_spdif_master = juli_is_spdif_master; diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index 5a158b7..de29be8 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -22,15 +22,24 @@   */  /* PHASE 22 overview: - *   Audio controller: VIA Envy24HT-S (slightly trimmed down version of Envy24HT) + *   Audio controller: VIA Envy24HT-S (slightly trimmed down Envy24HT, 4in/4out)   *   Analog chip: AK4524 (partially via Philip's 74HCT125) - *   Digital receiver: CS8414-CS (not supported in this release) + *   Digital receiver: CS8414-CS (supported in this release) + *		PHASE 22 revision 2.0 and Terrasoniq/Musonik TS22PCI have CS8416 + *		(support status unknown, please test and report)   *   *   Envy connects to AK4524   *	- CS directly from GPIO 10   *	- CCLK via 74HCT125's gate #4 from GPIO 4   *	- CDTI via 74HCT125's gate #2 from GPIO 5 - *		CDTI may be completely blocked by 74HCT125's gate #1 controlled by GPIO 3 + *		CDTI may be completely blocked by 74HCT125's gate #1 + *		controlled by GPIO 3 + */ + +/* PHASE 28 overview: + *   Audio controller: VIA Envy24HT (full untrimmed version, 4in/8out) + *   Analog chip: WM8770 (8 channel 192k DAC, 2 channel 96k ADC) + *   Digital receiver: CS8414-CS (supported in this release)   */  #include <asm/io.h> @@ -77,18 +86,18 @@ struct phase28_spec {   * Computed as 20 * Log10(255 / x)   */  static const unsigned char wm_vol[256] = { -	127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23, -	23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, -	17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13, -	13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, -	11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, -	8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, -	6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -	5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, -	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0 +	127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, +	24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, +	17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, +	14, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, +	11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, +	9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, +	7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, +	5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, +	4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +	3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +	2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +	1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  };  #define WM_VOL_MAX	(sizeof(wm_vol) - 1) @@ -117,26 +126,31 @@ static int __devinit phase22_init(struct snd_ice1712 *ice)  	struct snd_akm4xxx *ak;  	int err; -	// Configure DAC/ADC description for generic part of ice1724 +	/* Configure DAC/ADC description for generic part of ice1724 */  	switch (ice->eeprom.subvendor) {  	case VT1724_SUBDEVICE_PHASE22: +	case VT1724_SUBDEVICE_TS22:  		ice->num_total_dacs = 2;  		ice->num_total_adcs = 2; -		ice->vt1720 = 1; // Envy24HT-S have 16 bit wide GPIO +		ice->vt1720 = 1; /* Envy24HT-S have 16 bit wide GPIO */  		break;  	default:  		snd_BUG();  		return -EINVAL;  	} -	// Initialize analog chips -	ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); -	if (! ak) +	/* Initialize analog chips */ +	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); +	ak = ice->akm; +	if (!ak)  		return -ENOMEM;  	ice->akm_codecs = 1;  	switch (ice->eeprom.subvendor) {  	case VT1724_SUBDEVICE_PHASE22: -		if ((err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, &akm_phase22_priv, ice)) < 0) +	case VT1724_SUBDEVICE_TS22: +		err = snd_ice1712_akm4xxx_init(ak, &akm_phase22, +						&akm_phase22_priv, ice); +		if (err < 0)  			return err;  		break;  	} @@ -150,6 +164,7 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice)  	switch (ice->eeprom.subvendor) {  	case VT1724_SUBDEVICE_PHASE22: +	case VT1724_SUBDEVICE_TS22:  		err = snd_ice1712_akm4xxx_build_controls(ice);  		if (err < 0)  			return err; @@ -158,9 +173,10 @@ static int __devinit phase22_add_controls(struct snd_ice1712 *ice)  }  static unsigned char phase22_eeprom[] __devinitdata = { -	[ICE_EEP2_SYSCONF]     = 0x00,	/* 1xADC, 1xDACs */ +	[ICE_EEP2_SYSCONF]     = 0x28,  /* clock 512, mpu 401, +					spdif-in/1xADC, 1xDACs */  	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */ -	[ICE_EEP2_I2S]         = 0xf8,	/* vol, 96k, 24bit */ +	[ICE_EEP2_I2S]         = 0xf0,	/* vol, 96k, 24bit */  	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */  	[ICE_EEP2_GPIO_DIR]    = 0xff,  	[ICE_EEP2_GPIO_DIR1]   = 0xff, @@ -174,7 +190,8 @@ static unsigned char phase22_eeprom[] __devinitdata = {  };  static unsigned char phase28_eeprom[] __devinitdata = { -	[ICE_EEP2_SYSCONF]     = 0x0b,	/* clock 512, spdif-in/ADC, 4DACs */ +	[ICE_EEP2_SYSCONF]     = 0x2b,  /* clock 512, mpu401, +					spdif-in/1xADC, 4xDACs */  	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */  	[ICE_EEP2_I2S]         = 0xfc,	/* vol, 96k, 24bit, 192k */  	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, spdif-in */ @@ -192,15 +209,16 @@ static unsigned char phase28_eeprom[] __devinitdata = {  /*   * write data in the SPI mode   */ -static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs, unsigned int data, int bits) +static void phase28_spi_write(struct snd_ice1712 *ice, unsigned int cs, +				unsigned int data, int bits)  {  	unsigned int tmp;  	int i;  	tmp = snd_ice1712_gpio_read(ice); -	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK| -					 PHASE28_WM_CS)); +	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI| +					PHASE28_SPI_CLK|PHASE28_WM_CS));  	tmp |= PHASE28_WM_RW;  	tmp &= ~cs;  	snd_ice1712_gpio_write(ice, tmp); @@ -259,14 +277,16 @@ static void wm_put(struct snd_ice1712 *ice, int reg, unsigned short val)  	ice->akm[0].images[reg + 1] = val;  } -static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned short vol, unsigned short master) +static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, +			unsigned short vol, unsigned short master)  {  	unsigned char nvol;  	if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))  		nvol = 0;  	else -		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX]; +		nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * +			(master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];  	wm_put(ice, index, nvol);  	wm_put_nocache(ice, index, 0x180 | nvol); @@ -277,17 +297,20 @@ static void wm_set_vol(struct snd_ice1712 *ice, unsigned int index, unsigned sho   */  #define wm_pcm_mute_info	snd_ctl_boolean_mono_info -static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_pcm_mute_get(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	mutex_lock(&ice->gpio_mutex); -	ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1; +	ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? +						0 : 1;  	mutex_unlock(&ice->gpio_mutex);  	return 0;  } -static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned short nval, oval; @@ -296,7 +319,8 @@ static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va  	snd_ice1712_save_gpio_status(ice);  	oval = wm_get(ice, WM_MUTE);  	nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10); -	if ((change = (nval != oval))) +	change = (nval != oval); +	if (change)  		wm_put(ice, WM_MUTE, nval);  	snd_ice1712_restore_gpio_status(ice); @@ -306,7 +330,8 @@ static int wm_pcm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va  /*   * Master volume attenuation mixer control   */ -static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int wm_master_vol_info(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_info *uinfo)  {  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;  	uinfo->count = 2; @@ -315,17 +340,20 @@ static int wm_master_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem  	return 0;  } -static int wm_master_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_master_vol_get(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec;  	int i; -	for (i=0; i<2; i++) -		ucontrol->value.integer.value[i] = spec->master[i] & ~WM_VOL_MUTE; +	for (i = 0; i < 2; i++) +		ucontrol->value.integer.value[i] = spec->master[i] & +							~WM_VOL_MUTE;  	return 0;  } -static int wm_master_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_master_vol_put(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec; @@ -355,38 +383,38 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)  {  	static const unsigned short wm_inits_phase28[] = {  		/* These come first to reduce init pop noise */ -		0x1b, 0x044,		/* ADC Mux (AC'97 source) */ -		0x1c, 0x00B,		/* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */ -		0x1d, 0x009,		/* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */ - -		0x18, 0x000,		/* All power-up */ - -		0x16, 0x122,		/* I2S, normal polarity, 24bit */ -		0x17, 0x022,		/* 256fs, slave mode */ -		0x00, 0,		/* DAC1 analog mute */ -		0x01, 0,		/* DAC2 analog mute */ -		0x02, 0,		/* DAC3 analog mute */ -		0x03, 0,		/* DAC4 analog mute */ -		0x04, 0,		/* DAC5 analog mute */ -		0x05, 0,		/* DAC6 analog mute */ -		0x06, 0,		/* DAC7 analog mute */ -		0x07, 0,		/* DAC8 analog mute */ -		0x08, 0x100,		/* master analog mute */ -		0x09, 0xff,		/* DAC1 digital full */ -		0x0a, 0xff,		/* DAC2 digital full */ -		0x0b, 0xff,		/* DAC3 digital full */ -		0x0c, 0xff,		/* DAC4 digital full */ -		0x0d, 0xff,		/* DAC5 digital full */ -		0x0e, 0xff,		/* DAC6 digital full */ -		0x0f, 0xff,		/* DAC7 digital full */ -		0x10, 0xff,		/* DAC8 digital full */ -		0x11, 0x1ff,		/* master digital full */ -		0x12, 0x000,		/* phase normal */ -		0x13, 0x090,		/* unmute DAC L/R */ -		0x14, 0x000,		/* all unmute */ -		0x15, 0x000,		/* no deemphasis, no ZFLG */ -		0x19, 0x000,		/* -12dB ADC/L */ -		0x1a, 0x000,		/* -12dB ADC/R */ +		0x1b, 0x044,	/* ADC Mux (AC'97 source) */ +		0x1c, 0x00B,	/* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */ +		0x1d, 0x009,	/* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */ + +		0x18, 0x000,	/* All power-up */ + +		0x16, 0x122,	/* I2S, normal polarity, 24bit */ +		0x17, 0x022,	/* 256fs, slave mode */ +		0x00, 0,	/* DAC1 analog mute */ +		0x01, 0,	/* DAC2 analog mute */ +		0x02, 0,	/* DAC3 analog mute */ +		0x03, 0,	/* DAC4 analog mute */ +		0x04, 0,	/* DAC5 analog mute */ +		0x05, 0,	/* DAC6 analog mute */ +		0x06, 0,	/* DAC7 analog mute */ +		0x07, 0,	/* DAC8 analog mute */ +		0x08, 0x100,	/* master analog mute */ +		0x09, 0xff,	/* DAC1 digital full */ +		0x0a, 0xff,	/* DAC2 digital full */ +		0x0b, 0xff,	/* DAC3 digital full */ +		0x0c, 0xff,	/* DAC4 digital full */ +		0x0d, 0xff,	/* DAC5 digital full */ +		0x0e, 0xff,	/* DAC6 digital full */ +		0x0f, 0xff,	/* DAC7 digital full */ +		0x10, 0xff,	/* DAC8 digital full */ +		0x11, 0x1ff,	/* master digital full */ +		0x12, 0x000,	/* phase normal */ +		0x13, 0x090,	/* unmute DAC L/R */ +		0x14, 0x000,	/* all unmute */ +		0x15, 0x000,	/* no deemphasis, no ZFLG */ +		0x19, 0x000,	/* -12dB ADC/L */ +		0x1a, 0x000,	/* -12dB ADC/R */  		(unsigned short)-1  	}; @@ -404,17 +432,19 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)  		return -ENOMEM;  	ice->spec = spec; -	// Initialize analog chips -	ak = ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); +	/* Initialize analog chips */ +	ice->akm = kzalloc(sizeof(struct snd_akm4xxx), GFP_KERNEL); +	ak = ice->akm;  	if (!ak)  		return -ENOMEM;  	ice->akm_codecs = 1; -	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */ +	snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for time being */  	/* reset the wm codec as the SPI mode */  	snd_ice1712_save_gpio_status(ice); -	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL)); +	snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS| +					PHASE28_HP_SEL));  	tmp = snd_ice1712_gpio_read(ice);  	tmp &= ~PHASE28_WM_RESET; @@ -446,7 +476,8 @@ static int __devinit phase28_init(struct snd_ice1712 *ice)  /*   * DAC volume attenuation mixer control   */ -static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int wm_vol_info(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_info *uinfo)  {  	int voices = kcontrol->private_value >> 8;  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; @@ -456,7 +487,8 @@ static int wm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *  	return 0;  } -static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_vol_get(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec; @@ -470,7 +502,8 @@ static int wm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *  	return 0;  } -static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_vol_put(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec; @@ -501,7 +534,8 @@ static int wm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *  /*   * WM8770 mute control   */ -static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { +static int wm_mute_info(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_info *uinfo) {  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;  	uinfo->count = kcontrol->private_value >> 8;  	uinfo->value.integer.min = 0; @@ -509,7 +543,8 @@ static int wm_mute_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info  	return 0;  } -static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_mute_get(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec; @@ -524,7 +559,8 @@ static int wm_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value  	return 0;  } -static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_mute_put(struct snd_kcontrol *kcontrol, +			struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec; @@ -539,9 +575,10 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value  		if (ucontrol->value.integer.value[i] != val) {  			spec->vol[ofs + i] &= ~WM_VOL_MUTE;  			spec->vol[ofs + i] |= -				ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; +				ucontrol->value.integer.value[i] ? 0 : +				WM_VOL_MUTE;  			wm_set_vol(ice, ofs + i, spec->vol[ofs + i], -				   spec->master[i]); +					spec->master[i]);  			change = 1;  		}  	} @@ -555,7 +592,8 @@ static int wm_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value   */  #define wm_master_mute_info		snd_ctl_boolean_stereo_info -static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_master_mute_get(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec; @@ -567,7 +605,8 @@ static int wm_master_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem  	return 0;  } -static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_master_mute_put(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	struct phase28_spec *spec = ice->spec; @@ -580,11 +619,12 @@ static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem  			int dac;  			spec->master[i] &= ~WM_VOL_MUTE;  			spec->master[i] |= -				ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE; +				ucontrol->value.integer.value[i] ? 0 : +				WM_VOL_MUTE;  			for (dac = 0; dac < ice->num_total_dacs; dac += 2)  				wm_set_vol(ice, WM_DAC_ATTEN + dac + i, -					   spec->vol[dac + i], -					   spec->master[i]); +						spec->vol[dac + i], +						spec->master[i]);  			change = 1;  		}  	} @@ -597,7 +637,8 @@ static int wm_master_mute_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem  #define PCM_0dB 0xff  #define PCM_RES 128	/* -64dB */  #define PCM_MIN (PCM_0dB - PCM_RES) -static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_info *uinfo)  {  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;  	uinfo->count = 1; @@ -606,7 +647,8 @@ static int wm_pcm_vol_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in  	return 0;  } -static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned short val; @@ -619,7 +661,8 @@ static int wm_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val  	return 0;  } -static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned short ovol, nvol; @@ -633,7 +676,8 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val  	ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;  	if (ovol != nvol) {  		wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */ -		wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */ +		/* update */ +		wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100);  		change = 1;  	}  	snd_ice1712_restore_gpio_status(ice); @@ -645,18 +689,22 @@ static int wm_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_val   */  #define phase28_deemp_info	snd_ctl_boolean_mono_info -static int phase28_deemp_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int phase28_deemp_get(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf; +	ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == +						0xf;  	return 0;  } -static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int phase28_deemp_put(struct snd_kcontrol *kcontrol, +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int temp, temp2; -	temp2 = temp = wm_get(ice, WM_DAC_CTRL2); +	temp = wm_get(ice, WM_DAC_CTRL2); +	temp2 = temp;  	if (ucontrol->value.integer.value[0])  		temp |= 0xf;  	else @@ -671,7 +719,8 @@ static int phase28_deemp_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_  /*   * ADC Oversampling   */ -static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_info *uinfo) +static int phase28_oversampling_info(struct snd_kcontrol *k, +					struct snd_ctl_elem_info *uinfo)  {  	static char *texts[2] = { "128x", "64x"	}; @@ -680,25 +729,31 @@ static int phase28_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem  	uinfo->value.enumerated.items = 2;  	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) -		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; -	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); +		uinfo->value.enumerated.item = uinfo->value.enumerated.items - +						1; +	strcpy(uinfo->value.enumerated.name, +		texts[uinfo->value.enumerated.item]); -        return 0; +	return 0;  } -static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, +					struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8; +	ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == +						0x8;  	return 0;  } -static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int phase28_oversampling_put(struct snd_kcontrol *kcontrol, +					struct snd_ctl_elem_value *ucontrol)  {  	int temp, temp2;  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); -	temp2 = temp = wm_get(ice, WM_MASTER); +	temp = wm_get(ice, WM_MASTER); +	temp2 = temp;  	if (ucontrol->value.enumerated.item[0])  		temp |= 0x8; @@ -871,13 +926,16 @@ static int __devinit phase28_add_controls(struct snd_ice1712 *ice)  	counts = ARRAY_SIZE(phase28_dac_controls);  	for (i = 0; i < counts; i++) { -		err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice)); +		err = snd_ctl_add(ice->card, +					snd_ctl_new1(&phase28_dac_controls[i], +							ice));  		if (err < 0)  			return err;  	}  	for (i = 0; i < ARRAY_SIZE(wm_controls); i++) { -		err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice)); +		err = snd_ctl_add(ice->card, +					snd_ctl_new1(&wm_controls[i], ice));  		if (err < 0)  			return err;  	} @@ -904,5 +962,14 @@ struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {  		.eeprom_size = sizeof(phase28_eeprom),  		.eeprom_data = phase28_eeprom,  	}, +	{ +		.subvendor = VT1724_SUBDEVICE_TS22, +		.name = "Terrasoniq TS22 PCI", +		.model = "TS22", +		.chip_init = phase22_init, +		.build_controls = phase22_add_controls, +		.eeprom_size = sizeof(phase22_eeprom), +		.eeprom_data = phase22_eeprom, +	},  	{ } /* terminator */  }; diff --git a/sound/pci/ice1712/phase.h b/sound/pci/ice1712/phase.h index 13e841b..7fc22d9 100644 --- a/sound/pci/ice1712/phase.h +++ b/sound/pci/ice1712/phase.h @@ -22,13 +22,15 @@   *   along with this program; if not, write to the Free Software   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA   * - */       + */ -#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\ -                          "{Terratec,Phase 28}," +#define PHASE_DEVICE_DESC	"{Terratec,Phase 22},"\ +				"{Terratec,Phase 28},"\ +				"{Terrasoniq,TS22},"  #define VT1724_SUBDEVICE_PHASE22	0x3b155011  #define VT1724_SUBDEVICE_PHASE28	0x3b154911 +#define VT1724_SUBDEVICE_TS22		0x3b157b11  /* entry point */  extern struct snd_ice1712_card_info snd_vt1724_phase_cards[]; diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 203cdc1bf..6bc3f91 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -43,7 +43,8 @@  /* WM8776 registers */  #define WM_HP_ATTEN_L		0x00	/* headphone left attenuation */  #define WM_HP_ATTEN_R		0x01	/* headphone left attenuation */ -#define WM_HP_MASTER		0x02	/* headphone master (both channels), override LLR */ +#define WM_HP_MASTER		0x02	/* headphone master (both channels) */ +					/* override LLR */  #define WM_DAC_ATTEN_L		0x03	/* digital left attenuation */  #define WM_DAC_ATTEN_R		0x04  #define WM_DAC_MASTER		0x05 @@ -740,7 +741,7 @@ static int __devinit pontis_init(struct snd_ice1712 *ice)  		WM_DAC_ATTEN_L,	0x0100,	/* DAC 0dB */  		WM_DAC_ATTEN_R,	0x0000,	/* DAC 0dB */  		WM_DAC_ATTEN_R,	0x0100,	/* DAC 0dB */ -		// WM_DAC_MASTER,	0x0100,	/* DAC master muted */ +		/* WM_DAC_MASTER,	0x0100, */	/* DAC master muted */  		WM_PHASE_SWAP,	0x0000,	/* phase normal */  		WM_DAC_CTRL2,	0x0000,	/* no deemphasis, no ZFLG */  		WM_ADC_ATTEN_L,	0x0000,	/* ADC muted */ diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 4d26314..b508bb3 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -1,7 +1,7 @@  /*   *   ALSA driver for ICEnsemble ICE1712 (Envy24)   * - *   Lowlevel functions for M-Audio Revolution 7.1 + *   Lowlevel functions for M-Audio Audiophile 192, Revolution 7.1 and 5.1   *   *	Copyright (c) 2003 Takashi Iwai <tiwai@suse.de>   * @@ -48,7 +48,7 @@ static void revo_i2s_mclk_changed(struct snd_ice1712 *ice)  }  /* - * change the rate of envy24HT, AK4355 and AK4381 + * change the rate of Envy24HT, AK4355 and AK4381   */  static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)  { @@ -83,8 +83,8 @@ static void revo_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)  	tmp = snd_akm4xxx_get(ak, 0, reg);  	tmp &= ~(0x03 << shift);  	tmp |= dfs << shift; -	// snd_akm4xxx_write(ak, 0, reg, tmp); -	snd_akm4xxx_set(ak, 0, reg, tmp); /* the value is written in reset(0) */ +	/* snd_akm4xxx_write(ak, 0, reg, tmp); */ +	snd_akm4xxx_set(ak, 0, reg, tmp); /* value is written in reset(0) */  	snd_akm4xxx_reset(ak, 0);  } @@ -216,6 +216,7 @@ static const struct snd_akm4xxx_dac_channel revo51_dac[] = {  	AK_DAC("PCM Center Playback Volume", 1),  	AK_DAC("PCM LFE Playback Volume", 1),  	AK_DAC("PCM Rear Playback Volume", 2), +	AK_DAC("PCM Headphone Volume", 2),  };  static const char *revo51_adc_input_names[] = { @@ -279,7 +280,7 @@ static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = {  static struct snd_akm4xxx akm_revo51 __devinitdata = {  	.type = SND_AK4358, -	.num_dacs = 6, +	.num_dacs = 8,  	.ops = {  		.set_rate_val = revo_set_rate_val  	}, @@ -508,7 +509,7 @@ static int __devinit revo_init(struct snd_ice1712 *ice)  		ice->gpio.i2s_mclk_changed = revo_i2s_mclk_changed;  		break;  	case VT1724_SUBDEVICE_REVOLUTION51: -		ice->num_total_dacs = 6; +		ice->num_total_dacs = 8;  		ice->num_total_adcs = 2;  		break;  	case VT1724_SUBDEVICE_AUDIOPHILE192: @@ -524,16 +525,20 @@ static int __devinit revo_init(struct snd_ice1712 *ice)  	ak = ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL);  	if (! ak)  		return -ENOMEM; -	ice->akm_codecs = 2;  	switch (ice->eeprom.subvendor) {  	case VT1724_SUBDEVICE_REVOLUTION71:  		ice->akm_codecs = 2; -		if ((err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front, &akm_revo_front_priv, ice)) < 0) +		err = snd_ice1712_akm4xxx_init(ak, &akm_revo_front, +						&akm_revo_front_priv, ice); +		if (err < 0)  			return err; -		if ((err = snd_ice1712_akm4xxx_init(ak + 1, &akm_revo_surround, &akm_revo_surround_priv, ice)) < 0) +		err = snd_ice1712_akm4xxx_init(ak+1, &akm_revo_surround, +						&akm_revo_surround_priv, ice); +		if (err < 0)  			return err;  		/* unmute all codecs */ -		snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, VT1724_REVO_MUTE); +		snd_ice1712_gpio_write_bits(ice, VT1724_REVO_MUTE, +						VT1724_REVO_MUTE);  		break;  	case VT1724_SUBDEVICE_REVOLUTION51:  		ice->akm_codecs = 2; diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index a08d17c..5af9e84 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -1,12 +1,12 @@  /*   *	ALSA driver for ICEnsemble VT1724 (Envy24HT) - *	 + *   *	Lowlevel functions for Ego Sys Waveterminal 192M   *   *		Copyright (c) 2006 Guedez Clement <klem.dev@gmail.com>   *		Some functions are taken from the Prodigy192 driver   *		source - *		 + *   *	This program is free software; you can redistribute it and/or modify   *	it under the terms of the GNU General Public License as published by   *	the Free Software Foundation; either version 2 of the License, or @@ -20,12 +20,12 @@   *	You should have received a copy of the GNU General Public License   *	along with this program; if not, write to the Free Software   *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - *	 - */	 + * + */ -#include <asm/io.h> +#include <linux/io.h>  #include <linux/delay.h>  #include <linux/interrupt.h>  #include <linux/init.h> @@ -39,9 +39,9 @@  /* - *	2*ADC 6*DAC no1 ringbuffer r/w on i2c bus  + *	2*ADC 6*DAC no1 ringbuffer r/w on i2c bus   */ -static inline void stac9460_put(struct snd_ice1712 *ice, int reg,  +static inline void stac9460_put(struct snd_ice1712 *ice, int reg,  						unsigned char val)  {  	snd_vt1724_write_i2c(ice, STAC9460_I2C_ADDR, reg, val); @@ -73,7 +73,7 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg)  #define stac9460_dac_mute_info		snd_ctl_boolean_mono_info  static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char val; @@ -88,14 +88,14 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol,  	}  	if (id < 6)  		val = stac9460_get(ice, idx); -	else  -		val = stac9460_2_get(ice,idx - 6); +	else +		val = stac9460_2_get(ice, idx - 6);  	ucontrol->value.integer.value[0] = (~val >> 7) & 0x1;  	return 0;  }  static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char new, old; @@ -105,8 +105,8 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,  	if (kcontrol->private_value) {  		idx = STAC946X_MASTER_VOLUME;  		old = stac9460_get(ice, idx); -		new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | -		       					(old & ~0x80); +		new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | +							(old & ~0x80);  		change = (new != old);  		if (change) {  			stac9460_put(ice, idx, new); @@ -117,16 +117,16 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,  		idx = id + STAC946X_LF_VOLUME;  		if (id < 6)  			old = stac9460_get(ice, idx); -		else  +		else  			old = stac9460_2_get(ice, idx - 6); -		new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | +		new = (~ucontrol->value.integer.value[0] << 7 & 0x80) |  							(old & ~0x80);  		change = (new != old);  		if (change) {  			if (id < 6) -			       	stac9460_put(ice, idx, new); +				stac9460_put(ice, idx, new);  			else -			       	stac9460_2_put(ice, idx - 6, new); +				stac9460_2_put(ice, idx - 6, new);  		}  	}  	return change; @@ -136,7 +136,7 @@ static int stac9460_dac_mute_put(struct snd_kcontrol *kcontrol,   * 	DAC volume attenuation mixer control   */  static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_info *uinfo) +				struct snd_ctl_elem_info *uinfo)  {  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;  	uinfo->count = 1; @@ -146,7 +146,7 @@ static int stac9460_dac_vol_info(struct snd_kcontrol *kcontrol,  }  static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int idx, id; @@ -161,14 +161,14 @@ static int stac9460_dac_vol_get(struct snd_kcontrol *kcontrol,  	}  	if (id < 6)  		vol = stac9460_get(ice, idx) & 0x7f; -	else  +	else  		vol = stac9460_2_get(ice, idx - 6) & 0x7f;  	ucontrol->value.integer.value[0] = 0x7f - vol;  	return 0;  }  static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int idx, id; @@ -182,8 +182,8 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,  		ovol = 0x7f - (tmp & 0x7f);  		change = (ovol != nvol);  		if (change) { -			 stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); -			 stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); +			stac9460_put(ice, idx, (0x7f - nvol) | (tmp & 0x80)); +			stac9460_2_put(ice, idx, (0x7f - nvol) | (tmp & 0x80));  		}  	} else {  		id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); @@ -191,17 +191,17 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,  		nvol = ucontrol->value.integer.value[0] & 0x7f;  		if (id < 6)  			tmp = stac9460_get(ice, idx); -		else  +		else  			tmp = stac9460_2_get(ice, idx - 6);  		ovol = 0x7f - (tmp & 0x7f);  		change = (ovol != nvol);  		if (change) {  			if (id < 6)  				stac9460_put(ice, idx, (0x7f - nvol) | -					       		(tmp & 0x80)); -			else  +							(tmp & 0x80)); +			else  				stac9460_2_put(ice, idx-6, (0x7f - nvol) | -					       			(tmp & 0x80)); +							(tmp & 0x80));  		}  	}  	return change; @@ -213,12 +213,12 @@ static int stac9460_dac_vol_put(struct snd_kcontrol *kcontrol,  #define stac9460_adc_mute_info		snd_ctl_boolean_stereo_info  static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char val;  	int i, id; -	 +  	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	if (id == 0) {  		for (i = 0; i < 2; ++i) { @@ -235,20 +235,20 @@ static int stac9460_adc_mute_get(struct snd_kcontrol *kcontrol,  }  static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char new, old;  	int i, reg, id;  	int change; -	 +  	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	if (id == 0) {  		for (i = 0; i < 2; ++i) {  			reg = STAC946X_MIC_L_VOLUME + i;  			old = stac9460_get(ice, reg);  			new = (~ucontrol->value.integer.value[i]<<7&0x80) | -			       					(old&~0x80); +								(old&~0x80);  			change = (new != old);  			if (change)  				stac9460_put(ice, reg, new); @@ -258,7 +258,7 @@ static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,  			reg = STAC946X_MIC_L_VOLUME + i;  			old = stac9460_2_get(ice, reg);  			new = (~ucontrol->value.integer.value[i]<<7&0x80) | -			       					(old&~0x80); +								(old&~0x80);  			change = (new != old);  			if (change)  				stac9460_2_put(ice, reg, new); @@ -271,7 +271,7 @@ static int stac9460_adc_mute_put(struct snd_kcontrol *kcontrol,   *ADC gain mixer control   */  static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_info *uinfo) +				struct snd_ctl_elem_info *uinfo)  {  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;  	uinfo->count = 2; @@ -281,12 +281,12 @@ static int stac9460_adc_vol_info(struct snd_kcontrol *kcontrol,  }  static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol, -	       			struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int i, reg, id;  	unsigned char vol; -	 +  	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	if (id == 0) {  		for (i = 0; i < 2; ++i) { @@ -305,13 +305,13 @@ static int stac9460_adc_vol_get(struct snd_kcontrol *kcontrol,  }  static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, -	       		struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	int i, reg, id;  	unsigned char ovol, nvol;  	int change; -	 +  	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	if (id == 0) {  		for (i = 0; i < 2; ++i) { @@ -321,7 +321,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,  			change = ((ovol & 0x0f) != nvol);  			if (change)  				stac9460_put(ice, reg, (0x0f - nvol) | -					       		(ovol & ~0x0f)); +							(ovol & ~0x0f));  		}  	} else {  		for (i = 0; i < 2; ++i) { @@ -331,7 +331,7 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,  			change = ((ovol & 0x0f) != nvol);  			if (change)  				stac9460_2_put(ice, reg, (0x0f - nvol) | -					       		(ovol & ~0x0f)); +							(ovol & ~0x0f));  		}  	}  	return change; @@ -344,23 +344,23 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol,  #define stac9460_mic_sw_info		snd_ctl_boolean_mono_info  static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol, -	       		struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char val;  	int id; -		 +  	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	if (id == 0) -	       	val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); +		val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);  	else -	       	val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); +		val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE);  	ucontrol->value.integer.value[0] = ~val>>7 & 0x1;  	return 0;  }  static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, -	       		struct snd_ctl_elem_value *ucontrol) +				struct snd_ctl_elem_value *ucontrol)  {  	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);  	unsigned char new, old; @@ -368,16 +368,16 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol,  	id = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);  	if (id == 0) -	       	old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); +		old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE);  	else -	       	old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); -	new = (~ucontrol->value.integer.value[0]<< 7 & 0x80) | (old & ~0x80); +		old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); +	new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80);  	change = (new != old);  	if (change) {  		if (id == 0) -		       	stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new); +			stac9460_put(ice, STAC946X_GENERAL_PURPOSE, new);  		else -		       	stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new); +			stac9460_2_put(ice, STAC946X_GENERAL_PURPOSE, new);  	}  	return change;  } @@ -443,7 +443,7 @@ static struct snd_kcontrol_new stac9640_controls[] __devinitdata = {  		.get = stac9460_adc_vol_get,  		.put = stac9460_adc_vol_put, -	}	 +	}  }; @@ -470,7 +470,7 @@ static int __devinit wtm_init(struct snd_ice1712 *ice)  		(unsigned short)-1  	};  	unsigned short *p; -		 +  	/*WTM 192M*/  	ice->num_total_dacs = 8;  	ice->num_total_adcs = 4; diff --git a/sound/pci/ice1712/wtm.h b/sound/pci/ice1712/wtm.h index 03a394e..423c1a2 100644 --- a/sound/pci/ice1712/wtm.h +++ b/sound/pci/ice1712/wtm.h @@ -10,8 +10,8 @@   */  #define	AK4114_ADDR		0x20	/*S/PDIF receiver*/ -#define STAC9460_I2C_ADDR	0x54	/* ADC*2 | DAC*6 */	 -#define STAC9460_2_I2C_ADDR	0x56	/* ADC|DAC *2 */	 +#define STAC9460_I2C_ADDR	0x54	/* ADC*2 | DAC*6 */ +#define STAC9460_2_I2C_ADDR	0x56	/* ADC|DAC *2 */  extern struct snd_ice1712_card_info snd_vt1724_wtm_cards[]; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 048d99e..c88d1ea 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -59,6 +59,12 @@ MODULE_SUPPORTED_DEVICE("{{Intel,82801AA-ICH},"  		"{SiS,SI7012},"  		"{NVidia,nForce Audio},"  		"{NVidia,nForce2 Audio}," +		"{NVidia,nForce3 Audio}," +		"{NVidia,MCP04}," +		"{NVidia,MCP501}," +		"{NVidia,CK804}," +		"{NVidia,CK8}," +		"{NVidia,CK8S},"  		"{AMD,AMD768},"  		"{AMD,AMD8111},"  	        "{ALI,M5455}}"); @@ -77,7 +83,7 @@ MODULE_PARM_DESC(index, "Index value for Intel i8x0 soundcard.");  module_param(id, charp, 0444);  MODULE_PARM_DESC(id, "ID string for Intel i8x0 soundcard.");  module_param(ac97_clock, int, 0444); -MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = auto-detect)."); +MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (0 = whitelist + auto-detect, 1 = force autodetect).");  module_param(ac97_quirk, charp, 0444);  MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");  module_param(buggy_semaphore, bool, 0444); @@ -1957,6 +1963,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {  	},  	{  		.subvendor = 0x10cf, +		.subdevice = 0x127d, +		.name = "Fujitsu Lifebook P7010", +		.type = AC97_TUNE_HP_ONLY +	}, +	{ +		.subvendor = 0x10cf,  		.subdevice = 0x127e,  		.name = "Fujitsu Lifebook C1211D",  		.type = AC97_TUNE_HP_ONLY @@ -2132,8 +2144,8 @@ static int __devinit snd_intel8x0_mixer(struct intel8x0 *chip, int ac97_clock,  				snd_intel8x0_codec_read_test(chip, codecs);  				chip->ac97_sdin[codecs] =  					igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK; -				snd_assert(chip->ac97_sdin[codecs] < 3, -					   chip->ac97_sdin[codecs] = 0); +				if (snd_BUG_ON(chip->ac97_sdin[codecs] >= 3)) +					chip->ac97_sdin[codecs] = 0;  			} else  				chip->ac97_sdin[codecs] = i;  			codecs++; @@ -2686,6 +2698,28 @@ static void __devinit intel8x0_measure_ac97_clock(struct intel8x0 *chip)  	snd_ac97_update_power(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 0);  } +static struct snd_pci_quirk intel8x0_clock_list[] __devinitdata = { +	SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000), +	SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100), +	SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000), +	SND_PCI_QUIRK(0x1043, 0x80f3, "AD1985", 48000), +	{ }	/* terminator */ +}; + +static int __devinit intel8x0_in_clock_list(struct intel8x0 *chip) +{ +	struct pci_dev *pci = chip->pci; +	const struct snd_pci_quirk *wl; + +	wl = snd_pci_quirk_lookup(pci, intel8x0_clock_list); +	if (!wl) +		return 0; +	printk(KERN_INFO "intel8x0: white list rate for %04x:%04x is %i\n", +	       pci->subsystem_vendor, pci->subsystem_device, wl->value); +	chip->ac97_bus->clock = wl->value; +	return 1; +} +  #ifdef CONFIG_PROC_FS  static void snd_intel8x0_proc_read(struct snd_info_entry * entry,  				   struct snd_info_buffer *buffer) @@ -3081,8 +3115,14 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,  		 "%s with %s at irq %i", card->shortname,  		 snd_ac97_get_short_name(chip->ac97[0]), chip->irq); -	if (! ac97_clock) -		intel8x0_measure_ac97_clock(chip); +	if (ac97_clock == 0 || ac97_clock == 1) { +		if (ac97_clock == 0) { +			if (intel8x0_in_clock_list(chip) == 0) +				intel8x0_measure_ac97_clock(chip); +		} else { +			intel8x0_measure_ac97_clock(chip); +		} +	}  	if ((err = snd_card_register(card)) < 0) {  		snd_card_free(card); diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index faf674e..93449e4 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -306,7 +306,8 @@ static unsigned int get_ich_codec_bit(struct intel8x0m *chip, unsigned int codec  	static unsigned int codec_bit[3] = {  		ICH_PCR, ICH_SCR, ICH_TCR  	}; -	snd_assert(codec < 3, return ICH_PCR); +	if (snd_BUG_ON(codec >= 3)) +		return ICH_PCR;  	return codec_bit[codec];  } diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 4a44c0f..5f8006b 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1281,7 +1281,8 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun  	K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_silence pos=%d offset=%d size=%d count=%d\n",  				   pos, offset, size, count); -	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); +	if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) +		return -EINVAL;  	for (i=0; i < count; i++) {  #if K1212_DEBUG_LEVEL > 0 @@ -1306,7 +1307,8 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst,  	K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n",  				   pos, offset, size); -	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); +	if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) +		return -EINVAL;  	for (i=0; i < count; i++) {  #if K1212_DEBUG_LEVEL > 0 @@ -1336,7 +1338,8 @@ static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *sr  	K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n",  				   pos, offset, size, count); -	snd_assert(pos + count <= K1212_MAX_SAMPLES, return -EINVAL); +	if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) +		return -EINVAL;  	for (i=0; i < count; i++) {  #if K1212_DEBUG_LEVEL > 0 diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 0037be7..9ff3f9e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1175,7 +1175,8 @@ snd_m3_pcm_trigger(struct snd_pcm_substream *subs, int cmd)  	struct m3_dma *s = subs->runtime->private_data;  	int err = -EINVAL; -	snd_assert(s != NULL, return -ENXIO); +	if (snd_BUG_ON(!s)) +		return -ENXIO;  	spin_lock(&chip->reg_lock);  	switch (cmd) { @@ -1487,7 +1488,8 @@ snd_m3_pcm_prepare(struct snd_pcm_substream *subs)  	struct snd_pcm_runtime *runtime = subs->runtime;  	struct m3_dma *s = runtime->private_data; -	snd_assert(s != NULL, return -ENXIO); +	if (snd_BUG_ON(!s)) +		return -ENXIO;  	if (runtime->format != SNDRV_PCM_FORMAT_U8 &&  	    runtime->format != SNDRV_PCM_FORMAT_S16_LE) @@ -1546,7 +1548,9 @@ snd_m3_pcm_pointer(struct snd_pcm_substream *subs)  	struct snd_m3 *chip = snd_pcm_substream_chip(subs);  	unsigned int ptr;  	struct m3_dma *s = subs->runtime->private_data; -	snd_assert(s != NULL, return 0); + +	if (snd_BUG_ON(!s)) +		return 0;  	spin_lock(&chip->reg_lock);  	ptr = snd_m3_get_pointer(chip, s, subs); diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 3dd0c79..2d0dce6 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -708,7 +708,7 @@ static int snd_mixart_playback_open(struct snd_pcm_substream *subs)  		pcm_number = MIXART_PCM_ANALOG;  		runtime->hw = snd_mixart_analog_caps;  	} else { -		snd_assert ( pcm == chip->pcm_dig );  +		snd_BUG_ON(pcm != chip->pcm_dig);  		pcm_number = MIXART_PCM_DIGITAL;  		runtime->hw = snd_mixart_digital_caps;  	} @@ -783,7 +783,7 @@ static int snd_mixart_capture_open(struct snd_pcm_substream *subs)  		pcm_number = MIXART_PCM_ANALOG;  		runtime->hw = snd_mixart_analog_caps;  	} else { -		snd_assert ( pcm == chip->pcm_dig );  +		snd_BUG_ON(pcm != chip->pcm_dig);  		pcm_number = MIXART_PCM_DIGITAL;  		runtime->hw = snd_mixart_digital_caps;  	} diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index 785085e..b9a06c2 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -56,8 +56,10 @@ static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame)  	if (tailptr == headptr)  		return 0; /* no message posted */ -	snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */ -	snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */ +	if (tailptr < MSG_OUTBOUND_POST_STACK) +		return 0; /* error */ +	if (tailptr >= MSG_OUTBOUND_POST_STACK + MSG_BOUND_STACK_SIZE) +		return 0; /* error */  	*msg_frame = readl_be(MIXART_MEM(mgr, tailptr)); @@ -149,7 +151,8 @@ static int send_msg( struct mixart_mgr *mgr,  	u32 msg_frame_address;  	int err, i; -	snd_assert(msg->size % 4 == 0, return -EINVAL); +	if (snd_BUG_ON(msg->size % 4)) +		return -EINVAL;  	err = 0; @@ -289,9 +292,12 @@ int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,  	wait_queue_t wait;  	long timeout; -	snd_assert(notif_event != 0, return -EINVAL); -	snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL); -	snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL); +	if (snd_BUG_ON(!notif_event)) +		return -EINVAL; +	if (snd_BUG_ON((notif_event & MSG_TYPE_MASK) != MSG_TYPE_NOTIFY)) +		return -EINVAL; +	if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK)) +		return -EINVAL;  	mutex_lock(&mgr->msg_mutex); diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index f986031..3782b52 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -288,7 +288,9 @@ static int mixart_enum_physio(struct mixart_mgr *mgr)  		return -EINVAL;  	} -	snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2),  return -EINVAL); /* min 2 phys io per card (analog in + analog out) */ +	/* min 2 phys io per card (analog in + analog out) */ +	if (phys_io.nb_uid < MIXART_MAX_CARDS * 2) +		return -EINVAL;  	for(k=0; k<mgr->num_cards; k++) {  		mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k]; @@ -363,8 +365,10 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw  		}  		/* check xilinx validity */ -		snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL); -		snd_assert(dsp->size % 4 == 0, return -EINVAL); +		if (((u32*)(dsp->data))[0] == 0xffffffff) +			return -EINVAL; +		if (dsp->size % 4) +			return -EINVAL;  		/* set xilinx status to copying */  		writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET )); @@ -462,8 +466,10 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw  		}  		/* check daughterboard xilinx validity */ -		snd_assert(((u32*)(dsp->data))[0]==0xFFFFFFFF, return -EINVAL); -		snd_assert(dsp->size % 4 == 0, return -EINVAL); +		if (((u32*)(dsp->data))[0] == 0xffffffff) +			return -EINVAL; +		if (dsp->size % 4) +			return -EINVAL;  		/* inform mixart about the size of the file */  		writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET )); @@ -480,7 +486,8 @@ static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmw  		/* get the address where to write the file */  		val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET )); -		snd_assert(val != 0, return -EINVAL); +		if (!val) +			return -EINVAL;  		/* copy daughterboard xilinx code */  		memcpy_toio(  MIXART_MEM( mgr, val),  dsp->data,  dsp->size); diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c index 6fdda1f..3ba6174 100644 --- a/sound/pci/mixart/mixart_mixer.c +++ b/sound/pci/mixart/mixart_mixer.c @@ -837,7 +837,7 @@ static int mixart_pcm_vol_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem  		if(is_aes)	stored_volume = chip->digital_capture_volume[1];	/* AES capture */  		else		stored_volume = chip->digital_capture_volume[0];	/* analog capture */  	} else { -		snd_assert ( idx < MIXART_PLAYBACK_STREAMS );  +		snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);  		if(is_aes)	stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */  		else		stored_volume = chip->digital_playback_volume[idx];	/* analog playback */  	} @@ -863,7 +863,7 @@ static int mixart_pcm_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem  		else		/* analog capture */  			stored_volume = chip->digital_capture_volume[0];  	} else { -		snd_assert ( idx < MIXART_PLAYBACK_STREAMS );  +		snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);  		if (is_aes)	/* AES playback */  			stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx];  		else		/* analog playback */ @@ -909,7 +909,7 @@ static int mixart_pcm_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_  {  	struct snd_mixart *chip = snd_kcontrol_chip(kcontrol);  	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */ -	snd_assert ( idx < MIXART_PLAYBACK_STREAMS );  +	snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);  	mutex_lock(&chip->mgr->mixer_mutex);  	if(kcontrol->private_value & MIXART_VOL_AES_MASK)	/* AES playback */  		idx += MIXART_PLAYBACK_STREAMS; @@ -926,7 +926,7 @@ static int mixart_pcm_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_  	int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;  	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */  	int i, j; -	snd_assert ( idx < MIXART_PLAYBACK_STREAMS );  +	snd_BUG_ON(idx >= MIXART_PLAYBACK_STREAMS);  	mutex_lock(&chip->mgr->mixer_mutex);  	j = idx;  	if (is_aes) diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 06d13e7..50c9f8a 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -562,7 +562,8 @@ snd_nm256_playback_trigger(struct snd_pcm_substream *substream, int cmd)  	struct nm256_stream *s = substream->runtime->private_data;  	int err = 0; -	snd_assert(s != NULL, return -ENXIO); +	if (snd_BUG_ON(!s)) +		return -ENXIO;  	spin_lock(&chip->reg_lock);  	switch (cmd) { @@ -599,7 +600,8 @@ snd_nm256_capture_trigger(struct snd_pcm_substream *substream, int cmd)  	struct nm256_stream *s = substream->runtime->private_data;  	int err = 0; -	snd_assert(s != NULL, return -ENXIO); +	if (snd_BUG_ON(!s)) +		return -ENXIO;  	spin_lock(&chip->reg_lock);  	switch (cmd) { @@ -635,7 +637,8 @@ static int snd_nm256_pcm_prepare(struct snd_pcm_substream *substream)  	struct snd_pcm_runtime *runtime = substream->runtime;  	struct nm256_stream *s = runtime->private_data; -	snd_assert(s, return -ENXIO); +	if (snd_BUG_ON(!s)) +		return -ENXIO;  	s->dma_size = frames_to_bytes(runtime, substream->runtime->buffer_size);  	s->period_size = frames_to_bytes(runtime, substream->runtime->period_size);  	s->periods = substream->runtime->periods; @@ -660,7 +663,8 @@ snd_nm256_playback_pointer(struct snd_pcm_substream *substream)  	struct nm256_stream *s = substream->runtime->private_data;  	unsigned long curp; -	snd_assert(s, return 0); +	if (snd_BUG_ON(!s)) +		return 0;  	curp = snd_nm256_readl(chip, NM_PBUFFER_CURRP) - (unsigned long)s->buf;  	curp %= s->dma_size;  	return bytes_to_frames(substream->runtime, curp); @@ -673,7 +677,8 @@ snd_nm256_capture_pointer(struct snd_pcm_substream *substream)  	struct nm256_stream *s = substream->runtime->private_data;  	unsigned long curp; -	snd_assert(s != NULL, return 0); +	if (snd_BUG_ON(!s)) +		return 0;  	curp = snd_nm256_readl(chip, NM_RBUFFER_CURRP) - (unsigned long)s->buf;  	curp %= s->dma_size;	  	return bytes_to_frames(substream->runtime, curp); diff --git a/sound/pci/oxygen/hifier.c b/sound/pci/oxygen/hifier.c index dad393a..1ab833f 100644 --- a/sound/pci/oxygen/hifier.c +++ b/sound/pci/oxygen/hifier.c @@ -94,6 +94,11 @@ static void hifier_cleanup(struct oxygen *chip)  {  } +static void hifier_resume(struct oxygen *chip) +{ +	hifier_registers_init(chip); +} +  static void set_ak4396_params(struct oxygen *chip,  			       struct snd_pcm_hw_params *params)  { @@ -150,16 +155,16 @@ static const struct oxygen_model model_hifier = {  	.init = hifier_init,  	.control_filter = hifier_control_filter,  	.cleanup = hifier_cleanup, -	.resume = hifier_registers_init, +	.resume = hifier_resume,  	.set_dac_params = set_ak4396_params,  	.set_adc_params = set_cs5340_params,  	.update_dac_volume = update_ak4396_volume,  	.update_dac_mute = update_ak4396_mute,  	.dac_tlv = ak4396_db_scale,  	.model_data_size = sizeof(struct hifier_data), -	.pcm_dev_cfg = PLAYBACK_0_TO_I2S | -		       PLAYBACK_1_TO_SPDIF | -		       CAPTURE_0_FROM_I2S_1, +	.device_config = PLAYBACK_0_TO_I2S | +			 PLAYBACK_1_TO_SPDIF | +			 CAPTURE_0_FROM_I2S_1,  	.dac_channels = 2,  	.dac_volume_min = 0,  	.dac_volume_max = 255, @@ -180,7 +185,7 @@ static int __devinit hifier_probe(struct pci_dev *pci,  		++dev;  		return -ENOENT;  	} -	err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier); +	err = oxygen_pci_probe(pci, index[dev], id[dev], &model_hifier, 0);  	if (err >= 0)  		++dev;  	return err; diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c index c5829d3..b60f621 100644 --- a/sound/pci/oxygen/oxygen.c +++ b/sound/pci/oxygen/oxygen.c @@ -58,17 +58,22 @@ MODULE_PARM_DESC(id, "ID string");  module_param_array(enable, bool, NULL, 0444);  MODULE_PARM_DESC(enable, "enable card"); +enum { +	MODEL_CMEDIA_REF,	/* C-Media's reference design */ +	MODEL_MERIDIAN,		/* AuzenTech X-Meridian */ +}; +  static struct pci_device_id oxygen_ids[] __devinitdata = { -	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216) }, -	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218) }, -	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219) }, -	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001) }, -	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010) }, -	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788) }, -	{ OXYGEN_PCI_SUBID(0x147a, 0xa017) }, -	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910) }, -	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = 1 }, -	{ OXYGEN_PCI_SUBID(0x7284, 0x9761) }, +	{ OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x13f6, 0x0001), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x13f6, 0x0010), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x13f6, 0x8788), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x147a, 0xa017), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF }, +	{ OXYGEN_PCI_SUBID(0x415a, 0x5431), .driver_data = MODEL_MERIDIAN }, +	{ OXYGEN_PCI_SUBID(0x7284, 0x9761), .driver_data = MODEL_CMEDIA_REF },  	{ }  };  MODULE_DEVICE_TABLE(pci, oxygen_ids); @@ -199,6 +204,11 @@ static void generic_resume(struct oxygen *chip)  	wm8785_registers_init(chip);  } +static void meridian_resume(struct oxygen *chip) +{ +	ak4396_registers_init(chip); +} +  static void set_ak4396_params(struct oxygen *chip,  			      struct snd_pcm_hw_params *params)  { @@ -281,11 +291,28 @@ static void set_ak5385_params(struct oxygen *chip,  static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0); +static int generic_probe(struct oxygen *chip, unsigned long driver_data) +{ +	if (driver_data == MODEL_MERIDIAN) { +		chip->model.init = meridian_init; +		chip->model.resume = meridian_resume; +		chip->model.set_adc_params = set_ak5385_params; +		chip->model.device_config = PLAYBACK_0_TO_I2S | +					    PLAYBACK_1_TO_SPDIF | +					    CAPTURE_0_FROM_I2S_2 | +					    CAPTURE_1_FROM_SPDIF; +		chip->model.misc_flags = OXYGEN_MISC_MIDI; +		chip->model.device_config |= MIDI_OUTPUT | MIDI_INPUT; +	} +	return 0; +} +  static const struct oxygen_model model_generic = {  	.shortname = "C-Media CMI8788",  	.longname = "C-Media Oxygen HD Audio",  	.chip = "CMI8788",  	.owner = THIS_MODULE, +	.probe = generic_probe,  	.init = generic_init,  	.cleanup = generic_cleanup,  	.resume = generic_resume, @@ -295,44 +322,15 @@ static const struct oxygen_model model_generic = {  	.update_dac_mute = update_ak4396_mute,  	.dac_tlv = ak4396_db_scale,  	.model_data_size = sizeof(struct generic_data), -	.pcm_dev_cfg = PLAYBACK_0_TO_I2S | -		       PLAYBACK_1_TO_SPDIF | -		       PLAYBACK_2_TO_AC97_1 | -		       CAPTURE_0_FROM_I2S_1 | -		       CAPTURE_1_FROM_SPDIF | -		       CAPTURE_2_FROM_AC97_1, -	.dac_channels = 8, -	.dac_volume_min = 0, -	.dac_volume_max = 255, -	.function_flags = OXYGEN_FUNCTION_SPI | -			  OXYGEN_FUNCTION_ENABLE_SPI_4_5, -	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -}; -static const struct oxygen_model model_meridian = { -	.shortname = "C-Media CMI8788", -	.longname = "C-Media Oxygen HD Audio", -	.chip = "CMI8788", -	.owner = THIS_MODULE, -	.init = meridian_init, -	.cleanup = generic_cleanup, -	.resume = ak4396_registers_init, -	.set_dac_params = set_ak4396_params, -	.set_adc_params = set_ak5385_params, -	.update_dac_volume = update_ak4396_volume, -	.update_dac_mute = update_ak4396_mute, -	.dac_tlv = ak4396_db_scale, -	.model_data_size = sizeof(struct generic_data), -	.pcm_dev_cfg = PLAYBACK_0_TO_I2S | -		       PLAYBACK_1_TO_SPDIF | -		       PLAYBACK_2_TO_AC97_1 | -		       CAPTURE_0_FROM_I2S_2 | -		       CAPTURE_1_FROM_SPDIF | -		       CAPTURE_2_FROM_AC97_1, +	.device_config = PLAYBACK_0_TO_I2S | +			 PLAYBACK_1_TO_SPDIF | +			 PLAYBACK_2_TO_AC97_1 | +			 CAPTURE_0_FROM_I2S_1 | +			 CAPTURE_1_FROM_SPDIF | +			 CAPTURE_2_FROM_AC97_1,  	.dac_channels = 8,  	.dac_volume_min = 0,  	.dac_volume_max = 255, -	.misc_flags = OXYGEN_MISC_MIDI,  	.function_flags = OXYGEN_FUNCTION_SPI |  			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,  	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, @@ -343,7 +341,6 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,  					  const struct pci_device_id *pci_id)  {  	static int dev; -	int is_meridian;  	int err;  	if (dev >= SNDRV_CARDS) @@ -352,9 +349,8 @@ static int __devinit generic_oxygen_probe(struct pci_dev *pci,  		++dev;  		return -ENOENT;  	} -	is_meridian = pci_id->driver_data;  	err = oxygen_pci_probe(pci, index[dev], id[dev], -			       is_meridian ? &model_meridian : &model_generic); +			       &model_generic, pci_id->driver_data);  	if (err >= 0)  		++dev;  	return err; diff --git a/sound/pci/oxygen/oxygen.h b/sound/pci/oxygen/oxygen.h index 74a6448..19107c6 100644 --- a/sound/pci/oxygen/oxygen.h +++ b/sound/pci/oxygen/oxygen.h @@ -19,14 +19,19 @@  #define OXYGEN_IO_SIZE	0x100  /* model-specific configuration of outputs/inputs */ -#define PLAYBACK_0_TO_I2S	0x001 -#define PLAYBACK_1_TO_SPDIF	0x004 -#define PLAYBACK_2_TO_AC97_1	0x008 -#define CAPTURE_0_FROM_I2S_1	0x010 -#define CAPTURE_0_FROM_I2S_2	0x020 -#define CAPTURE_1_FROM_SPDIF	0x080 -#define CAPTURE_2_FROM_I2S_2	0x100 -#define CAPTURE_2_FROM_AC97_1	0x200 +#define PLAYBACK_0_TO_I2S	0x0001 +     /* PLAYBACK_0_TO_AC97_0		not implemented */ +#define PLAYBACK_1_TO_SPDIF	0x0004 +#define PLAYBACK_2_TO_AC97_1	0x0008 +#define CAPTURE_0_FROM_I2S_1	0x0010 +#define CAPTURE_0_FROM_I2S_2	0x0020 +     /* CAPTURE_0_FROM_AC97_0		not implemented */ +#define CAPTURE_1_FROM_SPDIF	0x0080 +#define CAPTURE_2_FROM_I2S_2	0x0100 +#define CAPTURE_2_FROM_AC97_1	0x0200 +     /* CAPTURE_3_FROM_I2S_3		not implemented */ +#define MIDI_OUTPUT		0x0800 +#define MIDI_INPUT		0x1000  enum {  	CONTROL_SPDIF_PCM, @@ -51,7 +56,43 @@ struct snd_pcm_hardware;  struct snd_pcm_hw_params;  struct snd_kcontrol_new;  struct snd_rawmidi; -struct oxygen_model; +struct oxygen; + +struct oxygen_model { +	const char *shortname; +	const char *longname; +	const char *chip; +	struct module *owner; +	int (*probe)(struct oxygen *chip, unsigned long driver_data); +	void (*init)(struct oxygen *chip); +	int (*control_filter)(struct snd_kcontrol_new *template); +	int (*mixer_init)(struct oxygen *chip); +	void (*cleanup)(struct oxygen *chip); +	void (*suspend)(struct oxygen *chip); +	void (*resume)(struct oxygen *chip); +	void (*pcm_hardware_filter)(unsigned int channel, +				    struct snd_pcm_hardware *hardware); +	void (*set_dac_params)(struct oxygen *chip, +			       struct snd_pcm_hw_params *params); +	void (*set_adc_params)(struct oxygen *chip, +			       struct snd_pcm_hw_params *params); +	void (*update_dac_volume)(struct oxygen *chip); +	void (*update_dac_mute)(struct oxygen *chip); +	void (*gpio_changed)(struct oxygen *chip); +	void (*uart_input)(struct oxygen *chip); +	void (*ac97_switch)(struct oxygen *chip, +			    unsigned int reg, unsigned int mute); +	const unsigned int *dac_tlv; +	size_t model_data_size; +	unsigned int device_config; +	u8 dac_channels; +	u8 dac_volume_min; +	u8 dac_volume_max; +	u8 misc_flags; +	u8 function_flags; +	u16 dac_i2s_format; +	u16 adc_i2s_format; +};  struct oxygen {  	unsigned long addr; @@ -61,7 +102,6 @@ struct oxygen {  	struct pci_dev *pci;  	struct snd_rawmidi *midi;  	int irq; -	const struct oxygen_model *model;  	void *model_data;  	unsigned int interrupt_mask;  	u8 dac_volume[8]; @@ -86,46 +126,16 @@ struct oxygen {  		__le32 _32[OXYGEN_IO_SIZE / 4];  	} saved_registers;  	u16 saved_ac97_registers[2][0x40]; -}; - -struct oxygen_model { -	const char *shortname; -	const char *longname; -	const char *chip; -	struct module *owner; -	void (*init)(struct oxygen *chip); -	int (*control_filter)(struct snd_kcontrol_new *template); -	int (*mixer_init)(struct oxygen *chip); -	void (*cleanup)(struct oxygen *chip); -	void (*suspend)(struct oxygen *chip); -	void (*resume)(struct oxygen *chip); -	void (*pcm_hardware_filter)(unsigned int channel, -				    struct snd_pcm_hardware *hardware); -	void (*set_dac_params)(struct oxygen *chip, -			       struct snd_pcm_hw_params *params); -	void (*set_adc_params)(struct oxygen *chip, -			       struct snd_pcm_hw_params *params); -	void (*update_dac_volume)(struct oxygen *chip); -	void (*update_dac_mute)(struct oxygen *chip); -	void (*gpio_changed)(struct oxygen *chip); -	void (*ac97_switch)(struct oxygen *chip, -			    unsigned int reg, unsigned int mute); -	const unsigned int *dac_tlv; -	size_t model_data_size; -	unsigned int pcm_dev_cfg; -	u8 dac_channels; -	u8 dac_volume_min; -	u8 dac_volume_max; -	u8 misc_flags; -	u8 function_flags; -	u16 dac_i2s_format; -	u16 adc_i2s_format; +	unsigned int uart_input_count; +	u8 uart_input[32]; +	struct oxygen_model model;  };  /* oxygen_lib.c */  int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, -		     const struct oxygen_model *model); +		     const struct oxygen_model *model, +		     unsigned long driver_data);  void oxygen_pci_remove(struct pci_dev *pci);  #ifdef CONFIG_PM  int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state); @@ -167,6 +177,9 @@ void oxygen_write_ac97_masked(struct oxygen *chip, unsigned int codec,  void oxygen_write_spi(struct oxygen *chip, u8 control, unsigned int data);  void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data); +void oxygen_reset_uart(struct oxygen *chip); +void oxygen_write_uart(struct oxygen *chip, u8 data); +  static inline void oxygen_set_bits8(struct oxygen *chip,  				    unsigned int reg, u8 value)  { diff --git a/sound/pci/oxygen/oxygen_io.c b/sound/pci/oxygen/oxygen_io.c index 83f135f..3126c4b 100644 --- a/sound/pci/oxygen/oxygen_io.c +++ b/sound/pci/oxygen/oxygen_io.c @@ -20,6 +20,7 @@  #include <linux/delay.h>  #include <linux/sched.h>  #include <sound/core.h> +#include <sound/mpu401.h>  #include <asm/io.h>  #include "oxygen.h" @@ -232,3 +233,24 @@ void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)  		      device | OXYGEN_2WIRE_DIR_WRITE);  }  EXPORT_SYMBOL(oxygen_write_i2c); + +static void _write_uart(struct oxygen *chip, unsigned int port, u8 data) +{ +	if (oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_TX_FULL) +		msleep(1); +	oxygen_write8(chip, OXYGEN_MPU401 + port, data); +} + +void oxygen_reset_uart(struct oxygen *chip) +{ +	_write_uart(chip, 1, MPU401_RESET); +	msleep(1); /* wait for ACK */ +	_write_uart(chip, 1, MPU401_ENTER_UART); +} +EXPORT_SYMBOL(oxygen_reset_uart); + +void oxygen_write_uart(struct oxygen *chip, u8 data) +{ +	_write_uart(chip, 0, data); +} +EXPORT_SYMBOL(oxygen_write_uart); diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c index 22f3785..84f481d 100644 --- a/sound/pci/oxygen/oxygen_lib.c +++ b/sound/pci/oxygen/oxygen_lib.c @@ -35,6 +35,30 @@ MODULE_DESCRIPTION("C-Media CMI8788 helper library");  MODULE_LICENSE("GPL v2"); +static inline int oxygen_uart_input_ready(struct oxygen *chip) +{ +	return !(oxygen_read8(chip, OXYGEN_MPU401 + 1) & MPU401_RX_EMPTY); +} + +static void oxygen_read_uart(struct oxygen *chip) +{ +	if (unlikely(!oxygen_uart_input_ready(chip))) { +		/* no data, but read it anyway to clear the interrupt */ +		oxygen_read8(chip, OXYGEN_MPU401); +		return; +	} +	do { +		u8 data = oxygen_read8(chip, OXYGEN_MPU401); +		if (data == MPU401_ACK) +			continue; +		if (chip->uart_input_count >= ARRAY_SIZE(chip->uart_input)) +			chip->uart_input_count = 0; +		chip->uart_input[chip->uart_input_count++] = data; +	} while (oxygen_uart_input_ready(chip)); +	if (chip->model.uart_input) +		chip->model.uart_input(chip); +} +  static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)  {  	struct oxygen *chip = dev_id; @@ -87,8 +111,12 @@ static irqreturn_t oxygen_interrupt(int dummy, void *dev_id)  	if (status & OXYGEN_INT_GPIO)  		schedule_work(&chip->gpio_work); -	if ((status & OXYGEN_INT_MIDI) && chip->midi) -		snd_mpu401_uart_interrupt(0, chip->midi->private_data); +	if (status & OXYGEN_INT_MIDI) { +		if (chip->midi) +			snd_mpu401_uart_interrupt(0, chip->midi->private_data); +		else +			oxygen_read_uart(chip); +	}  	if (status & OXYGEN_INT_AC97)  		wake_up(&chip->ac97_waitqueue); @@ -161,8 +189,8 @@ static void oxygen_gpio_changed(struct work_struct *work)  {  	struct oxygen *chip = container_of(work, struct oxygen, gpio_work); -	if (chip->model->gpio_changed) -		chip->model->gpio_changed(chip); +	if (chip->model.gpio_changed) +		chip->model.gpio_changed(chip);  }  #ifdef CONFIG_PROC_FS @@ -221,7 +249,7 @@ static void oxygen_init(struct oxygen *chip)  	chip->dac_routing = 1;  	for (i = 0; i < 8; ++i) -		chip->dac_volume[i] = chip->model->dac_volume_min; +		chip->dac_volume[i] = chip->model.dac_volume_min;  	chip->dac_mute = 1;  	chip->spdif_playback_enable = 1;  	chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL | @@ -243,7 +271,7 @@ static void oxygen_init(struct oxygen *chip)  	oxygen_write8_masked(chip, OXYGEN_FUNCTION,  			     OXYGEN_FUNCTION_RESET_CODEC | -			     chip->model->function_flags, +			     chip->model.function_flags,  			     OXYGEN_FUNCTION_RESET_CODEC |  			     OXYGEN_FUNCTION_2WIRE_SPI_MASK |  			     OXYGEN_FUNCTION_ENABLE_SPI_4_5); @@ -255,7 +283,7 @@ static void oxygen_init(struct oxygen *chip)  		      OXYGEN_DMA_MULTICH_BURST_8);  	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, 0);  	oxygen_write8_masked(chip, OXYGEN_MISC, -			     chip->model->misc_flags, +			     chip->model.misc_flags,  			     OXYGEN_MISC_WRITE_PCI_SUBID |  			     OXYGEN_MISC_REC_C_FROM_SPDIF |  			     OXYGEN_MISC_REC_B_FROM_AC97 | @@ -270,21 +298,21 @@ static void oxygen_init(struct oxygen *chip)  		      (OXYGEN_FORMAT_16 << OXYGEN_MULTICH_FORMAT_SHIFT));  	oxygen_write8(chip, OXYGEN_REC_CHANNELS, OXYGEN_REC_CHANNELS_2_2_2);  	oxygen_write16(chip, OXYGEN_I2S_MULTICH_FORMAT, -		       OXYGEN_RATE_48000 | chip->model->dac_i2s_format | +		       OXYGEN_RATE_48000 | chip->model.dac_i2s_format |  		       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |  		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64); -	if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) +	if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)  		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, -			       OXYGEN_RATE_48000 | chip->model->adc_i2s_format | +			       OXYGEN_RATE_48000 | chip->model.adc_i2s_format |  			       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |  			       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);  	else  		oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,  			       OXYGEN_I2S_MASTER | OXYGEN_I2S_MUTE_MCLK); -	if (chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_2 | -					CAPTURE_2_FROM_I2S_2)) +	if (chip->model.device_config & (CAPTURE_0_FROM_I2S_2 | +					 CAPTURE_2_FROM_I2S_2))  		oxygen_write16(chip, OXYGEN_I2S_B_FORMAT, -			       OXYGEN_RATE_48000 | chip->model->adc_i2s_format | +			       OXYGEN_RATE_48000 | chip->model.adc_i2s_format |  			       OXYGEN_I2S_MCLK_256 | OXYGEN_I2S_BITS_16 |  			       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);  	else @@ -295,7 +323,7 @@ static void oxygen_init(struct oxygen *chip)  	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,  			    OXYGEN_SPDIF_OUT_ENABLE |  			    OXYGEN_SPDIF_LOOPBACK); -	if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) +	if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)  		oxygen_write32_masked(chip, OXYGEN_SPDIF_CONTROL,  				      OXYGEN_SPDIF_SENSE_MASK |  				      OXYGEN_SPDIF_LOCK_MASK | @@ -417,14 +445,15 @@ static void oxygen_card_free(struct snd_card *card)  	if (chip->irq >= 0)  		free_irq(chip->irq, chip);  	flush_scheduled_work(); -	chip->model->cleanup(chip); +	chip->model.cleanup(chip);  	mutex_destroy(&chip->mutex);  	pci_release_regions(chip->pci);  	pci_disable_device(chip->pci);  }  int oxygen_pci_probe(struct pci_dev *pci, int index, char *id, -		     const struct oxygen_model *model) +		     const struct oxygen_model *model, +		     unsigned long driver_data)  {  	struct snd_card *card;  	struct oxygen *chip; @@ -439,7 +468,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	chip->card = card;  	chip->pci = pci;  	chip->irq = -1; -	chip->model = model; +	chip->model = *model;  	chip->model_data = chip + 1;  	spin_lock_init(&chip->reg_lock);  	mutex_init(&chip->mutex); @@ -470,23 +499,28 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	snd_card_set_dev(card, &pci->dev);  	card->private_free = oxygen_card_free; +	if (chip->model.probe) { +		err = chip->model.probe(chip, driver_data); +		if (err < 0) +			goto err_card; +	}  	oxygen_init(chip); -	model->init(chip); +	chip->model.init(chip);  	err = request_irq(pci->irq, oxygen_interrupt, IRQF_SHARED, -			  model->chip, chip); +			  chip->model.chip, chip);  	if (err < 0) {  		snd_printk(KERN_ERR "cannot grab interrupt %d\n", pci->irq);  		goto err_card;  	}  	chip->irq = pci->irq; -	strcpy(card->driver, model->chip); -	strcpy(card->shortname, model->shortname); +	strcpy(card->driver, chip->model.chip); +	strcpy(card->shortname, chip->model.shortname);  	sprintf(card->longname, "%s (rev %u) at %#lx, irq %i", -		model->longname, chip->revision, chip->addr, chip->irq); -	strcpy(card->mixername, model->chip); -	snd_component_add(card, model->chip); +		chip->model.longname, chip->revision, chip->addr, chip->irq); +	strcpy(card->mixername, chip->model.chip); +	snd_component_add(card, chip->model.chip);  	err = oxygen_pcm_init(chip);  	if (err < 0) @@ -496,10 +530,15 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	if (err < 0)  		goto err_card; -	if (model->misc_flags & OXYGEN_MISC_MIDI) { +	if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) { +		unsigned int info_flags = MPU401_INFO_INTEGRATED; +		if (chip->model.device_config & MIDI_OUTPUT) +			info_flags |= MPU401_INFO_OUTPUT; +		if (chip->model.device_config & MIDI_INPUT) +			info_flags |= MPU401_INFO_INPUT;  		err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,  					  chip->addr + OXYGEN_MPU401, -					  MPU401_INFO_INTEGRATED, 0, 0, +					  info_flags, 0, 0,  					  &chip->midi);  		if (err < 0)  			goto err_card; @@ -508,7 +547,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,  	oxygen_proc_init(chip);  	spin_lock_irq(&chip->reg_lock); -	if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) +	if (chip->model.device_config & CAPTURE_1_FROM_SPDIF)  		chip->interrupt_mask |= OXYGEN_INT_SPDIF_IN_DETECT;  	if (chip->has_ac97_0 | chip->has_ac97_1)  		chip->interrupt_mask |= OXYGEN_INT_AC97; @@ -552,8 +591,8 @@ int oxygen_pci_suspend(struct pci_dev *pci, pm_message_t state)  		if (chip->streams[i])  			snd_pcm_suspend(chip->streams[i]); -	if (chip->model->suspend) -		chip->model->suspend(chip); +	if (chip->model.suspend) +		chip->model.suspend(chip);  	spin_lock_irq(&chip->reg_lock);  	saved_interrupt_mask = chip->interrupt_mask; @@ -624,8 +663,8 @@ int oxygen_pci_resume(struct pci_dev *pci)  	if (chip->has_ac97_1)  		oxygen_restore_ac97(chip, 1); -	if (chip->model->resume) -		chip->model->resume(chip); +	if (chip->model.resume) +		chip->model.resume(chip);  	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask); diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c index 05eb899..304da16 100644 --- a/sound/pci/oxygen/oxygen_mixer.c +++ b/sound/pci/oxygen/oxygen_mixer.c @@ -31,9 +31,9 @@ static int dac_volume_info(struct snd_kcontrol *ctl,  	struct oxygen *chip = ctl->private_data;  	info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; -	info->count = chip->model->dac_channels; -	info->value.integer.min = chip->model->dac_volume_min; -	info->value.integer.max = chip->model->dac_volume_max; +	info->count = chip->model.dac_channels; +	info->value.integer.min = chip->model.dac_volume_min; +	info->value.integer.max = chip->model.dac_volume_max;  	return 0;  } @@ -44,7 +44,7 @@ static int dac_volume_get(struct snd_kcontrol *ctl,  	unsigned int i;  	mutex_lock(&chip->mutex); -	for (i = 0; i < chip->model->dac_channels; ++i) +	for (i = 0; i < chip->model.dac_channels; ++i)  		value->value.integer.value[i] = chip->dac_volume[i];  	mutex_unlock(&chip->mutex);  	return 0; @@ -59,13 +59,13 @@ static int dac_volume_put(struct snd_kcontrol *ctl,  	changed = 0;  	mutex_lock(&chip->mutex); -	for (i = 0; i < chip->model->dac_channels; ++i) +	for (i = 0; i < chip->model.dac_channels; ++i)  		if (value->value.integer.value[i] != chip->dac_volume[i]) {  			chip->dac_volume[i] = value->value.integer.value[i];  			changed = 1;  		}  	if (changed) -		chip->model->update_dac_volume(chip); +		chip->model.update_dac_volume(chip);  	mutex_unlock(&chip->mutex);  	return changed;  } @@ -91,7 +91,7 @@ static int dac_mute_put(struct snd_kcontrol *ctl,  	changed = !value->value.integer.value[0] != chip->dac_mute;  	if (changed) {  		chip->dac_mute = !value->value.integer.value[0]; -		chip->model->update_dac_mute(chip); +		chip->model.update_dac_mute(chip);  	}  	mutex_unlock(&chip->mutex);  	return changed; @@ -103,7 +103,7 @@ static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)  		"Front", "Front+Surround", "Front+Surround+Back"  	};  	struct oxygen *chip = ctl->private_data; -	unsigned int count = 2 + (chip->model->dac_channels == 8); +	unsigned int count = 2 + (chip->model.dac_channels == 8);  	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	info->count = 1; @@ -172,7 +172,7 @@ void oxygen_update_dac_routing(struct oxygen *chip)  static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)  {  	struct oxygen *chip = ctl->private_data; -	unsigned int count = 2 + (chip->model->dac_channels == 8); +	unsigned int count = 2 + (chip->model.dac_channels == 8);  	int changed;  	mutex_lock(&chip->mutex); @@ -211,13 +211,13 @@ static unsigned int oxygen_spdif_rate(unsigned int oxygen_rate)  	case OXYGEN_RATE_64000:  		return 0xb << OXYGEN_SPDIF_CS_RATE_SHIFT;  	case OXYGEN_RATE_88200: -		return 0x8 << OXYGEN_SPDIF_CS_RATE_SHIFT; +		return IEC958_AES3_CON_FS_88200 << OXYGEN_SPDIF_CS_RATE_SHIFT;  	case OXYGEN_RATE_96000: -		return 0xa << OXYGEN_SPDIF_CS_RATE_SHIFT; +		return IEC958_AES3_CON_FS_96000 << OXYGEN_SPDIF_CS_RATE_SHIFT;  	case OXYGEN_RATE_176400: -		return 0xc << OXYGEN_SPDIF_CS_RATE_SHIFT; +		return IEC958_AES3_CON_FS_176400 << OXYGEN_SPDIF_CS_RATE_SHIFT;  	case OXYGEN_RATE_192000: -		return 0xe << OXYGEN_SPDIF_CS_RATE_SHIFT; +		return IEC958_AES3_CON_FS_192000 << OXYGEN_SPDIF_CS_RATE_SHIFT;  	}  } @@ -521,8 +521,8 @@ static void mute_ac97_ctl(struct oxygen *chip, unsigned int control)  	value = oxygen_read_ac97(chip, 0, priv_idx);  	if (!(value & 0x8000)) {  		oxygen_write_ac97(chip, 0, priv_idx, value | 0x8000); -		if (chip->model->ac97_switch) -			chip->model->ac97_switch(chip, priv_idx, 0x8000); +		if (chip->model.ac97_switch) +			chip->model.ac97_switch(chip, priv_idx, 0x8000);  		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,  			       &chip->controls[control]->id);  	} @@ -549,8 +549,8 @@ static int ac97_switch_put(struct snd_kcontrol *ctl,  	change = newreg != oldreg;  	if (change) {  		oxygen_write_ac97(chip, codec, index, newreg); -		if (codec == 0 && chip->model->ac97_switch) -			chip->model->ac97_switch(chip, index, newreg & 0x8000); +		if (codec == 0 && chip->model.ac97_switch) +			chip->model.ac97_switch(chip, index, newreg & 0x8000);  		if (index == AC97_LINE) {  			oxygen_write_ac97_masked(chip, 0, CM9780_GPIO_STATUS,  						 newreg & 0x8000 ? @@ -939,16 +939,16 @@ static int add_controls(struct oxygen *chip,  	for (i = 0; i < count; ++i) {  		template = controls[i]; -		if (chip->model->control_filter) { -			err = chip->model->control_filter(&template); +		if (chip->model.control_filter) { +			err = chip->model.control_filter(&template);  			if (err < 0)  				return err;  			if (err == 1)  				continue;  		}  		if (!strcmp(template.name, "Master Playback Volume") && -		    chip->model->dac_tlv) { -			template.tlv.p = chip->model->dac_tlv; +		    chip->model.dac_tlv) { +			template.tlv.p = chip->model.dac_tlv;  			template.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;  		}  		ctl = snd_ctl_new1(&template, chip); @@ -974,14 +974,14 @@ int oxygen_mixer_init(struct oxygen *chip)  	err = add_controls(chip, controls, ARRAY_SIZE(controls));  	if (err < 0)  		return err; -	if (chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF) { +	if (chip->model.device_config & CAPTURE_1_FROM_SPDIF) {  		err = add_controls(chip, spdif_input_controls,  				   ARRAY_SIZE(spdif_input_controls));  		if (err < 0)  			return err;  	}  	for (i = 0; i < ARRAY_SIZE(monitor_controls); ++i) { -		if (!(chip->model->pcm_dev_cfg & monitor_controls[i].pcm_dev)) +		if (!(chip->model.device_config & monitor_controls[i].pcm_dev))  			continue;  		err = add_controls(chip, monitor_controls[i].controls,  				   ARRAY_SIZE(monitor_controls[i].controls)); @@ -1000,5 +1000,5 @@ int oxygen_mixer_init(struct oxygen *chip)  		if (err < 0)  			return err;  	} -	return chip->model->mixer_init ? chip->model->mixer_init(chip) : 0; +	return chip->model.mixer_init ? chip->model.mixer_init(chip) : 0;  } diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c index c4ad65a..c262049 100644 --- a/sound/pci/oxygen/oxygen_pcm.c +++ b/sound/pci/oxygen/oxygen_pcm.c @@ -129,7 +129,7 @@ static int oxygen_open(struct snd_pcm_substream *substream,  	runtime->private_data = (void *)(uintptr_t)channel;  	if (channel == PCM_B && chip->has_ac97_1 && -	    (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1)) +	    (chip->model.device_config & CAPTURE_2_FROM_AC97_1))  		runtime->hw = oxygen_ac97_hardware;  	else  		runtime->hw = *oxygen_hardware[channel]; @@ -140,11 +140,11 @@ static int oxygen_open(struct snd_pcm_substream *substream,  		runtime->hw.rate_min = 44100;  		break;  	case PCM_MULTICH: -		runtime->hw.channels_max = chip->model->dac_channels; +		runtime->hw.channels_max = chip->model.dac_channels;  		break;  	} -	if (chip->model->pcm_hardware_filter) -		chip->model->pcm_hardware_filter(channel, &runtime->hw); +	if (chip->model.pcm_hardware_filter) +		chip->model.pcm_hardware_filter(channel, &runtime->hw);  	err = snd_pcm_hw_constraint_step(runtime, 0,  					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);  	if (err < 0) @@ -355,7 +355,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,  	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,  			      oxygen_rate(hw_params) |  			      oxygen_i2s_mclk(hw_params) | -			      chip->model->adc_i2s_format | +			      chip->model.adc_i2s_format |  			      oxygen_i2s_bits(hw_params),  			      OXYGEN_I2S_RATE_MASK |  			      OXYGEN_I2S_FORMAT_MASK | @@ -364,7 +364,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,  	spin_unlock_irq(&chip->reg_lock);  	mutex_lock(&chip->mutex); -	chip->model->set_adc_params(chip, hw_params); +	chip->model.set_adc_params(chip, hw_params);  	mutex_unlock(&chip->mutex);  	return 0;  } @@ -381,7 +381,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,  		return err;  	is_ac97 = chip->has_ac97_1 && -		(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); +		(chip->model.device_config & CAPTURE_2_FROM_AC97_1);  	spin_lock_irq(&chip->reg_lock);  	oxygen_write8_masked(chip, OXYGEN_REC_FORMAT, @@ -391,7 +391,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,  		oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,  				      oxygen_rate(hw_params) |  				      oxygen_i2s_mclk(hw_params) | -				      chip->model->adc_i2s_format | +				      chip->model.adc_i2s_format |  				      oxygen_i2s_bits(hw_params),  				      OXYGEN_I2S_RATE_MASK |  				      OXYGEN_I2S_FORMAT_MASK | @@ -401,7 +401,7 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,  	if (!is_ac97) {  		mutex_lock(&chip->mutex); -		chip->model->set_adc_params(chip, hw_params); +		chip->model.set_adc_params(chip, hw_params);  		mutex_unlock(&chip->mutex);  	}  	return 0; @@ -468,7 +468,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,  			     OXYGEN_MULTICH_FORMAT_MASK);  	oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,  			      oxygen_rate(hw_params) | -			      chip->model->dac_i2s_format | +			      chip->model.dac_i2s_format |  			      oxygen_i2s_bits(hw_params),  			      OXYGEN_I2S_RATE_MASK |  			      OXYGEN_I2S_FORMAT_MASK | @@ -478,7 +478,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,  	spin_unlock_irq(&chip->reg_lock);  	mutex_lock(&chip->mutex); -	chip->model->set_dac_params(chip, hw_params); +	chip->model.set_dac_params(chip, hw_params);  	mutex_unlock(&chip->mutex);  	return 0;  } @@ -657,25 +657,26 @@ int oxygen_pcm_init(struct oxygen *chip)  	int outs, ins;  	int err; -	outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S); -	ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 | -					     CAPTURE_0_FROM_I2S_2)); +	outs = !!(chip->model.device_config & PLAYBACK_0_TO_I2S); +	ins = !!(chip->model.device_config & (CAPTURE_0_FROM_I2S_1 | +					      CAPTURE_0_FROM_I2S_2));  	if (outs | ins) { -		err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm); +		err = snd_pcm_new(chip->card, "Multichannel", +				  0, outs, ins, &pcm);  		if (err < 0)  			return err;  		if (outs)  			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,  					&oxygen_multich_ops); -		if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1) +		if (chip->model.device_config & CAPTURE_0_FROM_I2S_1)  			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,  					&oxygen_rec_a_ops); -		else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2) +		else if (chip->model.device_config & CAPTURE_0_FROM_I2S_2)  			snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,  					&oxygen_rec_b_ops);  		pcm->private_data = chip;  		pcm->private_free = oxygen_pcm_free; -		strcpy(pcm->name, "Analog"); +		strcpy(pcm->name, "Multichannel");  		if (outs)  			snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,  						      SNDRV_DMA_TYPE_DEV, @@ -690,8 +691,8 @@ int oxygen_pcm_init(struct oxygen *chip)  						      BUFFER_BYTES_MAX);  	} -	outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF); -	ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF); +	outs = !!(chip->model.device_config & PLAYBACK_1_TO_SPDIF); +	ins = !!(chip->model.device_config & CAPTURE_1_FROM_SPDIF);  	if (outs | ins) {  		err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);  		if (err < 0) @@ -712,11 +713,11 @@ int oxygen_pcm_init(struct oxygen *chip)  	}  	if (chip->has_ac97_1) { -		outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1); -		ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1); +		outs = !!(chip->model.device_config & PLAYBACK_2_TO_AC97_1); +		ins = !!(chip->model.device_config & CAPTURE_2_FROM_AC97_1);  	} else {  		outs = 0; -		ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2); +		ins = !!(chip->model.device_config & CAPTURE_2_FROM_I2S_2);  	}  	if (outs | ins) {  		err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2", diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c index 01d7b75..98c6a8c 100644 --- a/sound/pci/oxygen/virtuoso.c +++ b/sound/pci/oxygen/virtuoso.c @@ -62,14 +62,66 @@   * AD0 <- 0   */ +/* + * Xonar HDAV1.3 (Deluxe) + * ---------------------- + * + * CMI8788: + * + * I²C <-> PCM1796 (front) + * + * GPI 0 <- external power present + * + * GPIO 0 -> enable output to speakers + * GPIO 2 -> M0 of CS5381 + * GPIO 3 -> M1 of CS5381 + * GPIO 8 -> route input jack to line-in (0) or mic-in (1) + * + * TXD -> HDMI controller + * RXD <- HDMI controller + * + * PCM1796 front: AD1,0 <- 0,0 + * + * no daughterboard + * ---------------- + * + * GPIO 4 <- 1 + * + * H6 daughterboard + * ---------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 0 + * + * I²C <-> PCM1796 (surround) + *     <-> PCM1796 (center/LFE) + *     <-> PCM1796 (back) + * + * PCM1796 surround:   AD1,0 <- 0,1 + * PCM1796 center/LFE: AD1,0 <- 1,0 + * PCM1796 back:       AD1,0 <- 1,1 + * + * unknown daughterboard + * --------------------- + * + * GPIO 4 <- 0 + * GPIO 5 <- 1 + * + * I²C <-> CS4362A (surround, center/LFE, back) + * + * CS4362A: AD0 <- 0 + */ +  #include <linux/pci.h>  #include <linux/delay.h>  #include <linux/mutex.h>  #include <sound/ac97_codec.h> +#include <sound/asoundef.h>  #include <sound/control.h>  #include <sound/core.h>  #include <sound/initval.h>  #include <sound/pcm.h> +#include <sound/pcm_params.h>  #include <sound/tlv.h>  #include "oxygen.h"  #include "cm9780.h" @@ -98,12 +150,15 @@ enum {  	MODEL_D2X,  	MODEL_D1,  	MODEL_DX, +	MODEL_HDAV,	/* without daughterboard */ +	MODEL_HDAV_H6,	/* with H6 daughterboard */  };  static struct pci_device_id xonar_ids[] __devinitdata = {  	{ OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },  	{ OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },  	{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X }, +	{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },  	{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },  	{ }  }; @@ -124,11 +179,18 @@ MODULE_DEVICE_TABLE(pci, xonar_ids);  #define GPIO_DX_FRONT_PANEL	0x0002  #define GPIO_DX_INPUT_ROUTE	0x0100 +#define GPIO_HDAV_DB_MASK	0x0030 +#define GPIO_HDAV_DB_H6		0x0000 +#define GPIO_HDAV_DB_XX		0x0020 + +#define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ADx=i, /W=0 */  #define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */  #define I2C_DEVICE_CS4362A	0x30	/* 001100, AD0=0, /W=0 */  struct xonar_data { +	unsigned int model;  	unsigned int anti_pop_delay; +	unsigned int dacs;  	u16 output_enable_bit;  	u8 ext_power_reg;  	u8 ext_power_int_reg; @@ -137,10 +199,13 @@ struct xonar_data {  	u8 pcm1796_oversampling;  	u8 cs4398_fm;  	u8 cs4362a_fm; +	u8 hdmi_params[5];  }; -static void pcm1796_write(struct oxygen *chip, unsigned int codec, -			  u8 reg, u8 value) +static void xonar_gpio_changed(struct oxygen *chip); + +static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec, +				     u8 reg, u8 value)  {  	/* maps ALSA channel pair number to SPI output */  	static const u8 codec_map[4] = { @@ -154,6 +219,22 @@ static void pcm1796_write(struct oxygen *chip, unsigned int codec,  			 (reg << 8) | value);  } +static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec, +				     u8 reg, u8 value) +{ +	oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value); +} + +static void pcm1796_write(struct oxygen *chip, unsigned int codec, +			  u8 reg, u8 value) +{ +	if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) == +	    OXYGEN_FUNCTION_SPI) +		pcm1796_write_spi(chip, codec, reg, value); +	else +		pcm1796_write_i2c(chip, codec, reg, value); +} +  static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)  {  	oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value); @@ -164,6 +245,24 @@ static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)  	oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);  } +static void hdmi_write_command(struct oxygen *chip, u8 command, +			       unsigned int count, const u8 *params) +{ +	unsigned int i; +	u8 checksum; + +	oxygen_write_uart(chip, 0xfb); +	oxygen_write_uart(chip, 0xef); +	oxygen_write_uart(chip, command); +	oxygen_write_uart(chip, count); +	for (i = 0; i < count; ++i) +		oxygen_write_uart(chip, params[i]); +	checksum = 0xfb + 0xef + command + count; +	for (i = 0; i < count; ++i) +		checksum += params[i]; +	oxygen_write_uart(chip, checksum); +} +  static void xonar_enable_output(struct oxygen *chip)  {  	struct xonar_data *data = chip->model_data; @@ -180,6 +279,7 @@ static void xonar_common_init(struct oxygen *chip)  		oxygen_set_bits8(chip, data->ext_power_int_reg,  				 data->ext_power_bit);  		chip->interrupt_mask |= OXYGEN_INT_GPIO; +		chip->model.gpio_changed = xonar_gpio_changed;  		data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)  				     & data->ext_power_bit);  	} @@ -193,9 +293,10 @@ static void xonar_common_init(struct oxygen *chip)  static void update_pcm1796_volume(struct oxygen *chip)  { +	struct xonar_data *data = chip->model_data;  	unsigned int i; -	for (i = 0; i < 4; ++i) { +	for (i = 0; i < data->dacs; ++i) {  		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);  		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);  	} @@ -203,13 +304,14 @@ static void update_pcm1796_volume(struct oxygen *chip)  static void update_pcm1796_mute(struct oxygen *chip)  { +	struct xonar_data *data = chip->model_data;  	unsigned int i;  	u8 value;  	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;  	if (chip->dac_mute)  		value |= PCM1796_MUTE; -	for (i = 0; i < 4; ++i) +	for (i = 0; i < data->dacs; ++i)  		pcm1796_write(chip, i, 18, value);  } @@ -218,7 +320,7 @@ static void pcm1796_init(struct oxygen *chip)  	struct xonar_data *data = chip->model_data;  	unsigned int i; -	for (i = 0; i < 4; ++i) { +	for (i = 0; i < data->dacs; ++i) {  		pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);  		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);  		pcm1796_write(chip, i, 21, 0); @@ -234,6 +336,13 @@ static void xonar_d2_init(struct oxygen *chip)  	data->anti_pop_delay = 300;  	data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;  	data->pcm1796_oversampling = PCM1796_OS_64; +	if (data->model == MODEL_D2X) { +		data->ext_power_reg = OXYGEN_GPIO_DATA; +		data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; +		data->ext_power_bit = GPIO_D2X_EXT_POWER; +		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, +				    GPIO_D2X_EXT_POWER); +	}  	pcm1796_init(chip); @@ -246,17 +355,6 @@ static void xonar_d2_init(struct oxygen *chip)  	snd_component_add(chip->card, "CS5381");  } -static void xonar_d2x_init(struct oxygen *chip) -{ -	struct xonar_data *data = chip->model_data; - -	data->ext_power_reg = OXYGEN_GPIO_DATA; -	data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK; -	data->ext_power_bit = GPIO_D2X_EXT_POWER; -	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER); -	xonar_d2_init(chip); -} -  static void update_cs4362a_volumes(struct oxygen *chip)  {  	u8 mute; @@ -324,6 +422,11 @@ static void xonar_d1_init(struct oxygen *chip)  	data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;  	data->cs4362a_fm = CS4362A_FM_SINGLE |  		CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L; +	if (data->model == MODEL_DX) { +		data->ext_power_reg = OXYGEN_GPI_DATA; +		data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK; +		data->ext_power_bit = GPI_DX_EXT_POWER; +	}  	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,  		       OXYGEN_2WIRE_LENGTH_8 | @@ -344,30 +447,86 @@ static void xonar_d1_init(struct oxygen *chip)  	snd_component_add(chip->card, "CS5361");  } -static void xonar_dx_init(struct oxygen *chip) +static void xonar_hdav_init(struct oxygen *chip)  {  	struct xonar_data *data = chip->model_data; +	u8 param; +	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS, +		       OXYGEN_2WIRE_LENGTH_8 | +		       OXYGEN_2WIRE_INTERRUPT_MASK | +		       OXYGEN_2WIRE_SPEED_FAST); + +	data->anti_pop_delay = 100; +	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;  	data->ext_power_reg = OXYGEN_GPI_DATA;  	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;  	data->ext_power_bit = GPI_DX_EXT_POWER; -	xonar_d1_init(chip); +	data->pcm1796_oversampling = PCM1796_OS_64; + +	pcm1796_init(chip); + +	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE); +	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE); + +	oxygen_reset_uart(chip); +	param = 0; +	hdmi_write_command(chip, 0x61, 1, ¶m); +	param = 1; +	hdmi_write_command(chip, 0x74, 1, ¶m); +	data->hdmi_params[1] = IEC958_AES3_CON_FS_48000; +	data->hdmi_params[4] = 1; +	hdmi_write_command(chip, 0x54, 5, data->hdmi_params); + +	xonar_common_init(chip); + +	snd_component_add(chip->card, "PCM1796"); +	snd_component_add(chip->card, "CS5381");  } -static void xonar_cleanup(struct oxygen *chip) +static void xonar_disable_output(struct oxygen *chip)  {  	struct xonar_data *data = chip->model_data;  	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);  } +static void xonar_d2_cleanup(struct oxygen *chip) +{ +	xonar_disable_output(chip); +} +  static void xonar_d1_cleanup(struct oxygen *chip)  { -	xonar_cleanup(chip); +	xonar_disable_output(chip);  	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);  	oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);  } +static void xonar_hdav_cleanup(struct oxygen *chip) +{ +	u8 param = 0; + +	hdmi_write_command(chip, 0x74, 1, ¶m); +	xonar_disable_output(chip); +} + +static void xonar_d2_suspend(struct oxygen *chip) +{ +	xonar_d2_cleanup(chip); +} + +static void xonar_d1_suspend(struct oxygen *chip) +{ +	xonar_d1_cleanup(chip); +} + +static void xonar_hdav_suspend(struct oxygen *chip) +{ +	xonar_hdav_cleanup(chip); +	msleep(2); +} +  static void xonar_d2_resume(struct oxygen *chip)  {  	pcm1796_init(chip); @@ -380,6 +539,33 @@ static void xonar_d1_resume(struct oxygen *chip)  	xonar_enable_output(chip);  } +static void xonar_hdav_resume(struct oxygen *chip) +{ +	struct xonar_data *data = chip->model_data; +	u8 param; + +	oxygen_reset_uart(chip); +	param = 0; +	hdmi_write_command(chip, 0x61, 1, ¶m); +	param = 1; +	hdmi_write_command(chip, 0x74, 1, ¶m); +	hdmi_write_command(chip, 0x54, 5, data->hdmi_params); +	pcm1796_init(chip); +	xonar_enable_output(chip); +} + +static void xonar_hdav_pcm_hardware_filter(unsigned int channel, +					   struct snd_pcm_hardware *hardware) +{ +	if (channel == PCM_MULTICH) { +		hardware->rates = SNDRV_PCM_RATE_44100 | +				  SNDRV_PCM_RATE_48000 | +				  SNDRV_PCM_RATE_96000 | +				  SNDRV_PCM_RATE_192000; +		hardware->rate_min = 44100; +	} +} +  static void set_pcm1796_params(struct oxygen *chip,  			       struct snd_pcm_hw_params *params)  { @@ -388,7 +574,7 @@ static void set_pcm1796_params(struct oxygen *chip,  	data->pcm1796_oversampling =  		params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64; -	for (i = 0; i < 4; ++i) +	for (i = 0; i < data->dacs; ++i)  		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);  } @@ -430,6 +616,42 @@ static void set_cs43xx_params(struct oxygen *chip,  	cs4362a_write(chip, 0x0c, data->cs4362a_fm);  } +static void set_hdmi_params(struct oxygen *chip, +			    struct snd_pcm_hw_params *params) +{ +	struct xonar_data *data = chip->model_data; + +	data->hdmi_params[0] = 0; /* 1 = non-audio */ +	switch (params_rate(params)) { +	case 44100: +		data->hdmi_params[1] = IEC958_AES3_CON_FS_44100; +		break; +	case 48000: +		data->hdmi_params[1] = IEC958_AES3_CON_FS_48000; +		break; +	default: /* 96000 */ +		data->hdmi_params[1] = IEC958_AES3_CON_FS_96000; +		break; +	case 192000: +		data->hdmi_params[1] = IEC958_AES3_CON_FS_192000; +		break; +	} +	data->hdmi_params[2] = params_channels(params) / 2 - 1; +	if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE) +		data->hdmi_params[3] = 0; +	else +		data->hdmi_params[3] = 0xc0; +	data->hdmi_params[4] = 1; /* ? */ +	hdmi_write_command(chip, 0x54, 5, data->hdmi_params); +} + +static void set_hdav_params(struct oxygen *chip, +			    struct snd_pcm_hw_params *params) +{ +	set_pcm1796_params(chip, params); +	set_hdmi_params(chip, params); +} +  static void xonar_gpio_changed(struct oxygen *chip)  {  	struct xonar_data *data = chip->model_data; @@ -449,29 +671,43 @@ static void xonar_gpio_changed(struct oxygen *chip)  	}  } -static int alt_switch_get(struct snd_kcontrol *ctl, -			  struct snd_ctl_elem_value *value) +static void xonar_hdav_uart_input(struct oxygen *chip) +{ +	if (chip->uart_input_count >= 2 && +	    chip->uart_input[chip->uart_input_count - 2] == 'O' && +	    chip->uart_input[chip->uart_input_count - 1] == 'K') { +		printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:"); +		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, +				     chip->uart_input, chip->uart_input_count); +		chip->uart_input_count = 0; +	} +} + +static int gpio_bit_switch_get(struct snd_kcontrol *ctl, +			       struct snd_ctl_elem_value *value)  {  	struct oxygen *chip = ctl->private_data; +	u16 bit = ctl->private_value;  	value->value.integer.value[0] = -		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_D2_ALT); +		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);  	return 0;  } -static int alt_switch_put(struct snd_kcontrol *ctl, -			  struct snd_ctl_elem_value *value) +static int gpio_bit_switch_put(struct snd_kcontrol *ctl, +			       struct snd_ctl_elem_value *value)  {  	struct oxygen *chip = ctl->private_data; +	u16 bit = ctl->private_value;  	u16 old_bits, new_bits;  	int changed;  	spin_lock_irq(&chip->reg_lock);  	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);  	if (value->value.integer.value[0]) -		new_bits = old_bits | GPIO_D2_ALT; +		new_bits = old_bits | bit;  	else -		new_bits = old_bits & ~GPIO_D2_ALT; +		new_bits = old_bits & ~bit;  	changed = new_bits != old_bits;  	if (changed)  		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits); @@ -483,47 +719,22 @@ static const struct snd_kcontrol_new alt_switch = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  	.name = "Analog Loopback Switch",  	.info = snd_ctl_boolean_mono_info, -	.get = alt_switch_get, -	.put = alt_switch_put, +	.get = gpio_bit_switch_get, +	.put = gpio_bit_switch_put, +	.private_value = GPIO_D2_ALT,  }; -static int front_panel_get(struct snd_kcontrol *ctl, -			   struct snd_ctl_elem_value *value) -{ -	struct oxygen *chip = ctl->private_data; - -	value->value.integer.value[0] = -		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DX_FRONT_PANEL); -	return 0; -} - -static int front_panel_put(struct snd_kcontrol *ctl, -			   struct snd_ctl_elem_value *value) -{ -	struct oxygen *chip = ctl->private_data; -	u16 old_reg, new_reg; - -	spin_lock_irq(&chip->reg_lock); -	old_reg = oxygen_read16(chip, OXYGEN_GPIO_DATA); -	if (value->value.integer.value[0]) -		new_reg = old_reg | GPIO_DX_FRONT_PANEL; -	else -		new_reg = old_reg & ~GPIO_DX_FRONT_PANEL; -	oxygen_write16(chip, OXYGEN_GPIO_DATA, new_reg); -	spin_unlock_irq(&chip->reg_lock); -	return old_reg != new_reg; -} -  static const struct snd_kcontrol_new front_panel_switch = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  	.name = "Front Panel Switch",  	.info = snd_ctl_boolean_mono_info, -	.get = front_panel_get, -	.put = front_panel_put, +	.get = gpio_bit_switch_get, +	.put = gpio_bit_switch_put, +	.private_value = GPIO_DX_FRONT_PANEL,  }; -static void xonar_d1_ac97_switch(struct oxygen *chip, -				 unsigned int reg, unsigned int mute) +static void xonar_line_mic_ac97_switch(struct oxygen *chip, +				       unsigned int reg, unsigned int mute)  {  	if (reg == AC97_LINE) {  		spin_lock_irq(&chip->reg_lock); @@ -552,7 +763,7 @@ static int xonar_d1_control_filter(struct snd_kcontrol_new *template)  	return 0;  } -static int xonar_mixer_init(struct oxygen *chip) +static int xonar_d2_mixer_init(struct oxygen *chip)  {  	return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));  } @@ -562,130 +773,147 @@ static int xonar_d1_mixer_init(struct oxygen *chip)  	return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));  } -static const struct oxygen_model xonar_models[] = { -	[MODEL_D2] = { -		.shortname = "Xonar D2", -		.longname = "Asus Virtuoso 200", -		.chip = "AV200", -		.owner = THIS_MODULE, -		.init = xonar_d2_init, -		.control_filter = xonar_d2_control_filter, -		.mixer_init = xonar_mixer_init, -		.cleanup = xonar_cleanup, -		.suspend = xonar_cleanup, -		.resume = xonar_d2_resume, -		.set_dac_params = set_pcm1796_params, -		.set_adc_params = set_cs53x1_params, -		.update_dac_volume = update_pcm1796_volume, -		.update_dac_mute = update_pcm1796_mute, -		.dac_tlv = pcm1796_db_scale, -		.model_data_size = sizeof(struct xonar_data), -		.pcm_dev_cfg = PLAYBACK_0_TO_I2S | -			       PLAYBACK_1_TO_SPDIF | -			       CAPTURE_0_FROM_I2S_2 | -			       CAPTURE_1_FROM_SPDIF, -		.dac_channels = 8, -		.dac_volume_min = 0x0f, -		.dac_volume_max = 0xff, -		.misc_flags = OXYGEN_MISC_MIDI, -		.function_flags = OXYGEN_FUNCTION_SPI | -				  OXYGEN_FUNCTION_ENABLE_SPI_4_5, -		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -	}, -	[MODEL_D2X] = { -		.shortname = "Xonar D2X", -		.longname = "Asus Virtuoso 200", -		.chip = "AV200", -		.owner = THIS_MODULE, -		.init = xonar_d2x_init, -		.control_filter = xonar_d2_control_filter, -		.mixer_init = xonar_mixer_init, -		.cleanup = xonar_cleanup, -		.suspend = xonar_cleanup, -		.resume = xonar_d2_resume, -		.set_dac_params = set_pcm1796_params, -		.set_adc_params = set_cs53x1_params, -		.update_dac_volume = update_pcm1796_volume, -		.update_dac_mute = update_pcm1796_mute, -		.gpio_changed = xonar_gpio_changed, -		.dac_tlv = pcm1796_db_scale, -		.model_data_size = sizeof(struct xonar_data), -		.pcm_dev_cfg = PLAYBACK_0_TO_I2S | -			       PLAYBACK_1_TO_SPDIF | -			       CAPTURE_0_FROM_I2S_2 | -			       CAPTURE_1_FROM_SPDIF, -		.dac_channels = 8, -		.dac_volume_min = 0x0f, -		.dac_volume_max = 0xff, -		.misc_flags = OXYGEN_MISC_MIDI, -		.function_flags = OXYGEN_FUNCTION_SPI | -				  OXYGEN_FUNCTION_ENABLE_SPI_4_5, -		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -	}, -	[MODEL_D1] = { -		.shortname = "Xonar D1", -		.longname = "Asus Virtuoso 100", -		.chip = "AV200", -		.owner = THIS_MODULE, -		.init = xonar_d1_init, -		.control_filter = xonar_d1_control_filter, -		.mixer_init = xonar_d1_mixer_init, -		.cleanup = xonar_d1_cleanup, -		.suspend = xonar_d1_cleanup, -		.resume = xonar_d1_resume, -		.set_dac_params = set_cs43xx_params, -		.set_adc_params = set_cs53x1_params, -		.update_dac_volume = update_cs43xx_volume, -		.update_dac_mute = update_cs43xx_mute, -		.ac97_switch = xonar_d1_ac97_switch, -		.dac_tlv = cs4362a_db_scale, -		.model_data_size = sizeof(struct xonar_data), -		.pcm_dev_cfg = PLAYBACK_0_TO_I2S | -			       PLAYBACK_1_TO_SPDIF | -			       CAPTURE_0_FROM_I2S_2, -		.dac_channels = 8, -		.dac_volume_min = 0, -		.dac_volume_max = 127, -		.function_flags = OXYGEN_FUNCTION_2WIRE, -		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -	}, -	[MODEL_DX] = { -		.shortname = "Xonar DX", -		.longname = "Asus Virtuoso 100", -		.chip = "AV200", -		.owner = THIS_MODULE, -		.init = xonar_dx_init, -		.control_filter = xonar_d1_control_filter, -		.mixer_init = xonar_d1_mixer_init, -		.cleanup = xonar_d1_cleanup, -		.suspend = xonar_d1_cleanup, -		.resume = xonar_d1_resume, -		.set_dac_params = set_cs43xx_params, -		.set_adc_params = set_cs53x1_params, -		.update_dac_volume = update_cs43xx_volume, -		.update_dac_mute = update_cs43xx_mute, -		.gpio_changed = xonar_gpio_changed, -		.ac97_switch = xonar_d1_ac97_switch, -		.dac_tlv = cs4362a_db_scale, -		.model_data_size = sizeof(struct xonar_data), -		.pcm_dev_cfg = PLAYBACK_0_TO_I2S | -			       PLAYBACK_1_TO_SPDIF | -			       CAPTURE_0_FROM_I2S_2, -		.dac_channels = 8, -		.dac_volume_min = 0, -		.dac_volume_max = 127, -		.function_flags = OXYGEN_FUNCTION_2WIRE, -		.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -		.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, -	}, +static int xonar_model_probe(struct oxygen *chip, unsigned long driver_data) +{ +	static const char *const names[] = { +		[MODEL_D1]	= "Xonar D1", +		[MODEL_DX]	= "Xonar DX", +		[MODEL_D2]	= "Xonar D2", +		[MODEL_D2X]	= "Xonar D2X", +		[MODEL_HDAV]	= "Xonar HDAV1.3", +		[MODEL_HDAV_H6]	= "Xonar HDAV1.3+H6", +	}; +	static const u8 dacs[] = { +		[MODEL_D1]	= 2, +		[MODEL_DX]	= 2, +		[MODEL_D2]	= 4, +		[MODEL_D2X]	= 4, +		[MODEL_HDAV]	= 1, +		[MODEL_HDAV_H6]	= 4, +	}; +	struct xonar_data *data = chip->model_data; + +	data->model = driver_data; +	if (data->model == MODEL_HDAV) { +		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, +				    GPIO_HDAV_DB_MASK); +		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & +			GPIO_HDAV_DB_MASK) { +		case GPIO_HDAV_DB_H6: +			data->model = MODEL_HDAV_H6; +			break; +		case GPIO_HDAV_DB_XX: +			snd_printk(KERN_ERR "unknown daughterboard\n"); +			return -ENODEV; +		} +	} + +	data->dacs = dacs[data->model]; +	chip->model.shortname = names[data->model]; +	return 0; +} + +static const struct oxygen_model model_xonar_d2 = { +	.longname = "Asus Virtuoso 200", +	.chip = "AV200", +	.owner = THIS_MODULE, +	.probe = xonar_model_probe, +	.init = xonar_d2_init, +	.control_filter = xonar_d2_control_filter, +	.mixer_init = xonar_d2_mixer_init, +	.cleanup = xonar_d2_cleanup, +	.suspend = xonar_d2_suspend, +	.resume = xonar_d2_resume, +	.set_dac_params = set_pcm1796_params, +	.set_adc_params = set_cs53x1_params, +	.update_dac_volume = update_pcm1796_volume, +	.update_dac_mute = update_pcm1796_mute, +	.dac_tlv = pcm1796_db_scale, +	.model_data_size = sizeof(struct xonar_data), +	.device_config = PLAYBACK_0_TO_I2S | +			 PLAYBACK_1_TO_SPDIF | +			 CAPTURE_0_FROM_I2S_2 | +			 CAPTURE_1_FROM_SPDIF | +			 MIDI_OUTPUT | +			 MIDI_INPUT, +	.dac_channels = 8, +	.dac_volume_min = 0x0f, +	.dac_volume_max = 0xff, +	.misc_flags = OXYGEN_MISC_MIDI, +	.function_flags = OXYGEN_FUNCTION_SPI | +			  OXYGEN_FUNCTION_ENABLE_SPI_4_5, +	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static const struct oxygen_model model_xonar_d1 = { +	.longname = "Asus Virtuoso 100", +	.chip = "AV200", +	.owner = THIS_MODULE, +	.probe = xonar_model_probe, +	.init = xonar_d1_init, +	.control_filter = xonar_d1_control_filter, +	.mixer_init = xonar_d1_mixer_init, +	.cleanup = xonar_d1_cleanup, +	.suspend = xonar_d1_suspend, +	.resume = xonar_d1_resume, +	.set_dac_params = set_cs43xx_params, +	.set_adc_params = set_cs53x1_params, +	.update_dac_volume = update_cs43xx_volume, +	.update_dac_mute = update_cs43xx_mute, +	.ac97_switch = xonar_line_mic_ac97_switch, +	.dac_tlv = cs4362a_db_scale, +	.model_data_size = sizeof(struct xonar_data), +	.device_config = PLAYBACK_0_TO_I2S | +			 PLAYBACK_1_TO_SPDIF | +			 CAPTURE_0_FROM_I2S_2, +	.dac_channels = 8, +	.dac_volume_min = 0, +	.dac_volume_max = 127, +	.function_flags = OXYGEN_FUNCTION_2WIRE, +	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +}; + +static const struct oxygen_model model_xonar_hdav = { +	.longname = "Asus Virtuoso 200", +	.chip = "AV200", +	.owner = THIS_MODULE, +	.probe = xonar_model_probe, +	.init = xonar_hdav_init, +	.cleanup = xonar_hdav_cleanup, +	.suspend = xonar_hdav_suspend, +	.resume = xonar_hdav_resume, +	.pcm_hardware_filter = xonar_hdav_pcm_hardware_filter, +	.set_dac_params = set_hdav_params, +	.set_adc_params = set_cs53x1_params, +	.update_dac_volume = update_pcm1796_volume, +	.update_dac_mute = update_pcm1796_mute, +	.uart_input = xonar_hdav_uart_input, +	.ac97_switch = xonar_line_mic_ac97_switch, +	.dac_tlv = pcm1796_db_scale, +	.model_data_size = sizeof(struct xonar_data), +	.device_config = PLAYBACK_0_TO_I2S | +			 PLAYBACK_1_TO_SPDIF | +			 CAPTURE_0_FROM_I2S_2, +	.dac_channels = 8, +	.dac_volume_min = 0x0f, +	.dac_volume_max = 0xff, +	.function_flags = OXYGEN_FUNCTION_2WIRE, +	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST, +	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,  };  static int __devinit xonar_probe(struct pci_dev *pci,  				 const struct pci_device_id *pci_id)  { +	static const struct oxygen_model *const models[] = { +		[MODEL_D1]	= &model_xonar_d1, +		[MODEL_DX]	= &model_xonar_d1, +		[MODEL_D2]	= &model_xonar_d2, +		[MODEL_D2X]	= &model_xonar_d2, +		[MODEL_HDAV]	= &model_xonar_hdav, +	};  	static int dev;  	int err; @@ -695,8 +923,10 @@ static int __devinit xonar_probe(struct pci_dev *pci,  		++dev;  		return -ENOENT;  	} +	BUG_ON(pci_id->driver_data >= ARRAY_SIZE(models));  	err = oxygen_pci_probe(pci, index[dev], id[dev], -			       &xonar_models[pci_id->driver_data]); +			       models[pci_id->driver_data], +			       pci_id->driver_data);  	if (err >= 0)  		++dev;  	return err; diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 2c7e253..0e06c6c 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -464,7 +464,8 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)  	pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);  	pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0); -	snd_assert(subs->runtime->dma_bytes < 0x200000);	/* max buffer size is 2 MByte */ +	/* max buffer size is 2 MByte */ +	snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);  	rmh.cmd[1] = subs->runtime->dma_bytes * 8;		/* size in bits */  	rmh.cmd[2] = subs->runtime->dma_addr >> 24;		/* most significant byte */  	rmh.cmd[2] |= 1<<19;					/* this is a circular buffer */ @@ -1228,7 +1229,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id  		return -ENOMEM;  	} -	snd_assert(pci_id->driver_data < PCI_ID_LAST, return -ENODEV); +	if (snd_BUG_ON(pci_id->driver_data >= PCI_ID_LAST)) +		return -ENODEV;  	card_name = pcxhr_board_params[pci_id->driver_data].board_name;  	mgr->playback_chips = pcxhr_board_params[pci_id->driver_data].playback_chips;  	mgr->capture_chips  = pcxhr_board_params[pci_id->driver_data].capture_chips; diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index 000e6fe..7143259 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -319,16 +319,20 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)  	const unsigned char *data;  	unsigned char dummy;  	/* check the length of boot image */ -	snd_assert(dsp->size > 0, return -EINVAL); -	snd_assert(dsp->size % 3 == 0, return -EINVAL); -	snd_assert(dsp->data, return -EINVAL); +	if (dsp->size <= 0) +		return -EINVAL; +	if (dsp->size % 3) +		return -EINVAL; +	if (snd_BUG_ON(!dsp->data)) +		return -EINVAL;  	/* transfert data buffer from PC to DSP */  	for (i = 0; i < dsp->size; i += 3) {  		data = dsp->data + i;  		if (i == 0) {  			/* test data header consistency */  			len = (unsigned int)((data[0]<<16) + (data[1]<<8) + data[2]); -			snd_assert((len==0) || (dsp->size == (len+2)*3), return -EINVAL); +			if (len && dsp->size != (len + 2) * 3) +				return -EINVAL;  		}  		/* wait DSP ready for new transfer */  		err = pcxhr_check_reg_bit(mgr, PCXHR_DSP_ISR, PCXHR_ISR_HI08_TRDY, @@ -389,7 +393,8 @@ int pcxhr_load_boot_binary(struct pcxhr_mgr *mgr, const struct firmware *boot)  	unsigned char dummy;  	/* send the hostport address to the DSP (only the upper 24 bit !) */ -	snd_assert((physaddr & 0xff) == 0, return -EINVAL); +	if (snd_BUG_ON(physaddr & 0xff)) +		return -EINVAL;  	PCXHR_OUTPL(mgr, PCXHR_PLX_MBOX1, (physaddr >> 8));  	err = pcxhr_send_it_dsp(mgr, PCXHR_IT_DOWNLOAD_BOOT, 0); @@ -570,7 +575,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)  	u32 data;  	unsigned char reg; -	snd_assert(rmh->cmd_len<PCXHR_SIZE_MAX_CMD, return -EINVAL); +	if (snd_BUG_ON(rmh->cmd_len >= PCXHR_SIZE_MAX_CMD)) +		return -EINVAL;  	err = pcxhr_send_it_dsp(mgr, PCXHR_IT_MESSAGE, 1);  	if (err) {  		snd_printk(KERN_ERR "pcxhr_send_message : ED_DSP_CRASHED\n"); @@ -677,7 +683,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)   */  void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd)  { -	snd_assert(cmd < CMD_LAST_INDEX, return); +	if (snd_BUG_ON(cmd >= CMD_LAST_INDEX)) +		return;  	rmh->cmd[0] = pcxhr_dsp_cmds[cmd].opcode;  	rmh->cmd_len = 1;  	rmh->stat_len = pcxhr_dsp_cmds[cmd].st_length; @@ -690,17 +697,17 @@ void pcxhr_set_pipe_cmd_params(struct pcxhr_rmh *rmh, int capture,  			       unsigned int param1, unsigned int param2,  			       unsigned int param3)  { -	snd_assert(param1 <= MASK_FIRST_FIELD); +	snd_BUG_ON(param1 > MASK_FIRST_FIELD);  	if (capture)  		rmh->cmd[0] |= 0x800;		/* COMMAND_RECORD_MASK */  	if (param1)  		rmh->cmd[0] |= (param1 << FIELD_SIZE);  	if (param2) { -		snd_assert(param2 <= MASK_FIRST_FIELD); +		snd_BUG_ON(param2 > MASK_FIRST_FIELD);  		rmh->cmd[0] |= param2;  	}  	if(param3) { -		snd_assert(param3 <= MASK_DSP_WORD); +		snd_BUG_ON(param3 > MASK_DSP_WORD);  		rmh->cmd[1] = param3;  		rmh->cmd_len = 2;  	} diff --git a/sound/pci/pcxhr/pcxhr_hwdep.c b/sound/pci/pcxhr/pcxhr_hwdep.c index d2f0432..96640d9 100644 --- a/sound/pci/pcxhr/pcxhr_hwdep.c +++ b/sound/pci/pcxhr/pcxhr_hwdep.c @@ -65,15 +65,18 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)  	if (err)  		return err;  	/* test 8 or 12 phys out */ -	snd_assert((rmh.stat[0] & MASK_FIRST_FIELD) == mgr->playback_chips*2, -		   return -EINVAL); +	if ((rmh.stat[0] & MASK_FIRST_FIELD) != mgr->playback_chips * 2) +		return -EINVAL;  	/* test 8 or 2 phys in */ -	snd_assert(((rmh.stat[0] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD) == -		   mgr->capture_chips * 2, return -EINVAL); +	if (((rmh.stat[0] >> (2 * FIELD_SIZE)) & MASK_FIRST_FIELD) != +	    mgr->capture_chips * 2) +		return -EINVAL;  	/* test max nb substream per board */ -	snd_assert((rmh.stat[1] & 0x5F) >= card_streams, return -EINVAL); +	if ((rmh.stat[1] & 0x5F) < card_streams) +		return -EINVAL;  	/* test max nb substream per pipe */ -	snd_assert(((rmh.stat[1]>>7)&0x5F) >= PCXHR_PLAYBACK_STREAMS, return -EINVAL); +	if (((rmh.stat[1] >> 7) & 0x5F) < PCXHR_PLAYBACK_STREAMS) +		return -EINVAL;  	pcxhr_init_rmh(&rmh, CMD_VERSION);  	/* firmware num for DSP */ diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 6a35962..e9f0706 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -865,7 +865,8 @@ static int sendcmd(struct cmdif *cif, u32 flags, u32 cmd, u32 parm,  	struct riptideport *hwport;  	struct cmdport *cmdport = NULL; -	snd_assert(cif, return -EINVAL); +	if (snd_BUG_ON(!cif)) +		return -EINVAL;  	hwport = cif->hwport;  	if (cif->errcnt > MAX_ERROR_COUNT) { @@ -1482,7 +1483,6 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)  {  	struct snd_riptide *chip = snd_pcm_substream_chip(substream);  	struct snd_pcm_runtime *runtime = substream->runtime; -	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);  	struct pcmhw *data = get_pcmhwdev(substream);  	struct cmdif *cif = chip->cif;  	unsigned char *lbuspath = NULL; @@ -1490,7 +1490,8 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)  	int err = 0;  	snd_pcm_format_t format; -	snd_assert(cif && data, return -EINVAL); +	if (snd_BUG_ON(!cif || !data)) +		return -EINVAL;  	snd_printdd("prepare id %d ch: %d f:0x%x r:%d\n", data->id,  		    runtime->channels, runtime->format, runtime->rate); @@ -1513,9 +1514,9 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)  			lbuspath = data->paths.stereo;  		break;  	} -	snd_printdd("use sgdlist at 0x%p and buffer at 0x%p\n", -		    data->sgdlist.area, sgbuf); -	if (data->sgdlist.area && sgbuf) { +	snd_printdd("use sgdlist at 0x%p\n", +		    data->sgdlist.area); +	if (data->sgdlist.area) {  		unsigned int i, j, size, pages, f, pt, period;  		struct sgd *c, *p = NULL; @@ -1533,6 +1534,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)  		pt = 0;  		j = 0;  		for (i = 0; i < pages; i++) { +			unsigned int ofs, addr;  			c = &data->sgdbuf[i];  			if (p)  				p->dwNextLink = cpu_to_le32(data->sgdlist.addr + @@ -1540,8 +1542,9 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)  							     sizeof(struct  								    sgd)));  			c->dwNextLink = cpu_to_le32(data->sgdlist.addr); -			c->dwSegPtrPhys = -			    cpu_to_le32(sgbuf->table[j].addr + pt); +			ofs = j << PAGE_SHIFT; +			addr = snd_pcm_sgbuf_get_addr(substream, ofs) + pt; +			c->dwSegPtrPhys = cpu_to_le32(addr);  			pt = (pt + f) % PAGE_SIZE;  			if (pt == 0)  				j++; @@ -1772,7 +1775,8 @@ snd_riptide_codec_write(struct snd_ac97 *ac97, unsigned short reg,  	union cmdret rptr = CMDRET_ZERO;  	int i = 0; -	snd_assert(cif, return); +	if (snd_BUG_ON(!cif)) +		return;  	snd_printdd("Write AC97 reg 0x%x 0x%x\n", reg, val);  	do { @@ -1790,7 +1794,8 @@ static unsigned short snd_riptide_codec_read(struct snd_ac97 *ac97,  	struct cmdif *cif = chip->cif;  	union cmdret rptr = CMDRET_ZERO; -	snd_assert(cif, return 0); +	if (snd_BUG_ON(!cif)) +		return 0;  	if (SEND_RACR(cif, reg, &rptr) != 0)  		SEND_RACR(cif, reg, &rptr); @@ -1804,7 +1809,8 @@ static int snd_riptide_initialize(struct snd_riptide *chip)  	unsigned int device_id;  	int err; -	snd_assert(chip, return -EINVAL); +	if (snd_BUG_ON(!chip)) +		return -EINVAL;  	cif = chip->cif;  	if (!cif) { @@ -1836,7 +1842,8 @@ static int snd_riptide_free(struct snd_riptide *chip)  {  	struct cmdif *cif; -	snd_assert(chip, return 0); +	if (!chip) +		return 0;  	if ((cif = chip->cif)) {  		SET_GRESET(cif->hwport); diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 4d6fbb3..d723543 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1036,7 +1036,7 @@ static void hdsp_set_dds_value(struct hdsp *hdsp, int rate)  	n = DDS_NUMERATOR;  	div64_32(&n, rate, &r);  	/* n should be less than 2^32 for being written to FREQ register */ -	snd_assert((n >> 32) == 0); +	snd_BUG_ON(n >> 32);  	/* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS  	   value to write it after a reset */  	hdsp->dds_value = n; @@ -3043,7 +3043,7 @@ static int snd_hdsp_get_adat_sync_check(struct snd_kcontrol *kcontrol, struct sn  	struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);  	offset = ucontrol->id.index - 1; -	snd_assert(offset >= 0); +	snd_BUG_ON(offset < 0);  	switch (hdsp->io_type) {  	case Digiface: @@ -3767,7 +3767,8 @@ static char *hdsp_channel_buffer_location(struct hdsp *hdsp,  {  	int mapped_channel; -        snd_assert(channel >= 0 && channel < hdsp->max_channels, return NULL); +        if (snd_BUG_ON(channel < 0 || channel >= hdsp->max_channels)) +		return NULL;  	if ((mapped_channel = hdsp->channel_map[channel]) < 0)  		return NULL; @@ -3784,10 +3785,12 @@ static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, int chann  	struct hdsp *hdsp = snd_pcm_substream_chip(substream);  	char *channel_buf; -	snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); +	if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4)) +		return -EINVAL;  	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	if (copy_from_user(channel_buf + pos * 4, src, count * 4))  		return -EFAULT;  	return count; @@ -3799,10 +3802,12 @@ static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, int channe  	struct hdsp *hdsp = snd_pcm_substream_chip(substream);  	char *channel_buf; -	snd_assert(pos + count <= HDSP_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); +	if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4)) +		return -EINVAL;  	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	if (copy_to_user(dst, channel_buf + pos * 4, count * 4))  		return -EFAULT;  	return count; @@ -3815,7 +3820,8 @@ static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel,  	char *channel_buf;  	channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	memset(channel_buf + pos * 4, 0, count * 4);  	return count;  } @@ -3927,7 +3933,8 @@ static int snd_hdsp_channel_info(struct snd_pcm_substream *substream,  	struct hdsp *hdsp = snd_pcm_substream_chip(substream);  	int mapped_channel; -	snd_assert(info->channel < hdsp->max_channels, return -EINVAL); +	if (snd_BUG_ON(info->channel >= hdsp->max_channels)) +		return -EINVAL;  	if ((mapped_channel = hdsp->channel_map[info->channel]) < 0)  		return -EINVAL; diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index ab423bc..98762f9 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -535,7 +535,8 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm * hdspm);  static int hdspm_update_simple_mixer_controls(struct hdspm * hdspm);  static int hdspm_autosync_ref(struct hdspm * hdspm);  static int snd_hdspm_set_defaults(struct hdspm * hdspm); -static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf, +static void hdspm_set_sgbuf(struct hdspm * hdspm, +			    struct snd_pcm_substream *substream,  			     unsigned int reg, int channels);  static inline int HDSPM_bit2freq(int n) @@ -845,7 +846,7 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)  	n = 110100480000000ULL;    /* Value checked for AES32 and MADI */  	div64_32(&n, rate, &r);  	/* n should be less than 2^32 for being written to FREQ register */ -	snd_assert((n >> 32) == 0); +	snd_BUG_ON(n >> 32);  	hdspm_write(hdspm, HDSPM_freqReg, (u32)n);  } @@ -2617,8 +2618,8 @@ static int snd_hdspm_get_playback_mixer(struct snd_kcontrol *kcontrol,  	channel = ucontrol->id.index - 1; -	snd_assert(channel >= 0 -		   || channel < HDSPM_MAX_CHANNELS, return -EINVAL); +	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) +		return -EINVAL;  	mapped_channel = hdspm->channel_map[channel];  	if (mapped_channel < 0) @@ -2652,8 +2653,8 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,  	channel = ucontrol->id.index - 1; -	snd_assert(channel >= 0 -		   || channel < HDSPM_MAX_CHANNELS, return -EINVAL); +	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) +		return -EINVAL;  	mapped_channel = hdspm->channel_map[channel];  	if (mapped_channel < 0) @@ -3496,8 +3497,8 @@ static char *hdspm_channel_buffer_location(struct hdspm * hdspm,  {  	int mapped_channel; -	snd_assert(channel >= 0 -		   || channel < HDSPM_MAX_CHANNELS, return NULL); +	if (snd_BUG_ON(channel < 0 || channel >= HDSPM_MAX_CHANNELS)) +		return NULL;  	mapped_channel = hdspm->channel_map[channel];  	if (mapped_channel < 0) @@ -3520,14 +3521,15 @@ static int snd_hdspm_playback_copy(struct snd_pcm_substream *substream,  	struct hdspm *hdspm = snd_pcm_substream_chip(substream);  	char *channel_buf; -	snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4, -		   return -EINVAL); +	if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) +		return -EINVAL;  	channel_buf =  		hdspm_channel_buffer_location(hdspm, substream->pstr->stream,  					      channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	return copy_from_user(channel_buf + pos * 4, src, count * 4);  } @@ -3539,13 +3541,14 @@ static int snd_hdspm_capture_copy(struct snd_pcm_substream *substream,  	struct hdspm *hdspm = snd_pcm_substream_chip(substream);  	char *channel_buf; -	snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4, -		   return -EINVAL); +	if (snd_BUG_ON(pos + count > HDSPM_CHANNEL_BUFFER_BYTES / 4)) +		return -EINVAL;  	channel_buf =  		hdspm_channel_buffer_location(hdspm, substream->pstr->stream,  					      channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	return copy_to_user(dst, channel_buf + pos * 4, count * 4);  } @@ -3559,7 +3562,8 @@ static int snd_hdspm_hw_silence(struct snd_pcm_substream *substream,  	channel_buf =  		hdspm_channel_buffer_location(hdspm, substream->pstr->stream,  					      channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	memset(channel_buf + pos * 4, 0, count * 4);  	return 0;  } @@ -3601,8 +3605,6 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,  	int i;  	pid_t this_pid;  	pid_t other_pid; -	struct snd_sg_buf *sgbuf; -  	spin_lock_irq(&hdspm->lock); @@ -3670,11 +3672,9 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,  	if (err < 0)  		return err; -	sgbuf = snd_pcm_substream_sgbuf(substream); -  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -		hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferOut, +		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferOut,  				params_channels(params));  		for (i = 0; i < params_channels(params); ++i) @@ -3685,7 +3685,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,  		snd_printdd("Allocated sample buffer for playback at %p\n",  				hdspm->playback_buffer);  	} else { -		hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn, +		hdspm_set_sgbuf(hdspm, substream, HDSPM_pageAddressBufferIn,  				params_channels(params));  		for (i = 0; i < params_channels(params); ++i) @@ -3700,7 +3700,7 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,  	   snd_printdd("Allocated sample buffer for %s at 0x%08X\n",  	   substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?  	   "playback" : "capture", -	   snd_pcm_sgbuf_get_addr(sgbuf, 0)); +	   snd_pcm_sgbuf_get_addr(substream, 0));  	 */  	/*  	snd_printdd("set_hwparams: %s %d Hz, %d channels, bs = %d\n", @@ -3744,7 +3744,8 @@ static int snd_hdspm_channel_info(struct snd_pcm_substream *substream,  	struct hdspm *hdspm = snd_pcm_substream_chip(substream);  	int mapped_channel; -	snd_assert(info->channel < HDSPM_MAX_CHANNELS, return -EINVAL); +	if (snd_BUG_ON(info->channel >= HDSPM_MAX_CHANNELS)) +		return -EINVAL;  	mapped_channel = hdspm->channel_map[info->channel];  	if (mapped_channel < 0) @@ -4249,13 +4250,14 @@ static int __devinit snd_hdspm_preallocate_memory(struct hdspm * hdspm)  	return 0;  } -static void hdspm_set_sgbuf(struct hdspm * hdspm, struct snd_sg_buf *sgbuf, +static void hdspm_set_sgbuf(struct hdspm * hdspm, +			    struct snd_pcm_substream *substream,  			     unsigned int reg, int channels)  {  	int i;  	for (i = 0; i < (channels * 16); i++)  		hdspm_write(hdspm, reg + 4 * i, -			    snd_pcm_sgbuf_get_addr(sgbuf, (size_t) 4096 * i)); +			    snd_pcm_sgbuf_get_addr(substream, 4096 * i));  }  /* ------------- ALSA Devices ---------------------------- */ diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index a123f0e..2570907 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -595,8 +595,6 @@ static void rme9652_set_thru(struct snd_rme9652 *rme9652, int channel, int enabl  	} else {  		int mapped_channel; -		snd_assert(channel == RME9652_NCHANNELS, return); -  		mapped_channel = rme9652->channel_map[channel];  		if (enable) { @@ -1893,7 +1891,8 @@ static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652,  {  	int mapped_channel; -        snd_assert(channel >= 0 || channel < RME9652_NCHANNELS, return NULL); +	if (snd_BUG_ON(channel < 0 || channel >= RME9652_NCHANNELS)) +		return NULL;  	if ((mapped_channel = rme9652->channel_map[channel]) < 0) {  		return NULL; @@ -1914,12 +1913,14 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int ch  	struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);  	char *channel_buf; -	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); +	if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4)) +		return -EINVAL;  	channel_buf = rme9652_channel_buffer_location (rme9652,  						       substream->pstr->stream,  						       channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	if (copy_from_user(channel_buf + pos * 4, src, count * 4))  		return -EFAULT;  	return count; @@ -1931,12 +1932,14 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int cha  	struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);  	char *channel_buf; -	snd_assert(pos + count <= RME9652_CHANNEL_BUFFER_BYTES / 4, return -EINVAL); +	if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4)) +		return -EINVAL;  	channel_buf = rme9652_channel_buffer_location (rme9652,  						       substream->pstr->stream,  						       channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	if (copy_to_user(dst, channel_buf + pos * 4, count * 4))  		return -EFAULT;  	return count; @@ -1951,7 +1954,8 @@ static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int chann  	channel_buf = rme9652_channel_buffer_location (rme9652,  						       substream->pstr->stream,  						       channel); -	snd_assert(channel_buf != NULL, return -EIO); +	if (snd_BUG_ON(!channel_buf)) +		return -EIO;  	memset(channel_buf + pos * 4, 0, count * 4);  	return count;  } @@ -2053,7 +2057,8 @@ static int snd_rme9652_channel_info(struct snd_pcm_substream *substream,  	struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream);  	int chn; -	snd_assert(info->channel < RME9652_NCHANNELS, return -EINVAL); +	if (snd_BUG_ON(info->channel >= RME9652_NCHANNELS)) +		return -EINVAL;  	if ((chn = rme9652->channel_map[info->channel]) < 0) {  		return -EINVAL; diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 0d3d305..cd408b8 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -534,8 +534,8 @@ static int snd_sonicvibes_hw_constraint_dac_rate(struct snd_pcm_hw_params *param  			params->rate_den = 1;  		} else {  			snd_sonicvibes_pll(rate, &r, &m, &n); -			snd_assert((SV_REFFREQUENCY % 16) == 0, return -EINVAL); -			snd_assert((SV_ADCMULT % 512) == 0, return -EINVAL); +			snd_BUG_ON(SV_REFFREQUENCY % 16); +			snd_BUG_ON(SV_ADCMULT % 512);  			params->rate_num = (SV_REFFREQUENCY/16) * (n+2) * r;  			params->rate_den = (SV_ADCMULT/512) * (m+2);  		} @@ -849,7 +849,8 @@ static int __devinit snd_sonicvibes_pcm(struct sonicvibes * sonic, int device, s  	if ((err = snd_pcm_new(sonic->card, "s3_86c617", device, 1, 1, &pcm)) < 0)  		return err; -	snd_assert(pcm != NULL, return -EINVAL); +	if (snd_BUG_ON(!pcm)) +		return -EINVAL;  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sonicvibes_playback_ops);  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sonicvibes_capture_ops); @@ -1089,7 +1090,8 @@ static int __devinit snd_sonicvibes_mixer(struct sonicvibes * sonic)  	unsigned int idx;  	int err; -	snd_assert(sonic != NULL && sonic->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!sonic || !sonic->card)) +		return -EINVAL;  	card = sonic->card;  	strcpy(card->mixername, "S3 SonicVibes"); diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index a69b420..c612b43 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -2931,7 +2931,8 @@ static int snd_trident_pcm_mixer_build(struct snd_trident *trident,  {  	struct snd_trident_pcm_mixer *tmix; -	snd_assert(trident != NULL && voice != NULL && substream != NULL, return -EINVAL); +	if (snd_BUG_ON(!trident || !voice || !substream)) +		return -EINVAL;  	tmix = &trident->pcm_mixer[substream->number];  	tmix->voice = voice;  	tmix->vol = T4D_DEFAULT_PCM_VOL; @@ -2946,7 +2947,8 @@ static int snd_trident_pcm_mixer_free(struct snd_trident *trident, struct snd_tr  {  	struct snd_trident_pcm_mixer *tmix; -	snd_assert(trident != NULL && substream != NULL, return -EINVAL); +	if (snd_BUG_ON(!trident || !substream)) +		return -EINVAL;  	tmix = &trident->pcm_mixer[substream->number];  	tmix->voice = NULL;  	snd_trident_notify_pcm_change(trident, tmix, substream->number, 0); @@ -3131,7 +3133,8 @@ static unsigned char snd_trident_gameport_read(struct gameport *gameport)  {  	struct snd_trident *chip = gameport_get_port_data(gameport); -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	return inb(TRID_REG(chip, GAMEPORT_LEGACY));  } @@ -3139,7 +3142,8 @@ static void snd_trident_gameport_trigger(struct gameport *gameport)  {  	struct snd_trident *chip = gameport_get_port_data(gameport); -	snd_assert(chip, return); +	if (snd_BUG_ON(!chip)) +		return;  	outb(0xff, TRID_REG(chip, GAMEPORT_LEGACY));  } @@ -3148,7 +3152,8 @@ static int snd_trident_gameport_cooked_read(struct gameport *gameport, int *axes  	struct snd_trident *chip = gameport_get_port_data(gameport);  	int i; -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	*buttons = (~inb(TRID_REG(chip, GAMEPORT_LEGACY)) >> 4) & 0xf; @@ -3164,7 +3169,8 @@ static int snd_trident_gameport_open(struct gameport *gameport, int mode)  {  	struct snd_trident *chip = gameport_get_port_data(gameport); -	snd_assert(chip, return 0); +	if (snd_BUG_ON(!chip)) +		return 0;  	switch (mode) {  		case GAMEPORT_MODE_COOKED: @@ -3891,8 +3897,8 @@ static void snd_trident_clear_voices(struct snd_trident * trident, unsigned shor  {  	unsigned int i, val, mask[2] = { 0, 0 }; -	snd_assert(v_min <= 63, return); -	snd_assert(v_max <= 63, return); +	if (snd_BUG_ON(v_min > 63 || v_max > 63)) +		return;  	for (i = v_min; i <= v_max; i++)  		mask[i >> 5] |= 1 << (i & 0x1f);  	if (mask[0]) { diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index 3fd7f1b..f9779e2 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -194,11 +194,14 @@ snd_trident_alloc_sg_pages(struct snd_trident *trident,  	struct snd_util_memblk *blk;  	struct snd_pcm_runtime *runtime = substream->runtime;  	int idx, page; -	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream); -	snd_assert(runtime->dma_bytes > 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); +	if (snd_BUG_ON(runtime->dma_bytes <= 0 || +		       runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES * +					SNDRV_TRIDENT_PAGE_SIZE)) +		return NULL;  	hdr = trident->tlb.memhdr; -	snd_assert(hdr != NULL, return NULL); +	if (snd_BUG_ON(!hdr)) +		return NULL; @@ -208,18 +211,14 @@ snd_trident_alloc_sg_pages(struct snd_trident *trident,  		mutex_unlock(&hdr->block_mutex);  		return NULL;  	} -	if (lastpg(blk) - firstpg(blk) >= sgbuf->pages) { -		snd_printk(KERN_ERR "page calculation doesn't match: allocated pages = %d, trident = %d/%d\n", sgbuf->pages, firstpg(blk), lastpg(blk)); -		__snd_util_mem_free(hdr, blk); -		mutex_unlock(&hdr->block_mutex); -		return NULL; -	}  	/* set TLB entries */  	idx = 0;  	for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) { -		dma_addr_t addr = sgbuf->table[idx].addr; -		unsigned long ptr = (unsigned long)sgbuf->table[idx].buf; +		unsigned long ofs = idx << PAGE_SHIFT; +		dma_addr_t addr = snd_pcm_sgbuf_get_addr(substream, ofs); +		unsigned long ptr = (unsigned long) +			snd_pcm_sgbuf_get_ptr(substream, ofs);  		if (! is_valid_page(addr)) {  			__snd_util_mem_free(hdr, blk);  			mutex_unlock(&hdr->block_mutex); @@ -245,9 +244,13 @@ snd_trident_alloc_cont_pages(struct snd_trident *trident,  	dma_addr_t addr;  	unsigned long ptr; -	snd_assert(runtime->dma_bytes> 0 && runtime->dma_bytes <= SNDRV_TRIDENT_MAX_PAGES * SNDRV_TRIDENT_PAGE_SIZE, return NULL); +	if (snd_BUG_ON(runtime->dma_bytes <= 0 || +		       runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES * +					SNDRV_TRIDENT_PAGE_SIZE)) +		return NULL;  	hdr = trident->tlb.memhdr; -	snd_assert(hdr != NULL, return NULL); +	if (snd_BUG_ON(!hdr)) +		return NULL;  	mutex_lock(&hdr->block_mutex);  	blk = search_empty(hdr, runtime->dma_bytes); @@ -279,8 +282,8 @@ struct snd_util_memblk *  snd_trident_alloc_pages(struct snd_trident *trident,  			struct snd_pcm_substream *substream)  { -	snd_assert(trident != NULL, return NULL); -	snd_assert(substream != NULL, return NULL); +	if (snd_BUG_ON(!trident || !substream)) +		return NULL;  	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG)  		return snd_trident_alloc_sg_pages(trident, substream);  	else @@ -297,8 +300,8 @@ int snd_trident_free_pages(struct snd_trident *trident,  	struct snd_util_memhdr *hdr;  	int page; -	snd_assert(trident != NULL, return -EINVAL); -	snd_assert(blk != NULL, return -EINVAL); +	if (snd_BUG_ON(!trident || !blk)) +		return -EINVAL;  	hdr = trident->tlb.memhdr;  	mutex_lock(&hdr->block_mutex); diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 6781be9..1aafe95 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -313,6 +313,7 @@ struct snd_via_sg_table {  } ;  #define VIA_TABLE_SIZE	255 +#define VIA_MAX_BUFSIZE	(1<<24)  struct viadev {  	unsigned int reg_offset; @@ -420,7 +421,6 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre  {  	unsigned int i, idx, ofs, rest;  	struct via82xx *chip = snd_pcm_substream_chip(substream); -	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);  	if (dev->table.area == NULL) {  		/* the start of each lists must be aligned to 8 bytes, @@ -449,15 +449,15 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre  		do {  			unsigned int r;  			unsigned int flag; +			unsigned int addr;  			if (idx >= VIA_TABLE_SIZE) {  				snd_printk(KERN_ERR "via82xx: too much table size!\n");  				return -EINVAL;  			} -			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); -			r = PAGE_SIZE - (ofs % PAGE_SIZE); -			if (rest < r) -				r = rest; +			addr = snd_pcm_sgbuf_get_addr(substream, ofs); +			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr); +			r = snd_pcm_sgbuf_get_chunk_size(substream, ofs, rest);  			rest -= r;  			if (! rest) {  				if (i == periods - 1) @@ -824,7 +824,8 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr  	struct viadev *viadev = substream->runtime->private_data;  	unsigned int idx, ptr, count, res; -	snd_assert(viadev->tbl_entries, return 0); +	if (snd_BUG_ON(!viadev->tbl_entries)) +		return 0;  	if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))  		return 0; @@ -855,7 +856,8 @@ static snd_pcm_uframes_t snd_via8233_pcm_pointer(struct snd_pcm_substream *subst  	unsigned int idx, count, res;  	int status; -	snd_assert(viadev->tbl_entries, return 0); +	if (snd_BUG_ON(!viadev->tbl_entries)) +		return 0;  	spin_lock(&chip->reg_lock);  	count = inl(VIADEV_REG(viadev, OFFSET_CURR_COUNT)); @@ -1037,7 +1039,7 @@ static int snd_via8233_playback_prepare(struct snd_pcm_substream *substream)  	else  		rbits = (0x100000 / 48000) * runtime->rate +  			((0x100000 % 48000) * runtime->rate) / 48000; -	snd_assert((rbits & ~0xfffff) == 0, return -EINVAL); +	snd_BUG_ON(rbits & ~0xfffff);  	snd_via82xx_channel_reset(chip, viadev);  	snd_via82xx_set_table_ptr(chip, viadev);  	outb(chip->playback_volume[viadev->reg_offset / 0x10][0], @@ -1144,9 +1146,9 @@ static struct snd_pcm_hardware snd_via82xx_hw =  	.rate_max =		48000,  	.channels_min =		1,  	.channels_max =		2, -	.buffer_bytes_max =	128 * 1024, +	.buffer_bytes_max =	VIA_MAX_BUFSIZE,  	.period_bytes_min =	32, -	.period_bytes_max =	128 * 1024, +	.period_bytes_max =	VIA_MAX_BUFSIZE / 2,  	.periods_min =		2,  	.periods_max =		VIA_TABLE_SIZE / 2,  	.fifo_size =		0, @@ -1398,10 +1400,9 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip)  	/* capture */  	init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1); -	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, -							 snd_dma_pci_data(chip->pci), -							 64*1024, 128*1024)) < 0) -		return err; +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, +					      snd_dma_pci_data(chip->pci), +					      64*1024, VIA_MAX_BUFSIZE);  	/* PCM #1:  multi-channel playback and 2nd capture */  	err = snd_pcm_new(chip->card, chip->card->shortname, 1, 1, 1, &pcm); @@ -1417,11 +1418,9 @@ static int __devinit snd_via8233_pcm_new(struct via82xx *chip)  	/* set up capture */  	init_viadev(chip, chip->capture_devno + 1, VIA_REG_CAPTURE_8233_STATUS + 0x10, 7, 1); -	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, -						         snd_dma_pci_data(chip->pci), -							 64*1024, 128*1024)) < 0) -		return err; - +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, +					      snd_dma_pci_data(chip->pci), +					      64*1024, VIA_MAX_BUFSIZE);  	return 0;  } @@ -1453,10 +1452,9 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)  	/* capture */  	init_viadev(chip, chip->capture_devno, VIA_REG_CAPTURE_8233_STATUS, 6, 1); -	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, -							 snd_dma_pci_data(chip->pci), -							 64*1024, 128*1024)) < 0) -		return err; +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, +					      snd_dma_pci_data(chip->pci), +					      64*1024, VIA_MAX_BUFSIZE);  	/* SPDIF supported? */  	if (! ac97_can_spdif(chip->ac97)) @@ -1473,11 +1471,9 @@ static int __devinit snd_via8233a_pcm_new(struct via82xx *chip)  	/* set up playback */  	init_viadev(chip, chip->playback_devno, 0x30, 3, 0); -	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, -							 snd_dma_pci_data(chip->pci), -							 64*1024, 128*1024)) < 0) -		return err; - +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, +					      snd_dma_pci_data(chip->pci), +					      64*1024, VIA_MAX_BUFSIZE);  	return 0;  } @@ -1505,11 +1501,9 @@ static int __devinit snd_via686_pcm_new(struct via82xx *chip)  	init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0, 0);  	init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 0, 1); -	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, -							 snd_dma_pci_data(chip->pci), -							 64*1024, 128*1024)) < 0) -		return err; - +	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, +					      snd_dma_pci_data(chip->pci), +					      64*1024, VIA_MAX_BUFSIZE);  	return 0;  } diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 31f64ee..5bd79d2 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -281,7 +281,6 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre  {  	unsigned int i, idx, ofs, rest;  	struct via82xx_modem *chip = snd_pcm_substream_chip(substream); -	struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);  	if (dev->table.area == NULL) {  		/* the start of each lists must be aligned to 8 bytes, @@ -310,12 +309,14 @@ static int build_via_table(struct viadev *dev, struct snd_pcm_substream *substre  		do {  			unsigned int r;  			unsigned int flag; +			unsigned int addr;  			if (idx >= VIA_TABLE_SIZE) {  				snd_printk(KERN_ERR "via82xx: too much table size!\n");  				return -EINVAL;  			} -			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs)); +			addr = snd_pcm_sgbuf_get_addr(substream, ofs); +			((u32 *)dev->table.area)[idx << 1] = cpu_to_le32(addr);  			r = PAGE_SIZE - (ofs % PAGE_SIZE);  			if (rest < r)  				r = rest; @@ -612,7 +613,8 @@ static snd_pcm_uframes_t snd_via686_pcm_pointer(struct snd_pcm_substream *substr  	struct viadev *viadev = substream->runtime->private_data;  	unsigned int idx, ptr, count, res; -	snd_assert(viadev->tbl_entries, return 0); +	if (snd_BUG_ON(!viadev->tbl_entries)) +		return 0;  	if (!(inb(VIADEV_REG(viadev, OFFSET_STATUS)) & VIA_REG_STAT_ACTIVE))  		return 0; diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 631f3a6..7e87f39 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -253,7 +253,8 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime,  	int offset = pipe->hw_ptr;  	u32 *addr = (u32 *)(runtime->dma_area + offset); -	snd_assert(count % 4 == 0, return); +	if (snd_BUG_ON(count % 4)) +		return;  	vx2_setup_pseudo_dma(chip, 1); @@ -291,7 +292,8 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,  	u32 *addr = (u32 *)(runtime->dma_area + offset);  	unsigned long port = vx2_reg_addr(chip, VX_DMA); -	snd_assert(count % 4 == 0, return); +	if (snd_BUG_ON(count % 4)) +		return;  	vx2_setup_pseudo_dma(chip, 0);  	/* Transfer using pseudo-dma. @@ -675,7 +677,8 @@ static void vx2_write_akm(struct vx_core *chip, int reg, unsigned int data)  	   a look up table, as there is no linear matching between the driver codec values  	   and the real dBu value  	*/ -	snd_assert(data < sizeof(vx2_akm_gains_lut), return); +	if (snd_BUG_ON(data >= sizeof(vx2_akm_gains_lut))) +		return;  	switch (reg) {  	case XX_CODEC_LEVEL_LEFT_REGISTER: @@ -823,7 +826,8 @@ static void vx2_set_input_level(struct snd_vx222 *chip)  		preamp++;	/* raise pre ampli + 18dB */  		miclevel -= (18 * 2);   /* lower level 18 dB (*2 because of 0.5 dB steps !) */          } -	snd_assert(preamp < 4, return); +	if (snd_BUG_ON(preamp >= 4)) +		return;  	/* set pre-amp level */  	chip->regSELMIC &= ~MICRO_SELECT_PREAMPLI_MASK; diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 92d49aa..90d0d62 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -259,8 +259,10 @@ static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip,  	unsigned long flags;  	int result; -	snd_assert(rvoice != NULL, return -EINVAL); -	snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL); +	if (snd_BUG_ON(!rvoice)) +		return -EINVAL; +	if (snd_BUG_ON(pair && type != YMFPCI_PCM)) +		return -EINVAL;  	spin_lock_irqsave(&chip->voice_lock, flags);  	for (;;) { @@ -278,7 +280,8 @@ static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voic  {  	unsigned long flags; -	snd_assert(pvoice != NULL, return -EINVAL); +	if (snd_BUG_ON(!pvoice)) +		return -EINVAL;  	snd_ymfpci_hw_stop(chip);  	spin_lock_irqsave(&chip->voice_lock, flags);  	if (pvoice->number == chip->src441_used) { @@ -494,7 +497,8 @@ static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int  	u8 use_left, use_right;  	unsigned long flags; -	snd_assert(voice != NULL, return); +	if (snd_BUG_ON(!voice)) +		return;  	if (runtime->channels == 1) {  		use_left = 1;  		use_right = 1; @@ -1813,7 +1817,8 @@ int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)  	}  	/* add S/PDIF control */ -	snd_assert(chip->pcm_spdif != NULL, return -EIO); +	if (snd_BUG_ON(!chip->pcm_spdif)) +		return -ENXIO;  	if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0)  		return err;  	kctl->id.device = chip->pcm_spdif->device; @@ -2133,7 +2138,8 @@ static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)  	chip->work_base = ptr;  	chip->work_base_addr = ptr_addr; -	snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, ); +	snd_BUG_ON(ptr + chip->work_size != +		   chip->work_ptr.area + chip->work_ptr.bytes);  	snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr);  	snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr); @@ -2168,7 +2174,8 @@ static int snd_ymfpci_free(struct snd_ymfpci *chip)  {  	u16 ctrl; -	snd_assert(chip != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip)) +		return -EINVAL;  	if (chip->res_reg_area) {	/* don't touch busy hardware */  		snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0); diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 99bf2a6..989e04a 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -408,7 +408,8 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime,  	int offset = pipe->hw_ptr;  	unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); -	snd_assert(count % 2 == 0, return); +	if (snd_BUG_ON(count % 2)) +		return;  	vx_setup_pseudo_dma(chip, 0);  	if (offset + count > pipe->buffer_bytes) {  		int length = pipe->buffer_bytes - offset; diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 566a6d0..7bd33e6 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -319,7 +319,8 @@ static void awacs_amp_set_master(struct awacs_amp *amp, int vol)  static void awacs_amp_free(struct snd_pmac *chip)  {  	struct awacs_amp *amp = chip->mixer_data; -	snd_assert(amp, return); +	if (!amp) +		return;  	kfree(amp);  	chip->mixer_data = NULL;  	chip->mixer_free = NULL; @@ -345,8 +346,7 @@ static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol,  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	int index = kcontrol->private_value;  	struct awacs_amp *amp = chip->mixer_data; -	snd_assert(amp, return -EINVAL); -	snd_assert(index >= 0 && index <= 1, return -EINVAL); +  	ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31);  	ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31);  	return 0; @@ -359,8 +359,6 @@ static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol,  	int index = kcontrol->private_value;  	int vol[2];  	struct awacs_amp *amp = chip->mixer_data; -	snd_assert(amp, return -EINVAL); -	snd_assert(index >= 0 && index <= 1, return -EINVAL);  	vol[0] = (31 - (ucontrol->value.integer.value[0] & 31))  		| (amp->amp_vol[index][0] & 32); @@ -375,8 +373,7 @@ static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol,  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	int index = kcontrol->private_value;  	struct awacs_amp *amp = chip->mixer_data; -	snd_assert(amp, return -EINVAL); -	snd_assert(index >= 0 && index <= 1, return -EINVAL); +  	ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32)  					? 0 : 1;  	ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) @@ -391,8 +388,6 @@ static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol,  	int index = kcontrol->private_value;  	int vol[2];  	struct awacs_amp *amp = chip->mixer_data; -	snd_assert(amp, return -EINVAL); -	snd_assert(index >= 0 && index <= 1, return -EINVAL);  	vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32)  		| (amp->amp_vol[index][0] & 31); @@ -417,8 +412,7 @@ static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol,  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	int index = kcontrol->private_value;  	struct awacs_amp *amp = chip->mixer_data; -	snd_assert(amp, return -EINVAL); -	snd_assert(index >= 0 && index <= 1, return -EINVAL); +  	ucontrol->value.integer.value[0] = amp->amp_tone[index];  	return 0;  } @@ -430,8 +424,7 @@ static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol,  	int index = kcontrol->private_value;  	struct awacs_amp *amp = chip->mixer_data;  	unsigned int val; -	snd_assert(amp, return -EINVAL); -	snd_assert(index >= 0 && index <= 1, return -EINVAL); +  	val = ucontrol->value.integer.value[0];  	if (val > 14)  		return -EINVAL; @@ -458,7 +451,7 @@ static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol,  {  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	struct awacs_amp *amp = chip->mixer_data; -	snd_assert(amp, return -EINVAL); +  	ucontrol->value.integer.value[0] = amp->amp_master;  	return 0;  } @@ -469,7 +462,7 @@ static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	struct awacs_amp *amp = chip->mixer_data;  	unsigned int val; -	snd_assert(amp, return -EINVAL); +  	val = ucontrol->value.integer.value[0];  	if (val > 99)  		return -EINVAL; @@ -621,6 +614,13 @@ static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = {  	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),  }; +static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] __initdata = { +	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1), +	AWACS_VOLUME("Master Playback Volume", 5, 6, 1), +	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), +	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), +}; +  static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __initdata = {  	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),  	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), @@ -688,7 +688,10 @@ static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = {  static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata =  AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); -static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac __initdata = +static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __initdata = +AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 1); + +static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __initdata =  AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 0); @@ -765,11 +768,12 @@ static void snd_pmac_awacs_resume(struct snd_pmac *chip)  #define IS_PM7500 (machine_is_compatible("AAPL,7500"))  #define IS_BEIGE (machine_is_compatible("AAPL,Gossamer")) -#define IS_IMAC (machine_is_compatible("PowerMac2,1") \ -		|| machine_is_compatible("PowerMac2,2") \ +#define IS_IMAC1 (machine_is_compatible("PowerMac2,1")) +#define IS_IMAC2 (machine_is_compatible("PowerMac2,2") \  		|| machine_is_compatible("PowerMac4,1")) +#define IS_G4AGP (machine_is_compatible("PowerMac3,1")) -static int imac; +static int imac1, imac2;  #ifdef PMAC_SUPPORT_AUTOMUTE  /* @@ -815,13 +819,18 @@ static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)  		{  			int reg = chip->awacs_reg[1]  				| (MASK_HDMUTE | MASK_SPKMUTE); -			if (imac) { +			if (imac1) { +				reg &= ~MASK_SPKMUTE; +				reg |= MASK_PAROUT1; +			} else if (imac2) {  				reg &= ~MASK_SPKMUTE;  				reg &= ~MASK_PAROUT1;  			}  			if (snd_pmac_awacs_detect_headphone(chip))  				reg &= ~MASK_HDMUTE; -			else if (imac) +			else if (imac1) +				reg &= ~MASK_PAROUT1; +			else if (imac2)  				reg |= MASK_PAROUT1;  			else  				reg &= ~MASK_SPKMUTE; @@ -850,9 +859,13 @@ snd_pmac_awacs_init(struct snd_pmac *chip)  {  	int pm7500 = IS_PM7500;  	int beige = IS_BEIGE; +	int g4agp = IS_G4AGP; +	int imac;  	int err, vol; -	imac = IS_IMAC; +	imac1 = IS_IMAC1; +	imac2 = IS_IMAC2; +	imac = imac1 || imac2;  	/* looks like MASK_GAINLINE triggers something, so we set here  	 * as start-up  	 */ @@ -939,7 +952,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip)  				snd_pmac_awacs_mixers);  	if (err < 0)  		return err; -	if (beige) +	if (beige || g4agp)  		;  	else if (chip->model == PMAC_SCREAMER)  		err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2), @@ -961,13 +974,17 @@ snd_pmac_awacs_init(struct snd_pmac *chip)  		err = build_mixers(chip,  				   ARRAY_SIZE(snd_pmac_screamer_mixers_imac),  				   snd_pmac_screamer_mixers_imac); +	else if (g4agp) +		err = build_mixers(chip, +				   ARRAY_SIZE(snd_pmac_screamer_mixers_g4agp), +				   snd_pmac_screamer_mixers_g4agp);  	else  		err = build_mixers(chip,  				   ARRAY_SIZE(snd_pmac_awacs_mixers_pmac),  				   snd_pmac_awacs_mixers_pmac);  	if (err < 0)  		return err; -	chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac) +	chip->master_sw_ctl = snd_ctl_new1((pm7500 || imac || g4agp)  			? &snd_pmac_awacs_master_sw_imac  			: &snd_pmac_awacs_master_sw, chip);  	err = snd_ctl_add(chip->card, chip->master_sw_ctl); @@ -1004,15 +1021,17 @@ snd_pmac_awacs_init(struct snd_pmac *chip)  					snd_pmac_awacs_speaker_vol);  		if (err < 0)  			return err; -		chip->speaker_sw_ctl = snd_ctl_new1(imac -				? &snd_pmac_awacs_speaker_sw_imac +		chip->speaker_sw_ctl = snd_ctl_new1(imac1 +				? &snd_pmac_awacs_speaker_sw_imac1 +				: imac2 +				? &snd_pmac_awacs_speaker_sw_imac2  				: &snd_pmac_awacs_speaker_sw, chip);  		err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);  		if (err < 0)  			return err;  	} -	if (beige) +	if (beige || g4agp)  		err = build_mixers(chip,  				ARRAY_SIZE(snd_pmac_screamer_mic_boost_beige),  				snd_pmac_screamer_mic_boost_beige); diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index baa2a72..89f5c32 100644 --- a/sound/ppc/beep.c +++ b/sound/ppc/beep.c @@ -185,7 +185,8 @@ static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,  			     struct snd_ctl_elem_value *ucontrol)  {  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); -	snd_assert(chip->beep, return -ENXIO); +	if (snd_BUG_ON(!chip->beep)) +		return -ENXIO;  	ucontrol->value.integer.value[0] = chip->beep->volume;  	return 0;  } @@ -195,7 +196,8 @@ static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,  {  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	unsigned int oval, nval; -	snd_assert(chip->beep, return -ENXIO); +	if (snd_BUG_ON(!chip->beep)) +		return -ENXIO;  	oval = chip->beep->volume;  	nval = ucontrol->value.integer.value[0];  	if (nval > 100) diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 009df8d..f746e15 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -263,7 +263,7 @@ static int tumbler_get_master_volume(struct snd_kcontrol *kcontrol,  {  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	struct pmac_tumbler *mix = chip->mixer_data; -	snd_assert(mix, return -ENODEV); +  	ucontrol->value.integer.value[0] = mix->master_vol[0];  	ucontrol->value.integer.value[1] = mix->master_vol[1];  	return 0; @@ -277,7 +277,6 @@ static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol,  	unsigned int vol[2];  	int change; -	snd_assert(mix, return -ENODEV);  	vol[0] = ucontrol->value.integer.value[0];  	vol[1] = ucontrol->value.integer.value[1];  	if (vol[0] >= ARRAY_SIZE(master_volume_table) || @@ -299,7 +298,7 @@ static int tumbler_get_master_switch(struct snd_kcontrol *kcontrol,  {  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	struct pmac_tumbler *mix = chip->mixer_data; -	snd_assert(mix, return -ENODEV); +  	ucontrol->value.integer.value[0] = mix->master_switch[0];  	ucontrol->value.integer.value[1] = mix->master_switch[1];  	return 0; @@ -312,7 +311,6 @@ static int tumbler_put_master_switch(struct snd_kcontrol *kcontrol,  	struct pmac_tumbler *mix = chip->mixer_data;  	int change; -	snd_assert(mix, return -ENODEV);  	change = mix->master_switch[0] != ucontrol->value.integer.value[0] ||  		mix->master_switch[1] != ucontrol->value.integer.value[1];  	if (change) { @@ -807,7 +805,6 @@ static int snapper_get_capture_source(struct snd_kcontrol *kcontrol,  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);  	struct pmac_tumbler *mix = chip->mixer_data; -	snd_assert(mix, return -ENODEV);  	ucontrol->value.enumerated.item[0] = mix->capture_source;  	return 0;  } @@ -819,7 +816,6 @@ static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,  	struct pmac_tumbler *mix = chip->mixer_data;  	int change; -	snd_assert(mix, return -ENODEV);  	change = ucontrol->value.enumerated.item[0] != mix->capture_source;  	if (change) {  		mix->capture_source = !!ucontrol->value.enumerated.item[0]; @@ -978,7 +974,8 @@ static void device_change_handler(struct work_struct *work)  		return;  	mix = chip->mixer_data; -	snd_assert(mix, return); +	if (snd_BUG_ON(!mix)) +		return;  	headphone = tumbler_detect_headphone(chip);  	lineout = tumbler_detect_lineout(chip); @@ -1033,7 +1030,8 @@ static void tumbler_update_automute(struct snd_pmac *chip, int do_notify)  	if (chip->auto_mute) {  		struct pmac_tumbler *mix;  		mix = chip->mixer_data; -		snd_assert(mix, return); +		if (snd_BUG_ON(!mix)) +			return;  		mix->auto_mute_notify = do_notify;  		schedule_work(&device_change);  	} @@ -1227,8 +1225,6 @@ static void tumbler_resume(struct snd_pmac *chip)  {  	struct pmac_tumbler *mix = chip->mixer_data; -	snd_assert(mix, return); -  	mix->acs &= ~1;  	mix->master_switch[0] = mix->save_master_switch[0];  	mix->master_switch[1] = mix->save_master_switch[1]; @@ -1275,7 +1271,6 @@ static int __init tumbler_init(struct snd_pmac *chip)  {  	int irq;  	struct pmac_tumbler *mix = chip->mixer_data; -	snd_assert(mix, return -EINVAL);  	if (tumbler_find_device("audio-hw-reset",  				"platform-do-hw-reset", diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 54df8ba..7c920f3 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -106,7 +106,8 @@ static void spu_memset(u32 toi, u32 what, int length)  {  	int i;  	unsigned long flags; -	snd_assert(length % 4 == 0, return); +	if (snd_BUG_ON(length % 4)) +		return;  	for (i = 0; i < length; i++) {  		if (!(i % 8))  			spu_write_wait(); @@ -589,7 +590,7 @@ static int __devinit add_aicamixer_controls(struct snd_card_aica  	return 0;  } -static int snd_aica_remove(struct platform_device *devptr) +static int __devexit snd_aica_remove(struct platform_device *devptr)  {  	struct snd_card_aica *dreamcastcard;  	dreamcastcard = platform_get_drvdata(devptr); @@ -601,7 +602,7 @@ static int snd_aica_remove(struct platform_device *devptr)  	return 0;  } -static int __init snd_aica_probe(struct platform_device *devptr) +static int __devinit snd_aica_probe(struct platform_device *devptr)  {  	int err;  	struct snd_card_aica *dreamcastcard; @@ -650,7 +651,7 @@ static int __init snd_aica_probe(struct platform_device *devptr)  static struct platform_driver snd_aica_driver = {  	.probe = snd_aica_probe, -	.remove = snd_aica_remove, +	.remove = __devexit_p(snd_aica_remove),  	.driver = {  		   .name = SND_AICA_DRIVER},  }; diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index f743530..4dfda66 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -5,6 +5,7 @@  menuconfig SND_SOC  	tristate "ALSA for SoC audio support"  	select SND_PCM +	select AC97_BUS if SND_SOC_AC97_BUS  	---help---  	  If you want ASoC support, you should say Y here and also to the @@ -31,6 +32,7 @@ source "sound/soc/sh/Kconfig"  source "sound/soc/fsl/Kconfig"  source "sound/soc/davinci/Kconfig"  source "sound/soc/omap/Kconfig" +source "sound/soc/blackfin/Kconfig"  # Supported codecs  source "sound/soc/codecs/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 933a66d..d849349 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -2,4 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o  obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o  obj-$(CONFIG_SND_SOC)	+= codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ -obj-$(CONFIG_SND_SOC)	+= omap/ au1x/ +obj-$(CONFIG_SND_SOC)	+= omap/ au1x/ blackfin/ diff --git a/sound/soc/at32/at32-pcm.c b/sound/soc/at32/at32-pcm.c index 435f1da..c83584f 100644 --- a/sound/soc/at32/at32-pcm.c +++ b/sound/soc/at32/at32-pcm.c @@ -434,7 +434,8 @@ static int at32_pcm_suspend(struct platform_device *pdev,  	params = prtd->params;  	/* Disable the PDC and save the PDC registers */ -	ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable); +	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, +		   params->mask->pdc_disable);  	prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);  	prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr); @@ -464,7 +465,7 @@ static int at32_pcm_resume(struct platform_device *pdev,  	ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);  	ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save); -	ssc_writex(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable); +	ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR, params->mask->pdc_enable);  	return 0;  }  #else /* CONFIG_PM */ diff --git a/sound/soc/at32/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c index 3f32621..98a2d58 100644 --- a/sound/soc/at32/playpaq_wm8510.c +++ b/sound/soc/at32/playpaq_wm8510.c @@ -377,6 +377,7 @@ static struct snd_soc_machine snd_soc_machine_playpaq = {  static struct wm8510_setup_data playpaq_wm8510_setup = { +	.i2c_bus = 0,  	.i2c_address = 0x1a,  }; @@ -405,7 +406,6 @@ static int __init playpaq_asoc_init(void)  	ssc = ssc_request(0);  	if (IS_ERR(ssc)) {  		ret = PTR_ERR(ssc); -		ssc = NULL;  		goto err_ssc;  	}  	ssc_p->ssc = ssc; @@ -476,10 +476,7 @@ err_pll0:  		_gclk0 = NULL;  	}  err_gclk0: -	if (ssc != NULL) { -		ssc_free(ssc); -		ssc = NULL; -	} +	ssc_free(ssc);  err_ssc:  	return ret;  } diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c index 5d44515..a5b1a79 100644 --- a/sound/soc/at91/at91-ssc.c +++ b/sound/soc/at91/at91-ssc.c @@ -408,7 +408,7 @@ static int at91_ssc_hw_params(struct snd_pcm_substream *substream,  		dma_params->pdc_xfer_size = 4;  		break;  	default: -		printk(KERN_WARNING "at91-ssc: unsupported PCM format"); +		printk(KERN_WARNING "at91-ssc: unsupported PCM format\n");  		return -EINVAL;  	} diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index b81d6b2..684781e 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c @@ -243,6 +243,7 @@ static struct snd_soc_machine snd_soc_machine_eti_b1 = {  };  static struct wm8731_setup_data eti_b1_wm8731_setup = { +	.i2c_bus = 0,  	.i2c_address = 0x1a,  }; diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig new file mode 100644 index 0000000..f98331d --- /dev/null +++ b/sound/soc/blackfin/Kconfig @@ -0,0 +1,85 @@ +config SND_BF5XX_I2S +	tristate "SoC I2S Audio for the ADI BF5xx chip" +	depends on BLACKFIN && SND_SOC +	help +	  Say Y or M if you want to add support for codecs attached to +	  the Blackfin SPORT (synchronous serial ports) interface in I2S +	  mode (supports single stereo In/Out). +	  You will also need to select the audio interfaces to support below. + +config SND_BF5XX_SOC_SSM2602 +	tristate "SoC SSM2602 Audio support for BF52x ezkit" +	depends on SND_BF5XX_I2S +	select SND_BF5XX_SOC_I2S +	select SND_SOC_SSM2602 +	select I2C +	select I2C_BLACKFIN_TWI +	help +	  Say Y if you want to add support for SoC audio on BF527-EZKIT. + +config SND_BF5XX_AC97 +	tristate "SoC AC97 Audio for the ADI BF5xx chip" +	depends on BLACKFIN && SND_SOC +	help +	  Say Y or M if you want to add support for codecs attached to +	  the Blackfin SPORT (synchronous serial ports) interface in slot 16 +	  mode (pseudo AC97 interface). +	  You will also need to select the audio interfaces to support below. + +	  Note: +	  AC97 codecs which do not implment the slot-16 mode will not function +	  properly with this driver. This driver is known to work with the +	  Analog Devices line of AC97 codecs. + +config SND_MMAP_SUPPORT +	bool "Enable MMAP Support" +	depends on SND_BF5XX_AC97 +	default y +	help +	  Say y if you want AC97 driver to support mmap mode. +	  We introduce an intermediate buffer to simulate mmap. + +config SND_BF5XX_SOC_SPORT +	tristate +	 +config SND_BF5XX_SOC_I2S +	tristate +	select SND_BF5XX_SOC_SPORT + +config SND_BF5XX_SOC_AC97 +	tristate +	select AC97_BUS +	select SND_SOC_AC97_BUS +	select SND_BF5XX_SOC_SPORT + +config SND_BF5XX_SOC_AD1980 +	tristate "SoC AD1980/1 Audio support for BF5xx" +	depends on SND_BF5XX_AC97 +	select SND_BF5XX_SOC_AC97 +	select SND_SOC_AD1980 +	help +	  Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT. + +config SND_BF5XX_SPORT_NUM +	int "Set a SPORT for Sound chip" +	depends on (SND_BF5XX_I2S || SND_BF5XX_AC97) +	range 0 3 if BF54x +	range 0 1 if (BF53x || BF561) +	default 0 +	help +	  Set the correct SPORT for sound chip. + +config SND_BF5XX_HAVE_COLD_RESET +	bool "BOARD has COLD Reset GPIO" +	depends on SND_BF5XX_AC97 +	default y if BFIN548_EZKIT +	default n if !BFIN548_EZKIT +	 +config SND_BF5XX_RESET_GPIO_NUM +	int "Set a GPIO for cold reset" +	depends on SND_BF5XX_HAVE_COLD_RESET +	range 0 159 +	default 19 if BFIN548_EZKIT +	default 5 if BFIN537_STAMP +	help +	  Set the correct GPIO for RESET the sound chip. diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile new file mode 100644 index 0000000..9ea8bd9 --- /dev/null +++ b/sound/soc/blackfin/Makefile @@ -0,0 +1,20 @@ +# Blackfin Platform Support +snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o +snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o +snd-soc-bf5xx-sport-objs := bf5xx-sport.o +snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o +snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o + +obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o +obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o +obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o +obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o +obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o + +# Blackfin Machine Support +snd-ad1980-objs := bf5xx-ad1980.o +snd-ssm2602-objs := bf5xx-ssm2602.o + + +obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o +obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c new file mode 100644 index 0000000..51f4907 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -0,0 +1,429 @@ +/* + * File:         sound/soc/blackfin/bf5xx-ac97-pcm.c + * Author:       Cliff Cai <Cliff.Cai@analog.com> + * + * Created:      Tue June 06 2008 + * Description:  DMA Driver for AC97 sound chip + * + * Modified: + *               Copyright 2008 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <asm/dma.h> + +#include "bf5xx-ac97-pcm.h" +#include "bf5xx-ac97.h" +#include "bf5xx-sport.h" + +#if defined(CONFIG_SND_MMAP_SUPPORT) +static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, +	 snd_pcm_uframes_t count) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct sport_device *sport = runtime->private_data; +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		bf5xx_pcm_to_ac97( +			(struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos, +			(__u32 *)runtime->dma_area + sport->tx_pos, count); +		sport->tx_pos += runtime->period_size; +		if (sport->tx_pos >= runtime->buffer_size) +			sport->tx_pos %= runtime->buffer_size; +	} else { +		bf5xx_ac97_to_pcm( +			(struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos, +			(__u32 *)runtime->dma_area + sport->rx_pos, count); +		sport->rx_pos += runtime->period_size; +		if (sport->rx_pos >= runtime->buffer_size) +			sport->rx_pos %= runtime->buffer_size; +	} +} +#endif + +static void bf5xx_dma_irq(void *data) +{ +	struct snd_pcm_substream *pcm = data; +#if defined(CONFIG_SND_MMAP_SUPPORT) +	struct snd_pcm_runtime *runtime = pcm->runtime; +	bf5xx_mmap_copy(pcm, runtime->period_size); +#endif +	snd_pcm_period_elapsed(pcm); +} + +/* The memory size for pure pcm data is 128*1024 = 0x20000 bytes. + * The total rx/tx buffer is for ac97 frame to hold all pcm data + * is  0x20000 * sizeof(struct ac97_frame) / 4. + */ +#ifdef CONFIG_SND_MMAP_SUPPORT +static const struct snd_pcm_hardware bf5xx_pcm_hardware = { +	.info			= SNDRV_PCM_INFO_INTERLEAVED | +				   SNDRV_PCM_INFO_MMAP | +				   SNDRV_PCM_INFO_MMAP_VALID | +				   SNDRV_PCM_INFO_BLOCK_TRANSFER, +#else +static const struct snd_pcm_hardware bf5xx_pcm_hardware = { +	.info			= SNDRV_PCM_INFO_INTERLEAVED | +				  SNDRV_PCM_INFO_BLOCK_TRANSFER, +#endif +	.formats		= SNDRV_PCM_FMTBIT_S16_LE, +	.period_bytes_min	= 32, +	.period_bytes_max	= 0x10000, +	.periods_min		= 1, +	.periods_max		= PAGE_SIZE/32, +	.buffer_bytes_max	= 0x20000, /* 128 kbytes */ +	.fifo_size		= 16, +}; + +static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	size_t size = bf5xx_pcm_hardware.buffer_bytes_max +			* sizeof(struct ac97_frame) / 4; + +	snd_pcm_lib_malloc_pages(substream, size); + +	return 0; +} + +static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) +{ +	snd_pcm_lib_free_pages(substream); +	return 0; +} + +static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct sport_device *sport = runtime->private_data; + +	/* An intermediate buffer is introduced for implementing mmap for +	 * SPORT working in TMD mode(include AC97). +	 */ +#if defined(CONFIG_SND_MMAP_SUPPORT) +	size_t size = bf5xx_pcm_hardware.buffer_bytes_max +			* sizeof(struct ac97_frame) / 4; +	/*clean up intermediate buffer*/ +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		memset(sport->tx_dma_buf, 0, size); +		sport_set_tx_callback(sport, bf5xx_dma_irq, substream); +		sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, +			runtime->period_size * sizeof(struct ac97_frame)); +	} else { +		memset(sport->rx_dma_buf, 0, size); +		sport_set_rx_callback(sport, bf5xx_dma_irq, substream); +		sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods, +			runtime->period_size * sizeof(struct ac97_frame)); +	} +#else +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		sport_set_tx_callback(sport, bf5xx_dma_irq, substream); +		sport_config_tx_dma(sport, runtime->dma_area, runtime->periods, +			runtime->period_size * sizeof(struct ac97_frame)); +	} else { +		sport_set_rx_callback(sport, bf5xx_dma_irq, substream); +		sport_config_rx_dma(sport, runtime->dma_area, runtime->periods, +			runtime->period_size * sizeof(struct ac97_frame)); +	} +#endif +	return 0; +} + +static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct sport_device *sport = runtime->private_data; +	int ret = 0; + +	pr_debug("%s enter\n", __func__); +	switch (cmd) { +	case SNDRV_PCM_TRIGGER_START: +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +			sport_tx_start(sport); +		else +			sport_rx_start(sport); +		break; +	case SNDRV_PCM_TRIGGER_STOP: +	case SNDRV_PCM_TRIGGER_SUSPEND: +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +#if defined(CONFIG_SND_MMAP_SUPPORT) +			sport->tx_pos = 0; +#endif +			sport_tx_stop(sport); +		} else { +#if defined(CONFIG_SND_MMAP_SUPPORT) +			sport->rx_pos = 0; +#endif +			sport_rx_stop(sport); +		} +		break; +	default: +		ret = -EINVAL; +	} +	return ret; +} + +static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct sport_device *sport = runtime->private_data; +	unsigned int curr; + +#if defined(CONFIG_SND_MMAP_SUPPORT) +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		curr = sport->tx_pos; +	else +		curr = sport->rx_pos; +#else + +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame); +	else +		curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame); + +#endif +	return curr; +} + +static int bf5xx_pcm_open(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	int ret; + +	pr_debug("%s enter\n", __func__); +	snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); + +	ret = snd_pcm_hw_constraint_integer(runtime, +					    SNDRV_PCM_HW_PARAM_PERIODS); +	if (ret < 0) +		goto out; + +	if (sport_handle != NULL) +		runtime->private_data = sport_handle; +	else { +		pr_err("sport_handle is NULL\n"); +		return -1; +	} +	return 0; + + out: +	return ret; +} + +#ifdef CONFIG_SND_MMAP_SUPPORT +static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, +	struct vm_area_struct *vma) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	size_t size = vma->vm_end - vma->vm_start; +	vma->vm_start = (unsigned long)runtime->dma_area; +	vma->vm_end = vma->vm_start + size; +	vma->vm_flags |=  VM_SHARED; +	return 0 ; +} +#else +static	int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, +		    snd_pcm_uframes_t pos, +		    void __user *buf, snd_pcm_uframes_t count) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; + +	pr_debug("%s copy pos:0x%lx count:0x%lx\n", +			substream->stream ? "Capture" : "Playback", pos, count); + +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		bf5xx_pcm_to_ac97( +				(struct ac97_frame *)runtime->dma_area + pos, +				buf, count); +	else +		bf5xx_ac97_to_pcm( +				(struct ac97_frame *)runtime->dma_area + pos, +				buf, count); +	return 0; +} +#endif + +struct snd_pcm_ops bf5xx_pcm_ac97_ops = { +	.open		= bf5xx_pcm_open, +	.ioctl		= snd_pcm_lib_ioctl, +	.hw_params	= bf5xx_pcm_hw_params, +	.hw_free	= bf5xx_pcm_hw_free, +	.prepare	= bf5xx_pcm_prepare, +	.trigger	= bf5xx_pcm_trigger, +	.pointer	= bf5xx_pcm_pointer, +#ifdef CONFIG_SND_MMAP_SUPPORT +	.mmap		= bf5xx_pcm_mmap, +#else +	.copy		= bf5xx_pcm_copy, +#endif +}; + +static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ +	struct snd_pcm_substream *substream = pcm->streams[stream].substream; +	struct snd_dma_buffer *buf = &substream->dma_buffer; +	size_t size = bf5xx_pcm_hardware.buffer_bytes_max +			* sizeof(struct ac97_frame) / 4; + +	buf->dev.type = SNDRV_DMA_TYPE_DEV; +	buf->dev.dev = pcm->card->dev; +	buf->private_data = NULL; +	buf->area = dma_alloc_coherent(pcm->card->dev, size, +			&buf->addr, GFP_KERNEL); +	if (!buf->area) { +		pr_err("Failed to allocate dma memory\n"); +		pr_err("Please increase uncached DMA memory region\n"); +		return -ENOMEM; +	} +	buf->bytes = size; + +	pr_debug("%s, area:%p, size:0x%08lx\n", __func__, +			buf->area, buf->bytes); + +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) +		sport_handle->tx_buf = buf->area; +	else +		sport_handle->rx_buf = buf->area; + +/* + * Need to allocate local buffer when enable + * MMAP for SPORT working in TMD mode (include AC97). + */ +#if defined(CONFIG_SND_MMAP_SUPPORT) +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		if (!sport_handle->tx_dma_buf) { +			sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ +				size, &sport_handle->tx_dma_phy, GFP_KERNEL); +			if (!sport_handle->tx_dma_buf) { +				pr_err("Failed to allocate memory for tx dma \ +					buf - Please increase uncached DMA \ +					memory region\n"); +				return -ENOMEM; +			} else +				memset(sport_handle->tx_dma_buf, 0, size); +		} else +			memset(sport_handle->tx_dma_buf, 0, size); +	} else { +		if (!sport_handle->rx_dma_buf) { +			sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \ +				size, &sport_handle->rx_dma_phy, GFP_KERNEL); +			if (!sport_handle->rx_dma_buf) { +				pr_err("Failed to allocate memory for rx dma \ +					buf - Please increase uncached DMA \ +					memory region\n"); +				return -ENOMEM; +			} else +				memset(sport_handle->rx_dma_buf, 0, size); +		} else +			memset(sport_handle->rx_dma_buf, 0, size); +	} +#endif +	return 0; +} + +static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ +	struct snd_pcm_substream *substream; +	struct snd_dma_buffer *buf; +	int stream; +#if defined(CONFIG_SND_MMAP_SUPPORT) +	size_t size = bf5xx_pcm_hardware.buffer_bytes_max * +		sizeof(struct ac97_frame) / 4; +#endif +	for (stream = 0; stream < 2; stream++) { +		substream = pcm->streams[stream].substream; +		if (!substream) +			continue; + +		buf = &substream->dma_buffer; +		if (!buf->area) +			continue; +		dma_free_coherent(NULL, buf->bytes, buf->area, 0); +		buf->area = NULL; +#if defined(CONFIG_SND_MMAP_SUPPORT) +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		if (sport_handle->tx_dma_buf) +			dma_free_coherent(NULL, size, \ +				sport_handle->tx_dma_buf, 0); +		sport_handle->tx_dma_buf = NULL; +	} else { + +		if (sport_handle->rx_dma_buf) +			dma_free_coherent(NULL, size, \ +				sport_handle->rx_dma_buf, 0); +		sport_handle->rx_dma_buf = NULL; +	} +#endif +	} +	if (sport_handle) +		sport_done(sport_handle); +} + +static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; + +int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai, +	struct snd_pcm *pcm) +{ +	int ret = 0; + +	pr_debug("%s enter\n", __func__); +	if (!card->dev->dma_mask) +		card->dev->dma_mask = &bf5xx_pcm_dmamask; +	if (!card->dev->coherent_dma_mask) +		card->dev->coherent_dma_mask = DMA_32BIT_MASK; + +	if (dai->playback.channels_min) { +		ret = bf5xx_pcm_preallocate_dma_buffer(pcm, +			SNDRV_PCM_STREAM_PLAYBACK); +		if (ret) +			goto out; +	} + +	if (dai->capture.channels_min) { +		ret = bf5xx_pcm_preallocate_dma_buffer(pcm, +			SNDRV_PCM_STREAM_CAPTURE); +		if (ret) +			goto out; +	} + out: +	return ret; +} + +struct snd_soc_platform bf5xx_ac97_soc_platform = { +	.name		= "bf5xx-audio", +	.pcm_ops 	= &bf5xx_pcm_ac97_ops, +	.pcm_new	= bf5xx_pcm_ac97_new, +	.pcm_free	= bf5xx_pcm_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform); + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h new file mode 100644 index 0000000..350125a --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.h @@ -0,0 +1,29 @@ +/* + * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin + * + * Copyright 2007 Analog Device Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_AC97_PCM_H +#define _BF5XX_AC97_PCM_H + +struct bf5xx_pcm_dma_params { +	char *name;			/* stream identifier */ +}; + +struct bf5xx_gpio { +	u32 sys; +	u32 rx; +	u32 tx; +	u32 clk; +	u32 frm; +}; + +/* platform data */ +extern struct snd_soc_platform bf5xx_ac97_soc_platform; + +#endif diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c new file mode 100644 index 0000000..c782e31 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -0,0 +1,407 @@ +/* + * bf5xx-ac97.c -- AC97 support for the ADI blackfin chip. + * + * Author:	Roy Huang + * Created:	11th. June 2007 + * Copyright:	Analog Device Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/delay.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <asm/irq.h> +#include <asm/portmux.h> +#include <linux/mutex.h> +#include <linux/gpio.h> + +#include "bf5xx-sport.h" +#include "bf5xx-ac97.h" + +#if defined(CONFIG_BF54x) +#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \ +		P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0} + +#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \ +		P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0} + +#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \ +		P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0} + +#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \ +		P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0} +#else +#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ +		 P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0} + +#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ +		 P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0} +#endif + +static int *cmd_count; +static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; + +#if defined(CONFIG_BF54x) +static struct sport_param sport_params[4] = { +	{ +		.dma_rx_chan	= CH_SPORT0_RX, +		.dma_tx_chan	= CH_SPORT0_TX, +		.err_irq	= IRQ_SPORT0_ERR, +		.regs		= (struct sport_register *)SPORT0_TCR1, +	}, +	{ +		.dma_rx_chan	= CH_SPORT1_RX, +		.dma_tx_chan	= CH_SPORT1_TX, +		.err_irq	= IRQ_SPORT1_ERR, +		.regs		= (struct sport_register *)SPORT1_TCR1, +	}, +	{ +		.dma_rx_chan	= CH_SPORT2_RX, +		.dma_tx_chan	= CH_SPORT2_TX, +		.err_irq	= IRQ_SPORT2_ERR, +		.regs		= (struct sport_register *)SPORT2_TCR1, +	}, +	{ +		.dma_rx_chan	= CH_SPORT3_RX, +		.dma_tx_chan	= CH_SPORT3_TX, +		.err_irq	= IRQ_SPORT3_ERR, +		.regs		= (struct sport_register *)SPORT3_TCR1, +	} +}; +#else +static struct sport_param sport_params[2] = { +	{ +		.dma_rx_chan	= CH_SPORT0_RX, +		.dma_tx_chan	= CH_SPORT0_TX, +		.err_irq	= IRQ_SPORT0_ERROR, +		.regs		= (struct sport_register *)SPORT0_TCR1, +	}, +	{ +		.dma_rx_chan	= CH_SPORT1_RX, +		.dma_tx_chan	= CH_SPORT1_TX, +		.err_irq	= IRQ_SPORT1_ERROR, +		.regs		= (struct sport_register *)SPORT1_TCR1, +	} +}; +#endif + +void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \ +		size_t count) +{ +	while (count--) { +		dst->ac97_tag = TAG_VALID | TAG_PCM; +		(dst++)->ac97_pcm = *src++; +	} +} +EXPORT_SYMBOL(bf5xx_pcm_to_ac97); + +void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \ +		size_t count) +{ +	while (count--) +		*(dst++) = (src++)->ac97_pcm; +} +EXPORT_SYMBOL(bf5xx_ac97_to_pcm); + +static unsigned int sport_tx_curr_frag(struct sport_device *sport) +{ +	return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \ +			sport->tx_fragsize; +} + +static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) +{ +	struct sport_device *sport = sport_handle; +	int nextfrag = sport_tx_curr_frag(sport); +	struct ac97_frame *nextwrite; + +	sport_incfrag(sport, &nextfrag, 1); +	sport_incfrag(sport, &nextfrag, 1); + +	nextwrite = (struct ac97_frame *)(sport->tx_buf + \ +			nextfrag * sport->tx_fragsize); +	pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n", +		sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]); +	nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD; +	nextwrite[cmd_count[nextfrag]].ac97_addr = addr; +	nextwrite[cmd_count[nextfrag]].ac97_data = data; +	++cmd_count[nextfrag]; +	pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n", +			addr >> 8, data, nextfrag); +} + +static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97, +	unsigned short reg) +{ +	struct ac97_frame out_frame[2], in_frame[2]; + +	pr_debug("%s enter 0x%x\n", __func__, reg); + +	/* When dma descriptor is enabled, the register should not be read */ +	if (sport_handle->tx_run || sport_handle->rx_run) { +		pr_err("Could you send a mail to cliff.cai@analog.com " +				"to report this?\n"); +		return -EFAULT; +	} + +	memset(&out_frame, 0, 2 * sizeof(struct ac97_frame)); +	memset(&in_frame, 0, 2 * sizeof(struct ac97_frame)); +	out_frame[0].ac97_tag = TAG_VALID | TAG_CMD; +	out_frame[0].ac97_addr = ((reg << 8) | 0x8000); +	sport_send_and_recv(sport_handle, (unsigned char *)&out_frame, +			(unsigned char *)&in_frame, +			2 * sizeof(struct ac97_frame)); +	return in_frame[1].ac97_data; +} + +void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, +	unsigned short val) +{ +	pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val); + +	if (sport_handle->tx_run) { +		enqueue_cmd(ac97, (reg << 8), val); /* write */ +		enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */ +	} else { +		struct ac97_frame frame; +		memset(&frame, 0, sizeof(struct ac97_frame)); +		frame.ac97_tag = TAG_VALID | TAG_CMD; +		frame.ac97_addr = (reg << 8); +		frame.ac97_data = val; +		sport_send_and_recv(sport_handle, (unsigned char *)&frame, \ +				NULL, sizeof(struct ac97_frame)); +	} +} + +static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97) +{ +#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \ + (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1)) + +#define CONCAT(a, b, c) a ## b ## c +#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS) + +	u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM); +	u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM)); + +	pr_debug("%s enter\n", __func__); + +	peripheral_free(per); +	gpio_request(gpio, "bf5xx-ac97"); +	gpio_direction_output(gpio, 1); +	udelay(2); +	gpio_set_value(gpio, 0); +	udelay(1); +	gpio_free(gpio); +	peripheral_request(per, "soc-audio"); +#else +	pr_info("%s: Not implemented\n", __func__); +#endif +} + +static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97) +{ +#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET +	pr_debug("%s enter\n", __func__); + +	/* It is specified for bf548-ezkit */ +	gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0); +	/* Keep reset pin low for 1 ms */ +	mdelay(1); +	gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); +	/* Wait for bit clock recover */ +	mdelay(1); +#else +	pr_info("%s: Not implemented\n", __func__); +#endif +} + +struct snd_ac97_bus_ops soc_ac97_ops = { +	.read	= bf5xx_ac97_read, +	.write	= bf5xx_ac97_write, +	.warm_reset	= bf5xx_ac97_warm_reset, +	.reset	= bf5xx_ac97_cold_reset, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +#ifdef CONFIG_PM +static int bf5xx_ac97_suspend(struct platform_device *pdev, +	struct snd_soc_dai *dai) +{ +	struct sport_device *sport = +		(struct sport_device *)dai->private_data; + +	pr_debug("%s : sport %d\n", __func__, dai->id); +	if (!dai->active) +		return 0; +	if (dai->capture.active) +		sport_rx_stop(sport); +	if (dai->playback.active) +		sport_tx_stop(sport); +	return 0; +} + +static int bf5xx_ac97_resume(struct platform_device *pdev, +	struct snd_soc_dai *dai) +{ +	int ret; +	struct sport_device *sport = +		(struct sport_device *)dai->private_data; + +	pr_debug("%s : sport %d\n", __func__, dai->id); +	if (!dai->active) +		return 0; + +	ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		return -EBUSY; +	} + +	ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		return -EBUSY; +	} + +	ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		return -EBUSY; +	} + +	if (dai->capture.active) +		sport_rx_start(sport); +	if (dai->playback.active) +		sport_tx_start(sport); +	return 0; +} + +#else +#define bf5xx_ac97_suspend	NULL +#define bf5xx_ac97_resume	NULL +#endif + +static int bf5xx_ac97_probe(struct platform_device *pdev, +			    struct snd_soc_dai *dai) +{ +	int ret; +#if defined(CONFIG_BF54x) +	u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1, +				 PIN_REQ_SPORT_2, PIN_REQ_SPORT_3}; +#else +	u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1}; +#endif +	cmd_count = (int *)get_zeroed_page(GFP_KERNEL); +	if (cmd_count == NULL) +		return -ENOMEM; + +	if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { +		pr_err("Requesting Peripherals failed\n"); +		return -EFAULT; +		} + +#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET +	/* Request PB3 as reset pin */ +	if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) { +		pr_err("Failed to request GPIO_%d for reset\n", +				CONFIG_SND_BF5XX_RESET_GPIO_NUM); +		peripheral_free_list(&sport_req[sport_num][0]); +		return -1; +	} +	gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1); +#endif +	sport_handle = sport_init(&sport_params[sport_num], 2, \ +			sizeof(struct ac97_frame), NULL); +	if (!sport_handle) { +		peripheral_free_list(&sport_req[sport_num][0]); +#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET +		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); +#endif +		return -ENODEV; +	} +	/*SPORT works in TDM mode to simulate AC97 transfers*/ +	ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		kfree(sport_handle); +		peripheral_free_list(&sport_req[sport_num][0]); +#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET +		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); +#endif +		return -EBUSY; +	} + +	ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1)); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		kfree(sport_handle); +		peripheral_free_list(&sport_req[sport_num][0]); +#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET +		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); +#endif +		return -EBUSY; +	} + +	ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1)); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		kfree(sport_handle); +		peripheral_free_list(&sport_req[sport_num][0]); +#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET +		gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); +#endif +		return -EBUSY; +	} +	return 0; +} + +static void bf5xx_ac97_remove(struct platform_device *pdev, +			      struct snd_soc_dai *dai) +{ +	free_page((unsigned long)cmd_count); +	cmd_count = NULL; +#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET +	gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM); +#endif +} + +struct snd_soc_dai bfin_ac97_dai = { +	.name = "bf5xx-ac97", +	.id = 0, +	.type = SND_SOC_DAI_AC97, +	.probe = bf5xx_ac97_probe, +	.remove = bf5xx_ac97_remove, +	.suspend = bf5xx_ac97_suspend, +	.resume = bf5xx_ac97_resume, +	.playback = { +		.stream_name = "AC97 Playback", +		.channels_min = 2, +		.channels_max = 2, +		.rates = SNDRV_PCM_RATE_48000, +		.formats = SNDRV_PCM_FMTBIT_S16_LE, }, +	.capture = { +		.stream_name = "AC97 Capture", +		.channels_min = 2, +		.channels_max = 2, +		.rates = SNDRV_PCM_RATE_48000, +		.formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}; +EXPORT_SYMBOL_GPL(bfin_ac97_dai); + +MODULE_AUTHOR("Roy Huang"); +MODULE_DESCRIPTION("AC97 driver for ADI Blackfin"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h new file mode 100644 index 0000000..3f77cc5 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97.h @@ -0,0 +1,36 @@ +/* + * linux/sound/arm/bf5xx-ac97.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_AC97_H +#define _BF5XX_AC97_H + +extern struct snd_ac97_bus_ops bf5xx_ac97_ops; +extern struct snd_ac97 *ac97; +/* Frame format in memory, only support stereo currently */ +struct ac97_frame { +	u16 ac97_tag;		/* slot 0 */ +	u16 ac97_addr;		/* slot 1 */ +	u16 ac97_data;		/* slot 2 */ +	u32 ac97_pcm;		/* slot 3 and 4: left and right pcm data */ +} __attribute__ ((packed)); + +#define TAG_VALID		0x8000 +#define TAG_CMD			0x6000 +#define TAG_PCM_LEFT		0x1000 +#define TAG_PCM_RIGHT		0x0800 +#define TAG_PCM			(TAG_PCM_LEFT | TAG_PCM_RIGHT) + +extern struct snd_soc_dai bfin_ac97_dai; + +void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \ +		size_t count); + +void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \ +		size_t count); + +#endif diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c new file mode 100644 index 0000000..124425d --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad1980.c @@ -0,0 +1,113 @@ +/* + * File:         sound/soc/blackfin/bf5xx-ad1980.c + * Author:       Cliff Cai <Cliff.Cai@analog.com> + * + * Created:      Tue June 06 2008 + * Description:  Board driver for AD1980/1 audio codec + * + * Modified: + *               Copyright 2008 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <asm/dma.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> + +#include <linux/gpio.h> +#include <asm/portmux.h> + +#include "../codecs/ad1980.h" +#include "bf5xx-sport.h" +#include "bf5xx-ac97-pcm.h" +#include "bf5xx-ac97.h" + +static struct snd_soc_machine bf5xx_board; + +static int bf5xx_board_startup(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + +	pr_debug("%s enter\n", __func__); +	cpu_dai->private_data = sport_handle; +	return 0; +} + +static struct snd_soc_ops bf5xx_board_ops = { +	.startup = bf5xx_board_startup, +}; + +static struct snd_soc_dai_link bf5xx_board_dai = { +	.name = "AC97", +	.stream_name = "AC97 HiFi", +	.cpu_dai = &bfin_ac97_dai, +	.codec_dai = &ad1980_dai, +	.ops = &bf5xx_board_ops, +}; + +static struct snd_soc_machine bf5xx_board = { +	.name = "bf5xx-board", +	.dai_link = &bf5xx_board_dai, +	.num_links = 1, +}; + +static struct snd_soc_device bf5xx_board_snd_devdata = { +	.machine = &bf5xx_board, +	.platform = &bf5xx_ac97_soc_platform, +	.codec_dev = &soc_codec_dev_ad1980, +}; + +static struct platform_device *bf5xx_board_snd_device; + +static int __init bf5xx_board_init(void) +{ +	int ret; + +	bf5xx_board_snd_device = platform_device_alloc("soc-audio", -1); +	if (!bf5xx_board_snd_device) +		return -ENOMEM; + +	platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board_snd_devdata); +	bf5xx_board_snd_devdata.dev = &bf5xx_board_snd_device->dev; +	ret = platform_device_add(bf5xx_board_snd_device); + +	if (ret) +		platform_device_put(bf5xx_board_snd_device); + +	return ret; +} + +static void __exit bf5xx_board_exit(void) +{ +	platform_device_unregister(bf5xx_board_snd_device); +} + +module_init(bf5xx_board_init); +module_exit(bf5xx_board_exit); + +/* Module information */ +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c new file mode 100644 index 0000000..61fccf9 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -0,0 +1,288 @@ +/* + * File:         sound/soc/blackfin/bf5xx-i2s-pcm.c + * Author:       Cliff Cai <Cliff.Cai@analog.com> + * + * Created:      Tue June 06 2008 + * Description:  DMA driver for i2s codec + * + * Modified: + *               Copyright 2008 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <asm/dma.h> + +#include "bf5xx-i2s-pcm.h" +#include "bf5xx-i2s.h" +#include "bf5xx-sport.h" + +static void bf5xx_dma_irq(void *data) +{ +	struct snd_pcm_substream *pcm = data; +	snd_pcm_period_elapsed(pcm); +} + +static const struct snd_pcm_hardware bf5xx_pcm_hardware = { +	.info			= SNDRV_PCM_INFO_INTERLEAVED | +				   SNDRV_PCM_INFO_MMAP | +				   SNDRV_PCM_INFO_MMAP_VALID | +				   SNDRV_PCM_INFO_BLOCK_TRANSFER, +	.formats		= SNDRV_PCM_FMTBIT_S16_LE | +				   SNDRV_PCM_FMTBIT_S24_LE | +				   SNDRV_PCM_FMTBIT_S32_LE, +	.period_bytes_min	= 32, +	.period_bytes_max	= 0x10000, +	.periods_min		= 1, +	.periods_max		= PAGE_SIZE/32, +	.buffer_bytes_max	= 0x20000, /* 128 kbytes */ +	.fifo_size		= 16, +}; + +static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	size_t size = bf5xx_pcm_hardware.buffer_bytes_max; +	snd_pcm_lib_malloc_pages(substream, size); + +	return 0; +} + +static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) +{ +	snd_pcm_lib_free_pages(substream); + +	return 0; +} + +static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct sport_device *sport = runtime->private_data; +	int period_bytes = frames_to_bytes(runtime, runtime->period_size); + +	pr_debug("%s enter\n", __func__); +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		sport_set_tx_callback(sport, bf5xx_dma_irq, substream); +		sport_config_tx_dma(sport, runtime->dma_area, +			runtime->periods, period_bytes); +	} else { +		sport_set_rx_callback(sport, bf5xx_dma_irq, substream); +		sport_config_rx_dma(sport, runtime->dma_area, +			runtime->periods, period_bytes); +	} + +	return 0; +} + +static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct sport_device *sport = runtime->private_data; +	int ret = 0; + +	pr_debug("%s enter\n", __func__); +	switch (cmd) { +	case SNDRV_PCM_TRIGGER_START: +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +			sport_tx_start(sport); +		else +			sport_rx_start(sport); +		break; +	case SNDRV_PCM_TRIGGER_STOP: +	case SNDRV_PCM_TRIGGER_SUSPEND: +	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +			sport_tx_stop(sport); +		else +			sport_rx_stop(sport); +		break; +	default: +		ret = -EINVAL; +	} + +	return ret; +} + +static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct sport_device *sport = runtime->private_data; +	unsigned int diff; +	snd_pcm_uframes_t frames; +	pr_debug("%s enter\n", __func__); +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +		diff = sport_curr_offset_tx(sport); +		frames = bytes_to_frames(substream->runtime, diff); +	} else { +		diff = sport_curr_offset_rx(sport); +		frames = bytes_to_frames(substream->runtime, diff); +	} +	return frames; +} + +static int bf5xx_pcm_open(struct snd_pcm_substream *substream) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	int ret; + +	pr_debug("%s enter\n", __func__); +	snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); + +	ret = snd_pcm_hw_constraint_integer(runtime, \ +			SNDRV_PCM_HW_PARAM_PERIODS); +	if (ret < 0) +		goto out; + +	if (sport_handle != NULL) +		runtime->private_data = sport_handle; +	else { +		pr_err("sport_handle is NULL\n"); +		return -1; +	} +	return 0; + + out: +	return ret; +} + +static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, +	struct vm_area_struct *vma) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	size_t size = vma->vm_end - vma->vm_start; +	vma->vm_start = (unsigned long)runtime->dma_area; +	vma->vm_end = vma->vm_start + size; +	vma->vm_flags |=  VM_SHARED; + +	return 0 ; +} + +struct snd_pcm_ops bf5xx_pcm_i2s_ops = { +	.open		= bf5xx_pcm_open, +	.ioctl		= snd_pcm_lib_ioctl, +	.hw_params	= bf5xx_pcm_hw_params, +	.hw_free	= bf5xx_pcm_hw_free, +	.prepare	= bf5xx_pcm_prepare, +	.trigger	= bf5xx_pcm_trigger, +	.pointer	= bf5xx_pcm_pointer, +	.mmap		= bf5xx_pcm_mmap, +}; + +static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ +	struct snd_pcm_substream *substream = pcm->streams[stream].substream; +	struct snd_dma_buffer *buf = &substream->dma_buffer; +	size_t size = bf5xx_pcm_hardware.buffer_bytes_max; + +	buf->dev.type = SNDRV_DMA_TYPE_DEV; +	buf->dev.dev = pcm->card->dev; +	buf->private_data = NULL; +	buf->area = dma_alloc_coherent(pcm->card->dev, size, +			&buf->addr, GFP_KERNEL); +	if (!buf->area) { +		pr_err("Failed to allocate dma memory \ +			Please increase uncached DMA memory region\n"); +		return -ENOMEM; +	} +	buf->bytes = size; + +	pr_debug("%s, area:%p, size:0x%08lx\n", __func__, +		buf->area, buf->bytes); + +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) +		sport_handle->tx_buf = buf->area; +	else +		sport_handle->rx_buf = buf->area; + +	return 0; +} + +static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ +	struct snd_pcm_substream *substream; +	struct snd_dma_buffer *buf; +	int stream; + +	for (stream = 0; stream < 2; stream++) { +		substream = pcm->streams[stream].substream; +		if (!substream) +			continue; + +		buf = &substream->dma_buffer; +		if (!buf->area) +			continue; +		dma_free_coherent(NULL, buf->bytes, buf->area, 0); +		buf->area = NULL; +	} +	if (sport_handle) +		sport_done(sport_handle); +} + +static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK; + +int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai, +	struct snd_pcm *pcm) +{ +	int ret = 0; + +	pr_debug("%s enter\n", __func__); +	if (!card->dev->dma_mask) +		card->dev->dma_mask = &bf5xx_pcm_dmamask; +	if (!card->dev->coherent_dma_mask) +		card->dev->coherent_dma_mask = DMA_32BIT_MASK; + +	if (dai->playback.channels_min) { +		ret = bf5xx_pcm_preallocate_dma_buffer(pcm, +			SNDRV_PCM_STREAM_PLAYBACK); +		if (ret) +			goto out; +	} + +	if (dai->capture.channels_min) { +		ret = bf5xx_pcm_preallocate_dma_buffer(pcm, +			SNDRV_PCM_STREAM_CAPTURE); +		if (ret) +			goto out; +	} + out: +	return ret; +} + +struct snd_soc_platform bf5xx_i2s_soc_platform = { +	.name		= "bf5xx-audio", +	.pcm_ops 	= &bf5xx_pcm_i2s_ops, +	.pcm_new	= bf5xx_pcm_i2s_new, +	.pcm_free	= bf5xx_pcm_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform); + +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h new file mode 100644 index 0000000..4d4609a --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h @@ -0,0 +1,29 @@ +/* + * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin + * + * Copyright 2007 Analog Device Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_I2S_PCM_H +#define _BF5XX_I2S_PCM_H + +struct bf5xx_pcm_dma_params { +	char *name;			/* stream identifier */ +}; + +struct bf5xx_gpio { +	u32 sys; +	u32 rx; +	u32 tx; +	u32 clk; +	u32 frm; +}; + +/* platform data */ +extern struct snd_soc_platform bf5xx_i2s_soc_platform; + +#endif diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c new file mode 100644 index 0000000..43a4092 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -0,0 +1,292 @@ +/* + * File:         sound/soc/blackfin/bf5xx-i2s.c + * Author:       Cliff Cai <Cliff.Cai@analog.com> + * + * Created:      Tue June 06 2008 + * Description:  Blackfin I2S CPU DAI driver + * + * Modified: + *               Copyright 2008 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include <asm/irq.h> +#include <asm/portmux.h> +#include <linux/mutex.h> +#include <linux/gpio.h> + +#include "bf5xx-sport.h" +#include "bf5xx-i2s.h" + +struct bf5xx_i2s_port { +	u16 tcr1; +	u16 rcr1; +	u16 tcr2; +	u16 rcr2; +	int counter; +}; + +static struct bf5xx_i2s_port bf5xx_i2s; +static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM; + +static struct sport_param sport_params[2] = { +	{ +		.dma_rx_chan	= CH_SPORT0_RX, +		.dma_tx_chan	= CH_SPORT0_TX, +		.err_irq	= IRQ_SPORT0_ERROR, +		.regs		= (struct sport_register *)SPORT0_TCR1, +	}, +	{ +		.dma_rx_chan	= CH_SPORT1_RX, +		.dma_tx_chan	= CH_SPORT1_TX, +		.err_irq	= IRQ_SPORT1_ERROR, +		.regs		= (struct sport_register *)SPORT1_TCR1, +	} +}; + +static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, +		unsigned int fmt) +{ +	int ret = 0; + +	/* interface format:support I2S,slave mode */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_I2S: +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		ret = -EINVAL; +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBS_CFS: +		ret = -EINVAL; +		break; +	case SND_SOC_DAIFMT_CBM_CFS: +		ret = -EINVAL; +		break; +	case SND_SOC_DAIFMT_CBM_CFM: +		break; +	case SND_SOC_DAIFMT_CBS_CFM: +		ret = -EINVAL; +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} + +static int bf5xx_i2s_startup(struct snd_pcm_substream *substream) +{ +	pr_debug("%s enter\n", __func__); + +	/*this counter is used for counting how many pcm streams are opened*/ +	bf5xx_i2s.counter++; +	return 0; +} + +static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, +				struct snd_pcm_hw_params *params) +{ +	int ret = 0; + +	bf5xx_i2s.tcr2 &= ~0x1f; +	bf5xx_i2s.rcr2 &= ~0x1f; +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S16_LE: +		bf5xx_i2s.tcr2 |= 15; +		bf5xx_i2s.rcr2 |= 15; +		break; +	case SNDRV_PCM_FORMAT_S24_LE: +		bf5xx_i2s.tcr2 |= 23; +		bf5xx_i2s.rcr2 |= 23; +		break; +	case SNDRV_PCM_FORMAT_S32_LE: +		bf5xx_i2s.tcr2 |= 31; +		bf5xx_i2s.rcr2 |= 31; +		break; +	} + +	if (bf5xx_i2s.counter == 1) { +		/* +		 * TX and RX are not independent,they are enabled at the +		 * same time, even if only one side is running. So, we +		 * need to configure both of them at the time when the first +		 * stream is opened. +		 * +		 * CPU DAI format:I2S, slave mode. +		 */ +		ret = sport_config_rx(sport_handle, RFSR | RCKFE, +				      RSFSE|bf5xx_i2s.rcr2, 0, 0); +		if (ret) { +			pr_err("SPORT is busy!\n"); +			return -EBUSY; +		} + +		ret = sport_config_tx(sport_handle, TFSR | TCKFE, +				      TSFSE|bf5xx_i2s.tcr2, 0, 0); +		if (ret) { +			pr_err("SPORT is busy!\n"); +			return -EBUSY; +		} +	} + +	return 0; +} + +static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream) +{ +	pr_debug("%s enter\n", __func__); +	bf5xx_i2s.counter--; +} + +static int bf5xx_i2s_probe(struct platform_device *pdev, +			   struct snd_soc_dai *dai) +{ +	u16 sport_req[][7] = { +		{ P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, +		  P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}, +		{ P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, +		  P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}, +	}; + +	pr_debug("%s enter\n", __func__); +	if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { +		pr_err("Requesting Peripherals failed\n"); +		return -EFAULT; +	} + +	/* request DMA for SPORT */ +	sport_handle = sport_init(&sport_params[sport_num], 4, \ +			2 * sizeof(u32), NULL); +	if (!sport_handle) { +		peripheral_free_list(&sport_req[sport_num][0]); +		return -ENODEV; +	} + +	return 0; +} + +#ifdef CONFIG_PM +static int bf5xx_i2s_suspend(struct platform_device *dev, +			     struct snd_soc_dai *dai) +{ +	struct sport_device *sport = +		(struct sport_device *)dai->private_data; + +	pr_debug("%s : sport %d\n", __func__, dai->id); +	if (!dai->active) +		return 0; +	if (dai->capture.active) +		sport_rx_stop(sport); +	if (dai->playback.active) +		sport_tx_stop(sport); +	return 0; +} + +static int bf5xx_i2s_resume(struct platform_device *pdev, +			    struct snd_soc_dai *dai) +{ +	int ret; +	struct sport_device *sport = +		(struct sport_device *)dai->private_data; + +	pr_debug("%s : sport %d\n", __func__, dai->id); +	if (!dai->active) +		return 0; + +	ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		return -EBUSY; +	} + +	ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0); +	if (ret) { +		pr_err("SPORT is busy!\n"); +		return -EBUSY; +	} + +	if (dai->capture.active) +		sport_rx_start(sport); +	if (dai->playback.active) +		sport_tx_start(sport); +	return 0; +} + +#else +#define bf5xx_i2s_suspend	NULL +#define bf5xx_i2s_resume	NULL +#endif + +#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ +		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ +		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ +		SNDRV_PCM_RATE_96000) + +#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\ +	SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_dai bf5xx_i2s_dai = { +	.name = "bf5xx-i2s", +	.id = 0, +	.type = SND_SOC_DAI_I2S, +	.probe = bf5xx_i2s_probe, +	.suspend = bf5xx_i2s_suspend, +	.resume = bf5xx_i2s_resume, +	.playback = { +		.channels_min = 2, +		.channels_max = 2, +		.rates = BF5XX_I2S_RATES, +		.formats = BF5XX_I2S_FORMATS,}, +	.capture = { +		.channels_min = 2, +		.channels_max = 2, +		.rates = BF5XX_I2S_RATES, +		.formats = BF5XX_I2S_FORMATS,}, +	.ops = { +		.startup   = bf5xx_i2s_startup, +		.shutdown  = bf5xx_i2s_shutdown, +		.hw_params = bf5xx_i2s_hw_params,}, +	.dai_ops = { +		.set_fmt = bf5xx_i2s_set_dai_fmt, +	}, +}; +EXPORT_SYMBOL_GPL(bf5xx_i2s_dai); + +/* Module information */ +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("I2S driver for ADI Blackfin"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-i2s.h b/sound/soc/blackfin/bf5xx-i2s.h new file mode 100644 index 0000000..7107d1a --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s.h @@ -0,0 +1,14 @@ +/* + * linux/sound/arm/bf5xx-i2s.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _BF5XX_I2S_H +#define _BF5XX_I2S_H + +extern struct snd_soc_dai bf5xx_i2s_dai; + +#endif diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c new file mode 100644 index 0000000..3b99e48 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-sport.c @@ -0,0 +1,1032 @@ +/* + * File:         bf5xx_sport.c + * Based on: + * Author:       Roy Huang <roy.huang@analog.com> + * + * Created:      Tue Sep 21 10:52:42 CEST 2004 + * Description: + *               Blackfin SPORT Driver + * + *               Copyright 2004-2007 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/gpio.h> +#include <linux/bug.h> +#include <asm/portmux.h> +#include <asm/dma.h> +#include <asm/blackfin.h> +#include <asm/cacheflush.h> + +#include "bf5xx-sport.h" +/* delay between frame sync pulse and first data bit in multichannel mode */ +#define FRAME_DELAY (1<<12) + +struct sport_device *sport_handle; +EXPORT_SYMBOL(sport_handle); +/* note: multichannel is in units of 8 channels, + * tdm_count is # channels NOT / 8 ! */ +int sport_set_multichannel(struct sport_device *sport, +		int tdm_count, u32 mask, int packed) +{ +	pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__, +			tdm_count, mask, packed); + +	if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) +		return -EBUSY; + +	if (tdm_count & 0x7) +		return -EINVAL; + +	if (tdm_count > 32) +		return -EINVAL; /* Only support less than 32 channels now */ + +	if (tdm_count) { +		sport->regs->mcmc1 = ((tdm_count>>3)-1) << 12; +		sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \ +				(packed ? (MCDTXPE|MCDRXPE) : 0); + +		sport->regs->mtcs0 = mask; +		sport->regs->mrcs0 = mask; +		sport->regs->mtcs1 = 0; +		sport->regs->mrcs1 = 0; +		sport->regs->mtcs2 = 0; +		sport->regs->mrcs2 = 0; +		sport->regs->mtcs3 = 0; +		sport->regs->mrcs3 = 0; +	} else { +		sport->regs->mcmc1 = 0; +		sport->regs->mcmc2 = 0; + +		sport->regs->mtcs0 = 0; +		sport->regs->mrcs0 = 0; +	} + +	sport->regs->mtcs1 = 0; sport->regs->mtcs2 = 0; sport->regs->mtcs3 = 0; +	sport->regs->mrcs1 = 0; sport->regs->mrcs2 = 0; sport->regs->mrcs3 = 0; + +	SSYNC(); + +	return 0; +} +EXPORT_SYMBOL(sport_set_multichannel); + +int sport_config_rx(struct sport_device *sport, unsigned int rcr1, +		unsigned int rcr2, unsigned int clkdiv, unsigned int fsdiv) +{ +	if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) +		return -EBUSY; + +	sport->regs->rcr1 = rcr1; +	sport->regs->rcr2 = rcr2; +	sport->regs->rclkdiv = clkdiv; +	sport->regs->rfsdiv = fsdiv; + +	SSYNC(); + +	return 0; +} +EXPORT_SYMBOL(sport_config_rx); + +int sport_config_tx(struct sport_device *sport, unsigned int tcr1, +		unsigned int tcr2, unsigned int clkdiv, unsigned int fsdiv) +{ +	if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN)) +		return -EBUSY; + +	sport->regs->tcr1 = tcr1; +	sport->regs->tcr2 = tcr2; +	sport->regs->tclkdiv = clkdiv; +	sport->regs->tfsdiv = fsdiv; + +	SSYNC(); + +	return 0; +} +EXPORT_SYMBOL(sport_config_tx); + +static void setup_desc(struct dmasg *desc, void *buf, int fragcount, +		size_t fragsize, unsigned int cfg, +		unsigned int x_count, unsigned int ycount, size_t wdsize) +{ + +	int i; + +	for (i = 0; i < fragcount; ++i) { +		desc[i].next_desc_addr  = (unsigned long)&(desc[i + 1]); +		desc[i].start_addr = (unsigned long)buf + i*fragsize; +		desc[i].cfg = cfg; +		desc[i].x_count = x_count; +		desc[i].x_modify = wdsize; +		desc[i].y_count = ycount; +		desc[i].y_modify = wdsize; +	} + +	/* make circular */ +	desc[fragcount-1].next_desc_addr = (unsigned long)desc; + +	pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p," +		"next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n", +		&(desc[0]), desc[0].next_desc_addr, +		&(desc[1]), desc[1].next_desc_addr, +		desc[0].x_count, desc[0].y_count, +		desc[0].start_addr, desc[0].cfg); +} + +static int sport_start(struct sport_device *sport) +{ +	enable_dma(sport->dma_rx_chan); +	enable_dma(sport->dma_tx_chan); +	sport->regs->rcr1 |= RSPEN; +	sport->regs->tcr1 |= TSPEN; +	SSYNC(); + +	return 0; +} + +static int sport_stop(struct sport_device *sport) +{ +	sport->regs->tcr1 &= ~TSPEN; +	sport->regs->rcr1 &= ~RSPEN; +	SSYNC(); + +	disable_dma(sport->dma_rx_chan); +	disable_dma(sport->dma_tx_chan); +	return 0; +} + +static inline int sport_hook_rx_dummy(struct sport_device *sport) +{ +	struct dmasg *desc, temp_desc; +	unsigned long flags; + +	BUG_ON(sport->dummy_rx_desc == NULL); +	BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc); + +	/* Maybe the dummy buffer descriptor ring is damaged */ +	sport->dummy_rx_desc->next_desc_addr = \ +			(unsigned long)(sport->dummy_rx_desc+1); + +	local_irq_save(flags); +	desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan); +	/* Copy the descriptor which will be damaged to backup */ +	temp_desc = *desc; +	desc->x_count = 0xa; +	desc->y_count = 0; +	desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc); +	local_irq_restore(flags); +	/* Waiting for dummy buffer descriptor is already hooked*/ +	while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) - +			sizeof(struct dmasg)) != +			(unsigned long)sport->dummy_rx_desc) +		; +	sport->curr_rx_desc = sport->dummy_rx_desc; +	/* Restore the damaged descriptor */ +	*desc = temp_desc; + +	return 0; +} + +static inline int sport_rx_dma_start(struct sport_device *sport, int dummy) +{ +	if (dummy) { +		sport->dummy_rx_desc->next_desc_addr = \ +				(unsigned long) sport->dummy_rx_desc; +		sport->curr_rx_desc = sport->dummy_rx_desc; +	} else +		sport->curr_rx_desc = sport->dma_rx_desc; + +	set_dma_next_desc_addr(sport->dma_rx_chan, \ +			(unsigned long)(sport->curr_rx_desc)); +	set_dma_x_count(sport->dma_rx_chan, 0); +	set_dma_x_modify(sport->dma_rx_chan, 0); +	set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \ +				WDSIZE_32 | WNR)); +	set_dma_curr_addr(sport->dma_rx_chan, sport->curr_rx_desc->start_addr); +	SSYNC(); + +	return 0; +} + +static inline int sport_tx_dma_start(struct sport_device *sport, int dummy) +{ +	if (dummy) { +		sport->dummy_tx_desc->next_desc_addr = \ +				(unsigned long) sport->dummy_tx_desc; +		sport->curr_tx_desc = sport->dummy_tx_desc; +	} else +		sport->curr_tx_desc = sport->dma_tx_desc; + +	set_dma_next_desc_addr(sport->dma_tx_chan, \ +			(unsigned long)(sport->curr_tx_desc)); +	set_dma_x_count(sport->dma_tx_chan, 0); +	set_dma_x_modify(sport->dma_tx_chan, 0); +	set_dma_config(sport->dma_tx_chan, +			(DMAFLOW_LARGE | NDSIZE_9 | WDSIZE_32)); +	set_dma_curr_addr(sport->dma_tx_chan, sport->curr_tx_desc->start_addr); +	SSYNC(); + +	return 0; +} + +int sport_rx_start(struct sport_device *sport) +{ +	unsigned long flags; +	pr_debug("%s enter\n", __func__); +	if (sport->rx_run) +		return -EBUSY; +	if (sport->tx_run) { +		/* tx is running, rx is not running */ +		BUG_ON(sport->dma_rx_desc == NULL); +		BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc); +		local_irq_save(flags); +		while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) - +			sizeof(struct dmasg)) != +			(unsigned long)sport->dummy_rx_desc) +			; +		sport->dummy_rx_desc->next_desc_addr = +				(unsigned long)(sport->dma_rx_desc); +		local_irq_restore(flags); +		sport->curr_rx_desc = sport->dma_rx_desc; +	} else { +		sport_tx_dma_start(sport, 1); +		sport_rx_dma_start(sport, 0); +		sport_start(sport); +	} + +	sport->rx_run = 1; + +	return 0; +} +EXPORT_SYMBOL(sport_rx_start); + +int sport_rx_stop(struct sport_device *sport) +{ +	pr_debug("%s enter\n", __func__); + +	if (!sport->rx_run) +		return 0; +	if (sport->tx_run) { +		/* TX dma is still running, hook the dummy buffer */ +		sport_hook_rx_dummy(sport); +	} else { +		/* Both rx and tx dma will be stopped */ +		sport_stop(sport); +		sport->curr_rx_desc = NULL; +		sport->curr_tx_desc = NULL; +	} + +	sport->rx_run = 0; + +	return 0; +} +EXPORT_SYMBOL(sport_rx_stop); + +static inline int sport_hook_tx_dummy(struct sport_device *sport) +{ +	struct dmasg *desc, temp_desc; +	unsigned long flags; + +	BUG_ON(sport->dummy_tx_desc == NULL); +	BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc); + +	sport->dummy_tx_desc->next_desc_addr = \ +			(unsigned long)(sport->dummy_tx_desc+1); + +	/* Shorten the time on last normal descriptor */ +	local_irq_save(flags); +	desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan); +	/* Store the descriptor which will be damaged */ +	temp_desc = *desc; +	desc->x_count = 0xa; +	desc->y_count = 0; +	desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc); +	local_irq_restore(flags); +	/* Waiting for dummy buffer descriptor is already hooked*/ +	while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \ +			sizeof(struct dmasg)) != \ +			(unsigned long)sport->dummy_tx_desc) +		; +	sport->curr_tx_desc = sport->dummy_tx_desc; +	/* Restore the damaged descriptor */ +	*desc = temp_desc; + +	return 0; +} + +int sport_tx_start(struct sport_device *sport) +{ +	unsigned flags; +	pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__, +			sport->tx_run, sport->rx_run); +	if (sport->tx_run) +		return -EBUSY; +	if (sport->rx_run) { +		BUG_ON(sport->dma_tx_desc == NULL); +		BUG_ON(sport->curr_tx_desc != sport->dummy_tx_desc); +		/* Hook the normal buffer descriptor */ +		local_irq_save(flags); +		while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - +			sizeof(struct dmasg)) != +			(unsigned long)sport->dummy_tx_desc) +			; +		sport->dummy_tx_desc->next_desc_addr = +				(unsigned long)(sport->dma_tx_desc); +		local_irq_restore(flags); +		sport->curr_tx_desc = sport->dma_tx_desc; +	} else { + +		sport_tx_dma_start(sport, 0); +		/* Let rx dma run the dummy buffer */ +		sport_rx_dma_start(sport, 1); +		sport_start(sport); +	} +	sport->tx_run = 1; +	return 0; +} +EXPORT_SYMBOL(sport_tx_start); + +int sport_tx_stop(struct sport_device *sport) +{ +	if (!sport->tx_run) +		return 0; +	if (sport->rx_run) { +		/* RX is still running, hook the dummy buffer */ +		sport_hook_tx_dummy(sport); +	} else { +		/* Both rx and tx dma stopped */ +		sport_stop(sport); +		sport->curr_rx_desc = NULL; +		sport->curr_tx_desc = NULL; +	} + +	sport->tx_run = 0; + +	return 0; +} +EXPORT_SYMBOL(sport_tx_stop); + +static inline int compute_wdsize(size_t wdsize) +{ +	switch (wdsize) { +	case 1: +		return WDSIZE_8; +	case 2: +		return WDSIZE_16; +	case 4: +	default: +		return WDSIZE_32; +	} +} + +int sport_config_rx_dma(struct sport_device *sport, void *buf, +		int fragcount, size_t fragsize) +{ +	unsigned int x_count; +	unsigned int y_count; +	unsigned int cfg; +	dma_addr_t addr; + +	pr_debug("%s buf:%p, frag:%d, fragsize:0x%lx\n", __func__, \ +			buf, fragcount, fragsize); + +	x_count = fragsize / sport->wdsize; +	y_count = 0; + +	/* for fragments larger than 64k words we use 2d dma, +	 * denote fragecount as two numbers' mutliply and both of them +	 * are less than 64k.*/ +	if (x_count >= 0x10000) { +		int i, count = x_count; + +		for (i = 16; i > 0; i--) { +			x_count = 1 << i; +			if ((count & (x_count - 1)) == 0) { +				y_count = count >> i; +				if (y_count < 0x10000) +					break; +			} +		} +		if (i == 0) +			return -EINVAL; +	} +	pr_debug("%s(x_count:0x%x, y_count:0x%x)\n", __func__, +			x_count, y_count); + +	if (sport->dma_rx_desc) +		dma_free_coherent(NULL, sport->rx_desc_bytes, +					sport->dma_rx_desc, 0); + +	/* Allocate a new descritor ring as current one. */ +	sport->dma_rx_desc = dma_alloc_coherent(NULL, \ +			fragcount * sizeof(struct dmasg), &addr, 0); +	sport->rx_desc_bytes = fragcount * sizeof(struct dmasg); + +	if (!sport->dma_rx_desc) { +		pr_err("Failed to allocate memory for rx desc\n"); +		return -ENOMEM; +	} + +	sport->rx_buf = buf; +	sport->rx_fragsize = fragsize; +	sport->rx_frags = fragcount; + +	cfg     = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | WNR | \ +		  (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */ + +	if (y_count != 0) +		cfg |= DMA2D; + +	setup_desc(sport->dma_rx_desc, buf, fragcount, fragsize, +			cfg|DMAEN, x_count, y_count, sport->wdsize); + +	return 0; +} +EXPORT_SYMBOL(sport_config_rx_dma); + +int sport_config_tx_dma(struct sport_device *sport, void *buf, \ +		int fragcount, size_t fragsize) +{ +	unsigned int x_count; +	unsigned int y_count; +	unsigned int cfg; +	dma_addr_t addr; + +	pr_debug("%s buf:%p, fragcount:%d, fragsize:0x%lx\n", +			__func__, buf, fragcount, fragsize); + +	x_count = fragsize/sport->wdsize; +	y_count = 0; + +	/* for fragments larger than 64k words we use 2d dma, +	 * denote fragecount as two numbers' mutliply and both of them +	 * are less than 64k.*/ +	if (x_count >= 0x10000) { +		int i, count = x_count; + +		for (i = 16; i > 0; i--) { +			x_count = 1 << i; +			if ((count & (x_count - 1)) == 0) { +				y_count = count >> i; +				if (y_count < 0x10000) +					break; +			} +		} +		if (i == 0) +			return -EINVAL; +	} +	pr_debug("%s x_count:0x%x, y_count:0x%x\n", __func__, +			x_count, y_count); + + +	if (sport->dma_tx_desc) { +		dma_free_coherent(NULL, sport->tx_desc_bytes, \ +				sport->dma_tx_desc, 0); +	} + +	sport->dma_tx_desc = dma_alloc_coherent(NULL, \ +			fragcount * sizeof(struct dmasg), &addr, 0); +	sport->tx_desc_bytes = fragcount * sizeof(struct dmasg); +	if (!sport->dma_tx_desc) { +		pr_err("Failed to allocate memory for tx desc\n"); +		return -ENOMEM; +	} + +	sport->tx_buf = buf; +	sport->tx_fragsize = fragsize; +	sport->tx_frags = fragcount; +	cfg     = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | \ +		  (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */ + +	if (y_count != 0) +		cfg |= DMA2D; + +	setup_desc(sport->dma_tx_desc, buf, fragcount, fragsize, +			cfg|DMAEN, x_count, y_count, sport->wdsize); + +	return 0; +} +EXPORT_SYMBOL(sport_config_tx_dma); + +/* setup dummy dma descriptor ring, which don't generate interrupts, + * the x_modify is set to 0 */ +static int sport_config_rx_dummy(struct sport_device *sport) +{ +	struct dmasg *desc; +	unsigned config; + +	pr_debug("%s entered\n", __func__); +#if L1_DATA_A_LENGTH != 0 +	desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc)); +#else +	{ +		dma_addr_t addr; +		desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0); +	} +#endif +	if (desc == NULL) { +		pr_err("Failed to allocate memory for dummy rx desc\n"); +		return -ENOMEM; +	} +	memset(desc, 0, 2 * sizeof(*desc)); +	sport->dummy_rx_desc = desc; +	desc->start_addr = (unsigned long)sport->dummy_buf; +	config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize) +		 | WNR | DMAEN; +	desc->cfg = config; +	desc->x_count = sport->dummy_count/sport->wdsize; +	desc->x_modify = sport->wdsize; +	desc->y_count = 0; +	desc->y_modify = 0; +	memcpy(desc+1, desc, sizeof(*desc)); +	desc->next_desc_addr = (unsigned long)(desc+1); +	desc[1].next_desc_addr = (unsigned long)desc; +	return 0; +} + +static int sport_config_tx_dummy(struct sport_device *sport) +{ +	struct dmasg *desc; +	unsigned int config; + +	pr_debug("%s entered\n", __func__); + +#if L1_DATA_A_LENGTH != 0 +	desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc)); +#else +	{ +		dma_addr_t addr; +		desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0); +	} +#endif +	if (!desc) { +		pr_err("Failed to allocate memory for dummy tx desc\n"); +		return -ENOMEM; +	} +	memset(desc, 0, 2 * sizeof(*desc)); +	sport->dummy_tx_desc = desc; +	desc->start_addr = (unsigned long)sport->dummy_buf + \ +		sport->dummy_count; +	config = DMAFLOW_LARGE | NDSIZE_9 | +		 compute_wdsize(sport->wdsize) | DMAEN; +	desc->cfg = config; +	desc->x_count = sport->dummy_count/sport->wdsize; +	desc->x_modify = sport->wdsize; +	desc->y_count = 0; +	desc->y_modify = 0; +	memcpy(desc+1, desc, sizeof(*desc)); +	desc->next_desc_addr = (unsigned long)(desc+1); +	desc[1].next_desc_addr = (unsigned long)desc; +	return 0; +} + +unsigned long sport_curr_offset_rx(struct sport_device *sport) +{ +	unsigned long curr = get_dma_curr_addr(sport->dma_rx_chan); + +	return (unsigned char *)curr - sport->rx_buf; +} +EXPORT_SYMBOL(sport_curr_offset_rx); + +unsigned long sport_curr_offset_tx(struct sport_device *sport) +{ +	unsigned long curr = get_dma_curr_addr(sport->dma_tx_chan); + +	return (unsigned char *)curr - sport->tx_buf; +} +EXPORT_SYMBOL(sport_curr_offset_tx); + +void sport_incfrag(struct sport_device *sport, int *frag, int tx) +{ +	++(*frag); +	if (tx == 1 && *frag == sport->tx_frags) +		*frag = 0; + +	if (tx == 0 && *frag == sport->rx_frags) +		*frag = 0; +} +EXPORT_SYMBOL(sport_incfrag); + +void sport_decfrag(struct sport_device *sport, int *frag, int tx) +{ +	--(*frag); +	if (tx == 1 && *frag == 0) +		*frag = sport->tx_frags; + +	if (tx == 0 && *frag == 0) +		*frag = sport->rx_frags; +} +EXPORT_SYMBOL(sport_decfrag); + +static int sport_check_status(struct sport_device *sport, +		unsigned int *sport_stat, +		unsigned int *rx_stat, +		unsigned int *tx_stat) +{ +	int status = 0; + +	if (sport_stat) { +		SSYNC(); +		status = sport->regs->stat; +		if (status & (TOVF|TUVF|ROVF|RUVF)) +			sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF)); +		SSYNC(); +		*sport_stat = status; +	} + +	if (rx_stat) { +		SSYNC(); +		status = get_dma_curr_irqstat(sport->dma_rx_chan); +		if (status & (DMA_DONE|DMA_ERR)) +			clear_dma_irqstat(sport->dma_rx_chan); +		SSYNC(); +		*rx_stat = status; +	} + +	if (tx_stat) { +		SSYNC(); +		status = get_dma_curr_irqstat(sport->dma_tx_chan); +		if (status & (DMA_DONE|DMA_ERR)) +			clear_dma_irqstat(sport->dma_tx_chan); +		SSYNC(); +		*tx_stat = status; +	} + +	return 0; +} + +int  sport_dump_stat(struct sport_device *sport, char *buf, size_t len) +{ +	int ret; + +	ret = snprintf(buf, len, +			"sts: 0x%04x\n" +			"rx dma %d sts: 0x%04x tx dma %d sts: 0x%04x\n", +			sport->regs->stat, +			sport->dma_rx_chan, +			get_dma_curr_irqstat(sport->dma_rx_chan), +			sport->dma_tx_chan, +			get_dma_curr_irqstat(sport->dma_tx_chan)); +	buf += ret; +	len -= ret; + +	ret += snprintf(buf, len, +			"curr_rx_desc:0x%p, curr_tx_desc:0x%p\n" +			"dma_rx_desc:0x%p, dma_tx_desc:0x%p\n" +			"dummy_rx_desc:0x%p, dummy_tx_desc:0x%p\n", +			sport->curr_rx_desc, sport->curr_tx_desc, +			sport->dma_rx_desc, sport->dma_tx_desc, +			sport->dummy_rx_desc, sport->dummy_tx_desc); + +	return ret; +} + +static irqreturn_t rx_handler(int irq, void *dev_id) +{ +	unsigned int rx_stat; +	struct sport_device *sport = dev_id; + +	pr_debug("%s enter\n", __func__); +	sport_check_status(sport, NULL, &rx_stat, NULL); +	if (!(rx_stat & DMA_DONE)) +		pr_err("rx dma is already stopped\n"); + +	if (sport->rx_callback) { +		sport->rx_callback(sport->rx_data); +		return IRQ_HANDLED; +	} + +	return IRQ_NONE; +} + +static irqreturn_t tx_handler(int irq, void *dev_id) +{ +	unsigned int tx_stat; +	struct sport_device *sport = dev_id; +	pr_debug("%s enter\n", __func__); +	sport_check_status(sport, NULL, NULL, &tx_stat); +	if (!(tx_stat & DMA_DONE)) { +		pr_err("tx dma is already stopped\n"); +		return IRQ_HANDLED; +	} +	if (sport->tx_callback) { +		sport->tx_callback(sport->tx_data); +		return IRQ_HANDLED; +	} + +	return IRQ_NONE; +} + +static irqreturn_t err_handler(int irq, void *dev_id) +{ +	unsigned int status = 0; +	struct sport_device *sport = dev_id; + +	pr_debug("%s\n", __func__); +	if (sport_check_status(sport, &status, NULL, NULL)) { +		pr_err("error checking status ??"); +		return IRQ_NONE; +	} + +	if (status & (TOVF|TUVF|ROVF|RUVF)) { +		pr_info("sport status error:%s%s%s%s\n", +				status & TOVF ? " TOVF" : "", +				status & TUVF ? " TUVF" : "", +				status & ROVF ? " ROVF" : "", +				status & RUVF ? " RUVF" : ""); +		if (status & TOVF || status & TUVF) { +			disable_dma(sport->dma_tx_chan); +			if (sport->tx_run) +				sport_tx_dma_start(sport, 0); +			else +				sport_tx_dma_start(sport, 1); +			enable_dma(sport->dma_tx_chan); +		} else { +			disable_dma(sport->dma_rx_chan); +			if (sport->rx_run) +				sport_rx_dma_start(sport, 0); +			else +				sport_rx_dma_start(sport, 1); +			enable_dma(sport->dma_rx_chan); +		} +	} +	status = sport->regs->stat; +	if (status & (TOVF|TUVF|ROVF|RUVF)) +		sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF)); +	SSYNC(); + +	if (sport->err_callback) +		sport->err_callback(sport->err_data); + +	return IRQ_HANDLED; +} + +int sport_set_rx_callback(struct sport_device *sport, +		       void (*rx_callback)(void *), void *rx_data) +{ +	BUG_ON(rx_callback == NULL); +	sport->rx_callback = rx_callback; +	sport->rx_data = rx_data; + +	return 0; +} +EXPORT_SYMBOL(sport_set_rx_callback); + +int sport_set_tx_callback(struct sport_device *sport, +		void (*tx_callback)(void *), void *tx_data) +{ +	BUG_ON(tx_callback == NULL); +	sport->tx_callback = tx_callback; +	sport->tx_data = tx_data; + +	return 0; +} +EXPORT_SYMBOL(sport_set_tx_callback); + +int sport_set_err_callback(struct sport_device *sport, +		void (*err_callback)(void *), void *err_data) +{ +	BUG_ON(err_callback == NULL); +	sport->err_callback = err_callback; +	sport->err_data = err_data; + +	return 0; +} +EXPORT_SYMBOL(sport_set_err_callback); + +struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, +		unsigned dummy_count, void *private_data) +{ +	int ret; +	struct sport_device *sport; +	pr_debug("%s enter\n", __func__); +	BUG_ON(param == NULL); +	BUG_ON(wdsize == 0 || dummy_count == 0); +	sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL); +	if (!sport) { +		pr_err("Failed to allocate for sport device\n"); +		return NULL; +	} + +	memset(sport, 0, sizeof(struct sport_device)); +	sport->dma_rx_chan = param->dma_rx_chan; +	sport->dma_tx_chan = param->dma_tx_chan; +	sport->err_irq = param->err_irq; +	sport->regs = param->regs; +	sport->private_data = private_data; + +	if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) { +		pr_err("Failed to request RX dma %d\n", \ +				sport->dma_rx_chan); +		goto __init_err1; +	} +	if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) { +		pr_err("Failed to request RX irq %d\n", \ +				sport->dma_rx_chan); +		goto __init_err2; +	} + +	if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) { +		pr_err("Failed to request TX dma %d\n", \ +				sport->dma_tx_chan); +		goto __init_err2; +	} + +	if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) { +		pr_err("Failed to request TX irq %d\n", \ +				sport->dma_tx_chan); +		goto __init_err3; +	} + +	if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err", +			sport) < 0) { +		pr_err("Failed to request err irq:%d\n", \ +				sport->err_irq); +		goto __init_err3; +	} + +	pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n", +			sport->dma_rx_chan, sport->dma_tx_chan, +			sport->err_irq, sport->regs); + +	sport->wdsize = wdsize; +	sport->dummy_count = dummy_count; + +#if L1_DATA_A_LENGTH != 0 +	sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2); +#else +	sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL); +#endif +	if (sport->dummy_buf == NULL) { +		pr_err("Failed to allocate dummy buffer\n"); +		goto __error; +	} + +	memset(sport->dummy_buf, 0, dummy_count * 2); +	ret = sport_config_rx_dummy(sport); +	if (ret) { +		pr_err("Failed to config rx dummy ring\n"); +		goto __error; +	} +	ret = sport_config_tx_dummy(sport); +	if (ret) { +		pr_err("Failed to config tx dummy ring\n"); +		goto __error; +	} + +	return sport; +__error: +	free_irq(sport->err_irq, sport); +__init_err3: +	free_dma(sport->dma_tx_chan); +__init_err2: +	free_dma(sport->dma_rx_chan); +__init_err1: +	kfree(sport); +	return NULL; +} +EXPORT_SYMBOL(sport_init); + +void sport_done(struct sport_device *sport) +{ +	if (sport == NULL) +		return; + +	sport_stop(sport); +	if (sport->dma_rx_desc) +		dma_free_coherent(NULL, sport->rx_desc_bytes, +			sport->dma_rx_desc, 0); +	if (sport->dma_tx_desc) +		dma_free_coherent(NULL, sport->tx_desc_bytes, +			sport->dma_tx_desc, 0); + +#if L1_DATA_A_LENGTH != 0 +	l1_data_sram_free(sport->dummy_rx_desc); +	l1_data_sram_free(sport->dummy_tx_desc); +	l1_data_sram_free(sport->dummy_buf); +#else +	dma_free_coherent(NULL, 2*sizeof(struct dmasg), +		sport->dummy_rx_desc, 0); +	dma_free_coherent(NULL, 2*sizeof(struct dmasg), +		sport->dummy_tx_desc, 0); +	kfree(sport->dummy_buf); +#endif +	free_dma(sport->dma_rx_chan); +	free_dma(sport->dma_tx_chan); +	free_irq(sport->err_irq, sport); + +	kfree(sport); +		sport = NULL; +} +EXPORT_SYMBOL(sport_done); +/* +* It is only used to send several bytes when dma is not enabled + * sport controller is configured but not enabled. + * Multichannel cannot works with pio mode */ +/* Used by ac97 to write and read codec register */ +int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \ +		u8 *in_data, int len) +{ +	unsigned short dma_config; +	unsigned short status; +	unsigned long flags; +	unsigned long wait = 0; + +	pr_debug("%s enter, out_data:%p, in_data:%p len:%d\n", \ +			__func__, out_data, in_data, len); +	pr_debug("tcr1:0x%04x, tcr2:0x%04x, tclkdiv:0x%04x, tfsdiv:0x%04x\n" +			"mcmc1:0x%04x, mcmc2:0x%04x\n", +			sport->regs->tcr1, sport->regs->tcr2, +			sport->regs->tclkdiv, sport->regs->tfsdiv, +			sport->regs->mcmc1, sport->regs->mcmc2); +	flush_dcache_range((unsigned)out_data, (unsigned)(out_data + len)); + +	/* Enable tx dma */ +	dma_config = (RESTART | WDSIZE_16 | DI_EN); +	set_dma_start_addr(sport->dma_tx_chan, (unsigned long)out_data); +	set_dma_x_count(sport->dma_tx_chan, len/2); +	set_dma_x_modify(sport->dma_tx_chan, 2); +	set_dma_config(sport->dma_tx_chan, dma_config); +	enable_dma(sport->dma_tx_chan); + +	if (in_data != NULL) { +		invalidate_dcache_range((unsigned)in_data, \ +				(unsigned)(in_data + len)); +		/* Enable rx dma */ +		dma_config = (RESTART | WDSIZE_16 | WNR | DI_EN); +		set_dma_start_addr(sport->dma_rx_chan, (unsigned long)in_data); +		set_dma_x_count(sport->dma_rx_chan, len/2); +		set_dma_x_modify(sport->dma_rx_chan, 2); +		set_dma_config(sport->dma_rx_chan, dma_config); +		enable_dma(sport->dma_rx_chan); +	} + +	local_irq_save(flags); +	sport->regs->tcr1 |= TSPEN; +	sport->regs->rcr1 |= RSPEN; +	SSYNC(); + +	status = get_dma_curr_irqstat(sport->dma_tx_chan); +	while (status & DMA_RUN) { +		udelay(1); +		status = get_dma_curr_irqstat(sport->dma_tx_chan); +		pr_debug("DMA status:0x%04x\n", status); +		if (wait++ > 100) +			goto __over; +	} +	status = sport->regs->stat; +	wait = 0; + +	while (!(status & TXHRE)) { +		pr_debug("sport status:0x%04x\n", status); +		udelay(1); +		status = *(unsigned short *)&sport->regs->stat; +		if (wait++ > 1000) +			goto __over; +	} +	/* Wait for the last byte sent out */ +	udelay(20); +	pr_debug("sport status:0x%04x\n", status); + +__over: +	sport->regs->tcr1 &= ~TSPEN; +	sport->regs->rcr1 &= ~RSPEN; +	SSYNC(); +	disable_dma(sport->dma_tx_chan); +	/* Clear the status */ +	clear_dma_irqstat(sport->dma_tx_chan); +	if (in_data != NULL) { +		disable_dma(sport->dma_rx_chan); +		clear_dma_irqstat(sport->dma_rx_chan); +	} +	SSYNC(); +	local_irq_restore(flags); + +	return 0; +} +EXPORT_SYMBOL(sport_send_and_recv); + +MODULE_AUTHOR("Roy Huang"); +MODULE_DESCRIPTION("SPORT driver for ADI Blackfin"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h new file mode 100644 index 0000000..4c16345 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-sport.h @@ -0,0 +1,192 @@ +/* + * File:         bf5xx_ac97_sport.h + * Based on: + * Author:       Roy Huang <roy.huang@analog.com> + * + * Created: + * Description: + * + *               Copyright 2004-2007 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + + +#ifndef __BF5XX_SPORT_H__ +#define __BF5XX_SPORT_H__ + +#include <linux/types.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <asm/dma.h> + +struct sport_register { +	u16 tcr1;	u16 reserved0; +	u16 tcr2;	u16 reserved1; +	u16 tclkdiv;	u16 reserved2; +	u16 tfsdiv;	u16 reserved3; +	u32 tx; +	u32 reserved_l0; +	u32 rx; +	u32 reserved_l1; +	u16 rcr1;	u16 reserved4; +	u16 rcr2;	u16 reserved5; +	u16 rclkdiv;	u16 reserved6; +	u16 rfsdiv;	u16 reserved7; +	u16 stat;	u16 reserved8; +	u16 chnl;	u16 reserved9; +	u16 mcmc1;	u16 reserved10; +	u16 mcmc2;	u16 reserved11; +	u32 mtcs0; +	u32 mtcs1; +	u32 mtcs2; +	u32 mtcs3; +	u32 mrcs0; +	u32 mrcs1; +	u32 mrcs2; +	u32 mrcs3; +}; + +#define DESC_ELEMENT_COUNT 9 + +struct sport_device { +	int dma_rx_chan; +	int dma_tx_chan; +	int err_irq; +	struct sport_register *regs; + +	unsigned char *rx_buf; +	unsigned char *tx_buf; +	unsigned int rx_fragsize; +	unsigned int tx_fragsize; +	unsigned int rx_frags; +	unsigned int tx_frags; +	unsigned int wdsize; + +	/* for dummy dma transfer */ +	void *dummy_buf; +	unsigned int dummy_count; + +	/* DMA descriptor ring head of current audio stream*/ +	struct dmasg *dma_rx_desc; +	struct dmasg *dma_tx_desc; +	unsigned int rx_desc_bytes; +	unsigned int tx_desc_bytes; + +	unsigned int rx_run:1; /* rx is running */ +	unsigned int tx_run:1; /* tx is running */ + +	struct dmasg *dummy_rx_desc; +	struct dmasg *dummy_tx_desc; + +	struct dmasg *curr_rx_desc; +	struct dmasg *curr_tx_desc; + +	int rx_curr_frag; +	int tx_curr_frag; + +	unsigned int rcr1; +	unsigned int rcr2; +	int rx_tdm_count; + +	unsigned int tcr1; +	unsigned int tcr2; +	int tx_tdm_count; + +	void (*rx_callback)(void *data); +	void *rx_data; +	void (*tx_callback)(void *data); +	void *tx_data; +	void (*err_callback)(void *data); +	void *err_data; +	unsigned char *tx_dma_buf; +	unsigned char *rx_dma_buf; +#ifdef CONFIG_SND_MMAP_SUPPORT +	dma_addr_t tx_dma_phy; +	dma_addr_t rx_dma_phy; +	int tx_pos;/*pcm sample count*/ +	int rx_pos; +	unsigned int tx_buffer_size; +	unsigned int rx_buffer_size; +#endif +	void *private_data; +}; + +extern struct sport_device *sport_handle; + +struct sport_param { +	int dma_rx_chan; +	int dma_tx_chan; +	int err_irq; +	struct sport_register *regs; +}; + +struct sport_device *sport_init(struct sport_param *param, unsigned wdsize, +		unsigned dummy_count, void *private_data); + +void sport_done(struct sport_device *sport); + +/* first use these ...*/ + +/* note: multichannel is in units of 8 channels, tdm_count is number of channels + *  NOT / 8 ! all channels are enabled by default */ +int sport_set_multichannel(struct sport_device *sport, int tdm_count, +		u32 mask, int packed); + +int sport_config_rx(struct sport_device *sport, +		unsigned int rcr1, unsigned int rcr2, +		unsigned int clkdiv, unsigned int fsdiv); + +int sport_config_tx(struct sport_device *sport, +		unsigned int tcr1, unsigned int tcr2, +		unsigned int clkdiv, unsigned int fsdiv); + +/* ... then these: */ + +/* buffer size (in bytes) == fragcount * fragsize_bytes */ + +/* this is not a very general api, it sets the dma to 2d autobuffer mode */ + +int sport_config_rx_dma(struct sport_device *sport, void *buf, +		int fragcount, size_t fragsize_bytes); + +int sport_config_tx_dma(struct sport_device *sport, void *buf, +		int fragcount, size_t fragsize_bytes); + +int sport_tx_start(struct sport_device *sport); +int sport_tx_stop(struct sport_device *sport); +int sport_rx_start(struct sport_device *sport); +int sport_rx_stop(struct sport_device *sport); + +/* for use in interrupt handler */ +unsigned long sport_curr_offset_rx(struct sport_device *sport); +unsigned long sport_curr_offset_tx(struct sport_device *sport); + +void sport_incfrag(struct sport_device *sport, int *frag, int tx); +void sport_decfrag(struct sport_device *sport, int *frag, int tx); + +int sport_set_rx_callback(struct sport_device *sport, +		       void (*rx_callback)(void *), void *rx_data); +int sport_set_tx_callback(struct sport_device *sport, +		       void (*tx_callback)(void *), void *tx_data); +int sport_set_err_callback(struct sport_device *sport, +		       void (*err_callback)(void *), void *err_data); + +int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \ +		u8 *in_data, int len); +#endif /* BF53X_SPORT_H */ diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c new file mode 100644 index 0000000..e15f67f --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ssm2602.c @@ -0,0 +1,186 @@ +/* + * File:         sound/soc/blackfin/bf5xx-ssm2602.c + * Author:       Cliff Cai <Cliff.Cai@analog.com> + * + * Created:      Tue June 06 2008 + * Description:  board driver for SSM2602 sound chip + * + * Modified: + *               Copyright 2008 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm_params.h> + +#include <asm/dma.h> +#include <asm/portmux.h> +#include <linux/gpio.h> +#include "../codecs/ssm2602.h" +#include "bf5xx-sport.h" +#include "bf5xx-i2s-pcm.h" +#include "bf5xx-i2s.h" + +static struct snd_soc_machine bf5xx_ssm2602; + +static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + +	pr_debug("%s enter\n", __func__); +	cpu_dai->private_data = sport_handle; +	return 0; +} + +static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; +	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; +	unsigned int clk = 0; +	int ret = 0; + +	pr_debug("%s rate %d format %x\n", __func__, params_rate(params), +		params_format(params)); +	/* +	 * If you are using a crystal source which frequency is not 12MHz +	 * then modify the below case statement with frequency of the crystal. +	 * +	 * If you are using the SPORT to generate clocking then this is +	 * where to do it. +	 */ + +	switch (params_rate(params)) { +	case 8000: +	case 16000: +	case 48000: +	case 96000: +	case 11025: +	case 22050: +	case 44100: +		clk = 12000000; +		break; +	} + +	/* +	 * CODEC is master for BCLK and LRC in this configuration. +	 */ + +	/* set codec DAI configuration */ +	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | +		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); +	if (ret < 0) +		return ret; +	/* set cpu DAI configuration */ +	ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | +		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); +	if (ret < 0) +		return ret; + +	ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk, +		SND_SOC_CLOCK_IN); +	if (ret < 0) +		return ret; + +	return 0; +} + +static struct snd_soc_ops bf5xx_ssm2602_ops = { +	.startup = bf5xx_ssm2602_startup, +	.hw_params = bf5xx_ssm2602_hw_params, +}; + +static struct snd_soc_dai_link bf5xx_ssm2602_dai = { +	.name = "ssm2602", +	.stream_name = "SSM2602", +	.cpu_dai = &bf5xx_i2s_dai, +	.codec_dai = &ssm2602_dai, +	.ops = &bf5xx_ssm2602_ops, +}; + +/* + * SSM2602 2 wire address is determined by CSB + * state during powerup. + *    low  = 0x1a + *    high = 0x1b + */ + +static struct ssm2602_setup_data bf5xx_ssm2602_setup = { +	.i2c_bus = 0, +	.i2c_address = 0x1b, +}; + +static struct snd_soc_machine bf5xx_ssm2602 = { +	.name = "bf5xx_ssm2602", +	.dai_link = &bf5xx_ssm2602_dai, +	.num_links = 1, +}; + +static struct snd_soc_device bf5xx_ssm2602_snd_devdata = { +	.machine = &bf5xx_ssm2602, +	.platform = &bf5xx_i2s_soc_platform, +	.codec_dev = &soc_codec_dev_ssm2602, +	.codec_data = &bf5xx_ssm2602_setup, +}; + +static struct platform_device *bf52x_ssm2602_snd_device; + +static int __init bf5xx_ssm2602_init(void) +{ +	int ret; + +	pr_debug("%s enter\n", __func__); +	bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1); +	if (!bf52x_ssm2602_snd_device) +		return -ENOMEM; + +	platform_set_drvdata(bf52x_ssm2602_snd_device, +				&bf5xx_ssm2602_snd_devdata); +	bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev; +	ret = platform_device_add(bf52x_ssm2602_snd_device); + +	if (ret) +		platform_device_put(bf52x_ssm2602_snd_device); + +	return ret; +} + +static void __exit bf5xx_ssm2602_exit(void) +{ +	pr_debug("%s enter\n", __func__); +	platform_device_unregister(bf52x_ssm2602_snd_device); +} + +module_init(bf5xx_ssm2602_init); +module_exit(bf5xx_ssm2602_exit); + +/* Module information */ +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("ALSA SoC SSM2602 BF527-EZKIT"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1db04a2..e0b9869 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1,16 +1,80 @@ +config SND_SOC_ALL_CODECS +	tristate "Build all ASoC CODEC drivers" +	depends on I2C +	select SPI +	select SPI_MASTER +	select SND_SOC_AK4535 +	select SND_SOC_CS4270 +	select SND_SOC_SSM2602 +	select SND_SOC_TLV320AIC26 +	select SND_SOC_TLV320AIC3X +	select SND_SOC_UDA1380 +	select SND_SOC_WM8510 +	select SND_SOC_WM8580 +	select SND_SOC_WM8731 +	select SND_SOC_WM8750 +	select SND_SOC_WM8753 +	select SND_SOC_WM8900 +	select SND_SOC_WM8903 +	select SND_SOC_WM8971 +	select SND_SOC_WM8990 +        help +          Normally ASoC codec drivers are only built if a machine driver which +          uses them is also built since they are only usable with a machine +          driver.  Selecting this option will allow these drivers to be built +          without an explicit machine driver for test and development purposes. + +          If unsure select "N". + +  config SND_SOC_AC97_CODEC  	tristate  	select SND_AC97_CODEC +config SND_SOC_AD1980 +	tristate +  config SND_SOC_AK4535  	tristate +# Cirrus Logic CS4270 Codec +config SND_SOC_CS4270 +	tristate + +# Cirrus Logic CS4270 Codec Hardware Mute Support +# Select if you have external muting circuitry attached to your CS4270. +config SND_SOC_CS4270_HWMUTE +	bool +	depends on SND_SOC_CS4270 + +# Cirrus Logic CS4270 Codec VD = 3.3V Errata +# Select if you are affected by the errata where the part will not function +# if MCLK divide-by-1.5 is selected and VD is set to 3.3V.  The driver will +# not select any sample rates that require MCLK to be divided by 1.5. +config SND_SOC_CS4270_VD33_ERRATA +	bool +	depends on SND_SOC_CS4270 + +config SND_SOC_SSM2602 +	tristate + +config SND_SOC_TLV320AIC26 +	tristate "TI TLV320AIC26 Codec support" +	depends on SND_SOC && SPI + +config SND_SOC_TLV320AIC3X +	tristate +	depends on I2C +  config SND_SOC_UDA1380          tristate  config SND_SOC_WM8510  	tristate +config SND_SOC_WM8580 +	tristate +  config SND_SOC_WM8731  	tristate @@ -20,33 +84,20 @@ config SND_SOC_WM8750  config SND_SOC_WM8753  	tristate -config SND_SOC_WM8990 +config SND_SOC_WM8900  	tristate -config SND_SOC_WM9712 +config SND_SOC_WM8903  	tristate -config SND_SOC_WM9713 +config SND_SOC_WM8971  	tristate -# Cirrus Logic CS4270 Codec -config SND_SOC_CS4270 +config SND_SOC_WM8990  	tristate -# Cirrus Logic CS4270 Codec Hardware Mute Support -# Select if you have external muting circuitry attached to your CS4270. -config SND_SOC_CS4270_HWMUTE -	bool -	depends on SND_SOC_CS4270 - -# Cirrus Logic CS4270 Codec VD = 3.3V Errata -# Select if you are affected by the errata where the part will not function -# if MCLK divide-by-1.5 is selected and VD is set to 3.3V.  The driver will -# not select any sample rates that require MCLK to be divided by 1.5. -config SND_SOC_CS4270_VD33_ERRATA -	bool -	depends on SND_SOC_CS4270 +config SND_SOC_WM9712 +	tristate -config SND_SOC_TLV320AIC3X +config SND_SOC_WM9713  	tristate -	depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d7b97ab..f977978 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,25 +1,39 @@  snd-soc-ac97-objs := ac97.o +snd-soc-ad1980-objs := ad1980.o  snd-soc-ak4535-objs := ak4535.o +snd-soc-cs4270-objs := cs4270.o +snd-soc-ssm2602-objs := ssm2602.o +snd-soc-tlv320aic26-objs := tlv320aic26.o +snd-soc-tlv320aic3x-objs := tlv320aic3x.o  snd-soc-uda1380-objs := uda1380.o  snd-soc-wm8510-objs := wm8510.o +snd-soc-wm8580-objs := wm8580.o  snd-soc-wm8731-objs := wm8731.o  snd-soc-wm8750-objs := wm8750.o  snd-soc-wm8753-objs := wm8753.o +snd-soc-wm8900-objs := wm8900.o +snd-soc-wm8903-objs := wm8903.o +snd-soc-wm8971-objs := wm8971.o  snd-soc-wm8990-objs := wm8990.o  snd-soc-wm9712-objs := wm9712.o  snd-soc-wm9713-objs := wm9713.o -snd-soc-cs4270-objs := cs4270.o -snd-soc-tlv320aic3x-objs := tlv320aic3x.o  obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o +obj-$(CONFIG_SND_SOC_AD1980)	+= snd-soc-ad1980.o  obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o +obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o +obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o +obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o +obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o  obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o  obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o +obj-$(CONFIG_SND_SOC_WM8580)	+= snd-soc-wm8580.o  obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o  obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o  obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o +obj-$(CONFIG_SND_SOC_WM8900)	+= snd-soc-wm8900.o +obj-$(CONFIG_SND_SOC_WM8903)	+= snd-soc-wm8903.o +obj-$(CONFIG_SND_SOC_WM8971)	+= snd-soc-wm8971.o  obj-$(CONFIG_SND_SOC_WM8990)	+= snd-soc-wm8990.o  obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o  obj-$(CONFIG_SND_SOC_WM9713)	+= snd-soc-wm9713.o -obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o -obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c new file mode 100644 index 0000000..4e09c1f --- /dev/null +++ b/sound/soc/codecs/ad1980.c @@ -0,0 +1,309 @@ +/* + * ad1980.c  --  ALSA Soc AD1980 codec support + * + * Copyright:	Analog Device Inc. + * Author:	Roy Huang <roy.huang@analog.com> + * 		Cliff Cai <cliff.cai@analog.com> + * + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include "ad1980.h" + +static unsigned int ac97_read(struct snd_soc_codec *codec, +	unsigned int reg); +static int ac97_write(struct snd_soc_codec *codec, +	unsigned int reg, unsigned int val); + +/* + * AD1980 register cache + */ +static const u16 ad1980_reg[] = { +	0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6  */ +	0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e  */ +	0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */ +	0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */ +	0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */ +	0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */ +	0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */ +	0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */ +	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ +	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ +	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ +	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ +	0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */ +	0x0000, 0x0000, 0x0000, 0x0000, /* reserved */ +	0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */ +	0x0000, 0x0000, 0x4144, 0x5370  /* 78 - 7e */ +}; + +static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line", +		"Stereo Mix", "Mono Mix", "Phone"}; + +static const struct soc_enum ad1980_cap_src = +	SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 7, ad1980_rec_sel); + +static const struct snd_kcontrol_new ad1980_snd_ac97_controls[] = { +SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1), +SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1), + +SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1), +SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1), + +SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1), +SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1), + +SOC_DOUBLE("PCM Capture Volume", AC97_REC_GAIN, 8, 0, 31, 0), +SOC_SINGLE("PCM Capture Switch", AC97_REC_GAIN, 15, 1, 1), + +SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1), +SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1), + +SOC_SINGLE("Phone Capture Volume", AC97_PHONE, 0, 31, 1), +SOC_SINGLE("Phone Capture Switch", AC97_PHONE, 15, 1, 1), + +SOC_SINGLE("Mic Volume", AC97_MIC, 0, 31, 1), +SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1), + +SOC_SINGLE("Stereo Mic Switch", AC97_AD_MISC, 6, 1, 0), +SOC_DOUBLE("Line HP Swap Switch", AC97_AD_MISC, 10, 5, 1, 0), + +SOC_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), +SOC_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1), + +SOC_ENUM("Capture Source", ad1980_cap_src), + +SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0), +}; + +/* add non dapm controls */ +static int ad1980_add_controls(struct snd_soc_codec *codec) +{ +	int err, i; + +	for (i = 0; i < ARRAY_SIZE(ad1980_snd_ac97_controls); i++) { +		err = snd_ctl_add(codec->card, snd_soc_cnew( +				&ad1980_snd_ac97_controls[i], codec, NULL)); +		if (err < 0) +			return err; +	} +	return 0; +} + +static unsigned int ac97_read(struct snd_soc_codec *codec, +	unsigned int reg) +{ +	u16 *cache = codec->reg_cache; + +	switch (reg) { +	case AC97_RESET: +	case AC97_INT_PAGING: +	case AC97_POWERDOWN: +	case AC97_EXTENDED_STATUS: +	case AC97_VENDOR_ID1: +	case AC97_VENDOR_ID2: +		return soc_ac97_ops.read(codec->ac97, reg); +	default: +		reg = reg >> 1; + +		if (reg >= (ARRAY_SIZE(ad1980_reg))) +			return -EINVAL; + +		return cache[reg]; +	} +} + +static int ac97_write(struct snd_soc_codec *codec, unsigned int reg, +	unsigned int val) +{ +	u16 *cache = codec->reg_cache; + +	soc_ac97_ops.write(codec->ac97, reg, val); +	reg = reg >> 1; +	if (reg < (ARRAY_SIZE(ad1980_reg))) +		cache[reg] = val; + +	return 0; +} + +struct snd_soc_dai ad1980_dai = { +	.name = "AC97", +	.playback = { +		.stream_name = "Playback", +		.channels_min = 2, +		.channels_max = 2, +		.rates = SNDRV_PCM_RATE_48000, +		.formats = SNDRV_PCM_FMTBIT_S16_LE, }, +	.capture = { +		.stream_name = "Capture", +		.channels_min = 2, +		.channels_max = 2, +		.rates = SNDRV_PCM_RATE_48000, +		.formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}; +EXPORT_SYMBOL_GPL(ad1980_dai); + +static int ad1980_reset(struct snd_soc_codec *codec, int try_warm) +{ +	u16 retry_cnt = 0; + +retry: +	if (try_warm && soc_ac97_ops.warm_reset) { +		soc_ac97_ops.warm_reset(codec->ac97); +		if (ac97_read(codec, AC97_RESET) == 0x0090) +			return 1; +	} + +	soc_ac97_ops.reset(codec->ac97); +	/* Set bit 16slot in register 74h, then every slot will has only 16 +	 * bits. This command is sent out in 20bit mode, in which case the +	 * first nibble of data is eaten by the addr. (Tag is always 16 bit)*/ +	ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900); + +	if (ac97_read(codec, AC97_RESET)  != 0x0090) +		goto err; +	return 0; + +err: +	while (retry_cnt++ < 10) +		goto retry; + +	printk(KERN_ERR "AD1980 AC97 reset failed\n"); +	return -EIO; +} + +static int ad1980_soc_probe(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec; +	int ret = 0; +	u16 vendor_id2; + +	printk(KERN_INFO "AD1980 SoC Audio Codec\n"); + +	socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); +	if (socdev->codec == NULL) +		return -ENOMEM; +	codec = socdev->codec; +	mutex_init(&codec->mutex); + +	codec->reg_cache = +		kzalloc(sizeof(u16) * ARRAY_SIZE(ad1980_reg), GFP_KERNEL); +	if (codec->reg_cache == NULL) { +		ret = -ENOMEM; +		goto cache_err; +	} +	memcpy(codec->reg_cache, ad1980_reg, sizeof(u16) * \ +			ARRAY_SIZE(ad1980_reg)); +	codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ad1980_reg); +	codec->reg_cache_step = 2; +	codec->name = "AD1980"; +	codec->owner = THIS_MODULE; +	codec->dai = &ad1980_dai; +	codec->num_dai = 1; +	codec->write = ac97_write; +	codec->read = ac97_read; +	INIT_LIST_HEAD(&codec->dapm_widgets); +	INIT_LIST_HEAD(&codec->dapm_paths); + +	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0); +	if (ret < 0) { +		printk(KERN_ERR "ad1980: failed to register AC97 codec\n"); +		goto codec_err; +	} + +	/* register pcms */ +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); +	if (ret < 0) +		goto pcm_err; + + +	ret = ad1980_reset(codec, 0); +	if (ret < 0) { +		printk(KERN_ERR "AC97 link error\n"); +		goto reset_err; +	} + +	/* Read out vendor ID to make sure it is ad1980 */ +	if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) +		goto reset_err; + +	vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2); + +	if (vendor_id2 != 0x5370) { +		if (vendor_id2 != 0x5374) +			goto reset_err; +		else +			printk(KERN_WARNING "ad1980: " +				"Found AD1981 - only 2/2 IN/OUT Channels " +				"supported\n"); +	} + +	ac97_write(codec, AC97_MASTER, 0x0000); /* unmute line out volume */ +	ac97_write(codec, AC97_PCM, 0x0000);	/* unmute PCM out volume */ +	ac97_write(codec, AC97_REC_GAIN, 0x0000);/* unmute record volume */ + +	ad1980_add_controls(codec); +	ret = snd_soc_register_card(socdev); +	if (ret < 0) { +		printk(KERN_ERR "ad1980: failed to register card\n"); +		goto reset_err; +	} + +	return 0; + +reset_err: +	snd_soc_free_pcms(socdev); + +pcm_err: +	snd_soc_free_ac97_codec(codec); + +codec_err: +	kfree(codec->reg_cache); + +cache_err: +	kfree(socdev->codec); +	socdev->codec = NULL; +	return ret; +} + +static int ad1980_soc_remove(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	if (codec == NULL) +		return 0; + +	snd_soc_dapm_free(socdev); +	snd_soc_free_pcms(socdev); +	snd_soc_free_ac97_codec(codec); +	kfree(codec->reg_cache); +	kfree(codec); +	return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ad1980 = { +	.probe = 	ad1980_soc_probe, +	.remove = 	ad1980_soc_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ad1980); + +MODULE_DESCRIPTION("ASoC ad1980 driver"); +MODULE_AUTHOR("Roy Huang, Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h new file mode 100644 index 0000000..db6c850 --- /dev/null +++ b/sound/soc/codecs/ad1980.h @@ -0,0 +1,23 @@ +/* + * ad1980.h  --  ad1980 Soc Audio driver + */ + +#ifndef _AD1980_H +#define _AD1980_H +/* Bit definition of Power-Down Control/Status Register */ +#define ADC		0x0001 +#define DAC		0x0002 +#define ANL		0x0004 +#define REF		0x0008 +#define PR0		0x0100 +#define PR1		0x0200 +#define PR2		0x0400 +#define PR3		0x0800 +#define PR4		0x1000 +#define PR5		0x2000 +#define PR6		0x4000 + +extern struct snd_soc_dai ad1980_dai; +extern struct snd_soc_codec_device soc_codec_dev_ad1980; + +#endif diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 7da9f46..088cf99 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -535,87 +535,85 @@ static struct snd_soc_device *ak4535_socdev;  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -#define I2C_DRIVERID_AK4535 0xfefe /* liam -  need a proper id */ - -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; - -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver ak4535_i2c_driver; -static struct i2c_client client_template; - -/* If the i2c layer weren't so broken, we could pass this kind of data -   around */ -static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int ak4535_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = ak4535_socdev; -	struct ak4535_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		printk(KERN_ERR "failed to attach codec at addr %x\n", addr); -		goto err; -	} -  	ret = ak4535_init(socdev); -	if (ret < 0) { +	if (ret < 0)  		printk(KERN_ERR "failed to initialise AK4535\n"); -		goto err; -	} -	return ret; -err: -	kfree(i2c);  	return ret;  } -static int ak4535_i2c_detach(struct i2c_client *client) +static int ak4535_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int ak4535_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, ak4535_codec_probe); -} +static const struct i2c_device_id ak4535_i2c_id[] = { +	{ "ak4535", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id); -/* corgi i2c codec control layer */  static struct i2c_driver ak4535_i2c_driver = {  	.driver = {  		.name = "AK4535 I2C Codec",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_AK4535, -	.attach_adapter = ak4535_i2c_attach, -	.detach_client =  ak4535_i2c_detach, -	.command =        NULL, +	.probe =    ak4535_i2c_probe, +	.remove =   ak4535_i2c_remove, +	.id_table = ak4535_i2c_id,  }; -static struct i2c_client client_template = { -	.name =   "AK4535", -	.driver = &ak4535_i2c_driver, -}; +static int ak4535_add_i2c_device(struct platform_device *pdev, +				 const struct ak4535_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&ak4535_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "ak4535", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&ak4535_i2c_driver); +	return -ENODEV; +}  #endif  static int ak4535_probe(struct platform_device *pdev) @@ -624,7 +622,7 @@ static int ak4535_probe(struct platform_device *pdev)  	struct ak4535_setup_data *setup;  	struct snd_soc_codec *codec;  	struct ak4535_priv *ak4535; -	int ret = 0; +	int ret;  	printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION); @@ -646,17 +644,14 @@ static int ak4535_probe(struct platform_device *pdev)  	INIT_LIST_HEAD(&codec->dapm_paths);  	ak4535_socdev = socdev; +	ret = -ENODEV; +  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t)i2c_master_send;  		codec->hw_read = (hw_read_t)i2c_master_recv; -		ret = i2c_add_driver(&ak4535_i2c_driver); -		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +		ret = ak4535_add_i2c_device(pdev, setup);  	} -#else -	/* Add other interfaces here */  #endif  	if (ret != 0) { @@ -678,6 +673,7 @@ static int ak4535_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&ak4535_i2c_driver);  #endif  	kfree(codec->private_data); diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h index e9fe30e..c7a5870 100644 --- a/sound/soc/codecs/ak4535.h +++ b/sound/soc/codecs/ak4535.h @@ -37,6 +37,7 @@  #define AK4535_CACHEREGNUM 	0x10  struct ak4535_setup_data { +	int            i2c_bus;  	unsigned short i2c_address;  }; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 9deb8c7..0bbd945 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -490,34 +490,7 @@ static int cs4270_mute(struct snd_soc_dai *dai, int mute)  #endif -static int cs4270_i2c_probe(struct i2c_adapter *adap, int addr, int kind); - -/* - * Notify the driver that a new I2C bus has been found. - * - * This function is called for each I2C bus in the system.  The function - * then asks the I2C subsystem to probe that bus at the addresses on which - * our device (the CS4270) could exist.  If a device is found at one of - * those addresses, then our probe function (cs4270_i2c_probe) is called. - */ -static int cs4270_i2c_attach(struct i2c_adapter *adapter) -{ -	return i2c_probe(adapter, &addr_data, cs4270_i2c_probe); -} - -static int cs4270_i2c_detach(struct i2c_client *client) -{ -	struct snd_soc_codec *codec = i2c_get_clientdata(client); - -	i2c_detach_client(client); -	codec->control_data = NULL; - -	kfree(codec->reg_cache); -	codec->reg_cache = NULL; - -	kfree(client); -	return 0; -} +static int cs4270_i2c_probe(struct i2c_client *, const struct i2c_device_id *);  /* A list of non-DAPM controls that the CS4270 supports */  static const struct snd_kcontrol_new cs4270_snd_controls[] = { @@ -525,14 +498,19 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {  		CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1)  }; +static const struct i2c_device_id cs4270_id[] = { +	{"cs4270", 0}, +	{} +}; +MODULE_DEVICE_TABLE(i2c, cs4270_id); +  static struct i2c_driver cs4270_i2c_driver = {  	.driver = {  		.name = "CS4270 I2C",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_CS4270, -	.attach_adapter = cs4270_i2c_attach, -	.detach_client =  cs4270_i2c_detach, +	.id_table = cs4270_id, +	.probe = cs4270_i2c_probe,  };  /* @@ -561,11 +539,11 @@ static struct snd_soc_device *cs4270_socdev;   * Note: snd_soc_new_pcms() must be called before this function can be called,   * because of snd_ctl_add().   */ -static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind) +static int cs4270_i2c_probe(struct i2c_client *i2c_client, +	const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = cs4270_socdev;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c_client = NULL;  	int i;  	int ret = 0; @@ -578,12 +556,6 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)  	/* Note: codec_dai->codec is NULL here */ -	i2c_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); -	if (!i2c_client) { -		printk(KERN_ERR "cs4270: could not allocate I2C client\n"); -		return -ENOMEM; -	} -  	codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL);  	if (!codec->reg_cache) {  		printk(KERN_ERR "cs4270: could not allocate register cache\n"); @@ -591,13 +563,6 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)  		goto error;  	} -	i2c_set_clientdata(i2c_client, codec); -	strcpy(i2c_client->name, "CS4270"); - -	i2c_client->driver = &cs4270_i2c_driver; -	i2c_client->adapter = adapter; -	i2c_client->addr = addr; -  	/* Verify that we have a CS4270 */  	ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID); @@ -612,18 +577,10 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)  		goto error;  	} -	printk(KERN_INFO "cs4270: found device at I2C address %X\n", addr); +	printk(KERN_INFO "cs4270: found device at I2C address %X\n", +		i2c_client->addr);  	printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF); -	/* Tell the I2C layer a new client has arrived */ - -	ret = i2c_attach_client(i2c_client); -	if (ret) { -		printk(KERN_ERR "cs4270: could not attach codec, " -			"I2C address %x, error code %i\n", addr, ret); -		goto error; -	} -  	codec->control_data = i2c_client;  	codec->read = cs4270_read_reg_cache;  	codec->write = cs4270_i2c_write; @@ -648,20 +605,17 @@ static int cs4270_i2c_probe(struct i2c_adapter *adapter, int addr, int kind)  			goto error;  	} +	i2c_set_clientdata(i2c_client, codec); +  	return 0;  error: -	if (codec->control_data) { -		i2c_detach_client(i2c_client); -		codec->control_data = NULL; -	} +	codec->control_data = NULL;  	kfree(codec->reg_cache);  	codec->reg_cache = NULL;  	codec->reg_cache_size = 0; -	kfree(i2c_client); -  	return ret;  } @@ -727,7 +681,7 @@ static int cs4270_probe(struct platform_device *pdev)  	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);  	if (ret < 0) {  		printk(KERN_ERR "cs4270: failed to create PCMs\n"); -		return ret; +		goto error_free_codec;  	}  #ifdef USE_I2C @@ -736,8 +690,7 @@ static int cs4270_probe(struct platform_device *pdev)  	ret = i2c_add_driver(&cs4270_i2c_driver);  	if (ret) {  		printk(KERN_ERR "cs4270: failed to attach driver"); -		snd_soc_free_pcms(socdev); -		return ret; +		goto error_free_pcms;  	}  	/* Did we find a CS4270 on the I2C bus? */ @@ -759,10 +712,23 @@ static int cs4270_probe(struct platform_device *pdev)  	ret = snd_soc_register_card(socdev);  	if (ret < 0) {  		printk(KERN_ERR "cs4270: failed to register card\n"); -		snd_soc_free_pcms(socdev); -		return ret; +		goto error_del_driver;  	} +	return 0; + +error_del_driver: +#ifdef USE_I2C +	i2c_del_driver(&cs4270_i2c_driver); + +error_free_pcms: +#endif +	snd_soc_free_pcms(socdev); + +error_free_codec: +	kfree(socdev->codec); +	socdev->codec = NULL; +  	return ret;  } @@ -773,8 +739,7 @@ static int cs4270_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  #ifdef USE_I2C -	if (socdev->codec->control_data) -		i2c_del_driver(&cs4270_i2c_driver); +	i2c_del_driver(&cs4270_i2c_driver);  #endif  	kfree(socdev->codec); diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c new file mode 100644 index 0000000..940ce1c --- /dev/null +++ b/sound/soc/codecs/ssm2602.c @@ -0,0 +1,776 @@ +/* + * File:         sound/soc/codecs/ssm2602.c + * Author:       Cliff Cai <Cliff.Cai@analog.com> + * + * Created:      Tue June 06 2008 + * Description:  Driver for ssm2602 sound chip + * + * Modified: + *               Copyright 2008 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "ssm2602.h" + +#define AUDIO_NAME "ssm2602" +#define SSM2602_VERSION "0.1" + +struct snd_soc_codec_device soc_codec_dev_ssm2602; + +/* codec private data */ +struct ssm2602_priv { +	unsigned int sysclk; +	struct snd_pcm_substream *master_substream; +	struct snd_pcm_substream *slave_substream; +}; + +/* + * ssm2602 register cache + * We can't read the ssm2602 register space when we are + * using 2 wire for device control, so we cache them instead. + * There is no point in caching the reset register + */ +static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = { +	0x0017, 0x0017, 0x0079, 0x0079, +	0x0000, 0x0000, 0x0000, 0x000a, +	0x0000, 0x0000 +}; + +/* + * read ssm2602 register cache + */ +static inline unsigned int ssm2602_read_reg_cache(struct snd_soc_codec *codec, +	unsigned int reg) +{ +	u16 *cache = codec->reg_cache; +	if (reg == SSM2602_RESET) +		return 0; +	if (reg >= SSM2602_CACHEREGNUM) +		return -1; +	return cache[reg]; +} + +/* + * write ssm2602 register cache + */ +static inline void ssm2602_write_reg_cache(struct snd_soc_codec *codec, +	u16 reg, unsigned int value) +{ +	u16 *cache = codec->reg_cache; +	if (reg >= SSM2602_CACHEREGNUM) +		return; +	cache[reg] = value; +} + +/* + * write to the ssm2602 register space + */ +static int ssm2602_write(struct snd_soc_codec *codec, unsigned int reg, +	unsigned int value) +{ +	u8 data[2]; + +	/* data is +	 *   D15..D9 ssm2602 register offset +	 *   D8...D0 register data +	 */ +	data[0] = (reg << 1) | ((value >> 8) & 0x0001); +	data[1] = value & 0x00ff; + +	ssm2602_write_reg_cache(codec, reg, value); +	if (codec->hw_write(codec->control_data, data, 2) == 2) +		return 0; +	else +		return -EIO; +} + +#define ssm2602_reset(c)	ssm2602_write(c, SSM2602_RESET, 0) + +/*Appending several "None"s just for OSS mixer use*/ +static const char *ssm2602_input_select[] = { +	"Line", "Mic", "None", "None", "None", +	"None", "None", "None", +}; + +static const char *ssm2602_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; + +static const struct soc_enum ssm2602_enum[] = { +	SOC_ENUM_SINGLE(SSM2602_APANA, 2, 2, ssm2602_input_select), +	SOC_ENUM_SINGLE(SSM2602_APDIGI, 1, 4, ssm2602_deemph), +}; + +static const struct snd_kcontrol_new ssm2602_snd_controls[] = { + +SOC_DOUBLE_R("Master Playback Volume", SSM2602_LOUT1V, SSM2602_ROUT1V, +	0, 127, 0), +SOC_DOUBLE_R("Master Playback ZC Switch", SSM2602_LOUT1V, SSM2602_ROUT1V, +	7, 1, 0), + +SOC_DOUBLE_R("Capture Volume", SSM2602_LINVOL, SSM2602_RINVOL, 0, 31, 0), +SOC_DOUBLE_R("Capture Switch", SSM2602_LINVOL, SSM2602_RINVOL, 7, 1, 1), + +SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0), +SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1), + +SOC_SINGLE("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1), + +SOC_SINGLE("ADC High Pass Filter Switch", SSM2602_APDIGI, 0, 1, 1), +SOC_SINGLE("Store DC Offset Switch", SSM2602_APDIGI, 4, 1, 0), + +SOC_ENUM("Capture Source", ssm2602_enum[0]), + +SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]), +}; + +/* add non dapm controls */ +static int ssm2602_add_controls(struct snd_soc_codec *codec) +{ +	int err, i; + +	for (i = 0; i < ARRAY_SIZE(ssm2602_snd_controls); i++) { +		err = snd_ctl_add(codec->card, +			snd_soc_cnew(&ssm2602_snd_controls[i], codec, NULL)); +		if (err < 0) +			return err; +	} + +	return 0; +} + +/* Output Mixer */ +static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = { +SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0), +SOC_DAPM_SINGLE("Mic Sidetone Switch", SSM2602_APANA, 5, 1, 0), +SOC_DAPM_SINGLE("HiFi Playback Switch", SSM2602_APANA, 4, 1, 0), +}; + +/* Input mux */ +static const struct snd_kcontrol_new ssm2602_input_mux_controls = +SOC_DAPM_ENUM("Input Select", ssm2602_enum[0]); + +static const struct snd_soc_dapm_widget ssm2602_dapm_widgets[] = { +SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1, +	&ssm2602_output_mixer_controls[0], +	ARRAY_SIZE(ssm2602_output_mixer_controls)), +SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM2602_PWR, 3, 1), +SND_SOC_DAPM_OUTPUT("LOUT"), +SND_SOC_DAPM_OUTPUT("LHPOUT"), +SND_SOC_DAPM_OUTPUT("ROUT"), +SND_SOC_DAPM_OUTPUT("RHPOUT"), +SND_SOC_DAPM_ADC("ADC", "HiFi Capture", SSM2602_PWR, 2, 1), +SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &ssm2602_input_mux_controls), +SND_SOC_DAPM_PGA("Line Input", SSM2602_PWR, 0, 1, NULL, 0), +SND_SOC_DAPM_MICBIAS("Mic Bias", SSM2602_PWR, 1, 1), +SND_SOC_DAPM_INPUT("MICIN"), +SND_SOC_DAPM_INPUT("RLINEIN"), +SND_SOC_DAPM_INPUT("LLINEIN"), +}; + +static const struct snd_soc_dapm_route audio_conn[] = { +	/* output mixer */ +	{"Output Mixer", "Line Bypass Switch", "Line Input"}, +	{"Output Mixer", "HiFi Playback Switch", "DAC"}, +	{"Output Mixer", "Mic Sidetone Switch", "Mic Bias"}, + +	/* outputs */ +	{"RHPOUT", NULL, "Output Mixer"}, +	{"ROUT", NULL, "Output Mixer"}, +	{"LHPOUT", NULL, "Output Mixer"}, +	{"LOUT", NULL, "Output Mixer"}, + +	/* input mux */ +	{"Input Mux", "Line", "Line Input"}, +	{"Input Mux", "Mic", "Mic Bias"}, +	{"ADC", NULL, "Input Mux"}, + +	/* inputs */ +	{"Line Input", NULL, "LLINEIN"}, +	{"Line Input", NULL, "RLINEIN"}, +	{"Mic Bias", NULL, "MICIN"}, +}; + +static int ssm2602_add_widgets(struct snd_soc_codec *codec) +{ +	snd_soc_dapm_new_controls(codec, ssm2602_dapm_widgets, +				  ARRAY_SIZE(ssm2602_dapm_widgets)); + +	snd_soc_dapm_add_routes(codec, audio_conn, ARRAY_SIZE(audio_conn)); + +	snd_soc_dapm_new_widgets(codec); +	return 0; +} + +struct _coeff_div { +	u32 mclk; +	u32 rate; +	u16 fs; +	u8 sr:4; +	u8 bosr:1; +	u8 usb:1; +}; + +/* codec mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { +	/* 48k */ +	{12288000, 48000, 256, 0x0, 0x0, 0x0}, +	{18432000, 48000, 384, 0x0, 0x1, 0x0}, +	{12000000, 48000, 250, 0x0, 0x0, 0x1}, + +	/* 32k */ +	{12288000, 32000, 384, 0x6, 0x0, 0x0}, +	{18432000, 32000, 576, 0x6, 0x1, 0x0}, +	{12000000, 32000, 375, 0x6, 0x0, 0x1}, + +	/* 8k */ +	{12288000, 8000, 1536, 0x3, 0x0, 0x0}, +	{18432000, 8000, 2304, 0x3, 0x1, 0x0}, +	{11289600, 8000, 1408, 0xb, 0x0, 0x0}, +	{16934400, 8000, 2112, 0xb, 0x1, 0x0}, +	{12000000, 8000, 1500, 0x3, 0x0, 0x1}, + +	/* 96k */ +	{12288000, 96000, 128, 0x7, 0x0, 0x0}, +	{18432000, 96000, 192, 0x7, 0x1, 0x0}, +	{12000000, 96000, 125, 0x7, 0x0, 0x1}, + +	/* 44.1k */ +	{11289600, 44100, 256, 0x8, 0x0, 0x0}, +	{16934400, 44100, 384, 0x8, 0x1, 0x0}, +	{12000000, 44100, 272, 0x8, 0x1, 0x1}, + +	/* 88.2k */ +	{11289600, 88200, 128, 0xf, 0x0, 0x0}, +	{16934400, 88200, 192, 0xf, 0x1, 0x0}, +	{12000000, 88200, 136, 0xf, 0x1, 0x1}, +}; + +static inline int get_coeff(int mclk, int rate) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { +		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) +			return i; +	} +	return i; +} + +static int ssm2602_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	u16 srate; +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	struct ssm2602_priv *ssm2602 = codec->private_data; +	u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3; +	int i = get_coeff(ssm2602->sysclk, params_rate(params)); + +	/*no match is found*/ +	if (i == ARRAY_SIZE(coeff_div)) +		return -EINVAL; + +	srate = (coeff_div[i].sr << 2) | +		(coeff_div[i].bosr << 1) | coeff_div[i].usb; + +	ssm2602_write(codec, SSM2602_ACTIVE, 0); +	ssm2602_write(codec, SSM2602_SRATE, srate); + +	/* bit size */ +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S16_LE: +		break; +	case SNDRV_PCM_FORMAT_S20_3LE: +		iface |= 0x0004; +		break; +	case SNDRV_PCM_FORMAT_S24_LE: +		iface |= 0x0008; +		break; +	case SNDRV_PCM_FORMAT_S32_LE: +		iface |= 0x000c; +		break; +	} +	ssm2602_write(codec, SSM2602_IFACE, iface); +	ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); +	return 0; +} + +static int ssm2602_startup(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	struct ssm2602_priv *ssm2602 = codec->private_data; +	struct snd_pcm_runtime *master_runtime; + +	/* The DAI has shared clocks so if we already have a playback or +	 * capture going then constrain this substream to match it. +	 */ +	if (ssm2602->master_substream) { +		master_runtime = ssm2602->master_substream->runtime; +		snd_pcm_hw_constraint_minmax(substream->runtime, +					     SNDRV_PCM_HW_PARAM_RATE, +					     master_runtime->rate, +					     master_runtime->rate); + +		snd_pcm_hw_constraint_minmax(substream->runtime, +					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS, +					     master_runtime->sample_bits, +					     master_runtime->sample_bits); + +		ssm2602->slave_substream = substream; +	} else +		ssm2602->master_substream = substream; + +	return 0; +} + +static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	/* set active */ +	ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC); + +	return 0; +} + +static void ssm2602_shutdown(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	/* deactivate */ +	if (!codec->active) +		ssm2602_write(codec, SSM2602_ACTIVE, 0); +} + +static int ssm2602_mute(struct snd_soc_dai *dai, int mute) +{ +	struct snd_soc_codec *codec = dai->codec; +	u16 mute_reg = ssm2602_read_reg_cache(codec, SSM2602_APDIGI) & ~APDIGI_ENABLE_DAC_MUTE; +	if (mute) +		ssm2602_write(codec, SSM2602_APDIGI, +				mute_reg | APDIGI_ENABLE_DAC_MUTE); +	else +		ssm2602_write(codec, SSM2602_APDIGI, mute_reg); +	return 0; +} + +static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai, +		int clk_id, unsigned int freq, int dir) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct ssm2602_priv *ssm2602 = codec->private_data; +	switch (freq) { +	case 11289600: +	case 12000000: +	case 12288000: +	case 16934400: +	case 18432000: +		ssm2602->sysclk = freq; +		return 0; +	} +	return -EINVAL; +} + +static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai, +		unsigned int fmt) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	u16 iface = 0; + +	/* set master/slave audio interface */ +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBM_CFM: +		iface |= 0x0040; +		break; +	case SND_SOC_DAIFMT_CBS_CFS: +		break; +	default: +		return -EINVAL; +	} + +	/* interface format */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_I2S: +		iface |= 0x0002; +		break; +	case SND_SOC_DAIFMT_RIGHT_J: +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		iface |= 0x0001; +		break; +	case SND_SOC_DAIFMT_DSP_A: +		iface |= 0x0003; +		break; +	case SND_SOC_DAIFMT_DSP_B: +		iface |= 0x0013; +		break; +	default: +		return -EINVAL; +	} + +	/* clock inversion */ +	switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +	case SND_SOC_DAIFMT_NB_NF: +		break; +	case SND_SOC_DAIFMT_IB_IF: +		iface |= 0x0090; +		break; +	case SND_SOC_DAIFMT_IB_NF: +		iface |= 0x0080; +		break; +	case SND_SOC_DAIFMT_NB_IF: +		iface |= 0x0010; +		break; +	default: +		return -EINVAL; +	} + +	/* set iface */ +	ssm2602_write(codec, SSM2602_IFACE, iface); +	return 0; +} + +static int ssm2602_set_bias_level(struct snd_soc_codec *codec, +				 enum snd_soc_bias_level level) +{ +	u16 reg = ssm2602_read_reg_cache(codec, SSM2602_PWR) & 0xff7f; + +	switch (level) { +	case SND_SOC_BIAS_ON: +		/* vref/mid, osc on, dac unmute */ +		ssm2602_write(codec, SSM2602_PWR, reg); +		break; +	case SND_SOC_BIAS_PREPARE: +		break; +	case SND_SOC_BIAS_STANDBY: +		/* everything off except vref/vmid, */ +		ssm2602_write(codec, SSM2602_PWR, reg | PWR_CLK_OUT_PDN); +		break; +	case SND_SOC_BIAS_OFF: +		/* everything off, dac mute, inactive */ +		ssm2602_write(codec, SSM2602_ACTIVE, 0); +		ssm2602_write(codec, SSM2602_PWR, 0xffff); +		break; + +	} +	codec->bias_level = level; +	return 0; +} + +#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ +		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ +		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ +		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ +		SNDRV_PCM_RATE_96000) + +struct snd_soc_dai ssm2602_dai = { +	.name = "SSM2602", +	.playback = { +		.stream_name = "Playback", +		.channels_min = 2, +		.channels_max = 2, +		.rates = SSM2602_RATES, +		.formats = SNDRV_PCM_FMTBIT_S32_LE,}, +	.capture = { +		.stream_name = "Capture", +		.channels_min = 2, +		.channels_max = 2, +		.rates = SSM2602_RATES, +		.formats = SNDRV_PCM_FMTBIT_S32_LE,}, +	.ops = { +		.startup = ssm2602_startup, +		.prepare = ssm2602_pcm_prepare, +		.hw_params = ssm2602_hw_params, +		.shutdown = ssm2602_shutdown, +	}, +	.dai_ops = { +		.digital_mute = ssm2602_mute, +		.set_sysclk = ssm2602_set_dai_sysclk, +		.set_fmt = ssm2602_set_dai_fmt, +	} +}; +EXPORT_SYMBOL_GPL(ssm2602_dai); + +static int ssm2602_suspend(struct platform_device *pdev, pm_message_t state) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF); +	return 0; +} + +static int ssm2602_resume(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; +	int i; +	u8 data[2]; +	u16 *cache = codec->reg_cache; + +	/* Sync reg_cache with the hardware */ +	for (i = 0; i < ARRAY_SIZE(ssm2602_reg); i++) { +		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); +		data[1] = cache[i] & 0x00ff; +		codec->hw_write(codec->control_data, data, 2); +	} +	ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +	ssm2602_set_bias_level(codec, codec->suspend_bias_level); +	return 0; +} + +/* + * initialise the ssm2602 driver + * register the mixer and dsp interfaces with the kernel + */ +static int ssm2602_init(struct snd_soc_device *socdev) +{ +	struct snd_soc_codec *codec = socdev->codec; +	int reg, ret = 0; + +	codec->name = "SSM2602"; +	codec->owner = THIS_MODULE; +	codec->read = ssm2602_read_reg_cache; +	codec->write = ssm2602_write; +	codec->set_bias_level = ssm2602_set_bias_level; +	codec->dai = &ssm2602_dai; +	codec->num_dai = 1; +	codec->reg_cache_size = sizeof(ssm2602_reg); +	codec->reg_cache = kmemdup(ssm2602_reg, sizeof(ssm2602_reg), +					GFP_KERNEL); +	if (codec->reg_cache == NULL) +		return -ENOMEM; + +	ssm2602_reset(codec); + +	/* register pcms */ +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); +	if (ret < 0) { +		pr_err("ssm2602: failed to create pcms\n"); +		goto pcm_err; +	} +	/*power on device*/ +	ssm2602_write(codec, SSM2602_ACTIVE, 0); +	/* set the update bits */ +	reg = ssm2602_read_reg_cache(codec, SSM2602_LINVOL); +	ssm2602_write(codec, SSM2602_LINVOL, reg | LINVOL_LRIN_BOTH); +	reg = ssm2602_read_reg_cache(codec, SSM2602_RINVOL); +	ssm2602_write(codec, SSM2602_RINVOL, reg | RINVOL_RLIN_BOTH); +	reg = ssm2602_read_reg_cache(codec, SSM2602_LOUT1V); +	ssm2602_write(codec, SSM2602_LOUT1V, reg | LOUT1V_LRHP_BOTH); +	reg = ssm2602_read_reg_cache(codec, SSM2602_ROUT1V); +	ssm2602_write(codec, SSM2602_ROUT1V, reg | ROUT1V_RLHP_BOTH); +	/*select Line in as default input*/ +	ssm2602_write(codec, SSM2602_APANA, +			APANA_ENABLE_MIC_BOOST2 | APANA_SELECT_DAC | +			APANA_ENABLE_MIC_BOOST); +	ssm2602_write(codec, SSM2602_PWR, 0); + +	ssm2602_add_controls(codec); +	ssm2602_add_widgets(codec); +	ret = snd_soc_register_card(socdev); +	if (ret < 0) { +		pr_err("ssm2602: failed to register card\n"); +		goto card_err; +	} + +	return ret; + +card_err: +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +pcm_err: +	kfree(codec->reg_cache); +	return ret; +} + +static struct snd_soc_device *ssm2602_socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +/* + * ssm2602 2 wire address is determined by GPIO5 + * state during powerup. + *    low  = 0x1a + *    high = 0x1b + */ +static int ssm2602_i2c_probe(struct i2c_client *i2c, +			     const struct i2c_device_id *id) +{ +	struct snd_soc_device *socdev = ssm2602_socdev; +	struct snd_soc_codec *codec = socdev->codec; +	int ret; + +	i2c_set_clientdata(i2c, codec); +	codec->control_data = i2c; + +	ret = ssm2602_init(socdev); +	if (ret < 0) +		pr_err("failed to initialise SSM2602\n"); + +	return ret; +} + +static int ssm2602_i2c_remove(struct i2c_client *client) +{ +	struct snd_soc_codec *codec = i2c_get_clientdata(client); +	kfree(codec->reg_cache); +	return 0; +} + +static const struct i2c_device_id ssm2602_i2c_id[] = { +	{ "ssm2602", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); +/* corgi i2c codec control layer */ +static struct i2c_driver ssm2602_i2c_driver = { +	.driver = { +		.name = "SSM2602 I2C Codec", +		.owner = THIS_MODULE, +	}, +	.probe = ssm2602_i2c_probe, +	.remove = ssm2602_i2c_remove, +	.id_table = ssm2602_i2c_id, +}; + +static int ssm2602_add_i2c_device(struct platform_device *pdev, +				  const struct ssm2602_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&ssm2602_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "ssm2602", I2C_NAME_SIZE); +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +		setup->i2c_bus); +		goto err_driver; +	} +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +		(unsigned int)info.addr); +		goto err_driver; +	} +	return 0; +err_driver: +	i2c_del_driver(&ssm2602_i2c_driver); +	return -ENODEV; +} +#endif + +static int ssm2602_probe(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct ssm2602_setup_data *setup; +	struct snd_soc_codec *codec; +	struct ssm2602_priv *ssm2602; +	int ret = 0; + +	pr_info("ssm2602 Audio Codec %s", SSM2602_VERSION); + +	setup = socdev->codec_data; +	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); +	if (codec == NULL) +		return -ENOMEM; + +	ssm2602 = kzalloc(sizeof(struct ssm2602_priv), GFP_KERNEL); +	if (ssm2602 == NULL) { +		kfree(codec); +		return -ENOMEM; +	} + +	codec->private_data = ssm2602; +	socdev->codec = codec; +	mutex_init(&codec->mutex); +	INIT_LIST_HEAD(&codec->dapm_widgets); +	INIT_LIST_HEAD(&codec->dapm_paths); + +	ssm2602_socdev = socdev; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	if (setup->i2c_address) { +		codec->hw_write = (hw_write_t)i2c_master_send; +		ret = ssm2602_add_i2c_device(pdev, setup); +	} +#else +	/* other interfaces */ +#endif +	return ret; +} + +/* remove everything here */ +static int ssm2602_remove(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	if (codec->control_data) +		ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF); + +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data); +	i2c_del_driver(&ssm2602_i2c_driver); +#endif +	kfree(codec->private_data); +	kfree(codec); + +	return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ssm2602 = { +	.probe = 	ssm2602_probe, +	.remove = 	ssm2602_remove, +	.suspend = 	ssm2602_suspend, +	.resume =	ssm2602_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ssm2602); + +MODULE_DESCRIPTION("ASoC ssm2602 driver"); +MODULE_AUTHOR("Cliff Cai"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h new file mode 100644 index 0000000..f344e6d --- /dev/null +++ b/sound/soc/codecs/ssm2602.h @@ -0,0 +1,130 @@ +/* + * File:         sound/soc/codecs/ssm2602.h + * Author:       Cliff Cai <Cliff.Cai@analog.com> + * + * Created:      Tue June 06 2008 + * + * Modified: + *               Copyright 2008 Analog Devices Inc. + * + * Bugs:         Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + */ + +#ifndef _SSM2602_H +#define _SSM2602_H + +/* SSM2602 Codec Register definitions */ + +#define SSM2602_LINVOL   0x00 +#define SSM2602_RINVOL   0x01 +#define SSM2602_LOUT1V   0x02 +#define SSM2602_ROUT1V   0x03 +#define SSM2602_APANA    0x04 +#define SSM2602_APDIGI   0x05 +#define SSM2602_PWR      0x06 +#define SSM2602_IFACE    0x07 +#define SSM2602_SRATE    0x08 +#define SSM2602_ACTIVE   0x09 +#define SSM2602_RESET	 0x0f + +/*SSM2602 Codec Register Field definitions + *(Mask value to extract the corresponding Register field) + */ + +/*Left ADC Volume Control (SSM2602_REG_LEFT_ADC_VOL)*/ +#define     LINVOL_LIN_VOL                0x01F   /* Left Channel PGA Volume control                      */ +#define     LINVOL_LIN_ENABLE_MUTE        0x080   /* Left Channel Input Mute                              */ +#define     LINVOL_LRIN_BOTH              0x100   /* Left Channel Line Input Volume update                */ + +/*Right ADC Volume Control (SSM2602_REG_RIGHT_ADC_VOL)*/ +#define     RINVOL_RIN_VOL                0x01F   /* Right Channel PGA Volume control                     */ +#define     RINVOL_RIN_ENABLE_MUTE        0x080   /* Right Channel Input Mute                             */ +#define     RINVOL_RLIN_BOTH              0x100   /* Right Channel Line Input Volume update               */ + +/*Left DAC Volume Control (SSM2602_REG_LEFT_DAC_VOL)*/ +#define     LOUT1V_LHP_VOL                0x07F   /* Left Channel Headphone volume control                */ +#define     LOUT1V_ENABLE_LZC             0x080   /* Left Channel Zero cross detect enable                */ +#define     LOUT1V_LRHP_BOTH              0x100   /* Left Channel Headphone volume update                 */ + +/*Right DAC Volume Control (SSM2602_REG_RIGHT_DAC_VOL)*/ +#define     ROUT1V_RHP_VOL                0x07F   /* Right Channel Headphone volume control               */ +#define     ROUT1V_ENABLE_RZC             0x080   /* Right Channel Zero cross detect enable               */ +#define     ROUT1V_RLHP_BOTH              0x100   /* Right Channel Headphone volume update                */ + +/*Analogue Audio Path Control (SSM2602_REG_ANALOGUE_PATH)*/ +#define     APANA_ENABLE_MIC_BOOST       0x001   /* Primary Microphone Amplifier gain booster control    */ +#define     APANA_ENABLE_MIC_MUTE        0x002   /* Microphone Mute Control                              */ +#define     APANA_ADC_IN_SELECT          0x004   /* Microphone/Line IN select to ADC (1=MIC, 0=Line In)  */ +#define     APANA_ENABLE_BYPASS          0x008   /* Line input bypass to line output                     */ +#define     APANA_SELECT_DAC             0x010   /* Select DAC (1=Select DAC, 0=Don't Select DAC)        */ +#define     APANA_ENABLE_SIDETONE        0x020   /* Enable/Disable Side Tone                             */ +#define     APANA_SIDETONE_ATTN          0x0C0   /* Side Tone Attenuation                                */ +#define     APANA_ENABLE_MIC_BOOST2      0x100   /* Secondary Microphone Amplifier gain booster control  */ + +/*Digital Audio Path Control (SSM2602_REG_DIGITAL_PATH)*/ +#define     APDIGI_ENABLE_ADC_HPF         0x001   /* Enable/Disable ADC Highpass Filter                   */ +#define     APDIGI_DE_EMPHASIS            0x006   /* De-Emphasis Control                                  */ +#define     APDIGI_ENABLE_DAC_MUTE        0x008   /* DAC Mute Control                                     */ +#define     APDIGI_STORE_OFFSET           0x010   /* Store/Clear DC offset when HPF is disabled           */ + +/*Power Down Control (SSM2602_REG_POWER) + *(1=Enable PowerDown, 0=Disable PowerDown) + */ +#define     PWR_LINE_IN_PDN            0x001   /* Line Input Power Down                                */ +#define     PWR_MIC_PDN                0x002   /* Microphone Input & Bias Power Down                   */ +#define     PWR_ADC_PDN                0x004   /* ADC Power Down                                       */ +#define     PWR_DAC_PDN                0x008   /* DAC Power Down                                       */ +#define     PWR_OUT_PDN                0x010   /* Outputs Power Down                                   */ +#define     PWR_OSC_PDN                0x020   /* Oscillator Power Down                                */ +#define     PWR_CLK_OUT_PDN            0x040   /* CLKOUT Power Down                                    */ +#define     PWR_POWER_OFF              0x080   /* POWEROFF Mode                                        */ + +/*Digital Audio Interface Format (SSM2602_REG_DIGITAL_IFACE)*/ +#define     IFACE_IFACE_FORMAT           0x003   /* Digital Audio input format control                   */ +#define     IFACE_AUDIO_DATA_LEN         0x00C   /* Audio Data word length control                       */ +#define     IFACE_DAC_LR_POLARITY        0x010   /* Polarity Control for clocks in RJ,LJ and I2S modes   */ +#define     IFACE_DAC_LR_SWAP            0x020   /* Swap DAC data control                                */ +#define     IFACE_ENABLE_MASTER          0x040   /* Enable/Disable Master Mode                           */ +#define     IFACE_BCLK_INVERT            0x080   /* Bit Clock Inversion control                          */ + +/*Sampling Control (SSM2602_REG_SAMPLING_CTRL)*/ +#define     SRATE_ENABLE_USB_MODE        0x001   /* Enable/Disable USB Mode                              */ +#define     SRATE_BOS_RATE               0x002   /* Base Over-Sampling rate                              */ +#define     SRATE_SAMPLE_RATE            0x03C   /* Clock setting condition (Sampling rate control)      */ +#define     SRATE_CORECLK_DIV2           0x040   /* Core Clock divider select                            */ +#define     SRATE_CLKOUT_DIV2            0x080   /* Clock Out divider select                             */ + +/*Active Control (SSM2602_REG_ACTIVE_CTRL)*/ +#define     ACTIVE_ACTIVATE_CODEC         0x001   /* Activate Codec Digital Audio Interface               */ + +/*********************************************************************/ + +#define SSM2602_CACHEREGNUM 	10 + +#define SSM2602_SYSCLK	0 +#define SSM2602_DAI		0 + +struct ssm2602_setup_data { +	int i2c_bus; +	unsigned short i2c_address; +}; + +extern struct snd_soc_dai ssm2602_dai; +extern struct snd_soc_codec_device soc_codec_dev_ssm2602; + +#endif diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c new file mode 100644 index 0000000..bed8a9e --- /dev/null +++ b/sound/soc/codecs/tlv320aic26.c @@ -0,0 +1,520 @@ +/* + * Texas Instruments TLV320AIC26 low power audio CODEC + * ALSA SoC CODEC driver + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/soc-of-simple.h> +#include <sound/initval.h> + +#include "tlv320aic26.h" + +MODULE_DESCRIPTION("ASoC TLV320AIC26 codec driver"); +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); +MODULE_LICENSE("GPL"); + +/* AIC26 driver private data */ +struct aic26 { +	struct spi_device *spi; +	struct snd_soc_codec codec; +	u16 reg_cache[AIC26_NUM_REGS];	/* shadow registers */ +	int master; +	int datfm; +	int mclk; + +	/* Keyclick parameters */ +	int keyclick_amplitude; +	int keyclick_freq; +	int keyclick_len; +}; + +/* --------------------------------------------------------------------- + * Register access routines + */ +static unsigned int aic26_reg_read(struct snd_soc_codec *codec, +				   unsigned int reg) +{ +	struct aic26 *aic26 = codec->private_data; +	u16 *cache = codec->reg_cache; +	u16 cmd, value; +	u8 buffer[2]; +	int rc; + +	if (reg >= AIC26_NUM_REGS) { +		WARN_ON_ONCE(1); +		return 0; +	} + +	/* Do SPI transfer; first 16bits are command; remaining is +	 * register contents */ +	cmd = AIC26_READ_COMMAND_WORD(reg); +	buffer[0] = (cmd >> 8) & 0xff; +	buffer[1] = cmd & 0xff; +	rc = spi_write_then_read(aic26->spi, buffer, 2, buffer, 2); +	if (rc) { +		dev_err(&aic26->spi->dev, "AIC26 reg read error\n"); +		return -EIO; +	} +	value = (buffer[0] << 8) | buffer[1]; + +	/* Update the cache before returning with the value */ +	cache[reg] = value; +	return value; +} + +static unsigned int aic26_reg_read_cache(struct snd_soc_codec *codec, +					 unsigned int reg) +{ +	u16 *cache = codec->reg_cache; + +	if (reg >= AIC26_NUM_REGS) { +		WARN_ON_ONCE(1); +		return 0; +	} + +	return cache[reg]; +} + +static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg, +			   unsigned int value) +{ +	struct aic26 *aic26 = codec->private_data; +	u16 *cache = codec->reg_cache; +	u16 cmd; +	u8 buffer[4]; +	int rc; + +	if (reg >= AIC26_NUM_REGS) { +		WARN_ON_ONCE(1); +		return -EINVAL; +	} + +	/* Do SPI transfer; first 16bits are command; remaining is data +	 * to write into register */ +	cmd = AIC26_WRITE_COMMAND_WORD(reg); +	buffer[0] = (cmd >> 8) & 0xff; +	buffer[1] = cmd & 0xff; +	buffer[2] = value >> 8; +	buffer[3] = value; +	rc = spi_write(aic26->spi, buffer, 4); +	if (rc) { +		dev_err(&aic26->spi->dev, "AIC26 reg read error\n"); +		return -EIO; +	} + +	/* update cache before returning */ +	cache[reg] = value; +	return 0; +} + +/* --------------------------------------------------------------------- + * Digital Audio Interface Operations + */ +static int aic26_hw_params(struct snd_pcm_substream *substream, +			   struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	struct aic26 *aic26 = codec->private_data; +	int fsref, divisor, wlen, pval, jval, dval, qval; +	u16 reg; + +	dev_dbg(&aic26->spi->dev, "aic26_hw_params(substream=%p, params=%p)\n", +		substream, params); +	dev_dbg(&aic26->spi->dev, "rate=%i format=%i\n", params_rate(params), +		params_format(params)); + +	switch (params_rate(params)) { +	case 8000:  fsref = 48000; divisor = AIC26_DIV_6; break; +	case 11025: fsref = 44100; divisor = AIC26_DIV_4; break; +	case 12000: fsref = 48000; divisor = AIC26_DIV_4; break; +	case 16000: fsref = 48000; divisor = AIC26_DIV_3; break; +	case 22050: fsref = 44100; divisor = AIC26_DIV_2; break; +	case 24000: fsref = 48000; divisor = AIC26_DIV_2; break; +	case 32000: fsref = 48000; divisor = AIC26_DIV_1_5; break; +	case 44100: fsref = 44100; divisor = AIC26_DIV_1; break; +	case 48000: fsref = 48000; divisor = AIC26_DIV_1; break; +	default: +		dev_dbg(&aic26->spi->dev, "bad rate\n"); return -EINVAL; +	} + +	/* select data word length */ +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S8:     wlen = AIC26_WLEN_16; break; +	case SNDRV_PCM_FORMAT_S16_BE: wlen = AIC26_WLEN_16; break; +	case SNDRV_PCM_FORMAT_S24_BE: wlen = AIC26_WLEN_24; break; +	case SNDRV_PCM_FORMAT_S32_BE: wlen = AIC26_WLEN_32; break; +	default: +		dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL; +	} + +	/* Configure PLL */ +	pval = 1; +	jval = (fsref == 44100) ? 7 : 8; +	dval = (fsref == 44100) ? 5264 : 1920; +	qval = 0; +	reg = 0x8000 | qval << 11 | pval << 8 | jval << 2; +	aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg); +	reg = dval << 2; +	aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg); + +	/* Audio Control 3 (master mode, fsref rate) */ +	reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3); +	reg &= ~0xf800; +	if (aic26->master) +		reg |= 0x0800; +	if (fsref == 48000) +		reg |= 0x2000; +	aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg); + +	/* Audio Control 1 (FSref divisor) */ +	reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1); +	reg &= ~0x0fff; +	reg |= wlen | aic26->datfm | (divisor << 3) | divisor; +	aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg); + +	return 0; +} + +/** + * aic26_mute - Mute control to reduce noise when changing audio format + */ +static int aic26_mute(struct snd_soc_dai *dai, int mute) +{ +	struct snd_soc_codec *codec = dai->codec; +	struct aic26 *aic26 = codec->private_data; +	u16 reg = aic26_reg_read_cache(codec, AIC26_REG_DAC_GAIN); + +	dev_dbg(&aic26->spi->dev, "aic26_mute(dai=%p, mute=%i)\n", +		dai, mute); + +	if (mute) +		reg |= 0x8080; +	else +		reg &= ~0x8080; +	aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg); + +	return 0; +} + +static int aic26_set_sysclk(struct snd_soc_dai *codec_dai, +			    int clk_id, unsigned int freq, int dir) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct aic26 *aic26 = codec->private_data; + +	dev_dbg(&aic26->spi->dev, "aic26_set_sysclk(dai=%p, clk_id==%i," +		" freq=%i, dir=%i)\n", +		codec_dai, clk_id, freq, dir); + +	/* MCLK needs to fall between 2MHz and 50 MHz */ +	if ((freq < 2000000) || (freq > 50000000)) +		return -EINVAL; + +	aic26->mclk = freq; +	return 0; +} + +static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct aic26 *aic26 = codec->private_data; + +	dev_dbg(&aic26->spi->dev, "aic26_set_fmt(dai=%p, fmt==%i)\n", +		codec_dai, fmt); + +	/* set master/slave audio interface */ +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBM_CFM: aic26->master = 1; break; +	case SND_SOC_DAIFMT_CBS_CFS: aic26->master = 0; break; +	default: +		dev_dbg(&aic26->spi->dev, "bad master\n"); return -EINVAL; +	} + +	/* interface format */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_I2S:     aic26->datfm = AIC26_DATFM_I2S; break; +	case SND_SOC_DAIFMT_DSP_A:   aic26->datfm = AIC26_DATFM_DSP; break; +	case SND_SOC_DAIFMT_RIGHT_J: aic26->datfm = AIC26_DATFM_RIGHTJ; break; +	case SND_SOC_DAIFMT_LEFT_J:  aic26->datfm = AIC26_DATFM_LEFTJ; break; +	default: +		dev_dbg(&aic26->spi->dev, "bad format\n"); return -EINVAL; +	} + +	return 0; +} + +/* --------------------------------------------------------------------- + * Digital Audio Interface Definition + */ +#define AIC26_RATES	(SNDRV_PCM_RATE_8000  | SNDRV_PCM_RATE_11025 |\ +			 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ +			 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ +			 SNDRV_PCM_RATE_48000) +#define AIC26_FORMATS	(SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_BE |\ +			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE) + +struct snd_soc_dai aic26_dai = { +	.name = "tlv320aic26", +	.playback = { +		.stream_name = "Playback", +		.channels_min = 2, +		.channels_max = 2, +		.rates = AIC26_RATES, +		.formats = AIC26_FORMATS, +	}, +	.capture = { +		.stream_name = "Capture", +		.channels_min = 2, +		.channels_max = 2, +		.rates = AIC26_RATES, +		.formats = AIC26_FORMATS, +	}, +	.ops = { +		.hw_params = aic26_hw_params, +	}, +	.dai_ops = { +		.digital_mute = aic26_mute, +		.set_sysclk = aic26_set_sysclk, +		.set_fmt = aic26_set_fmt, +	}, +}; +EXPORT_SYMBOL_GPL(aic26_dai); + +/* --------------------------------------------------------------------- + * ALSA controls + */ +static const char *aic26_capture_src_text[] = {"Mic", "Aux"}; +static const struct soc_enum aic26_capture_src_enum = +	SOC_ENUM_SINGLE(AIC26_REG_AUDIO_CTRL1, 12, 2, aic26_capture_src_text); + +static const struct snd_kcontrol_new aic26_snd_controls[] = { +	/* Output */ +	SOC_DOUBLE("PCM Playback Volume", AIC26_REG_DAC_GAIN, 8, 0, 0x7f, 1), +	SOC_DOUBLE("PCM Playback Switch", AIC26_REG_DAC_GAIN, 15, 7, 1, 1), +	SOC_SINGLE("PCM Capture Volume", AIC26_REG_ADC_GAIN, 8, 0x7f, 0), +	SOC_SINGLE("PCM Capture Mute", AIC26_REG_ADC_GAIN, 15, 1, 1), +	SOC_SINGLE("Keyclick activate", AIC26_REG_AUDIO_CTRL2, 15, 0x1, 0), +	SOC_SINGLE("Keyclick amplitude", AIC26_REG_AUDIO_CTRL2, 12, 0x7, 0), +	SOC_SINGLE("Keyclick frequency", AIC26_REG_AUDIO_CTRL2, 8, 0x7, 0), +	SOC_SINGLE("Keyclick period", AIC26_REG_AUDIO_CTRL2, 4, 0xf, 0), +	SOC_ENUM("Capture Source", aic26_capture_src_enum), +}; + +/* --------------------------------------------------------------------- + * SoC CODEC portion of driver: probe and release routines + */ +static int aic26_probe(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec; +	struct snd_kcontrol *kcontrol; +	struct aic26 *aic26; +	int i, ret, err; + +	dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n"); +	dev_dbg(&pdev->dev, "socdev=%p\n", socdev); +	dev_dbg(&pdev->dev, "codec_data=%p\n", socdev->codec_data); + +	/* Fetch the relevant aic26 private data here (it's already been +	 * stored in the .codec pointer) */ +	aic26 = socdev->codec_data; +	if (aic26 == NULL) { +		dev_err(&pdev->dev, "aic26: missing codec pointer\n"); +		return -ENODEV; +	} +	codec = &aic26->codec; +	socdev->codec = codec; + +	dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n", +		&pdev->dev, socdev->dev); +	/* register pcms */ +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); +	if (ret < 0) { +		dev_err(&pdev->dev, "aic26: failed to create pcms\n"); +		return -ENODEV; +	} + +	/* register controls */ +	dev_dbg(&pdev->dev, "Registering controls\n"); +	for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) { +		kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL); +		err = snd_ctl_add(codec->card, kcontrol); +		WARN_ON(err < 0); +	} + +	/* CODEC is setup, we can register the card now */ +	dev_dbg(&pdev->dev, "Registering card\n"); +	ret = snd_soc_register_card(socdev); +	if (ret < 0) { +		dev_err(&pdev->dev, "aic26: failed to register card\n"); +		goto card_err; +	} +	return 0; + + card_err: +	snd_soc_free_pcms(socdev); +	return ret; +} + +static int aic26_remove(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	snd_soc_free_pcms(socdev); +	return 0; +} + +struct snd_soc_codec_device aic26_soc_codec_dev = { +	.probe = aic26_probe, +	.remove = aic26_remove, +}; +EXPORT_SYMBOL_GPL(aic26_soc_codec_dev); + +/* --------------------------------------------------------------------- + * SPI device portion of driver: sysfs files for debugging + */ + +static ssize_t aic26_keyclick_show(struct device *dev, +				   struct device_attribute *attr, char *buf) +{ +	struct aic26 *aic26 = dev_get_drvdata(dev); +	int val, amp, freq, len; + +	val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); +	amp = (val >> 12) & 0x7; +	freq = (125 << ((val >> 8) & 0x7)) >> 1; +	len = 2 * (1 + ((val >> 4) & 0xf)); + +	return sprintf(buf, "amp=%x freq=%iHz len=%iclks\n", amp, freq, len); +} + +/* Any write to the keyclick attribute will trigger the keyclick event */ +static ssize_t aic26_keyclick_set(struct device *dev, +				  struct device_attribute *attr, +				  const char *buf, size_t count) +{ +	struct aic26 *aic26 = dev_get_drvdata(dev); +	int val; + +	val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2); +	val |= 0x8000; +	aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val); + +	return count; +} + +static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set); + +/* --------------------------------------------------------------------- + * SPI device portion of driver: probe and release routines and SPI + * 				 driver registration. + */ +static int aic26_spi_probe(struct spi_device *spi) +{ +	struct aic26 *aic26; +	int rc, i, reg; + +	dev_dbg(&spi->dev, "probing tlv320aic26 spi device\n"); + +	/* Allocate driver data */ +	aic26 = kzalloc(sizeof *aic26, GFP_KERNEL); +	if (!aic26) +		return -ENOMEM; + +	/* Initialize the driver data */ +	aic26->spi = spi; +	dev_set_drvdata(&spi->dev, aic26); + +	/* Setup what we can in the codec structure so that the register +	 * access functions will work as expected.  More will be filled +	 * out when it is probed by the SoC CODEC part of this driver */ +	aic26->codec.private_data = aic26; +	aic26->codec.name = "aic26"; +	aic26->codec.owner = THIS_MODULE; +	aic26->codec.dai = &aic26_dai; +	aic26->codec.num_dai = 1; +	aic26->codec.read = aic26_reg_read; +	aic26->codec.write = aic26_reg_write; +	aic26->master = 1; +	mutex_init(&aic26->codec.mutex); +	INIT_LIST_HEAD(&aic26->codec.dapm_widgets); +	INIT_LIST_HEAD(&aic26->codec.dapm_paths); +	aic26->codec.reg_cache_size = AIC26_NUM_REGS; +	aic26->codec.reg_cache = aic26->reg_cache; + +	/* Reset the codec to power on defaults */ +	aic26_reg_write(&aic26->codec, AIC26_REG_RESET, 0xBB00); + +	/* Power up CODEC */ +	aic26_reg_write(&aic26->codec, AIC26_REG_POWER_CTRL, 0); + +	/* Audio Control 3 (master mode, fsref rate) */ +	reg = aic26_reg_read(&aic26->codec, AIC26_REG_AUDIO_CTRL3); +	reg &= ~0xf800; +	reg |= 0x0800; /* set master mode */ +	aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL3, reg); + +	/* Fill register cache */ +	for (i = 0; i < ARRAY_SIZE(aic26->reg_cache); i++) +		aic26_reg_read(&aic26->codec, i); + +	/* Register the sysfs files for debugging */ +	/* Create SysFS files */ +	rc = device_create_file(&spi->dev, &dev_attr_keyclick); +	if (rc) +		dev_info(&spi->dev, "error creating sysfs files\n"); + +#if defined(CONFIG_SND_SOC_OF_SIMPLE) +	/* Tell the of_soc helper about this codec */ +	of_snd_soc_register_codec(&aic26_soc_codec_dev, aic26, &aic26_dai, +				  spi->dev.archdata.of_node); +#endif + +	dev_dbg(&spi->dev, "SPI device initialized\n"); +	return 0; +} + +static int aic26_spi_remove(struct spi_device *spi) +{ +	struct aic26 *aic26 = dev_get_drvdata(&spi->dev); + +	kfree(aic26); + +	return 0; +} + +static struct spi_driver aic26_spi = { +	.driver = { +		.name = "tlv320aic26", +		.owner = THIS_MODULE, +	}, +	.probe = aic26_spi_probe, +	.remove = aic26_spi_remove, +}; + +static int __init aic26_init(void) +{ +	return spi_register_driver(&aic26_spi); +} +module_init(aic26_init); + +static void __exit aic26_exit(void) +{ +	spi_unregister_driver(&aic26_spi); +} +module_exit(aic26_exit); diff --git a/sound/soc/codecs/tlv320aic26.h b/sound/soc/codecs/tlv320aic26.h new file mode 100644 index 0000000..786ba16 --- /dev/null +++ b/sound/soc/codecs/tlv320aic26.h @@ -0,0 +1,96 @@ +/* + * Texas Instruments TLV320AIC26 low power audio CODEC + * register definitions + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + */ + +#ifndef _TLV320AIC16_H_ +#define _TLV320AIC16_H_ + +/* AIC26 Registers */ +#define AIC26_READ_COMMAND_WORD(addr)	((1 << 15) | (addr << 5)) +#define AIC26_WRITE_COMMAND_WORD(addr)	((0 << 15) | (addr << 5)) +#define AIC26_PAGE_ADDR(page, offset)	((page << 6) | offset) +#define AIC26_NUM_REGS			AIC26_PAGE_ADDR(3, 0) + +/* Page 0: Auxillary data registers */ +#define AIC26_REG_BAT1			AIC26_PAGE_ADDR(0, 0x05) +#define AIC26_REG_BAT2			AIC26_PAGE_ADDR(0, 0x06) +#define AIC26_REG_AUX			AIC26_PAGE_ADDR(0, 0x07) +#define AIC26_REG_TEMP1			AIC26_PAGE_ADDR(0, 0x09) +#define AIC26_REG_TEMP2			AIC26_PAGE_ADDR(0, 0x0A) + +/* Page 1: Auxillary control registers */ +#define AIC26_REG_AUX_ADC		AIC26_PAGE_ADDR(1, 0x00) +#define AIC26_REG_STATUS		AIC26_PAGE_ADDR(1, 0x01) +#define AIC26_REG_REFERENCE		AIC26_PAGE_ADDR(1, 0x03) +#define AIC26_REG_RESET			AIC26_PAGE_ADDR(1, 0x04) + +/* Page 2: Audio control registers */ +#define AIC26_REG_AUDIO_CTRL1		AIC26_PAGE_ADDR(2, 0x00) +#define AIC26_REG_ADC_GAIN		AIC26_PAGE_ADDR(2, 0x01) +#define AIC26_REG_DAC_GAIN		AIC26_PAGE_ADDR(2, 0x02) +#define AIC26_REG_SIDETONE		AIC26_PAGE_ADDR(2, 0x03) +#define AIC26_REG_AUDIO_CTRL2		AIC26_PAGE_ADDR(2, 0x04) +#define AIC26_REG_POWER_CTRL		AIC26_PAGE_ADDR(2, 0x05) +#define AIC26_REG_AUDIO_CTRL3		AIC26_PAGE_ADDR(2, 0x06) + +#define AIC26_REG_FILTER_COEFF_L_N0	AIC26_PAGE_ADDR(2, 0x07) +#define AIC26_REG_FILTER_COEFF_L_N1	AIC26_PAGE_ADDR(2, 0x08) +#define AIC26_REG_FILTER_COEFF_L_N2	AIC26_PAGE_ADDR(2, 0x09) +#define AIC26_REG_FILTER_COEFF_L_N3	AIC26_PAGE_ADDR(2, 0x0A) +#define AIC26_REG_FILTER_COEFF_L_N4	AIC26_PAGE_ADDR(2, 0x0B) +#define AIC26_REG_FILTER_COEFF_L_N5	AIC26_PAGE_ADDR(2, 0x0C) +#define AIC26_REG_FILTER_COEFF_L_D1	AIC26_PAGE_ADDR(2, 0x0D) +#define AIC26_REG_FILTER_COEFF_L_D2	AIC26_PAGE_ADDR(2, 0x0E) +#define AIC26_REG_FILTER_COEFF_L_D4	AIC26_PAGE_ADDR(2, 0x0F) +#define AIC26_REG_FILTER_COEFF_L_D5	AIC26_PAGE_ADDR(2, 0x10) +#define AIC26_REG_FILTER_COEFF_R_N0	AIC26_PAGE_ADDR(2, 0x11) +#define AIC26_REG_FILTER_COEFF_R_N1	AIC26_PAGE_ADDR(2, 0x12) +#define AIC26_REG_FILTER_COEFF_R_N2	AIC26_PAGE_ADDR(2, 0x13) +#define AIC26_REG_FILTER_COEFF_R_N3	AIC26_PAGE_ADDR(2, 0x14) +#define AIC26_REG_FILTER_COEFF_R_N4	AIC26_PAGE_ADDR(2, 0x15) +#define AIC26_REG_FILTER_COEFF_R_N5	AIC26_PAGE_ADDR(2, 0x16) +#define AIC26_REG_FILTER_COEFF_R_D1	AIC26_PAGE_ADDR(2, 0x17) +#define AIC26_REG_FILTER_COEFF_R_D2	AIC26_PAGE_ADDR(2, 0x18) +#define AIC26_REG_FILTER_COEFF_R_D4	AIC26_PAGE_ADDR(2, 0x19) +#define AIC26_REG_FILTER_COEFF_R_D5	AIC26_PAGE_ADDR(2, 0x1A) + +#define AIC26_REG_PLL_PROG1		AIC26_PAGE_ADDR(2, 0x1B) +#define AIC26_REG_PLL_PROG2		AIC26_PAGE_ADDR(2, 0x1C) +#define AIC26_REG_AUDIO_CTRL4		AIC26_PAGE_ADDR(2, 0x1D) +#define AIC26_REG_AUDIO_CTRL5		AIC26_PAGE_ADDR(2, 0x1E) + +/* fsref dividers; used in register 'Audio Control 1' */ +enum aic26_divisors { +	AIC26_DIV_1	= 0, +	AIC26_DIV_1_5	= 1, +	AIC26_DIV_2	= 2, +	AIC26_DIV_3	= 3, +	AIC26_DIV_4	= 4, +	AIC26_DIV_5	= 5, +	AIC26_DIV_5_5	= 6, +	AIC26_DIV_6	= 7, +}; + +/* Digital data format */ +enum aic26_datfm { +	AIC26_DATFM_I2S		= 0 << 8, +	AIC26_DATFM_DSP		= 1 << 8, +	AIC26_DATFM_RIGHTJ	= 2 << 8, /* right justified */ +	AIC26_DATFM_LEFTJ	= 3 << 8, /* left justified */ +}; + +/* Sample word length in bits; used in register 'Audio Control 1' */ +enum aic26_wlen { +	AIC26_WLEN_16	= 0 << 10, +	AIC26_WLEN_20	= 1 << 10, +	AIC26_WLEN_24	= 2 << 10, +	AIC26_WLEN_32	= 3 << 10, +}; + +extern struct snd_soc_dai aic26_dai; +extern struct snd_soc_codec_device aic26_soc_codec_dev; + +#endif /* _TLV320AIC16_H_ */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 5f9abb1..566a427 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1,7 +1,7 @@  /*   * ALSA SoC TLV320AIC3X codec driver   * - * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com> + * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>   * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>   *   * Based on sound/soc/codecs/wm8753.c by Liam Girdwood @@ -1172,71 +1172,39 @@ static struct snd_soc_device *aic3x_socdev;   * AIC3X 2 wire address can be up to 4 devices with device addresses   * 0x18, 0x19, 0x1A, 0x1B   */ -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; - -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver aic3x_i2c_driver; -static struct i2c_client client_template;  /*   * If the i2c layer weren't so broken, we could pass this kind of data   * around   */ -static int aic3x_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int aic3x_i2c_probe(struct i2c_client *i2c, +			   const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = aic3x_socdev; -	struct aic3x_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		printk(KERN_ERR "aic3x: failed to attach codec at addr %x\n", -		       addr); -		goto err; -	} -  	ret = aic3x_init(socdev); -	if (ret < 0) { +	if (ret < 0)  		printk(KERN_ERR "aic3x: failed to initialise AIC3X\n"); -		goto err; -	} -	return ret; - -err: -	kfree(i2c);  	return ret;  } -static int aic3x_i2c_detach(struct i2c_client *client) +static int aic3x_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int aic3x_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, aic3x_codec_probe); -} +static const struct i2c_device_id aic3x_i2c_id[] = { +	{ "tlv320aic3x", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);  /* machine i2c codec control layer */  static struct i2c_driver aic3x_i2c_driver = { @@ -1244,13 +1212,9 @@ static struct i2c_driver aic3x_i2c_driver = {  		.name = "aic3x I2C Codec",  		.owner = THIS_MODULE,  	}, -	.attach_adapter = aic3x_i2c_attach, -	.detach_client = aic3x_i2c_detach, -}; - -static struct i2c_client client_template = { -	.name = "AIC3X", -	.driver = &aic3x_i2c_driver, +	.probe = aic3x_i2c_probe, +	.remove = aic3x_i2c_remove, +	.id_table = aic3x_i2c_id,  };  static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len) @@ -1258,6 +1222,46 @@ static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)  	value[0] = i2c_smbus_read_byte_data(client, value[0]);  	return (len == 1);  } + +static int aic3x_add_i2c_device(struct platform_device *pdev, +				 const struct aic3x_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&aic3x_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "tlv320aic3x", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&aic3x_i2c_driver); +	return -ENODEV; +}  #endif  static int aic3x_probe(struct platform_device *pdev) @@ -1290,12 +1294,9 @@ static int aic3x_probe(struct platform_device *pdev)  	aic3x_socdev = socdev;  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t) i2c_master_send;  		codec->hw_read = (hw_read_t) aic3x_i2c_read; -		ret = i2c_add_driver(&aic3x_i2c_driver); -		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +		ret = aic3x_add_i2c_device(pdev, setup);  	}  #else  	/* Add other interfaces here */ @@ -1320,6 +1321,7 @@ static int aic3x_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&aic3x_i2c_driver);  #endif  	kfree(codec->private_data); diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index d76c079..00a195a 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h @@ -1,7 +1,7 @@  /*   * ALSA SoC TLV320AIC3X codec driver   * - * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com> + * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>   * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>   *   * This program is free software; you can redistribute it and/or modify @@ -224,6 +224,7 @@ int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio);  int aic3x_headset_detected(struct snd_soc_codec *codec);  struct aic3x_setup_data { +	int i2c_bus;  	unsigned short i2c_address;  	unsigned int gpio_func[2];  }; diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 807318f..d206d7f 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -701,87 +701,86 @@ static struct snd_soc_device *uda1380_socdev;  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -#define I2C_DRIVERID_UDA1380 0xfefe /* liam -  need a proper id */ - -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; - -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver uda1380_i2c_driver; -static struct i2c_client client_template; - -/* If the i2c layer weren't so broken, we could pass this kind of data -   around */ - -static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int uda1380_i2c_probe(struct i2c_client *i2c, +			     const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = uda1380_socdev;  	struct uda1380_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		pr_err("uda1380: failed to attach codec at addr %x\n", addr); -		goto err; -	} -  	ret = uda1380_init(socdev, setup->dac_clk); -	if (ret < 0) { +	if (ret < 0)  		pr_err("uda1380: failed to initialise UDA1380\n"); -		goto err; -	} -	return ret; -err: -	kfree(i2c);  	return ret;  } -static int uda1380_i2c_detach(struct i2c_client *client) +static int uda1380_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int uda1380_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, uda1380_codec_probe); -} +static const struct i2c_device_id uda1380_i2c_id[] = { +	{ "uda1380", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);  static struct i2c_driver uda1380_i2c_driver = {  	.driver = {  		.name =  "UDA1380 I2C Codec",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_UDA1380, -	.attach_adapter = uda1380_i2c_attach, -	.detach_client =  uda1380_i2c_detach, -	.command =        NULL, +	.probe =    uda1380_i2c_probe, +	.remove =   uda1380_i2c_remove, +	.id_table = uda1380_i2c_id,  }; -static struct i2c_client client_template = { -	.name =   "UDA1380", -	.driver = &uda1380_i2c_driver, -}; +static int uda1380_add_i2c_device(struct platform_device *pdev, +				  const struct uda1380_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&uda1380_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "uda1380", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&uda1380_i2c_driver); +	return -ENODEV; +}  #endif  static int uda1380_probe(struct platform_device *pdev) @@ -789,7 +788,7 @@ static int uda1380_probe(struct platform_device *pdev)  	struct snd_soc_device *socdev = platform_get_drvdata(pdev);  	struct uda1380_setup_data *setup;  	struct snd_soc_codec *codec; -	int ret = 0; +	int ret;  	pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION); @@ -804,16 +803,13 @@ static int uda1380_probe(struct platform_device *pdev)  	INIT_LIST_HEAD(&codec->dapm_paths);  	uda1380_socdev = socdev; +	ret = -ENODEV; +  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t)i2c_master_send; -		ret = i2c_add_driver(&uda1380_i2c_driver); -		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +		ret = uda1380_add_i2c_device(pdev, setup);  	} -#else -	/* Add other interfaces here */  #endif  	if (ret != 0) @@ -833,6 +829,7 @@ static int uda1380_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&uda1380_i2c_driver);  #endif  	kfree(codec); diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h index 50c603e..c55c17a 100644 --- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -73,6 +73,7 @@  #define R23_AGC_EN	0x0001  struct uda1380_setup_data { +	int            i2c_bus;  	unsigned short i2c_address;  	int            dac_clk;  #define UDA1380_DAC_CLK_SYSCLK 0 diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 3d998e6..9a37c8d 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -199,7 +199,7 @@ SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 0),  };  static const struct snd_kcontrol_new wm8510_boost_controls[] = { -SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA,  6, 1, 0), +SOC_DAPM_SINGLE("Mic PGA Switch", WM8510_INPPGA,  6, 1, 1),  SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0),  SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0),  }; @@ -665,88 +665,86 @@ static struct snd_soc_device *wm8510_socdev;  /*   * WM8510 2 wire address is 0x1a   */ -#define I2C_DRIVERID_WM8510 0xfefe /* liam -  need a proper id */ -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; - -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver wm8510_i2c_driver; -static struct i2c_client client_template; - -/* If the i2c layer weren't so broken, we could pass this kind of data -   around */ - -static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int wm8510_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = wm8510_socdev; -	struct wm8510_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		pr_err("failed to attach codec at addr %x\n", addr); -		goto err; -	} -  	ret = wm8510_init(socdev); -	if (ret < 0) { +	if (ret < 0)  		pr_err("failed to initialise WM8510\n"); -		goto err; -	} -	return ret; -err: -	kfree(i2c);  	return ret;  } -static int wm8510_i2c_detach(struct i2c_client *client) +static int wm8510_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int wm8510_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, wm8510_codec_probe); -} +static const struct i2c_device_id wm8510_i2c_id[] = { +	{ "wm8510", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); -/* corgi i2c codec control layer */  static struct i2c_driver wm8510_i2c_driver = {  	.driver = {  		.name = "WM8510 I2C Codec",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_WM8510, -	.attach_adapter = wm8510_i2c_attach, -	.detach_client =  wm8510_i2c_detach, -	.command =        NULL, +	.probe =    wm8510_i2c_probe, +	.remove =   wm8510_i2c_remove, +	.id_table = wm8510_i2c_id,  }; -static struct i2c_client client_template = { -	.name =   "WM8510", -	.driver = &wm8510_i2c_driver, -}; +static int wm8510_add_i2c_device(struct platform_device *pdev, +				 const struct wm8510_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&wm8510_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "wm8510", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&wm8510_i2c_driver); +	return -ENODEV; +}  #endif  static int wm8510_probe(struct platform_device *pdev) @@ -771,11 +769,8 @@ static int wm8510_probe(struct platform_device *pdev)  	wm8510_socdev = socdev;  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t)i2c_master_send; -		ret = i2c_add_driver(&wm8510_i2c_driver); -		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +		ret = wm8510_add_i2c_device(pdev, setup);  	}  #else  	/* Add other interfaces here */ @@ -798,6 +793,7 @@ static int wm8510_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&wm8510_i2c_driver);  #endif  	kfree(codec); diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h index f5d2e42..c536839 100644 --- a/sound/soc/codecs/wm8510.h +++ b/sound/soc/codecs/wm8510.h @@ -94,6 +94,7 @@  #define WM8510_MCLKDIV_12	(7 << 5)  struct wm8510_setup_data { +	int i2c_bus;  	unsigned short i2c_address;  }; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c new file mode 100644 index 0000000..df1ffbe --- /dev/null +++ b/sound/soc/codecs/wm8580.c @@ -0,0 +1,1055 @@ +/* + * wm8580.c  --  WM8580 ALSA Soc Audio driver + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + * + * Notes: + *  The WM8580 is a multichannel codec with S/PDIF support, featuring six + *  DAC channels and two ADC channels. + * + *  Currently only the primary audio interface is supported - S/PDIF and + *  the secondary audio interfaces are not. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <sound/initval.h> +#include <asm/div64.h> + +#include "wm8580.h" + +#define AUDIO_NAME "wm8580" +#define WM8580_VERSION "0.1" + +struct pll_state { +	unsigned int in; +	unsigned int out; +}; + +/* codec private data */ +struct wm8580_priv { +	struct pll_state a; +	struct pll_state b; +}; + +/* WM8580 register space */ +#define WM8580_PLLA1                         0x00 +#define WM8580_PLLA2                         0x01 +#define WM8580_PLLA3                         0x02 +#define WM8580_PLLA4                         0x03 +#define WM8580_PLLB1                         0x04 +#define WM8580_PLLB2                         0x05 +#define WM8580_PLLB3                         0x06 +#define WM8580_PLLB4                         0x07 +#define WM8580_CLKSEL                        0x08 +#define WM8580_PAIF1                         0x09 +#define WM8580_PAIF2                         0x0A +#define WM8580_SAIF1                         0x0B +#define WM8580_PAIF3                         0x0C +#define WM8580_PAIF4                         0x0D +#define WM8580_SAIF2                         0x0E +#define WM8580_DAC_CONTROL1                  0x0F +#define WM8580_DAC_CONTROL2                  0x10 +#define WM8580_DAC_CONTROL3                  0x11 +#define WM8580_DAC_CONTROL4                  0x12 +#define WM8580_DAC_CONTROL5                  0x13 +#define WM8580_DIGITAL_ATTENUATION_DACL1     0x14 +#define WM8580_DIGITAL_ATTENUATION_DACR1     0x15 +#define WM8580_DIGITAL_ATTENUATION_DACL2     0x16 +#define WM8580_DIGITAL_ATTENUATION_DACR2     0x17 +#define WM8580_DIGITAL_ATTENUATION_DACL3     0x18 +#define WM8580_DIGITAL_ATTENUATION_DACR3     0x19 +#define WM8580_MASTER_DIGITAL_ATTENUATION    0x1C +#define WM8580_ADC_CONTROL1                  0x1D +#define WM8580_SPDTXCHAN0                    0x1E +#define WM8580_SPDTXCHAN1                    0x1F +#define WM8580_SPDTXCHAN2                    0x20 +#define WM8580_SPDTXCHAN3                    0x21 +#define WM8580_SPDTXCHAN4                    0x22 +#define WM8580_SPDTXCHAN5                    0x23 +#define WM8580_SPDMODE                       0x24 +#define WM8580_INTMASK                       0x25 +#define WM8580_GPO1                          0x26 +#define WM8580_GPO2                          0x27 +#define WM8580_GPO3                          0x28 +#define WM8580_GPO4                          0x29 +#define WM8580_GPO5                          0x2A +#define WM8580_INTSTAT                       0x2B +#define WM8580_SPDRXCHAN1                    0x2C +#define WM8580_SPDRXCHAN2                    0x2D +#define WM8580_SPDRXCHAN3                    0x2E +#define WM8580_SPDRXCHAN4                    0x2F +#define WM8580_SPDRXCHAN5                    0x30 +#define WM8580_SPDSTAT                       0x31 +#define WM8580_PWRDN1                        0x32 +#define WM8580_PWRDN2                        0x33 +#define WM8580_READBACK                      0x34 +#define WM8580_RESET                         0x35 + +/* PLLB4 (register 7h) */ +#define WM8580_PLLB4_MCLKOUTSRC_MASK   0x60 +#define WM8580_PLLB4_MCLKOUTSRC_PLLA   0x20 +#define WM8580_PLLB4_MCLKOUTSRC_PLLB   0x40 +#define WM8580_PLLB4_MCLKOUTSRC_OSC    0x60 + +#define WM8580_PLLB4_CLKOUTSRC_MASK    0x180 +#define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080 +#define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100 +#define WM8580_PLLB4_CLKOUTSRC_OSCCLK  0x180 + +/* CLKSEL (register 8h) */ +#define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03 +#define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01 +#define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02 + +/* AIF control 1 (registers 9h-bh) */ +#define WM8580_AIF_RATE_MASK       0x7 +#define WM8580_AIF_RATE_128        0x0 +#define WM8580_AIF_RATE_192        0x1 +#define WM8580_AIF_RATE_256        0x2 +#define WM8580_AIF_RATE_384        0x3 +#define WM8580_AIF_RATE_512        0x4 +#define WM8580_AIF_RATE_768        0x5 +#define WM8580_AIF_RATE_1152       0x6 + +#define WM8580_AIF_BCLKSEL_MASK   0x18 +#define WM8580_AIF_BCLKSEL_64     0x00 +#define WM8580_AIF_BCLKSEL_128    0x08 +#define WM8580_AIF_BCLKSEL_256    0x10 +#define WM8580_AIF_BCLKSEL_SYSCLK 0x18 + +#define WM8580_AIF_MS             0x20 + +#define WM8580_AIF_CLKSRC_MASK    0xc0 +#define WM8580_AIF_CLKSRC_PLLA    0x40 +#define WM8580_AIF_CLKSRC_PLLB    0x40 +#define WM8580_AIF_CLKSRC_MCLK    0xc0 + +/* AIF control 2 (registers ch-eh) */ +#define WM8580_AIF_FMT_MASK    0x03 +#define WM8580_AIF_FMT_RIGHTJ  0x00 +#define WM8580_AIF_FMT_LEFTJ   0x01 +#define WM8580_AIF_FMT_I2S     0x02 +#define WM8580_AIF_FMT_DSP     0x03 + +#define WM8580_AIF_LENGTH_MASK   0x0c +#define WM8580_AIF_LENGTH_16     0x00 +#define WM8580_AIF_LENGTH_20     0x04 +#define WM8580_AIF_LENGTH_24     0x08 +#define WM8580_AIF_LENGTH_32     0x0c + +#define WM8580_AIF_LRP         0x10 +#define WM8580_AIF_BCP         0x20 + +/* Powerdown Register 1 (register 32h) */ +#define WM8580_PWRDN1_PWDN     0x001 +#define WM8580_PWRDN1_ALLDACPD 0x040 + +/* Powerdown Register 2 (register 33h) */ +#define WM8580_PWRDN2_OSSCPD   0x001 +#define WM8580_PWRDN2_PLLAPD   0x002 +#define WM8580_PWRDN2_PLLBPD   0x004 +#define WM8580_PWRDN2_SPDIFPD  0x008 +#define WM8580_PWRDN2_SPDIFTXD 0x010 +#define WM8580_PWRDN2_SPDIFRXD 0x020 + +#define WM8580_DAC_CONTROL5_MUTEALL 0x10 + +/* + * wm8580 register cache + * We can't read the WM8580 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const u16 wm8580_reg[] = { +	0x0121, 0x017e, 0x007d, 0x0014, /*R3*/ +	0x0121, 0x017e, 0x007d, 0x0194, /*R7*/ +	0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/ +	0x0182, 0x0082, 0x000a, 0x0024, /*R15*/ +	0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/ +	0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/ +	0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/ +	0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/ +	0x0000, 0x0000, 0x0031, 0x000b, /*R35*/ +	0x0039, 0x0000, 0x0010, 0x0032, /*R39*/ +	0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/ +	0x0000, 0x0000, 0x0000, 0x0000, /*R47*/ +	0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/ +	0x0000, 0x0000 /*R53*/ +}; + +/* + * read wm8580 register cache + */ +static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec, +	unsigned int reg) +{ +	u16 *cache = codec->reg_cache; +	BUG_ON(reg > ARRAY_SIZE(wm8580_reg)); +	return cache[reg]; +} + +/* + * write wm8580 register cache + */ +static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec, +	unsigned int reg, unsigned int value) +{ +	u16 *cache = codec->reg_cache; + +	cache[reg] = value; +} + +/* + * write to the WM8580 register space + */ +static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg, +	unsigned int value) +{ +	u8 data[2]; + +	BUG_ON(reg > ARRAY_SIZE(wm8580_reg)); + +	/* Registers are 9 bits wide */ +	value &= 0x1ff; + +	switch (reg) { +	case WM8580_RESET: +		/* Uncached */ +		break; +	default: +		if (value == wm8580_read_reg_cache(codec, reg)) +			return 0; +	} + +	/* data is +	 *   D15..D9 WM8580 register offset +	 *   D8...D0 register data +	 */ +	data[0] = (reg << 1) | ((value >> 8) & 0x0001); +	data[1] = value & 0x00ff; + +	wm8580_write_reg_cache(codec, reg, value); +	if (codec->hw_write(codec->control_data, data, 2) == 2) +		return 0; +	else +		return -EIO; +} + +static inline unsigned int wm8580_read(struct snd_soc_codec *codec, +				       unsigned int reg) +{ +	switch (reg) { +	default: +		return wm8580_read_reg_cache(codec, reg); +	} +} + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); + +static int wm8580_out_vu(struct snd_kcontrol *kcontrol, +			 struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); +	int reg = kcontrol->private_value & 0xff; +	int reg2 = (kcontrol->private_value >> 24) & 0xff; +	int ret; +	u16 val; + +	/* Clear the register cache so we write without VU set */ +	wm8580_write_reg_cache(codec, reg, 0); +	wm8580_write_reg_cache(codec, reg2, 0); + +	ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); +	if (ret < 0) +		return ret; + +	/* Now write again with the volume update bit set */ +	val = wm8580_read_reg_cache(codec, reg); +	wm8580_write(codec, reg, val | 0x0100); + +	val = wm8580_read_reg_cache(codec, reg2); +	wm8580_write(codec, reg2, val | 0x0100); + +	return 0; +} + +#define SOC_WM8580_OUT_DOUBLE_R_TLV(xname, reg_left, reg_right, shift, max, invert, tlv_array) \ +{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ +	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ +		SNDRV_CTL_ELEM_ACCESS_READWRITE,  \ +	.tlv.p = (tlv_array), \ +	.info = snd_soc_info_volsw_2r, \ +	.get = snd_soc_get_volsw_2r, .put = wm8580_out_vu, \ +	.private_value = (reg_left) | ((shift) << 8)  |		\ +		((max) << 12) | ((invert) << 20) | ((reg_right) << 24) } + +static const struct snd_kcontrol_new wm8580_snd_controls[] = { +SOC_WM8580_OUT_DOUBLE_R_TLV("DAC1 Playback Volume", +			    WM8580_DIGITAL_ATTENUATION_DACL1, +			    WM8580_DIGITAL_ATTENUATION_DACR1, +			    0, 0xff, 0, dac_tlv), +SOC_WM8580_OUT_DOUBLE_R_TLV("DAC2 Playback Volume", +			    WM8580_DIGITAL_ATTENUATION_DACL2, +			    WM8580_DIGITAL_ATTENUATION_DACR2, +			    0, 0xff, 0, dac_tlv), +SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume", +			    WM8580_DIGITAL_ATTENUATION_DACL3, +			    WM8580_DIGITAL_ATTENUATION_DACR3, +			    0, 0xff, 0, dac_tlv), + +SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0), +SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0), +SOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0), + +SOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4,  0, 1, 1, 0), +SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4,  2, 3, 1, 0), +SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4,  4, 5, 1, 0), + +SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0), +SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 0), +SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 0), +SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 0), + +SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0), +SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), +}; + +/* Add non-DAPM controls */ +static int wm8580_add_controls(struct snd_soc_codec *codec) +{ +	int err, i; + +	for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) { +		err = snd_ctl_add(codec->card, +				  snd_soc_cnew(&wm8580_snd_controls[i], +					       codec, NULL)); +		if (err < 0) +			return err; +	} +	return 0; +} +static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { +SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1), +SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1), +SND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1), + +SND_SOC_DAPM_OUTPUT("VOUT1L"), +SND_SOC_DAPM_OUTPUT("VOUT1R"), +SND_SOC_DAPM_OUTPUT("VOUT2L"), +SND_SOC_DAPM_OUTPUT("VOUT2R"), +SND_SOC_DAPM_OUTPUT("VOUT3L"), +SND_SOC_DAPM_OUTPUT("VOUT3R"), + +SND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1), + +SND_SOC_DAPM_INPUT("AINL"), +SND_SOC_DAPM_INPUT("AINR"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { +	{ "VOUT1L", NULL, "DAC1" }, +	{ "VOUT1R", NULL, "DAC1" }, + +	{ "VOUT2L", NULL, "DAC2" }, +	{ "VOUT2R", NULL, "DAC2" }, + +	{ "VOUT3L", NULL, "DAC3" }, +	{ "VOUT3R", NULL, "DAC3" }, + +	{ "ADC", NULL, "AINL" }, +	{ "ADC", NULL, "AINR" }, +}; + +static int wm8580_add_widgets(struct snd_soc_codec *codec) +{ +	snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets, +				  ARRAY_SIZE(wm8580_dapm_widgets)); + +	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + +	snd_soc_dapm_new_widgets(codec); +	return 0; +} + +/* PLL divisors */ +struct _pll_div { +	u32 prescale:1; +	u32 postscale:1; +	u32 freqmode:2; +	u32 n:4; +	u32 k:24; +}; + +/* The size in bits of the pll divide */ +#define FIXED_PLL_SIZE (1 << 22) + +/* PLL rate to output rate divisions */ +static struct { +	unsigned int div; +	unsigned int freqmode; +	unsigned int postscale; +} post_table[] = { +	{  2,  0, 0 }, +	{  4,  0, 1 }, +	{  4,  1, 0 }, +	{  8,  1, 1 }, +	{  8,  2, 0 }, +	{ 16,  2, 1 }, +	{ 12,  3, 0 }, +	{ 24,  3, 1 } +}; + +static int pll_factors(struct _pll_div *pll_div, unsigned int target, +		       unsigned int source) +{ +	u64 Kpart; +	unsigned int K, Ndiv, Nmod; +	int i; + +	pr_debug("wm8580: PLL %dHz->%dHz\n", source, target); + +	/* Scale the output frequency up; the PLL should run in the +	 * region of 90-100MHz. +	 */ +	for (i = 0; i < ARRAY_SIZE(post_table); i++) { +		if (target * post_table[i].div >=  90000000 && +		    target * post_table[i].div <= 100000000) { +			pll_div->freqmode = post_table[i].freqmode; +			pll_div->postscale = post_table[i].postscale; +			target *= post_table[i].div; +			break; +		} +	} + +	if (i == ARRAY_SIZE(post_table)) { +		printk(KERN_ERR "wm8580: Unable to scale output frequency " +		       "%u\n", target); +		return -EINVAL; +	} + +	Ndiv = target / source; + +	if (Ndiv < 5) { +		source /= 2; +		pll_div->prescale = 1; +		Ndiv = target / source; +	} else +		pll_div->prescale = 0; + +	if ((Ndiv < 5) || (Ndiv > 13)) { +		printk(KERN_ERR +			"WM8580 N=%d outside supported range\n", Ndiv); +		return -EINVAL; +	} + +	pll_div->n = Ndiv; +	Nmod = target % source; +	Kpart = FIXED_PLL_SIZE * (long long)Nmod; + +	do_div(Kpart, source); + +	K = Kpart & 0xFFFFFFFF; + +	pll_div->k = K; + +	pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n", +		 pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode, +		 pll_div->postscale); + +	return 0; +} + +static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, +		int pll_id, unsigned int freq_in, unsigned int freq_out) +{ +	int offset; +	struct snd_soc_codec *codec = codec_dai->codec; +	struct wm8580_priv *wm8580 = codec->private_data; +	struct pll_state *state; +	struct _pll_div pll_div; +	unsigned int reg; +	unsigned int pwr_mask; +	int ret; + +	/* GCC isn't able to work out the ifs below for initialising/using +	 * pll_div so suppress warnings. +	 */ +	memset(&pll_div, 0, sizeof(pll_div)); + +	switch (pll_id) { +	case WM8580_PLLA: +		state = &wm8580->a; +		offset = 0; +		pwr_mask = WM8580_PWRDN2_PLLAPD; +		break; +	case WM8580_PLLB: +		state = &wm8580->b; +		offset = 4; +		pwr_mask = WM8580_PWRDN2_PLLBPD; +		break; +	default: +		return -ENODEV; +	} + +	if (freq_in && freq_out) { +		ret = pll_factors(&pll_div, freq_out, freq_in); +		if (ret != 0) +			return ret; +	} + +	state->in = freq_in; +	state->out = freq_out; + +	/* Always disable the PLL - it is not safe to leave it running +	 * while reprogramming it. +	 */ +	reg = wm8580_read(codec, WM8580_PWRDN2); +	wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask); + +	if (!freq_in || !freq_out) +		return 0; + +	wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff); +	wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff); +	wm8580_write(codec, WM8580_PLLA3 + offset, +		     (pll_div.k >> 18 & 0xf) | (pll_div.n << 4)); + +	reg = wm8580_read(codec, WM8580_PLLA4 + offset); +	reg &= ~0x3f; +	reg |= pll_div.prescale | pll_div.postscale << 1 | +		pll_div.freqmode << 4; + +	wm8580_write(codec, WM8580_PLLA4 + offset, reg); + +	/* All done, turn it on */ +	reg = wm8580_read(codec, WM8580_PWRDN2); +	wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask); + +	return 0; +} + +/* + * Set PCM DAI bit size and sample rate. + */ +static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai_link *dai = rtd->dai; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->codec_dai->id); + +	paifb &= ~WM8580_AIF_LENGTH_MASK; +	/* bit size */ +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S16_LE: +		break; +	case SNDRV_PCM_FORMAT_S20_3LE: +		paifb |= WM8580_AIF_LENGTH_20; +		break; +	case SNDRV_PCM_FORMAT_S24_LE: +		paifb |= WM8580_AIF_LENGTH_24; +		break; +	case SNDRV_PCM_FORMAT_S32_LE: +		paifb |= WM8580_AIF_LENGTH_24; +		break; +	default: +		return -EINVAL; +	} + +	wm8580_write(codec, WM8580_PAIF3 + dai->codec_dai->id, paifb); +	return 0; +} + +static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, +				      unsigned int fmt) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	unsigned int aifa; +	unsigned int aifb; +	int can_invert_lrclk; + +	aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id); +	aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id); + +	aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); + +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBS_CFS: +		aifa &= ~WM8580_AIF_MS; +		break; +	case SND_SOC_DAIFMT_CBM_CFM: +		aifa |= WM8580_AIF_MS; +		break; +	default: +		return -EINVAL; +	} + +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_I2S: +		can_invert_lrclk = 1; +		aifb |= WM8580_AIF_FMT_I2S; +		break; +	case SND_SOC_DAIFMT_RIGHT_J: +		can_invert_lrclk = 1; +		aifb |= WM8580_AIF_FMT_RIGHTJ; +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		can_invert_lrclk = 1; +		aifb |= WM8580_AIF_FMT_LEFTJ; +		break; +	case SND_SOC_DAIFMT_DSP_A: +		can_invert_lrclk = 0; +		aifb |= WM8580_AIF_FMT_DSP; +		break; +	case SND_SOC_DAIFMT_DSP_B: +		can_invert_lrclk = 0; +		aifb |= WM8580_AIF_FMT_DSP; +		aifb |= WM8580_AIF_LRP; +		break; +	default: +		return -EINVAL; +	} + +	switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +	case SND_SOC_DAIFMT_NB_NF: +		break; + +	case SND_SOC_DAIFMT_IB_IF: +		if (!can_invert_lrclk) +			return -EINVAL; +		aifb |= WM8580_AIF_BCP; +		aifb |= WM8580_AIF_LRP; +		break; + +	case SND_SOC_DAIFMT_IB_NF: +		aifb |= WM8580_AIF_BCP; +		break; + +	case SND_SOC_DAIFMT_NB_IF: +		if (!can_invert_lrclk) +			return -EINVAL; +		aifb |= WM8580_AIF_LRP; +		break; + +	default: +		return -EINVAL; +	} + +	wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa); +	wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb); + +	return 0; +} + +static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, +				 int div_id, int div) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	unsigned int reg; + +	switch (div_id) { +	case WM8580_MCLK: +		reg = wm8580_read(codec, WM8580_PLLB4); +		reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK; + +		switch (div) { +		case WM8580_CLKSRC_MCLK: +			/* Input */ +			break; + +		case WM8580_CLKSRC_PLLA: +			reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA; +			break; +		case WM8580_CLKSRC_PLLB: +			reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB; +			break; + +		case WM8580_CLKSRC_OSC: +			reg |= WM8580_PLLB4_MCLKOUTSRC_OSC; +			break; + +		default: +			return -EINVAL; +		} +		wm8580_write(codec, WM8580_PLLB4, reg); +		break; + +	case WM8580_DAC_CLKSEL: +		reg = wm8580_read(codec, WM8580_CLKSEL); +		reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK; + +		switch (div) { +		case WM8580_CLKSRC_MCLK: +			break; + +		case WM8580_CLKSRC_PLLA: +			reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA; +			break; + +		case WM8580_CLKSRC_PLLB: +			reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB; +			break; + +		default: +			return -EINVAL; +		} +		wm8580_write(codec, WM8580_CLKSEL, reg); +		break; + +	case WM8580_CLKOUTSRC: +		reg = wm8580_read(codec, WM8580_PLLB4); +		reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; + +		switch (div) { +		case WM8580_CLKSRC_NONE: +			break; + +		case WM8580_CLKSRC_PLLA: +			reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK; +			break; + +		case WM8580_CLKSRC_PLLB: +			reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK; +			break; + +		case WM8580_CLKSRC_OSC: +			reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK; +			break; + +		default: +			return -EINVAL; +		} +		wm8580_write(codec, WM8580_PLLB4, reg); +		break; + +	default: +		return -EINVAL; +	} + +	return 0; +} + +static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	unsigned int reg; + +	reg = wm8580_read(codec, WM8580_DAC_CONTROL5); + +	if (mute) +		reg |= WM8580_DAC_CONTROL5_MUTEALL; +	else +		reg &= ~WM8580_DAC_CONTROL5_MUTEALL; + +	wm8580_write(codec, WM8580_DAC_CONTROL5, reg); + +	return 0; +} + +static int wm8580_set_bias_level(struct snd_soc_codec *codec, +	enum snd_soc_bias_level level) +{ +	u16 reg; +	switch (level) { +	case SND_SOC_BIAS_ON: +	case SND_SOC_BIAS_PREPARE: +	case SND_SOC_BIAS_STANDBY: +		break; +	case SND_SOC_BIAS_OFF: +		reg = wm8580_read(codec, WM8580_PWRDN1); +		wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN); +		break; +	} +	codec->bias_level = level; +	return 0; +} + +#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ +			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_dai wm8580_dai[] = { +	{ +		.name = "WM8580 PAIFRX", +		.id = 0, +		.playback = { +			.stream_name = "Playback", +			.channels_min = 1, +			.channels_max = 6, +			.rates = SNDRV_PCM_RATE_8000_192000, +			.formats = WM8580_FORMATS, +		}, +		.ops = { +			 .hw_params = wm8580_paif_hw_params, +		 }, +		.dai_ops = { +			 .set_fmt = wm8580_set_paif_dai_fmt, +			 .set_clkdiv = wm8580_set_dai_clkdiv, +			 .set_pll = wm8580_set_dai_pll, +			 .digital_mute = wm8580_digital_mute, +		 }, +	}, +	{ +		.name = "WM8580 PAIFTX", +		.id = 1, +		.capture = { +			.stream_name = "Capture", +			.channels_min = 2, +			.channels_max = 2, +			.rates = SNDRV_PCM_RATE_8000_192000, +			.formats = WM8580_FORMATS, +		}, +		.ops = { +			 .hw_params = wm8580_paif_hw_params, +		 }, +		.dai_ops = { +			 .set_fmt = wm8580_set_paif_dai_fmt, +			 .set_clkdiv = wm8580_set_dai_clkdiv, +			 .set_pll = wm8580_set_dai_pll, +		 }, +	}, +}; +EXPORT_SYMBOL_GPL(wm8580_dai); + +/* + * initialise the WM8580 driver + * register the mixer and dsp interfaces with the kernel + */ +static int wm8580_init(struct snd_soc_device *socdev) +{ +	struct snd_soc_codec *codec = socdev->codec; +	int ret = 0; + +	codec->name = "WM8580"; +	codec->owner = THIS_MODULE; +	codec->read = wm8580_read_reg_cache; +	codec->write = wm8580_write; +	codec->set_bias_level = wm8580_set_bias_level; +	codec->dai = wm8580_dai; +	codec->num_dai = ARRAY_SIZE(wm8580_dai); +	codec->reg_cache_size = ARRAY_SIZE(wm8580_reg); +	codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg), +				   GFP_KERNEL); + +	if (codec->reg_cache == NULL) +		return -ENOMEM; + +	/* Get the codec into a known state */ +	wm8580_write(codec, WM8580_RESET, 0); + +	/* Power up and get individual control of the DACs */ +	wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) & +		     ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD)); + +	/* Make VMID high impedence */ +	wm8580_write(codec, WM8580_ADC_CONTROL1, +		     wm8580_read(codec,  WM8580_ADC_CONTROL1) & ~0x100); + +	/* register pcms */ +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, +			       SNDRV_DEFAULT_STR1); +	if (ret < 0) { +		printk(KERN_ERR "wm8580: failed to create pcms\n"); +		goto pcm_err; +	} + +	wm8580_add_controls(codec); +	wm8580_add_widgets(codec); + +	ret = snd_soc_register_card(socdev); +	if (ret < 0) { +		printk(KERN_ERR "wm8580: failed to register card\n"); +		goto card_err; +	} +	return ret; + +card_err: +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +pcm_err: +	kfree(codec->reg_cache); +	return ret; +} + +/* If the i2c layer weren't so broken, we could pass this kind of data +   around */ +static struct snd_soc_device *wm8580_socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + +/* + * WM8580 2 wire address is determined by GPIO5 + * state during powerup. + *    low  = 0x1a + *    high = 0x1b + */ +static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static struct i2c_driver wm8580_i2c_driver; +static struct i2c_client client_template; + +static int wm8580_codec_probe(struct i2c_adapter *adap, int addr, int kind) +{ +	struct snd_soc_device *socdev = wm8580_socdev; +	struct wm8580_setup_data *setup = socdev->codec_data; +	struct snd_soc_codec *codec = socdev->codec; +	struct i2c_client *i2c; +	int ret; + +	if (addr != setup->i2c_address) +		return -ENODEV; + +	client_template.adapter = adap; +	client_template.addr = addr; + +	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); +	if (i2c == NULL) { +		kfree(codec); +		return -ENOMEM; +	} +	i2c_set_clientdata(i2c, codec); +	codec->control_data = i2c; + +	ret = i2c_attach_client(i2c); +	if (ret < 0) { +		dev_err(&i2c->dev, "failed to attach codec at addr %x\n", addr); +		goto err; +	} + +	ret = wm8580_init(socdev); +	if (ret < 0) { +		dev_err(&i2c->dev, "failed to initialise WM8580\n"); +		goto err; +	} + +	return ret; + +err: +	kfree(codec); +	kfree(i2c); +	return ret; +} + +static int wm8580_i2c_detach(struct i2c_client *client) +{ +	struct snd_soc_codec *codec = i2c_get_clientdata(client); +	i2c_detach_client(client); +	kfree(codec->reg_cache); +	kfree(client); +	return 0; +} + +static int wm8580_i2c_attach(struct i2c_adapter *adap) +{ +	return i2c_probe(adap, &addr_data, wm8580_codec_probe); +} + +/* corgi i2c codec control layer */ +static struct i2c_driver wm8580_i2c_driver = { +	.driver = { +		.name = "WM8580 I2C Codec", +		.owner = THIS_MODULE, +	}, +	.attach_adapter = wm8580_i2c_attach, +	.detach_client =  wm8580_i2c_detach, +	.command =        NULL, +}; + +static struct i2c_client client_template = { +	.name =   "WM8580", +	.driver = &wm8580_i2c_driver, +}; +#endif + +static int wm8580_probe(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct wm8580_setup_data *setup; +	struct snd_soc_codec *codec; +	struct wm8580_priv *wm8580; +	int ret = 0; + +	pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION); + +	setup = socdev->codec_data; +	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); +	if (codec == NULL) +		return -ENOMEM; + +	wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL); +	if (wm8580 == NULL) { +		kfree(codec); +		return -ENOMEM; +	} + +	codec->private_data = wm8580; +	socdev->codec = codec; +	mutex_init(&codec->mutex); +	INIT_LIST_HEAD(&codec->dapm_widgets); +	INIT_LIST_HEAD(&codec->dapm_paths); +	wm8580_socdev = socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	if (setup->i2c_address) { +		normal_i2c[0] = setup->i2c_address; +		codec->hw_write = (hw_write_t)i2c_master_send; +		ret = i2c_add_driver(&wm8580_i2c_driver); +		if (ret != 0) +			printk(KERN_ERR "can't add i2c driver"); +	} +#else +		/* Add other interfaces here */ +#endif +	return ret; +} + +/* power down chip */ +static int wm8580_remove(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	if (codec->control_data) +		wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF); +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_del_driver(&wm8580_i2c_driver); +#endif +	kfree(codec->private_data); +	kfree(codec); + +	return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8580 = { +	.probe = 	wm8580_probe, +	.remove = 	wm8580_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580); + +MODULE_DESCRIPTION("ASoC WM8580 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8580.h b/sound/soc/codecs/wm8580.h new file mode 100644 index 0000000..589ddab --- /dev/null +++ b/sound/soc/codecs/wm8580.h @@ -0,0 +1,42 @@ +/* + * wm8580.h  --  audio driver for WM8580 + * + * Copyright 2008 Samsung Electronics. + * Author: Ryu Euiyoul + *         ryu.real@gmail.com + * + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + * + */ + +#ifndef _WM8580_H +#define _WM8580_H + +#define WM8580_PLLA  1 +#define WM8580_PLLB  2 + +#define WM8580_MCLK       1 +#define WM8580_DAC_CLKSEL 2 +#define WM8580_CLKOUTSRC  3 + +#define WM8580_CLKSRC_MCLK 1 +#define WM8580_CLKSRC_PLLA 2 +#define WM8580_CLKSRC_PLLB 3 +#define WM8580_CLKSRC_OSC  4 +#define WM8580_CLKSRC_NONE 5 + +struct wm8580_setup_data { +	unsigned short i2c_address; +}; + +#define WM8580_DAI_PAIFRX 0 +#define WM8580_DAI_PAIFTX 1 + +extern struct snd_soc_dai wm8580_dai[]; +extern struct snd_soc_codec_device soc_codec_dev_wm8580; + +#endif + diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 9402fca..7b64d9a 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -19,6 +19,7 @@  #include <linux/pm.h>  #include <linux/i2c.h>  #include <linux/platform_device.h> +#include <linux/spi/spi.h>  #include <sound/core.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h> @@ -570,88 +571,144 @@ static struct snd_soc_device *wm8731_socdev;   *    low  = 0x1a   *    high = 0x1b   */ -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver wm8731_i2c_driver; -static struct i2c_client client_template; - -/* If the i2c layer weren't so broken, we could pass this kind of data -   around */ - -static int wm8731_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int wm8731_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = wm8731_socdev; -	struct wm8731_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		pr_err("failed to attach codec at addr %x\n", addr); -		goto err; -	} -  	ret = wm8731_init(socdev); -	if (ret < 0) { +	if (ret < 0)  		pr_err("failed to initialise WM8731\n"); -		goto err; -	} -	return ret; -err: -	kfree(i2c);  	return ret;  } -static int wm8731_i2c_detach(struct i2c_client *client) +static int wm8731_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int wm8731_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, wm8731_codec_probe); -} +static const struct i2c_device_id wm8731_i2c_id[] = { +	{ "wm8731", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); -/* corgi i2c codec control layer */  static struct i2c_driver wm8731_i2c_driver = {  	.driver = {  		.name = "WM8731 I2C Codec",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_WM8731, -	.attach_adapter = wm8731_i2c_attach, -	.detach_client =  wm8731_i2c_detach, -	.command =        NULL, +	.probe =    wm8731_i2c_probe, +	.remove =   wm8731_i2c_remove, +	.id_table = wm8731_i2c_id,  }; -static struct i2c_client client_template = { -	.name =   "WM8731", -	.driver = &wm8731_i2c_driver, -}; +static int wm8731_add_i2c_device(struct platform_device *pdev, +				 const struct wm8731_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&wm8731_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "wm8731", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&wm8731_i2c_driver); +	return -ENODEV; +}  #endif +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8731_spi_probe(struct spi_device *spi) +{ +	struct snd_soc_device *socdev = wm8731_socdev; +	struct snd_soc_codec *codec = socdev->codec; +	int ret; + +	codec->control_data = spi; + +	ret = wm8731_init(socdev); +	if (ret < 0) +		dev_err(&spi->dev, "failed to initialise WM8731\n"); + +	return ret; +} + +static int __devexit wm8731_spi_remove(struct spi_device *spi) +{ +	return 0; +} + +static struct spi_driver wm8731_spi_driver = { +	.driver = { +		.name	= "wm8731", +		.bus	= &spi_bus_type, +		.owner	= THIS_MODULE, +	}, +	.probe		= wm8731_spi_probe, +	.remove		= __devexit_p(wm8731_spi_remove), +}; + +static int wm8731_spi_write(struct spi_device *spi, const char *data, int len) +{ +	struct spi_transfer t; +	struct spi_message m; +	u8 msg[2]; + +	if (len <= 0) +		return 0; + +	msg[0] = data[0]; +	msg[1] = data[1]; + +	spi_message_init(&m); +	memset(&t, 0, (sizeof t)); + +	t.tx_buf = &msg[0]; +	t.len = len; + +	spi_message_add_tail(&t, &m); +	spi_sync(spi, &m); + +	return len; +} +#endif /* CONFIG_SPI_MASTER */ +  static int wm8731_probe(struct platform_device *pdev)  {  	struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -680,16 +737,21 @@ static int wm8731_probe(struct platform_device *pdev)  	INIT_LIST_HEAD(&codec->dapm_paths);  	wm8731_socdev = socdev; +	ret = -ENODEV; +  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t)i2c_master_send; -		ret = i2c_add_driver(&wm8731_i2c_driver); +		ret = wm8731_add_i2c_device(pdev, setup); +	} +#endif +#if defined(CONFIG_SPI_MASTER) +	if (setup->spi) { +		codec->hw_write = (hw_write_t)wm8731_spi_write; +		ret = spi_register_driver(&wm8731_spi_driver);  		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +			printk(KERN_ERR "can't add spi driver");  	} -#else -	/* Add other interfaces here */  #endif  	if (ret != 0) { @@ -711,8 +773,12 @@ static int wm8731_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&wm8731_i2c_driver);  #endif +#if defined(CONFIG_SPI_MASTER) +	spi_unregister_driver(&wm8731_spi_driver); +#endif  	kfree(codec->private_data);  	kfree(codec); diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h index 99f2e3c..95190e9 100644 --- a/sound/soc/codecs/wm8731.h +++ b/sound/soc/codecs/wm8731.h @@ -35,6 +35,8 @@  #define WM8731_DAI		0  struct wm8731_setup_data { +	int            spi; +	int            i2c_bus;  	unsigned short i2c_address;  }; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index dd1f554..4892e39 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -19,6 +19,7 @@  #include <linux/pm.h>  #include <linux/i2c.h>  #include <linux/platform_device.h> +#include <linux/spi/spi.h>  #include <sound/core.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h> @@ -841,88 +842,147 @@ static struct snd_soc_device *wm8750_socdev;  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  /* - * WM8731 2 wire address is determined by GPIO5 + * WM8750 2 wire address is determined by GPIO5   * state during powerup.   *    low  = 0x1a   *    high = 0x1b   */ -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver wm8750_i2c_driver; -static struct i2c_client client_template; - -static int wm8750_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int wm8750_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = wm8750_socdev; -	struct wm8750_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		pr_err("failed to attach codec at addr %x\n", addr); -		goto err; -	} -  	ret = wm8750_init(socdev); -	if (ret < 0) { +	if (ret < 0)  		pr_err("failed to initialise WM8750\n"); -		goto err; -	} -	return ret; -err: -	kfree(i2c);  	return ret;  } -static int wm8750_i2c_detach(struct i2c_client *client) +static int wm8750_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int wm8750_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, wm8750_codec_probe); -} +static const struct i2c_device_id wm8750_i2c_id[] = { +	{ "wm8750", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); -/* corgi i2c codec control layer */  static struct i2c_driver wm8750_i2c_driver = {  	.driver = {  		.name = "WM8750 I2C Codec",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_WM8750, -	.attach_adapter = wm8750_i2c_attach, -	.detach_client =  wm8750_i2c_detach, -	.command =        NULL, +	.probe =    wm8750_i2c_probe, +	.remove =   wm8750_i2c_remove, +	.id_table = wm8750_i2c_id,  }; -static struct i2c_client client_template = { -	.name =   "WM8750", -	.driver = &wm8750_i2c_driver, +static int wm8750_add_i2c_device(struct platform_device *pdev, +				 const struct wm8750_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&wm8750_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "wm8750", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&wm8750_i2c_driver); +	return -ENODEV; +} +#endif + +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8750_spi_probe(struct spi_device *spi) +{ +	struct snd_soc_device *socdev = wm8750_socdev; +	struct snd_soc_codec *codec = socdev->codec; +	int ret; + +	codec->control_data = spi; + +	ret = wm8750_init(socdev); +	if (ret < 0) +		dev_err(&spi->dev, "failed to initialise WM8750\n"); + +	return ret; +} + +static int __devexit wm8750_spi_remove(struct spi_device *spi) +{ +	return 0; +} + +static struct spi_driver wm8750_spi_driver = { +	.driver = { +		.name	= "wm8750", +		.bus	= &spi_bus_type, +		.owner	= THIS_MODULE, +	}, +	.probe		= wm8750_spi_probe, +	.remove		= __devexit_p(wm8750_spi_remove),  }; + +static int wm8750_spi_write(struct spi_device *spi, const char *data, int len) +{ +	struct spi_transfer t; +	struct spi_message m; +	u8 msg[2]; + +	if (len <= 0) +		return 0; + +	msg[0] = data[0]; +	msg[1] = data[1]; + +	spi_message_init(&m); +	memset(&t, 0, (sizeof t)); + +	t.tx_buf = &msg[0]; +	t.len = len; + +	spi_message_add_tail(&t, &m); +	spi_sync(spi, &m); + +	return len; +}  #endif  static int wm8750_probe(struct platform_device *pdev) @@ -931,7 +991,7 @@ static int wm8750_probe(struct platform_device *pdev)  	struct wm8750_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec;  	struct wm8750_priv *wm8750; -	int ret = 0; +	int ret;  	pr_info("WM8750 Audio Codec %s", WM8750_VERSION);  	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); @@ -952,16 +1012,21 @@ static int wm8750_probe(struct platform_device *pdev)  	wm8750_socdev = socdev;  	INIT_DELAYED_WORK(&codec->delayed_work, wm8750_work); +	ret = -ENODEV; +  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t)i2c_master_send; -		ret = i2c_add_driver(&wm8750_i2c_driver); +		ret = wm8750_add_i2c_device(pdev, setup); +	} +#endif +#if defined(CONFIG_SPI_MASTER) +	if (setup->spi) { +		codec->hw_write = (hw_write_t)wm8750_spi_write; +		ret = spi_register_driver(&wm8750_spi_driver);  		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +			printk(KERN_ERR "can't add spi driver");  	} -#else -		/* Add other interfaces here */  #endif  	if (ret != 0) { @@ -1002,8 +1067,12 @@ static int wm8750_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&wm8750_i2c_driver);  #endif +#if defined(CONFIG_SPI_MASTER) +	spi_unregister_driver(&wm8750_spi_driver); +#endif  	kfree(codec->private_data);  	kfree(codec); diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h index 8ef30e6..1dc100e 100644 --- a/sound/soc/codecs/wm8750.h +++ b/sound/soc/codecs/wm8750.h @@ -58,6 +58,8 @@  #define WM8750_SYSCLK	0  struct wm8750_setup_data { +	int spi; +	int i2c_bus;  	unsigned short i2c_address;  }; diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 5761164..8c4df44 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -583,7 +583,7 @@ static const struct snd_soc_dapm_route audio_map[] = {  	/* out 4 */  	{"Out4 Mux", "VREF", "VREF"}, -	{"Out4 Mux", "Capture ST", "Capture ST Mixer"}, +	{"Out4 Mux", "Capture ST", "Playback Mixer"},  	{"Out4 Mux", "LOUT2", "LOUT2"},  	{"Out 4", NULL, "Out4 Mux"},  	{"OUT4", NULL, "Out 4"}, @@ -607,7 +607,7 @@ static const struct snd_soc_dapm_route audio_map[] = {  	/* Capture Right Mux */  	{"Capture Right Mux", "PGA", "Right Capture Volume"},  	{"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"}, -	{"Capture Right Mux", "Sidetone", "Capture ST Mixer"}, +	{"Capture Right Mux", "Sidetone", "Playback Mixer"},  	/* Mono Capture mixer-mux */  	{"Capture Right Mixer", "Stereo", "Capture Right Mux"}, @@ -1637,84 +1637,86 @@ static struct snd_soc_device *wm8753_socdev;   *    low  = 0x1a   *    high = 0x1b   */ -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver wm8753_i2c_driver; -static struct i2c_client client_template; - -static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int wm8753_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = wm8753_socdev; -	struct wm8753_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (!i2c) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		pr_err("failed to attach codec at addr %x\n", addr); -		goto err; -	} -  	ret = wm8753_init(socdev); -	if (ret < 0) { +	if (ret < 0)  		pr_err("failed to initialise WM8753\n"); -		goto err; -	} - -	return ret; -err: -	kfree(i2c);  	return ret;  } -static int wm8753_i2c_detach(struct i2c_client *client) +static int wm8753_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int wm8753_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, wm8753_codec_probe); -} +static const struct i2c_device_id wm8753_i2c_id[] = { +	{ "wm8753", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id); -/* corgi i2c codec control layer */  static struct i2c_driver wm8753_i2c_driver = {  	.driver = {  		.name = "WM8753 I2C Codec",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_WM8753, -	.attach_adapter = wm8753_i2c_attach, -	.detach_client =  wm8753_i2c_detach, -	.command =        NULL, +	.probe =    wm8753_i2c_probe, +	.remove =   wm8753_i2c_remove, +	.id_table = wm8753_i2c_id,  }; -static struct i2c_client client_template = { -	.name =   "WM8753", -	.driver = &wm8753_i2c_driver, -}; +static int wm8753_add_i2c_device(struct platform_device *pdev, +				 const struct wm8753_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&wm8753_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "wm8753", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&wm8753_i2c_driver); +	return -ENODEV; +}  #endif  static int wm8753_probe(struct platform_device *pdev) @@ -1748,11 +1750,8 @@ static int wm8753_probe(struct platform_device *pdev)  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t)i2c_master_send; -		ret = i2c_add_driver(&wm8753_i2c_driver); -		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +		ret = wm8753_add_i2c_device(pdev, setup);  	}  #else  		/* Add other interfaces here */ @@ -1796,6 +1795,7 @@ static int wm8753_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&wm8753_i2c_driver);  #endif  	kfree(codec->private_data); diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h index 44f5f1f..7defde0 100644 --- a/sound/soc/codecs/wm8753.h +++ b/sound/soc/codecs/wm8753.h @@ -79,6 +79,7 @@  #define WM8753_ADCTL2		0x3f  struct wm8753_setup_data { +	int i2c_bus;  	unsigned short i2c_address;  }; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c new file mode 100644 index 0000000..0b8c6d3 --- /dev/null +++ b/sound/soc/codecs/wm8900.c @@ -0,0 +1,1542 @@ +/* + * wm8900.c  --  WM8900 ALSA Soc Audio driver + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: + *  - Tristating. + *  - TDM. + *  - Jack detect. + *  - FLL source configuration, currently only MCLK is supported. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> +#include <sound/tlv.h> + +#include "wm8900.h" + +/* WM8900 register space */ +#define WM8900_REG_RESET	0x0 +#define WM8900_REG_ID		0x0 +#define WM8900_REG_POWER1	0x1 +#define WM8900_REG_POWER2	0x2 +#define WM8900_REG_POWER3	0x3 +#define WM8900_REG_AUDIO1	0x4 +#define WM8900_REG_AUDIO2	0x5 +#define WM8900_REG_CLOCKING1    0x6 +#define WM8900_REG_CLOCKING2    0x7 +#define WM8900_REG_AUDIO3       0x8 +#define WM8900_REG_AUDIO4       0x9 +#define WM8900_REG_DACCTRL      0xa +#define WM8900_REG_LDAC_DV      0xb +#define WM8900_REG_RDAC_DV      0xc +#define WM8900_REG_SIDETONE     0xd +#define WM8900_REG_ADCCTRL      0xe +#define WM8900_REG_LADC_DV	0xf +#define WM8900_REG_RADC_DV      0x10 +#define WM8900_REG_GPIO         0x12 +#define WM8900_REG_INCTL	0x15 +#define WM8900_REG_LINVOL	0x16 +#define WM8900_REG_RINVOL	0x17 +#define WM8900_REG_INBOOSTMIX1  0x18 +#define WM8900_REG_INBOOSTMIX2  0x19 +#define WM8900_REG_ADCPATH	0x1a +#define WM8900_REG_AUXBOOST	0x1b +#define WM8900_REG_ADDCTL       0x1e +#define WM8900_REG_FLLCTL1      0x24 +#define WM8900_REG_FLLCTL2      0x25 +#define WM8900_REG_FLLCTL3      0x26 +#define WM8900_REG_FLLCTL4      0x27 +#define WM8900_REG_FLLCTL5      0x28 +#define WM8900_REG_FLLCTL6      0x29 +#define WM8900_REG_LOUTMIXCTL1  0x2c +#define WM8900_REG_ROUTMIXCTL1  0x2d +#define WM8900_REG_BYPASS1	0x2e +#define WM8900_REG_BYPASS2	0x2f +#define WM8900_REG_AUXOUT_CTL   0x30 +#define WM8900_REG_LOUT1CTL     0x33 +#define WM8900_REG_ROUT1CTL     0x34 +#define WM8900_REG_LOUT2CTL	0x35 +#define WM8900_REG_ROUT2CTL	0x36 +#define WM8900_REG_HPCTL1	0x3a +#define WM8900_REG_OUTBIASCTL   0x73 + +#define WM8900_MAXREG		0x80 + +#define WM8900_REG_ADDCTL_OUT1_DIS    0x80 +#define WM8900_REG_ADDCTL_OUT2_DIS    0x40 +#define WM8900_REG_ADDCTL_VMID_DIS    0x20 +#define WM8900_REG_ADDCTL_BIAS_SRC    0x10 +#define WM8900_REG_ADDCTL_VMID_SOFTST 0x04 +#define WM8900_REG_ADDCTL_TEMP_SD     0x02 + +#define WM8900_REG_GPIO_TEMP_ENA   0x2 + +#define WM8900_REG_POWER1_STARTUP_BIAS_ENA 0x0100 +#define WM8900_REG_POWER1_BIAS_ENA         0x0008 +#define WM8900_REG_POWER1_VMID_BUF_ENA     0x0004 +#define WM8900_REG_POWER1_FLL_ENA          0x0040 + +#define WM8900_REG_POWER2_SYSCLK_ENA  0x8000 +#define WM8900_REG_POWER2_ADCL_ENA    0x0002 +#define WM8900_REG_POWER2_ADCR_ENA    0x0001 + +#define WM8900_REG_POWER3_DACL_ENA    0x0002 +#define WM8900_REG_POWER3_DACR_ENA    0x0001 + +#define WM8900_REG_AUDIO1_AIF_FMT_MASK 0x0018 +#define WM8900_REG_AUDIO1_LRCLK_INV    0x0080 +#define WM8900_REG_AUDIO1_BCLK_INV     0x0100 + +#define WM8900_REG_CLOCKING1_BCLK_DIR   0x1 +#define WM8900_REG_CLOCKING1_MCLK_SRC   0x100 +#define WM8900_REG_CLOCKING1_BCLK_MASK  (~0x01e) +#define WM8900_REG_CLOCKING1_OPCLK_MASK (~0x7000) + +#define WM8900_REG_CLOCKING2_ADC_CLKDIV 0xe0 +#define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c + +#define WM8900_REG_DACCTRL_MUTE          0x004 +#define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400 + +#define WM8900_REG_AUDIO3_ADCLRC_DIR    0x0800 + +#define WM8900_REG_AUDIO4_DACLRC_DIR    0x0800 + +#define WM8900_REG_FLLCTL1_OSC_ENA    0x100 + +#define WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF 0x100 + +#define WM8900_REG_HPCTL1_HP_IPSTAGE_ENA 0x80 +#define WM8900_REG_HPCTL1_HP_OPSTAGE_ENA 0x40 +#define WM8900_REG_HPCTL1_HP_CLAMP_IP    0x20 +#define WM8900_REG_HPCTL1_HP_CLAMP_OP    0x10 +#define WM8900_REG_HPCTL1_HP_SHORT       0x08 +#define WM8900_REG_HPCTL1_HP_SHORT2      0x04 + +#define WM8900_LRC_MASK 0xfc00 + +struct snd_soc_codec_device soc_codec_dev_wm8900; + +struct wm8900_priv { +	u32 fll_in; /* FLL input frequency */ +	u32 fll_out; /* FLL output frequency */ +}; + +/* + * wm8900 register cache.  We can't read the entire register space and we + * have slow control buses so we cache the registers. + */ +static const u16 wm8900_reg_defaults[WM8900_MAXREG] = { +	0x8900, 0x0000, +	0xc000, 0x0000, +	0x4050, 0x4000, +	0x0008, 0x0000, +	0x0040, 0x0040, +	0x1004, 0x00c0, +	0x00c0, 0x0000, +	0x0100, 0x00c0, +	0x00c0, 0x0000, +	0xb001, 0x0000, +	0x0000, 0x0044, +	0x004c, 0x004c, +	0x0044, 0x0044, +	0x0000, 0x0044, +	0x0000, 0x0000, +	0x0002, 0x0000, +	0x0000, 0x0000, +	0x0000, 0x0000, +	0x0008, 0x0000, +	0x0000, 0x0008, +	0x0097, 0x0100, +	0x0000, 0x0000, +	0x0050, 0x0050, +	0x0055, 0x0055, +	0x0055, 0x0000, +	0x0000, 0x0079, +	0x0079, 0x0079, +	0x0079, 0x0000, +	/* Remaining registers all zero */ +}; + +/* + * read wm8900 register cache + */ +static inline unsigned int wm8900_read_reg_cache(struct snd_soc_codec *codec, +	unsigned int reg) +{ +	u16 *cache = codec->reg_cache; + +	BUG_ON(reg >= WM8900_MAXREG); + +	if (reg == WM8900_REG_ID) +		return 0; + +	return cache[reg]; +} + +/* + * write wm8900 register cache + */ +static inline void wm8900_write_reg_cache(struct snd_soc_codec *codec, +	u16 reg, unsigned int value) +{ +	u16 *cache = codec->reg_cache; + +	BUG_ON(reg >= WM8900_MAXREG); + +	cache[reg] = value; +} + +/* + * write to the WM8900 register space + */ +static int wm8900_write(struct snd_soc_codec *codec, unsigned int reg, +			unsigned int value) +{ +	u8 data[3]; + +	if (value == wm8900_read_reg_cache(codec, reg)) +		return 0; + +	/* data is +	 *   D15..D9 WM8900 register offset +	 *   D8...D0 register data +	 */ +	data[0] = reg; +	data[1] = value >> 8; +	data[2] = value & 0x00ff; + +	wm8900_write_reg_cache(codec, reg, value); +	if (codec->hw_write(codec->control_data, data, 3) == 3) +		return 0; +	else +		return -EIO; +} + +/* + * Read from the wm8900. + */ +static unsigned int wm8900_chip_read(struct snd_soc_codec *codec, u8 reg) +{ +	struct i2c_msg xfer[2]; +	u16 data; +	int ret; +	struct i2c_client *client = codec->control_data; + +	BUG_ON(reg != WM8900_REG_ID && reg != WM8900_REG_POWER1); + +	/* Write register */ +	xfer[0].addr = client->addr; +	xfer[0].flags = 0; +	xfer[0].len = 1; +	xfer[0].buf = ® + +	/* Read data */ +	xfer[1].addr = client->addr; +	xfer[1].flags = I2C_M_RD; +	xfer[1].len = 2; +	xfer[1].buf = (u8 *)&data; + +	ret = i2c_transfer(client->adapter, xfer, 2); +	if (ret != 2) { +		printk(KERN_CRIT "i2c_transfer returned %d\n", ret); +		return 0; +	} + +	return (data >> 8) | ((data & 0xff) << 8); +} + +/* + * Read from the WM8900 register space.  Most registers can't be read + * and are therefore supplied from cache. + */ +static unsigned int wm8900_read(struct snd_soc_codec *codec, unsigned int reg) +{ +	switch (reg) { +	case WM8900_REG_ID: +		return wm8900_chip_read(codec, reg); +	default: +		return wm8900_read_reg_cache(codec, reg); +	} +} + +static void wm8900_reset(struct snd_soc_codec *codec) +{ +	wm8900_write(codec, WM8900_REG_RESET, 0); + +	memcpy(codec->reg_cache, wm8900_reg_defaults, +	       sizeof(codec->reg_cache)); +} + +static int wm8900_hp_event(struct snd_soc_dapm_widget *w, +			   struct snd_kcontrol *kcontrol, int event) +{ +	struct snd_soc_codec *codec = w->codec; +	u16 hpctl1 = wm8900_read(codec, WM8900_REG_HPCTL1); + +	switch (event) { +	case SND_SOC_DAPM_PRE_PMU: +		/* Clamp headphone outputs */ +		hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP | +			WM8900_REG_HPCTL1_HP_CLAMP_OP; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); +		break; + +	case SND_SOC_DAPM_POST_PMU: +		/* Enable the input stage */ +		hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_IP; +		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT | +			WM8900_REG_HPCTL1_HP_SHORT2 | +			WM8900_REG_HPCTL1_HP_IPSTAGE_ENA; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); + +		msleep(400); + +		/* Enable the output stage */ +		hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP; +		hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); + +		/* Remove the shorts */ +		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); +		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); +		break; + +	case SND_SOC_DAPM_PRE_PMD: +		/* Short the output */ +		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); + +		/* Disable the output stage */ +		hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); + +		/* Clamp the outputs and power down input */ +		hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP | +			WM8900_REG_HPCTL1_HP_CLAMP_OP; +		hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA; +		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1); +		break; + +	case SND_SOC_DAPM_POST_PMD: +		/* Disable everything */ +		wm8900_write(codec, WM8900_REG_HPCTL1, 0); +		break; + +	default: +		BUG(); +	} + +	return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -5700, 100, 0); + +static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -1500, 300, 0); + +static const DECLARE_TLV_DB_SCALE(in_boost_tlv, -1200, 600, 0); + +static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1200, 100, 0); + +static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0); + +static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1); + +static const DECLARE_TLV_DB_SCALE(adc_svol_tlv, -3600, 300, 0); + +static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); + +static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" }; + +static const struct soc_enum mic_bias_level = +SOC_ENUM_SINGLE(WM8900_REG_INCTL, 8, 2, mic_bias_level_txt); + +static const char *dac_mute_rate_txt[] = { "Fast", "Slow" }; + +static const struct soc_enum dac_mute_rate = +SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 7, 2, dac_mute_rate_txt); + +static const char *dac_deemphasis_txt[] = { +	"Disabled", "32kHz", "44.1kHz", "48kHz" +}; + +static const struct soc_enum dac_deemphasis = +SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 4, 4, dac_deemphasis_txt); + +static const char *adc_hpf_cut_txt[] = { +	"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3" +}; + +static const struct soc_enum adc_hpf_cut = +SOC_ENUM_SINGLE(WM8900_REG_ADCCTRL, 5, 4, adc_hpf_cut_txt); + +static const char *lr_txt[] = { +	"Left", "Right" +}; + +static const struct soc_enum aifl_src = +SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 15, 2, lr_txt); + +static const struct soc_enum aifr_src = +SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 14, 2, lr_txt); + +static const struct soc_enum dacl_src = +SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 15, 2, lr_txt); + +static const struct soc_enum dacr_src = +SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 14, 2, lr_txt); + +static const char *sidetone_txt[] = { +	"Disabled", "Left ADC", "Right ADC" +}; + +static const struct soc_enum dacl_sidetone = +SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 2, 3, sidetone_txt); + +static const struct soc_enum dacr_sidetone = +SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 0, 3, sidetone_txt); + +static const struct snd_kcontrol_new wm8900_snd_controls[] = { +SOC_ENUM("Mic Bias Level", mic_bias_level), + +SOC_SINGLE_TLV("Left Input PGA Volume", WM8900_REG_LINVOL, 0, 31, 0, +	       in_pga_tlv), +SOC_SINGLE("Left Input PGA Switch", WM8900_REG_LINVOL, 6, 1, 1), +SOC_SINGLE("Left Input PGA ZC Switch", WM8900_REG_LINVOL, 7, 1, 0), + +SOC_SINGLE_TLV("Right Input PGA Volume", WM8900_REG_RINVOL, 0, 31, 0, +	       in_pga_tlv), +SOC_SINGLE("Right Input PGA Switch", WM8900_REG_RINVOL, 6, 1, 1), +SOC_SINGLE("Right Input PGA ZC Switch", WM8900_REG_RINVOL, 7, 1, 0), + +SOC_SINGLE("DAC Soft Mute Switch", WM8900_REG_DACCTRL, 6, 1, 1), +SOC_ENUM("DAC Mute Rate", dac_mute_rate), +SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0), +SOC_ENUM("DAC Deemphasis", dac_deemphasis), +SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0), +SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL, +	   12, 1, 0), + +SOC_SINGLE("ADC HPF Switch", WM8900_REG_ADCCTRL, 8, 1, 0), +SOC_ENUM("ADC HPF Cut-Off", adc_hpf_cut), +SOC_DOUBLE("ADC Invert Switch", WM8900_REG_ADCCTRL, 1, 0, 1, 0), +SOC_SINGLE_TLV("Left ADC Sidetone Volume", WM8900_REG_SIDETONE, 9, 12, 0, +	       adc_svol_tlv), +SOC_SINGLE_TLV("Right ADC Sidetone Volume", WM8900_REG_SIDETONE, 5, 12, 0, +	       adc_svol_tlv), +SOC_ENUM("Left Digital Audio Source", aifl_src), +SOC_ENUM("Right Digital Audio Source", aifr_src), + +SOC_SINGLE_TLV("DAC Input Boost Volume", WM8900_REG_AUDIO2, 10, 4, 0, +	       dac_boost_tlv), +SOC_ENUM("Left DAC Source", dacl_src), +SOC_ENUM("Right DAC Source", dacr_src), +SOC_ENUM("Left DAC Sidetone", dacl_sidetone), +SOC_ENUM("Right DAC Sidetone", dacr_sidetone), +SOC_DOUBLE("DAC Invert Switch", WM8900_REG_DACCTRL, 1, 0, 1, 0), + +SOC_DOUBLE_R_TLV("Digital Playback Volume", +		 WM8900_REG_LDAC_DV, WM8900_REG_RDAC_DV, +		 1, 96, 0, dac_tlv), +SOC_DOUBLE_R_TLV("Digital Capture Volume", +		 WM8900_REG_LADC_DV, WM8900_REG_RADC_DV, 1, 119, 0, adc_tlv), + +SOC_SINGLE_TLV("LINPUT3 Bypass Volume", WM8900_REG_LOUTMIXCTL1, 4, 7, 0, +	       out_mix_tlv), +SOC_SINGLE_TLV("RINPUT3 Bypass Volume", WM8900_REG_ROUTMIXCTL1, 4, 7, 0, +	       out_mix_tlv), +SOC_SINGLE_TLV("Left AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 4, 7, 0, +	       out_mix_tlv), +SOC_SINGLE_TLV("Right AUX Bypass Volume", WM8900_REG_AUXOUT_CTL, 0, 7, 0, +	       out_mix_tlv), + +SOC_SINGLE_TLV("LeftIn to RightOut Mixer Volume", WM8900_REG_BYPASS1, 0, 7, 0, +	       out_mix_tlv), +SOC_SINGLE_TLV("LeftIn to LeftOut Mixer Volume", WM8900_REG_BYPASS1, 4, 7, 0, +	       out_mix_tlv), +SOC_SINGLE_TLV("RightIn to LeftOut Mixer Volume", WM8900_REG_BYPASS2, 0, 7, 0, +	       out_mix_tlv), +SOC_SINGLE_TLV("RightIn to RightOut Mixer Volume", WM8900_REG_BYPASS2, 4, 7, 0, +	       out_mix_tlv), + +SOC_SINGLE_TLV("IN2L Boost Volume", WM8900_REG_INBOOSTMIX1, 0, 3, 0, +	       in_boost_tlv), +SOC_SINGLE_TLV("IN3L Boost Volume", WM8900_REG_INBOOSTMIX1, 4, 3, 0, +	       in_boost_tlv), +SOC_SINGLE_TLV("IN2R Boost Volume", WM8900_REG_INBOOSTMIX2, 0, 3, 0, +	       in_boost_tlv), +SOC_SINGLE_TLV("IN3R Boost Volume", WM8900_REG_INBOOSTMIX2, 4, 3, 0, +	       in_boost_tlv), +SOC_SINGLE_TLV("Left AUX Boost Volume", WM8900_REG_AUXBOOST, 4, 3, 0, +	       in_boost_tlv), +SOC_SINGLE_TLV("Right AUX Boost Volume", WM8900_REG_AUXBOOST, 0, 3, 0, +	       in_boost_tlv), + +SOC_DOUBLE_R_TLV("LINEOUT1 Volume", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, +	       0, 63, 0, out_pga_tlv), +SOC_DOUBLE_R("LINEOUT1 Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, +	     6, 1, 1), +SOC_DOUBLE_R("LINEOUT1 ZC Switch", WM8900_REG_LOUT1CTL, WM8900_REG_ROUT1CTL, +	     7, 1, 0), + +SOC_DOUBLE_R_TLV("LINEOUT2 Volume", +		 WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, +		 0, 63, 0, out_pga_tlv), +SOC_DOUBLE_R("LINEOUT2 Switch", +	     WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 6, 1, 1), +SOC_DOUBLE_R("LINEOUT2 ZC Switch", +	     WM8900_REG_LOUT2CTL, WM8900_REG_ROUT2CTL, 7, 1, 0), +SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1, +	   0, 1, 1), + +}; + +/* add non dapm controls */ +static int wm8900_add_controls(struct snd_soc_codec *codec) +{ +	int err, i; + +	for (i = 0; i < ARRAY_SIZE(wm8900_snd_controls); i++) { +		err = snd_ctl_add(codec->card, +				  snd_soc_cnew(&wm8900_snd_controls[i], +					       codec, NULL)); +		if (err < 0) +			return err; +	} + +	return 0; +} + +static const struct snd_kcontrol_new wm8900_dapm_loutput2_control = +SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0); + +static const struct snd_kcontrol_new wm8900_dapm_routput2_control = +SOC_DAPM_SINGLE("LINEOUT2R Switch", WM8900_REG_POWER3, 5, 1, 0); + +static const struct snd_kcontrol_new wm8900_loutmix_controls[] = { +SOC_DAPM_SINGLE("LINPUT3 Bypass Switch", WM8900_REG_LOUTMIXCTL1, 7, 1, 0), +SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 7, 1, 0), +SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 3, 1, 0), +SOC_DAPM_SINGLE("DACL Switch", WM8900_REG_LOUTMIXCTL1, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_routmix_controls[] = { +SOC_DAPM_SINGLE("RINPUT3 Bypass Switch", WM8900_REG_ROUTMIXCTL1, 7, 1, 0), +SOC_DAPM_SINGLE("AUX Bypass Switch", WM8900_REG_AUXOUT_CTL, 3, 1, 0), +SOC_DAPM_SINGLE("Left Input Mixer Switch", WM8900_REG_BYPASS1, 3, 1, 0), +SOC_DAPM_SINGLE("Right Input Mixer Switch", WM8900_REG_BYPASS2, 7, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8900_REG_ROUTMIXCTL1, 8, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_linmix_controls[] = { +SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INBOOSTMIX1, 2, 1, 1), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INBOOSTMIX1, 6, 1, 1), +SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 6, 1, 1), +SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 6, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_rinmix_controls[] = { +SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INBOOSTMIX2, 2, 1, 1), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INBOOSTMIX2, 6, 1, 1), +SOC_DAPM_SINGLE("AUX Switch", WM8900_REG_AUXBOOST, 2, 1, 1), +SOC_DAPM_SINGLE("Input PGA Switch", WM8900_REG_ADCPATH, 2, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_linpga_controls[] = { +SOC_DAPM_SINGLE("LINPUT1 Switch", WM8900_REG_INCTL, 6, 1, 0), +SOC_DAPM_SINGLE("LINPUT2 Switch", WM8900_REG_INCTL, 5, 1, 0), +SOC_DAPM_SINGLE("LINPUT3 Switch", WM8900_REG_INCTL, 4, 1, 0), +}; + +static const struct snd_kcontrol_new wm8900_rinpga_controls[] = { +SOC_DAPM_SINGLE("RINPUT1 Switch", WM8900_REG_INCTL, 2, 1, 0), +SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INCTL, 1, 1, 0), +SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0), +}; + +static const char *wm9700_lp_mux[] = { "Disabled", "Enabled" }; + +static const struct soc_enum wm8900_lineout2_lp_mux = +SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm9700_lp_mux); + +static const struct snd_kcontrol_new wm8900_lineout2_lp = +SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux); + +static const struct snd_soc_dapm_widget wm8900_dapm_widgets[] = { + +/* Externally visible pins */ +SND_SOC_DAPM_OUTPUT("LINEOUT1L"), +SND_SOC_DAPM_OUTPUT("LINEOUT1R"), +SND_SOC_DAPM_OUTPUT("LINEOUT2L"), +SND_SOC_DAPM_OUTPUT("LINEOUT2R"), +SND_SOC_DAPM_OUTPUT("HP_L"), +SND_SOC_DAPM_OUTPUT("HP_R"), + +SND_SOC_DAPM_INPUT("RINPUT1"), +SND_SOC_DAPM_INPUT("LINPUT1"), +SND_SOC_DAPM_INPUT("RINPUT2"), +SND_SOC_DAPM_INPUT("LINPUT2"), +SND_SOC_DAPM_INPUT("RINPUT3"), +SND_SOC_DAPM_INPUT("LINPUT3"), +SND_SOC_DAPM_INPUT("AUX"), + +SND_SOC_DAPM_VMID("VMID"), + +/* Input */ +SND_SOC_DAPM_MIXER("Left Input PGA", WM8900_REG_POWER2, 3, 0, +		   wm8900_linpga_controls, +		   ARRAY_SIZE(wm8900_linpga_controls)), +SND_SOC_DAPM_MIXER("Right Input PGA", WM8900_REG_POWER2, 2, 0, +		   wm8900_rinpga_controls, +		   ARRAY_SIZE(wm8900_rinpga_controls)), + +SND_SOC_DAPM_MIXER("Left Input Mixer", WM8900_REG_POWER2, 5, 0, +		   wm8900_linmix_controls, +		   ARRAY_SIZE(wm8900_linmix_controls)), +SND_SOC_DAPM_MIXER("Right Input Mixer", WM8900_REG_POWER2, 4, 0, +		   wm8900_rinmix_controls, +		   ARRAY_SIZE(wm8900_rinmix_controls)), + +SND_SOC_DAPM_MICBIAS("Mic Bias", WM8900_REG_POWER1, 4, 0), + +SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8900_REG_POWER2, 1, 0), +SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8900_REG_POWER2, 0, 0), + +/* Output */ +SND_SOC_DAPM_DAC("DACL", "Left HiFi Playback", WM8900_REG_POWER3, 1, 0), +SND_SOC_DAPM_DAC("DACR", "Right HiFi Playback", WM8900_REG_POWER3, 0, 0), + +SND_SOC_DAPM_PGA_E("Headphone Amplifier", WM8900_REG_POWER3, 7, 0, NULL, 0, +		   wm8900_hp_event, +		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | +		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA("LINEOUT1L PGA", WM8900_REG_POWER2, 8, 0, NULL, 0), +SND_SOC_DAPM_PGA("LINEOUT1R PGA", WM8900_REG_POWER2, 7, 0, NULL, 0), + +SND_SOC_DAPM_MUX("LINEOUT2 LP", SND_SOC_NOPM, 0, 0, &wm8900_lineout2_lp), +SND_SOC_DAPM_PGA("LINEOUT2L PGA", WM8900_REG_POWER3, 6, 0, NULL, 0), +SND_SOC_DAPM_PGA("LINEOUT2R PGA", WM8900_REG_POWER3, 5, 0, NULL, 0), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8900_REG_POWER3, 3, 0, +		   wm8900_loutmix_controls, +		   ARRAY_SIZE(wm8900_loutmix_controls)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8900_REG_POWER3, 2, 0, +		   wm8900_routmix_controls, +		   ARRAY_SIZE(wm8900_routmix_controls)), +}; + +/* Target, Path, Source */ +static const struct snd_soc_dapm_route audio_map[] = { +/* Inputs */ +{"Left Input PGA", "LINPUT1 Switch", "LINPUT1"}, +{"Left Input PGA", "LINPUT2 Switch", "LINPUT2"}, +{"Left Input PGA", "LINPUT3 Switch", "LINPUT3"}, + +{"Right Input PGA", "RINPUT1 Switch", "RINPUT1"}, +{"Right Input PGA", "RINPUT2 Switch", "RINPUT2"}, +{"Right Input PGA", "RINPUT3 Switch", "RINPUT3"}, + +{"Left Input Mixer", "LINPUT2 Switch", "LINPUT2"}, +{"Left Input Mixer", "LINPUT3 Switch", "LINPUT3"}, +{"Left Input Mixer", "AUX Switch", "AUX"}, +{"Left Input Mixer", "Input PGA Switch", "Left Input PGA"}, + +{"Right Input Mixer", "RINPUT2 Switch", "RINPUT2"}, +{"Right Input Mixer", "RINPUT3 Switch", "RINPUT3"}, +{"Right Input Mixer", "AUX Switch", "AUX"}, +{"Right Input Mixer", "Input PGA Switch", "Right Input PGA"}, + +{"ADCL", NULL, "Left Input Mixer"}, +{"ADCR", NULL, "Right Input Mixer"}, + +/* Outputs */ +{"LINEOUT1L", NULL, "LINEOUT1L PGA"}, +{"LINEOUT1L PGA", NULL, "Left Output Mixer"}, +{"LINEOUT1R", NULL, "LINEOUT1R PGA"}, +{"LINEOUT1R PGA", NULL, "Right Output Mixer"}, + +{"LINEOUT2L PGA", NULL, "Left Output Mixer"}, +{"LINEOUT2 LP", "Disabled", "LINEOUT2L PGA"}, +{"LINEOUT2 LP", "Enabled", "Left Output Mixer"}, +{"LINEOUT2L", NULL, "LINEOUT2 LP"}, + +{"LINEOUT2R PGA", NULL, "Right Output Mixer"}, +{"LINEOUT2 LP", "Disabled", "LINEOUT2R PGA"}, +{"LINEOUT2 LP", "Enabled", "Right Output Mixer"}, +{"LINEOUT2R", NULL, "LINEOUT2 LP"}, + +{"Left Output Mixer", "LINPUT3 Bypass Switch", "LINPUT3"}, +{"Left Output Mixer", "AUX Bypass Switch", "AUX"}, +{"Left Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"}, +{"Left Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"}, +{"Left Output Mixer", "DACL Switch", "DACL"}, + +{"Right Output Mixer", "RINPUT3 Bypass Switch", "RINPUT3"}, +{"Right Output Mixer", "AUX Bypass Switch", "AUX"}, +{"Right Output Mixer", "Left Input Mixer Switch", "Left Input Mixer"}, +{"Right Output Mixer", "Right Input Mixer Switch", "Right Input Mixer"}, +{"Right Output Mixer", "DACR Switch", "DACR"}, + +/* Note that the headphone output stage needs to be connected + * externally to LINEOUT2 via DC blocking capacitors.  Other + * configurations are not supported. + * + * Note also that left and right headphone paths are treated as a + * mono path. + */ +{"Headphone Amplifier", NULL, "LINEOUT2 LP"}, +{"Headphone Amplifier", NULL, "LINEOUT2 LP"}, +{"HP_L", NULL, "Headphone Amplifier"}, +{"HP_R", NULL, "Headphone Amplifier"}, +}; + +static int wm8900_add_widgets(struct snd_soc_codec *codec) +{ +	snd_soc_dapm_new_controls(codec, wm8900_dapm_widgets, +				  ARRAY_SIZE(wm8900_dapm_widgets)); + +	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + +	snd_soc_dapm_new_widgets(codec); + +	return 0; +} + +static int wm8900_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	u16 reg; + +	reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60; + +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S16_LE: +		break; +	case SNDRV_PCM_FORMAT_S20_3LE: +		reg |= 0x20; +		break; +	case SNDRV_PCM_FORMAT_S24_LE: +		reg |= 0x40; +		break; +	case SNDRV_PCM_FORMAT_S32_LE: +		reg |= 0x60; +		break; +	default: +		return -EINVAL; +	} + +	wm8900_write(codec, WM8900_REG_AUDIO1, reg); + +	return 0; +} + +/* FLL divisors */ +struct _fll_div { +	u16 fll_ratio; +	u16 fllclk_div; +	u16 fll_slow_lock_ref; +	u16 n; +	u16 k; +}; + +/* The size in bits of the FLL divide multiplied by 10 + * to allow rounding later */ +#define FIXED_FLL_SIZE ((1 << 16) * 10) + +static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, +		       unsigned int Fout) +{ +	u64 Kpart; +	unsigned int K, Ndiv, Nmod, target; +	unsigned int div; + +	BUG_ON(!Fout); + +	/* The FLL must run at 90-100MHz which is then scaled down to +	 * the output value by FLLCLK_DIV. */ +	target = Fout; +	div = 1; +	while (target < 90000000) { +		div *= 2; +		target *= 2; +	} + +	if (target > 100000000) +		printk(KERN_WARNING "wm8900: FLL rate %d out of range, Fref=%d" +		       " Fout=%d\n", target, Fref, Fout); +	if (div > 32) { +		printk(KERN_ERR "wm8900: Invalid FLL division rate %u, " +		       "Fref=%d, Fout=%d, target=%d\n", +		       div, Fref, Fout, target); +		return -EINVAL; +	} + +	fll_div->fllclk_div = div >> 2; + +	if (Fref < 48000) +		fll_div->fll_slow_lock_ref = 1; +	else +		fll_div->fll_slow_lock_ref = 0; + +	Ndiv = target / Fref; + +	if (Fref < 1000000) +		fll_div->fll_ratio = 8; +	else +		fll_div->fll_ratio = 1; + +	fll_div->n = Ndiv / fll_div->fll_ratio; +	Nmod = (target / fll_div->fll_ratio) % Fref; + +	/* Calculate fractional part - scale up so we can round. */ +	Kpart = FIXED_FLL_SIZE * (long long)Nmod; + +	do_div(Kpart, Fref); + +	K = Kpart & 0xFFFFFFFF; + +	if ((K % 10) >= 5) +		K += 5; + +	/* Move down to proper range now rounding is done */ +	fll_div->k = K / 10; + +	BUG_ON(target != Fout * (fll_div->fllclk_div << 2)); +	BUG_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n); + +	return 0; +} + +static int wm8900_set_fll(struct snd_soc_codec *codec, +	int fll_id, unsigned int freq_in, unsigned int freq_out) +{ +	struct wm8900_priv *wm8900 = codec->private_data; +	struct _fll_div fll_div; +	unsigned int reg; + +	if (wm8900->fll_in == freq_in && wm8900->fll_out == freq_out) +		return 0; + +	/* The digital side should be disabled during any change. */ +	reg = wm8900_read(codec, WM8900_REG_POWER1); +	wm8900_write(codec, WM8900_REG_POWER1, +		     reg & (~WM8900_REG_POWER1_FLL_ENA)); + +	/* Disable the FLL? */ +	if (!freq_in || !freq_out) { +		reg = wm8900_read(codec, WM8900_REG_CLOCKING1); +		wm8900_write(codec, WM8900_REG_CLOCKING1, +			     reg & (~WM8900_REG_CLOCKING1_MCLK_SRC)); + +		reg = wm8900_read(codec, WM8900_REG_FLLCTL1); +		wm8900_write(codec, WM8900_REG_FLLCTL1, +			     reg & (~WM8900_REG_FLLCTL1_OSC_ENA)); + +		wm8900->fll_in = freq_in; +		wm8900->fll_out = freq_out; + +		return 0; +	} + +	if (fll_factors(&fll_div, freq_in, freq_out) != 0) +		goto reenable; + +	wm8900->fll_in = freq_in; +	wm8900->fll_out = freq_out; + +	/* The osclilator *MUST* be enabled before we enable the +	 * digital circuit. */ +	wm8900_write(codec, WM8900_REG_FLLCTL1, +		     fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA); + +	wm8900_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5); +	wm8900_write(codec, WM8900_REG_FLLCTL5, +		     (fll_div.fllclk_div << 6) | (fll_div.n & 0x1f)); + +	if (fll_div.k) { +		wm8900_write(codec, WM8900_REG_FLLCTL2, +			     (fll_div.k >> 8) | 0x100); +		wm8900_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff); +	} else +		wm8900_write(codec, WM8900_REG_FLLCTL2, 0); + +	if (fll_div.fll_slow_lock_ref) +		wm8900_write(codec, WM8900_REG_FLLCTL6, +			     WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF); +	else +		wm8900_write(codec, WM8900_REG_FLLCTL6, 0); + +	reg = wm8900_read(codec, WM8900_REG_POWER1); +	wm8900_write(codec, WM8900_REG_POWER1, +		     reg | WM8900_REG_POWER1_FLL_ENA); + +reenable: +	reg = wm8900_read(codec, WM8900_REG_CLOCKING1); +	wm8900_write(codec, WM8900_REG_CLOCKING1, +		     reg | WM8900_REG_CLOCKING1_MCLK_SRC); + +	return 0; +} + +static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, +		int pll_id, unsigned int freq_in, unsigned int freq_out) +{ +	return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out); +} + +static int wm8900_set_dai_clkdiv(struct snd_soc_dai *codec_dai, +				 int div_id, int div) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	unsigned int reg; + +	switch (div_id) { +	case WM8900_BCLK_DIV: +		reg = wm8900_read(codec, WM8900_REG_CLOCKING1); +		wm8900_write(codec, WM8900_REG_CLOCKING1, +			     div | (reg & WM8900_REG_CLOCKING1_BCLK_MASK)); +		break; +	case WM8900_OPCLK_DIV: +		reg = wm8900_read(codec, WM8900_REG_CLOCKING1); +		wm8900_write(codec, WM8900_REG_CLOCKING1, +			     div | (reg & WM8900_REG_CLOCKING1_OPCLK_MASK)); +		break; +	case WM8900_DAC_LRCLK: +		reg = wm8900_read(codec, WM8900_REG_AUDIO4); +		wm8900_write(codec, WM8900_REG_AUDIO4, +			     div | (reg & WM8900_LRC_MASK)); +		break; +	case WM8900_ADC_LRCLK: +		reg = wm8900_read(codec, WM8900_REG_AUDIO3); +		wm8900_write(codec, WM8900_REG_AUDIO3, +			     div | (reg & WM8900_LRC_MASK)); +		break; +	case WM8900_DAC_CLKDIV: +		reg = wm8900_read(codec, WM8900_REG_CLOCKING2); +		wm8900_write(codec, WM8900_REG_CLOCKING2, +			     div | (reg & WM8900_REG_CLOCKING2_DAC_CLKDIV)); +		break; +	case WM8900_ADC_CLKDIV: +		reg = wm8900_read(codec, WM8900_REG_CLOCKING2); +		wm8900_write(codec, WM8900_REG_CLOCKING2, +			     div | (reg & WM8900_REG_CLOCKING2_ADC_CLKDIV)); +		break; +	case WM8900_LRCLK_MODE: +		reg = wm8900_read(codec, WM8900_REG_DACCTRL); +		wm8900_write(codec, WM8900_REG_DACCTRL, +			     div | (reg & WM8900_REG_DACCTRL_AIF_LRCLKRATE)); +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + + +static int wm8900_set_dai_fmt(struct snd_soc_dai *codec_dai, +			      unsigned int fmt) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	unsigned int clocking1, aif1, aif3, aif4; + +	clocking1 = wm8900_read(codec, WM8900_REG_CLOCKING1); +	aif1 = wm8900_read(codec, WM8900_REG_AUDIO1); +	aif3 = wm8900_read(codec, WM8900_REG_AUDIO3); +	aif4 = wm8900_read(codec, WM8900_REG_AUDIO4); + +	/* set master/slave audio interface */ +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBS_CFS: +		clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; +		aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; +		aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; +		break; +	case SND_SOC_DAIFMT_CBS_CFM: +		clocking1 &= ~WM8900_REG_CLOCKING1_BCLK_DIR; +		aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; +		aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; +		break; +	case SND_SOC_DAIFMT_CBM_CFM: +		clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; +		aif3 |= WM8900_REG_AUDIO3_ADCLRC_DIR; +		aif4 |= WM8900_REG_AUDIO4_DACLRC_DIR; +		break; +	case SND_SOC_DAIFMT_CBM_CFS: +		clocking1 |= WM8900_REG_CLOCKING1_BCLK_DIR; +		aif3 &= ~WM8900_REG_AUDIO3_ADCLRC_DIR; +		aif4 &= ~WM8900_REG_AUDIO4_DACLRC_DIR; +		break; +	default: +		return -EINVAL; +	} + +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_DSP_A: +		aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK; +		aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; +		break; +	case SND_SOC_DAIFMT_DSP_B: +		aif1 |= WM8900_REG_AUDIO1_AIF_FMT_MASK; +		aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; +		break; +	case SND_SOC_DAIFMT_I2S: +		aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; +		aif1 |= 0x10; +		break; +	case SND_SOC_DAIFMT_RIGHT_J: +		aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		aif1 &= ~WM8900_REG_AUDIO1_AIF_FMT_MASK; +		aif1 |= 0x8; +		break; +	default: +		return -EINVAL; +	} + +	/* Clock inversion */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_DSP_A: +	case SND_SOC_DAIFMT_DSP_B: +		/* frame inversion not valid for DSP modes */ +		switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +		case SND_SOC_DAIFMT_NB_NF: +			aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; +			break; +		case SND_SOC_DAIFMT_IB_NF: +			aif1 |= WM8900_REG_AUDIO1_BCLK_INV; +			break; +		default: +			return -EINVAL; +		} +		break; +	case SND_SOC_DAIFMT_I2S: +	case SND_SOC_DAIFMT_RIGHT_J: +	case SND_SOC_DAIFMT_LEFT_J: +		switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +		case SND_SOC_DAIFMT_NB_NF: +			aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; +			aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; +			break; +		case SND_SOC_DAIFMT_IB_IF: +			aif1 |= WM8900_REG_AUDIO1_BCLK_INV; +			aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; +			break; +		case SND_SOC_DAIFMT_IB_NF: +			aif1 |= WM8900_REG_AUDIO1_BCLK_INV; +			aif1 &= ~WM8900_REG_AUDIO1_LRCLK_INV; +			break; +		case SND_SOC_DAIFMT_NB_IF: +			aif1 &= ~WM8900_REG_AUDIO1_BCLK_INV; +			aif1 |= WM8900_REG_AUDIO1_LRCLK_INV; +			break; +		default: +			return -EINVAL; +		} +		break; +	default: +		return -EINVAL; +	} + +	wm8900_write(codec, WM8900_REG_CLOCKING1, clocking1); +	wm8900_write(codec, WM8900_REG_AUDIO1, aif1); +	wm8900_write(codec, WM8900_REG_AUDIO3, aif3); +	wm8900_write(codec, WM8900_REG_AUDIO4, aif4); + +	return 0; +} + +static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	u16 reg; + +	reg = wm8900_read(codec, WM8900_REG_DACCTRL); + +	if (mute) +		reg |= WM8900_REG_DACCTRL_MUTE; +	else +		reg &= ~WM8900_REG_DACCTRL_MUTE; + +	wm8900_write(codec, WM8900_REG_DACCTRL, reg); + +	return 0; +} + +#define WM8900_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ +		      SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ +		      SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) + +#define WM8900_PCM_FORMATS \ +	(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \ +	 SNDRV_PCM_FORMAT_S24_LE) + +struct snd_soc_dai wm8900_dai = { +	.name = "WM8900 HiFi", +	.playback = { +		.stream_name = "HiFi Playback", +		.channels_min = 1, +		.channels_max = 2, +		.rates = WM8900_RATES, +		.formats = WM8900_PCM_FORMATS, +	}, +	.capture = { +		.stream_name = "HiFi Capture", +		.channels_min = 1, +		.channels_max = 2, +		.rates = WM8900_RATES, +		.formats = WM8900_PCM_FORMATS, +	 }, +	.ops = { +		.hw_params = wm8900_hw_params, +	 }, +	.dai_ops = { +		 .set_clkdiv = wm8900_set_dai_clkdiv, +		 .set_pll = wm8900_set_dai_pll, +		 .set_fmt = wm8900_set_dai_fmt, +		 .digital_mute = wm8900_digital_mute, +	 }, +}; +EXPORT_SYMBOL_GPL(wm8900_dai); + +static int wm8900_set_bias_level(struct snd_soc_codec *codec, +				 enum snd_soc_bias_level level) +{ +	u16 reg; + +	switch (level) { +	case SND_SOC_BIAS_ON: +		/* Enable thermal shutdown */ +		reg = wm8900_read(codec, WM8900_REG_GPIO); +		wm8900_write(codec, WM8900_REG_GPIO, +			     reg | WM8900_REG_GPIO_TEMP_ENA); +		reg = wm8900_read(codec, WM8900_REG_ADDCTL); +		wm8900_write(codec, WM8900_REG_ADDCTL, +			     reg | WM8900_REG_ADDCTL_TEMP_SD); +		break; + +	case SND_SOC_BIAS_PREPARE: +		break; + +	case SND_SOC_BIAS_STANDBY: +		/* Charge capacitors if initial power up */ +		if (codec->bias_level == SND_SOC_BIAS_OFF) { +			/* STARTUP_BIAS_ENA on */ +			wm8900_write(codec, WM8900_REG_POWER1, +				     WM8900_REG_POWER1_STARTUP_BIAS_ENA); + +			/* Startup bias mode */ +			wm8900_write(codec, WM8900_REG_ADDCTL, +				     WM8900_REG_ADDCTL_BIAS_SRC | +				     WM8900_REG_ADDCTL_VMID_SOFTST); + +			/* VMID 2x50k */ +			wm8900_write(codec, WM8900_REG_POWER1, +				     WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1); + +			/* Allow capacitors to charge */ +			schedule_timeout_interruptible(msecs_to_jiffies(400)); + +			/* Enable bias */ +			wm8900_write(codec, WM8900_REG_POWER1, +				     WM8900_REG_POWER1_STARTUP_BIAS_ENA | +				     WM8900_REG_POWER1_BIAS_ENA | 0x1); + +			wm8900_write(codec, WM8900_REG_ADDCTL, 0); + +			wm8900_write(codec, WM8900_REG_POWER1, +				     WM8900_REG_POWER1_BIAS_ENA | 0x1); +		} + +		reg = wm8900_read(codec, WM8900_REG_POWER1); +		wm8900_write(codec, WM8900_REG_POWER1, +			     (reg & WM8900_REG_POWER1_FLL_ENA) | +			     WM8900_REG_POWER1_BIAS_ENA | 0x1); +		wm8900_write(codec, WM8900_REG_POWER2, +			     WM8900_REG_POWER2_SYSCLK_ENA); +		wm8900_write(codec, WM8900_REG_POWER3, 0); +		break; + +	case SND_SOC_BIAS_OFF: +		/* Startup bias enable */ +		reg = wm8900_read(codec, WM8900_REG_POWER1); +		wm8900_write(codec, WM8900_REG_POWER1, +			     reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA); +		wm8900_write(codec, WM8900_REG_ADDCTL, +			     WM8900_REG_ADDCTL_BIAS_SRC | +			     WM8900_REG_ADDCTL_VMID_SOFTST); + +		/* Discharge caps */ +		wm8900_write(codec, WM8900_REG_POWER1, +			     WM8900_REG_POWER1_STARTUP_BIAS_ENA); +		schedule_timeout_interruptible(msecs_to_jiffies(500)); + +		/* Remove clamp */ +		wm8900_write(codec, WM8900_REG_HPCTL1, 0); + +		/* Power down */ +		wm8900_write(codec, WM8900_REG_ADDCTL, 0); +		wm8900_write(codec, WM8900_REG_POWER1, 0); +		wm8900_write(codec, WM8900_REG_POWER2, 0); +		wm8900_write(codec, WM8900_REG_POWER3, 0); + +		/* Need to let things settle before stopping the clock +		 * to ensure that restart works, see "Stopping the +		 * master clock" in the datasheet. */ +		schedule_timeout_interruptible(msecs_to_jiffies(1)); +		wm8900_write(codec, WM8900_REG_POWER2, +			     WM8900_REG_POWER2_SYSCLK_ENA); +		break; +	} +	codec->bias_level = level; +	return 0; +} + +static int wm8900_suspend(struct platform_device *pdev, pm_message_t state) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; +	struct wm8900_priv *wm8900 = codec->private_data; +	int fll_out = wm8900->fll_out; +	int fll_in  = wm8900->fll_in; +	int ret; + +	/* Stop the FLL in an orderly fashion */ +	ret = wm8900_set_fll(codec, 0, 0, 0); +	if (ret != 0) { +		dev_err(&pdev->dev, "Failed to stop FLL\n"); +		return ret; +	} + +	wm8900->fll_out = fll_out; +	wm8900->fll_in = fll_in; + +	wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF); + +	return 0; +} + +static int wm8900_resume(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; +	struct wm8900_priv *wm8900 = codec->private_data; +	u16 *cache; +	int i, ret; + +	cache = kmemdup(codec->reg_cache, sizeof(wm8900_reg_defaults), +			GFP_KERNEL); + +	wm8900_reset(codec); +	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + +	/* Restart the FLL? */ +	if (wm8900->fll_out) { +		int fll_out = wm8900->fll_out; +		int fll_in  = wm8900->fll_in; + +		wm8900->fll_in = 0; +		wm8900->fll_out = 0; + +		ret = wm8900_set_fll(codec, 0, fll_in, fll_out); +		if (ret != 0) { +			dev_err(&pdev->dev, "Failed to restart FLL\n"); +			return ret; +		} +	} + +	if (cache) { +		for (i = 0; i < WM8900_MAXREG; i++) +			wm8900_write(codec, i, cache[i]); +		kfree(cache); +	} else +		dev_err(&pdev->dev, "Unable to allocate register cache\n"); + +	return 0; +} + +/* + * initialise the WM8900 driver + * register the mixer and dsp interfaces with the kernel + */ +static int wm8900_init(struct snd_soc_device *socdev) +{ +	struct snd_soc_codec *codec = socdev->codec; +	int ret = 0; +	unsigned int reg; +	struct i2c_client *i2c_client = socdev->codec->control_data; + +	codec->name = "WM8900"; +	codec->owner = THIS_MODULE; +	codec->read = wm8900_read; +	codec->write = wm8900_write; +	codec->dai = &wm8900_dai; +	codec->num_dai = 1; +	codec->reg_cache_size = WM8900_MAXREG; +	codec->reg_cache = kmemdup(wm8900_reg_defaults, +				   sizeof(wm8900_reg_defaults), GFP_KERNEL); + +	if (codec->reg_cache == NULL) +		return -ENOMEM; + +	reg = wm8900_read(codec, WM8900_REG_ID); +	if (reg != 0x8900) { +		dev_err(&i2c_client->dev, "Device is not a WM8900 - ID %x\n", +			reg); +		return -ENODEV; +	} + +	codec->private_data = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL); +	if (codec->private_data == NULL) { +		ret = -ENOMEM; +		goto priv_err; +	} + +	/* Read back from the chip */ +	reg = wm8900_chip_read(codec, WM8900_REG_POWER1); +	reg = (reg >> 12) & 0xf; +	dev_info(&i2c_client->dev, "WM8900 revision %d\n", reg); + +	wm8900_reset(codec); + +	/* Latch the volume update bits */ +	wm8900_write(codec, WM8900_REG_LINVOL, +		     wm8900_read(codec, WM8900_REG_LINVOL) | 0x100); +	wm8900_write(codec, WM8900_REG_RINVOL, +		     wm8900_read(codec, WM8900_REG_RINVOL) | 0x100); +	wm8900_write(codec, WM8900_REG_LOUT1CTL, +		     wm8900_read(codec, WM8900_REG_LOUT1CTL) | 0x100); +	wm8900_write(codec, WM8900_REG_ROUT1CTL, +		     wm8900_read(codec, WM8900_REG_ROUT1CTL) | 0x100); +	wm8900_write(codec, WM8900_REG_LOUT2CTL, +		     wm8900_read(codec, WM8900_REG_LOUT2CTL) | 0x100); +	wm8900_write(codec, WM8900_REG_ROUT2CTL, +		     wm8900_read(codec, WM8900_REG_ROUT2CTL) | 0x100); +	wm8900_write(codec, WM8900_REG_LDAC_DV, +		     wm8900_read(codec, WM8900_REG_LDAC_DV) | 0x100); +	wm8900_write(codec, WM8900_REG_RDAC_DV, +		     wm8900_read(codec, WM8900_REG_RDAC_DV) | 0x100); +	wm8900_write(codec, WM8900_REG_LADC_DV, +		     wm8900_read(codec, WM8900_REG_LADC_DV) | 0x100); +	wm8900_write(codec, WM8900_REG_RADC_DV, +		     wm8900_read(codec, WM8900_REG_RADC_DV) | 0x100); + +	/* Set the DAC and mixer output bias */ +	wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81); + +	/* Register pcms */ +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); +	if (ret < 0) { +		dev_err(&i2c_client->dev, "Failed to register new PCMs\n"); +		goto pcm_err; +	} + +	/* Turn the chip on */ +	codec->bias_level = SND_SOC_BIAS_OFF; +	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + +	wm8900_add_controls(codec); +	wm8900_add_widgets(codec); + +	ret = snd_soc_register_card(socdev); +	if (ret < 0) { +		dev_err(&i2c_client->dev, "Failed to register card\n"); +		goto card_err; +	} +	return ret; + +card_err: +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +pcm_err: +	kfree(codec->reg_cache); +priv_err: +	kfree(codec->private_data); +	return ret; +} + +static struct snd_soc_device *wm8900_socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + +static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; + +/* Magic definition of all other variables and things */ +I2C_CLIENT_INSMOD; + +static struct i2c_driver wm8900_i2c_driver; +static struct i2c_client client_template; + +/* If the i2c layer weren't so broken, we could pass this kind of data +   around */ +static int wm8900_codec_probe(struct i2c_adapter *adap, int addr, int kind) +{ +	struct snd_soc_device *socdev = wm8900_socdev; +	struct wm8900_setup_data *setup = socdev->codec_data; +	struct snd_soc_codec *codec = socdev->codec; +	struct i2c_client *i2c; +	int ret; + +	if (addr != setup->i2c_address) +		return -ENODEV; + +	dev_err(&adap->dev, "Probe on %x\n", addr); + +	client_template.adapter = adap; +	client_template.addr = addr; + +	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); +	if (i2c == NULL) { +		kfree(codec); +		return -ENOMEM; +	} +	i2c_set_clientdata(i2c, codec); +	codec->control_data = i2c; + +	ret = i2c_attach_client(i2c); +	if (ret < 0) { +		dev_err(&adap->dev, +			"failed to attach codec at addr %x\n", addr); +		goto err; +	} + +	ret = wm8900_init(socdev); +	if (ret < 0) { +		dev_err(&adap->dev, "failed to initialise WM8900\n"); +		goto err; +	} +	return ret; + +err: +	kfree(codec); +	kfree(i2c); +	return ret; +} + +static int wm8900_i2c_detach(struct i2c_client *client) +{ +	struct snd_soc_codec *codec = i2c_get_clientdata(client); +	i2c_detach_client(client); +	kfree(codec->reg_cache); +	kfree(client); +	return 0; +} + +static int wm8900_i2c_attach(struct i2c_adapter *adap) +{ +	return i2c_probe(adap, &addr_data, wm8900_codec_probe); +} + +/* corgi i2c codec control layer */ +static struct i2c_driver wm8900_i2c_driver = { +	.driver = { +		.name = "WM8900 I2C codec", +		.owner = THIS_MODULE, +	}, +	.attach_adapter = wm8900_i2c_attach, +	.detach_client =  wm8900_i2c_detach, +	.command =        NULL, +}; + +static struct i2c_client client_template = { +	.name =   "WM8900", +	.driver = &wm8900_i2c_driver, +}; +#endif + +static int wm8900_probe(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct wm8900_setup_data *setup; +	struct snd_soc_codec *codec; +	int ret = 0; + +	dev_info(&pdev->dev, "WM8900 Audio Codec\n"); + +	setup = socdev->codec_data; +	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); +	if (codec == NULL) +		return -ENOMEM; + +	mutex_init(&codec->mutex); +	INIT_LIST_HEAD(&codec->dapm_widgets); +	INIT_LIST_HEAD(&codec->dapm_paths); + +	socdev->codec = codec; + +	codec->set_bias_level = wm8900_set_bias_level; + +	wm8900_socdev = socdev; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	if (setup->i2c_address) { +		normal_i2c[0] = setup->i2c_address; +		codec->hw_write = (hw_write_t)i2c_master_send; +		ret = i2c_add_driver(&wm8900_i2c_driver); +		if (ret != 0) +			printk(KERN_ERR "can't add i2c driver"); +	} +#else +#error Non-I2C interfaces not yet supported +#endif +	return ret; +} + +/* power down chip */ +static int wm8900_remove(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	if (codec->control_data) +		wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF); + +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_del_driver(&wm8900_i2c_driver); +#endif +	kfree(codec); + +	return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8900 = { +	.probe = 	wm8900_probe, +	.remove = 	wm8900_remove, +	.suspend = 	wm8900_suspend, +	.resume =	wm8900_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8900); + +MODULE_DESCRIPTION("ASoC WM8900 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8900.h b/sound/soc/codecs/wm8900.h new file mode 100644 index 0000000..ba450d9 --- /dev/null +++ b/sound/soc/codecs/wm8900.h @@ -0,0 +1,64 @@ +/* + * wm8900.h  --  WM890 Soc Audio driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WM8900_H +#define _WM8900_H + +#define WM8900_FLL 1 + +#define WM8900_BCLK_DIV   1 +#define WM8900_ADC_CLKDIV 2 +#define WM8900_DAC_CLKDIV 3 +#define WM8900_ADC_LRCLK  4 +#define WM8900_DAC_LRCLK  5 +#define WM8900_OPCLK_DIV  6 +#define WM8900_LRCLK_MODE 7 + +#define WM8900_BCLK_DIV_1   0x00 +#define WM8900_BCLK_DIV_1_5 0x02 +#define WM8900_BCLK_DIV_2   0x04 +#define WM8900_BCLK_DIV_3   0x06 +#define WM8900_BCLK_DIV_4   0x08 +#define WM8900_BCLK_DIV_5_5 0x0a +#define WM8900_BCLK_DIV_6   0x0c +#define WM8900_BCLK_DIV_8   0x0e +#define WM8900_BCLK_DIV_11  0x10 +#define WM8900_BCLK_DIV_12  0x12 +#define WM8900_BCLK_DIV_16  0x14 +#define WM8900_BCLK_DIV_22  0x16 +#define WM8900_BCLK_DIV_24  0x18 +#define WM8900_BCLK_DIV_32  0x1a +#define WM8900_BCLK_DIV_44  0x1c +#define WM8900_BCLK_DIV_48  0x1e + +#define WM8900_ADC_CLKDIV_1   0x00 +#define WM8900_ADC_CLKDIV_1_5 0x20 +#define WM8900_ADC_CLKDIV_2   0x40 +#define WM8900_ADC_CLKDIV_3   0x60 +#define WM8900_ADC_CLKDIV_4   0x80 +#define WM8900_ADC_CLKDIV_5_5 0xa0 +#define WM8900_ADC_CLKDIV_6   0xc0 + +#define WM8900_DAC_CLKDIV_1   0x00 +#define WM8900_DAC_CLKDIV_1_5 0x04 +#define WM8900_DAC_CLKDIV_2   0x08 +#define WM8900_DAC_CLKDIV_3   0x0c +#define WM8900_DAC_CLKDIV_4   0x10 +#define WM8900_DAC_CLKDIV_5_5 0x14 +#define WM8900_DAC_CLKDIV_6   0x18 + +#define WM8900_ + +struct wm8900_setup_data { +	unsigned short i2c_address; +}; + +extern struct snd_soc_dai wm8900_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8900; + +#endif diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c new file mode 100644 index 0000000..a3f54ec --- /dev/null +++ b/sound/soc/codecs/wm8903.c @@ -0,0 +1,1813 @@ +/* + * wm8903.c  --  WM8903 ALSA SoC Audio driver + * + * Copyright 2008 Wolfson Microelectronics + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: + *  - TDM mode configuration. + *  - Mic detect. + *  - Digital microphone support. + *  - Interrupt support (mic detect and sequencer). + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "wm8903.h" + +struct wm8903_priv { +	int sysclk; + +	/* Reference counts */ +	int charge_pump_users; +	int class_w_users; +	int playback_active; +	int capture_active; + +	struct snd_pcm_substream *master_substream; +	struct snd_pcm_substream *slave_substream; +}; + +/* Register defaults at reset */ +static u16 wm8903_reg_defaults[] = { +	0x8903,     /* R0   - SW Reset and ID */ +	0x0000,     /* R1   - Revision Number */ +	0x0000,     /* R2 */ +	0x0000,     /* R3 */ +	0x0018,     /* R4   - Bias Control 0 */ +	0x0000,     /* R5   - VMID Control 0 */ +	0x0000,     /* R6   - Mic Bias Control 0 */ +	0x0000,     /* R7 */ +	0x0001,     /* R8   - Analogue DAC 0 */ +	0x0000,     /* R9 */ +	0x0001,     /* R10  - Analogue ADC 0 */ +	0x0000,     /* R11 */ +	0x0000,     /* R12  - Power Management 0 */ +	0x0000,     /* R13  - Power Management 1 */ +	0x0000,     /* R14  - Power Management 2 */ +	0x0000,     /* R15  - Power Management 3 */ +	0x0000,     /* R16  - Power Management 4 */ +	0x0000,     /* R17  - Power Management 5 */ +	0x0000,     /* R18  - Power Management 6 */ +	0x0000,     /* R19 */ +	0x0400,     /* R20  - Clock Rates 0 */ +	0x0D07,     /* R21  - Clock Rates 1 */ +	0x0000,     /* R22  - Clock Rates 2 */ +	0x0000,     /* R23 */ +	0x0050,     /* R24  - Audio Interface 0 */ +	0x0242,     /* R25  - Audio Interface 1 */ +	0x0008,     /* R26  - Audio Interface 2 */ +	0x0022,     /* R27  - Audio Interface 3 */ +	0x0000,     /* R28 */ +	0x0000,     /* R29 */ +	0x00C0,     /* R30  - DAC Digital Volume Left */ +	0x00C0,     /* R31  - DAC Digital Volume Right */ +	0x0000,     /* R32  - DAC Digital 0 */ +	0x0000,     /* R33  - DAC Digital 1 */ +	0x0000,     /* R34 */ +	0x0000,     /* R35 */ +	0x00C0,     /* R36  - ADC Digital Volume Left */ +	0x00C0,     /* R37  - ADC Digital Volume Right */ +	0x0000,     /* R38  - ADC Digital 0 */ +	0x0073,     /* R39  - Digital Microphone 0 */ +	0x09BF,     /* R40  - DRC 0 */ +	0x3241,     /* R41  - DRC 1 */ +	0x0020,     /* R42  - DRC 2 */ +	0x0000,     /* R43  - DRC 3 */ +	0x0085,     /* R44  - Analogue Left Input 0 */ +	0x0085,     /* R45  - Analogue Right Input 0 */ +	0x0044,     /* R46  - Analogue Left Input 1 */ +	0x0044,     /* R47  - Analogue Right Input 1 */ +	0x0000,     /* R48 */ +	0x0000,     /* R49 */ +	0x0008,     /* R50  - Analogue Left Mix 0 */ +	0x0004,     /* R51  - Analogue Right Mix 0 */ +	0x0000,     /* R52  - Analogue Spk Mix Left 0 */ +	0x0000,     /* R53  - Analogue Spk Mix Left 1 */ +	0x0000,     /* R54  - Analogue Spk Mix Right 0 */ +	0x0000,     /* R55  - Analogue Spk Mix Right 1 */ +	0x0000,     /* R56 */ +	0x002D,     /* R57  - Analogue OUT1 Left */ +	0x002D,     /* R58  - Analogue OUT1 Right */ +	0x0039,     /* R59  - Analogue OUT2 Left */ +	0x0039,     /* R60  - Analogue OUT2 Right */ +	0x0100,     /* R61 */ +	0x0139,     /* R62  - Analogue OUT3 Left */ +	0x0139,     /* R63  - Analogue OUT3 Right */ +	0x0000,     /* R64 */ +	0x0000,     /* R65  - Analogue SPK Output Control 0 */ +	0x0000,     /* R66 */ +	0x0010,     /* R67  - DC Servo 0 */ +	0x0100,     /* R68 */ +	0x00A4,     /* R69  - DC Servo 2 */ +	0x0807,     /* R70 */ +	0x0000,     /* R71 */ +	0x0000,     /* R72 */ +	0x0000,     /* R73 */ +	0x0000,     /* R74 */ +	0x0000,     /* R75 */ +	0x0000,     /* R76 */ +	0x0000,     /* R77 */ +	0x0000,     /* R78 */ +	0x000E,     /* R79 */ +	0x0000,     /* R80 */ +	0x0000,     /* R81 */ +	0x0000,     /* R82 */ +	0x0000,     /* R83 */ +	0x0000,     /* R84 */ +	0x0000,     /* R85 */ +	0x0000,     /* R86 */ +	0x0006,     /* R87 */ +	0x0000,     /* R88 */ +	0x0000,     /* R89 */ +	0x0000,     /* R90  - Analogue HP 0 */ +	0x0060,     /* R91 */ +	0x0000,     /* R92 */ +	0x0000,     /* R93 */ +	0x0000,     /* R94  - Analogue Lineout 0 */ +	0x0060,     /* R95 */ +	0x0000,     /* R96 */ +	0x0000,     /* R97 */ +	0x0000,     /* R98  - Charge Pump 0 */ +	0x1F25,     /* R99 */ +	0x2B19,     /* R100 */ +	0x01C0,     /* R101 */ +	0x01EF,     /* R102 */ +	0x2B00,     /* R103 */ +	0x0000,     /* R104 - Class W 0 */ +	0x01C0,     /* R105 */ +	0x1C10,     /* R106 */ +	0x0000,     /* R107 */ +	0x0000,     /* R108 - Write Sequencer 0 */ +	0x0000,     /* R109 - Write Sequencer 1 */ +	0x0000,     /* R110 - Write Sequencer 2 */ +	0x0000,     /* R111 - Write Sequencer 3 */ +	0x0000,     /* R112 - Write Sequencer 4 */ +	0x0000,     /* R113 */ +	0x0000,     /* R114 - Control Interface */ +	0x0000,     /* R115 */ +	0x00A8,     /* R116 - GPIO Control 1 */ +	0x00A8,     /* R117 - GPIO Control 2 */ +	0x00A8,     /* R118 - GPIO Control 3 */ +	0x0220,     /* R119 - GPIO Control 4 */ +	0x01A0,     /* R120 - GPIO Control 5 */ +	0x0000,     /* R121 - Interrupt Status 1 */ +	0xFFFF,     /* R122 - Interrupt Status 1 Mask */ +	0x0000,     /* R123 - Interrupt Polarity 1 */ +	0x0000,     /* R124 */ +	0x0003,     /* R125 */ +	0x0000,     /* R126 - Interrupt Control */ +	0x0000,     /* R127 */ +	0x0005,     /* R128 */ +	0x0000,     /* R129 - Control Interface Test 1 */ +	0x0000,     /* R130 */ +	0x0000,     /* R131 */ +	0x0000,     /* R132 */ +	0x0000,     /* R133 */ +	0x0000,     /* R134 */ +	0x03FF,     /* R135 */ +	0x0007,     /* R136 */ +	0x0040,     /* R137 */ +	0x0000,     /* R138 */ +	0x0000,     /* R139 */ +	0x0000,     /* R140 */ +	0x0000,     /* R141 */ +	0x0000,     /* R142 */ +	0x0000,     /* R143 */ +	0x0000,     /* R144 */ +	0x0000,     /* R145 */ +	0x0000,     /* R146 */ +	0x0000,     /* R147 */ +	0x4000,     /* R148 */ +	0x6810,     /* R149 - Charge Pump Test 1 */ +	0x0004,     /* R150 */ +	0x0000,     /* R151 */ +	0x0000,     /* R152 */ +	0x0000,     /* R153 */ +	0x0000,     /* R154 */ +	0x0000,     /* R155 */ +	0x0000,     /* R156 */ +	0x0000,     /* R157 */ +	0x0000,     /* R158 */ +	0x0000,     /* R159 */ +	0x0000,     /* R160 */ +	0x0000,     /* R161 */ +	0x0000,     /* R162 */ +	0x0000,     /* R163 */ +	0x0028,     /* R164 - Clock Rate Test 4 */ +	0x0004,     /* R165 */ +	0x0000,     /* R166 */ +	0x0060,     /* R167 */ +	0x0000,     /* R168 */ +	0x0000,     /* R169 */ +	0x0000,     /* R170 */ +	0x0000,     /* R171 */ +	0x0000,     /* R172 - Analogue Output Bias 0 */ +}; + +static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec, +						 unsigned int reg) +{ +	u16 *cache = codec->reg_cache; + +	BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults)); + +	return cache[reg]; +} + +static unsigned int wm8903_hw_read(struct snd_soc_codec *codec, u8 reg) +{ +	struct i2c_msg xfer[2]; +	u16 data; +	int ret; +	struct i2c_client *client = codec->control_data; + +	/* Write register */ +	xfer[0].addr = client->addr; +	xfer[0].flags = 0; +	xfer[0].len = 1; +	xfer[0].buf = ® + +	/* Read data */ +	xfer[1].addr = client->addr; +	xfer[1].flags = I2C_M_RD; +	xfer[1].len = 2; +	xfer[1].buf = (u8 *)&data; + +	ret = i2c_transfer(client->adapter, xfer, 2); +	if (ret != 2) { +		pr_err("i2c_transfer returned %d\n", ret); +		return 0; +	} + +	return (data >> 8) | ((data & 0xff) << 8); +} + +static unsigned int wm8903_read(struct snd_soc_codec *codec, +				unsigned int reg) +{ +	switch (reg) { +	case WM8903_SW_RESET_AND_ID: +	case WM8903_REVISION_NUMBER: +	case WM8903_INTERRUPT_STATUS_1: +	case WM8903_WRITE_SEQUENCER_4: +		return wm8903_hw_read(codec, reg); + +	default: +		return wm8903_read_reg_cache(codec, reg); +	} +} + +static void wm8903_write_reg_cache(struct snd_soc_codec *codec, +				   u16 reg, unsigned int value) +{ +	u16 *cache = codec->reg_cache; + +	BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults)); + +	switch (reg) { +	case WM8903_SW_RESET_AND_ID: +	case WM8903_REVISION_NUMBER: +		break; + +	default: +		cache[reg] = value; +		break; +	} +} + +static int wm8903_write(struct snd_soc_codec *codec, unsigned int reg, +			unsigned int value) +{ +	u8 data[3]; + +	wm8903_write_reg_cache(codec, reg, value); + +	/* Data format is 1 byte of address followed by 2 bytes of data */ +	data[0] = reg; +	data[1] = (value >> 8) & 0xff; +	data[2] = value & 0xff; + +	if (codec->hw_write(codec->control_data, data, 3) == 2) +		return 0; +	else +		return -EIO; +} + +static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) +{ +	u16 reg[5]; +	struct i2c_client *i2c = codec->control_data; + +	BUG_ON(start > 48); + +	/* Enable the sequencer */ +	reg[0] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_0); +	reg[0] |= WM8903_WSEQ_ENA; +	wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]); + +	dev_dbg(&i2c->dev, "Starting sequence at %d\n", start); + +	wm8903_write(codec, WM8903_WRITE_SEQUENCER_3, +		     start | WM8903_WSEQ_START); + +	/* Wait for it to complete.  If we have the interrupt wired up then +	 * we could block waiting for an interrupt, though polling may still +	 * be desirable for diagnostic purposes. +	 */ +	do { +		msleep(10); + +		reg[4] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_4); +	} while (reg[4] & WM8903_WSEQ_BUSY); + +	dev_dbg(&i2c->dev, "Sequence complete\n"); + +	/* Disable the sequencer again */ +	wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, +		     reg[0] & ~WM8903_WSEQ_ENA); + +	return 0; +} + +static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache) +{ +	int i; + +	/* There really ought to be something better we can do here :/ */ +	for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++) +		cache[i] = wm8903_hw_read(codec, i); +} + +static void wm8903_reset(struct snd_soc_codec *codec) +{ +	wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0); +} + +#define WM8903_OUTPUT_SHORT 0x8 +#define WM8903_OUTPUT_OUT   0x4 +#define WM8903_OUTPUT_INT   0x2 +#define WM8903_OUTPUT_IN    0x1 + +/* + * Event for headphone and line out amplifier power changes.  Special + * power up/down sequences are required in order to maximise pop/click + * performance. + */ +static int wm8903_output_event(struct snd_soc_dapm_widget *w, +			       struct snd_kcontrol *kcontrol, int event) +{ +	struct snd_soc_codec *codec = w->codec; +	struct wm8903_priv *wm8903 = codec->private_data; +	struct i2c_client *i2c = codec->control_data; +	u16 val; +	u16 reg; +	int shift; +	u16 cp_reg = wm8903_read(codec, WM8903_CHARGE_PUMP_0); + +	switch (w->reg) { +	case WM8903_POWER_MANAGEMENT_2: +		reg = WM8903_ANALOGUE_HP_0; +		break; +	case WM8903_POWER_MANAGEMENT_3: +		reg = WM8903_ANALOGUE_LINEOUT_0; +		break; +	default: +		BUG(); +	} + +	switch (w->shift) { +	case 0: +		shift = 0; +		break; +	case 1: +		shift = 4; +		break; +	default: +		BUG(); +	} + +	if (event & SND_SOC_DAPM_PRE_PMU) { +		val = wm8903_read(codec, reg); + +		/* Short the output */ +		val &= ~(WM8903_OUTPUT_SHORT << shift); +		wm8903_write(codec, reg, val); + +		wm8903->charge_pump_users++; + +		dev_dbg(&i2c->dev, "Charge pump use count now %d\n", +			wm8903->charge_pump_users); + +		if (wm8903->charge_pump_users == 1) { +			dev_dbg(&i2c->dev, "Enabling charge pump\n"); +			wm8903_write(codec, WM8903_CHARGE_PUMP_0, +				     cp_reg | WM8903_CP_ENA); +			mdelay(4); +		} +	} + +	if (event & SND_SOC_DAPM_POST_PMU) { +		val = wm8903_read(codec, reg); + +		val |= (WM8903_OUTPUT_IN << shift); +		wm8903_write(codec, reg, val); + +		val |= (WM8903_OUTPUT_INT << shift); +		wm8903_write(codec, reg, val); + +		/* Turn on the output ENA_OUTP */ +		val |= (WM8903_OUTPUT_OUT << shift); +		wm8903_write(codec, reg, val); + +		/* Remove the short */ +		val |= (WM8903_OUTPUT_SHORT << shift); +		wm8903_write(codec, reg, val); +	} + +	if (event & SND_SOC_DAPM_PRE_PMD) { +		val = wm8903_read(codec, reg); + +		/* Short the output */ +		val &= ~(WM8903_OUTPUT_SHORT << shift); +		wm8903_write(codec, reg, val); + +		/* Then disable the intermediate and output stages */ +		val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT | +			  WM8903_OUTPUT_IN) << shift); +		wm8903_write(codec, reg, val); +	} + +	if (event & SND_SOC_DAPM_POST_PMD) { +		wm8903->charge_pump_users--; + +		dev_dbg(&i2c->dev, "Charge pump use count now %d\n", +			wm8903->charge_pump_users); + +		if (wm8903->charge_pump_users == 0) { +			dev_dbg(&i2c->dev, "Disabling charge pump\n"); +			wm8903_write(codec, WM8903_CHARGE_PUMP_0, +				     cp_reg & ~WM8903_CP_ENA); +		} +	} + +	return 0; +} + +/* + * When used with DAC outputs only the WM8903 charge pump supports + * operation in class W mode, providing very low power consumption + * when used with digital sources.  Enable and disable this mode + * automatically depending on the mixer configuration. + * + * All the relevant controls are simple switches. + */ +static int wm8903_class_w_put(struct snd_kcontrol *kcontrol, +			      struct snd_ctl_elem_value *ucontrol) +{ +	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); +	struct snd_soc_codec *codec = widget->codec; +	struct wm8903_priv *wm8903 = codec->private_data; +	struct i2c_client *i2c = codec->control_data; +	u16 reg; +	int ret; + +	reg = wm8903_read(codec, WM8903_CLASS_W_0); + +	/* Turn it off if we're about to enable bypass */ +	if (ucontrol->value.integer.value[0]) { +		if (wm8903->class_w_users == 0) { +			dev_dbg(&i2c->dev, "Disabling Class W\n"); +			wm8903_write(codec, WM8903_CLASS_W_0, reg & +				     ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V)); +		} +		wm8903->class_w_users++; +	} + +	/* Implement the change */ +	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol); + +	/* If we've just disabled the last bypass path turn Class W on */ +	if (!ucontrol->value.integer.value[0]) { +		if (wm8903->class_w_users == 1) { +			dev_dbg(&i2c->dev, "Enabling Class W\n"); +			wm8903_write(codec, WM8903_CLASS_W_0, reg | +				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); +		} +		wm8903->class_w_users--; +	} + +	dev_dbg(&i2c->dev, "Bypass use count now %d\n", +		wm8903->class_w_users); + +	return ret; +} + +#define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \ +{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ +	.info = snd_soc_info_volsw, \ +	.get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \ +	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) } + + +/* ALSA can only do steps of .01dB */ +static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); + +static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0); + +static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_amp, -2250, 75, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_min, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_max, 1200, 600, 0); +static const DECLARE_TLV_DB_SCALE(drc_tlv_startup, -300, 50, 0); + +static const char *drc_slope_text[] = { +	"1", "1/2", "1/4", "1/8", "1/16", "0" +}; + +static const struct soc_enum drc_slope_r0 = +	SOC_ENUM_SINGLE(WM8903_DRC_2, 3, 6, drc_slope_text); + +static const struct soc_enum drc_slope_r1 = +	SOC_ENUM_SINGLE(WM8903_DRC_2, 0, 6, drc_slope_text); + +static const char *drc_attack_text[] = { +	"instantaneous", +	"363us", "762us", "1.45ms", "2.9ms", "5.8ms", "11.6ms", "23.2ms", +	"46.4ms", "92.8ms", "185.6ms" +}; + +static const struct soc_enum drc_attack = +	SOC_ENUM_SINGLE(WM8903_DRC_1, 12, 11, drc_attack_text); + +static const char *drc_decay_text[] = { +	"186ms", "372ms", "743ms", "1.49s", "2.97s", "5.94s", "11.89s", +	"23.87s", "47.56s" +}; + +static const struct soc_enum drc_decay = +	SOC_ENUM_SINGLE(WM8903_DRC_1, 8, 9, drc_decay_text); + +static const char *drc_ff_delay_text[] = { +	"5 samples", "9 samples" +}; + +static const struct soc_enum drc_ff_delay = +	SOC_ENUM_SINGLE(WM8903_DRC_0, 5, 2, drc_ff_delay_text); + +static const char *drc_qr_decay_text[] = { +	"0.725ms", "1.45ms", "5.8ms" +}; + +static const struct soc_enum drc_qr_decay = +	SOC_ENUM_SINGLE(WM8903_DRC_1, 4, 3, drc_qr_decay_text); + +static const char *drc_smoothing_text[] = { +	"Low", "Medium", "High" +}; + +static const struct soc_enum drc_smoothing = +	SOC_ENUM_SINGLE(WM8903_DRC_0, 11, 3, drc_smoothing_text); + +static const char *soft_mute_text[] = { +	"Fast (fs/2)", "Slow (fs/32)" +}; + +static const struct soc_enum soft_mute = +	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 10, 2, soft_mute_text); + +static const char *mute_mode_text[] = { +	"Hard", "Soft" +}; + +static const struct soc_enum mute_mode = +	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 9, 2, mute_mode_text); + +static const char *dac_deemphasis_text[] = { +	"Disabled", "32kHz", "44.1kHz", "48kHz" +}; + +static const struct soc_enum dac_deemphasis = +	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_1, 1, 4, dac_deemphasis_text); + +static const char *companding_text[] = { +	"ulaw", "alaw" +}; + +static const struct soc_enum dac_companding = +	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 0, 2, companding_text); + +static const struct soc_enum adc_companding = +	SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 2, 2, companding_text); + +static const char *input_mode_text[] = { +	"Single-Ended", "Differential Line", "Differential Mic" +}; + +static const struct soc_enum linput_mode_enum = +	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text); + +static const struct soc_enum rinput_mode_enum = +	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text); + +static const char *linput_mux_text[] = { +	"IN1L", "IN2L", "IN3L" +}; + +static const struct soc_enum linput_enum = +	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 2, 3, linput_mux_text); + +static const struct soc_enum linput_inv_enum = +	SOC_ENUM_SINGLE(WM8903_ANALOGUE_LEFT_INPUT_1, 4, 3, linput_mux_text); + +static const char *rinput_mux_text[] = { +	"IN1R", "IN2R", "IN3R" +}; + +static const struct soc_enum rinput_enum = +	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 2, 3, rinput_mux_text); + +static const struct soc_enum rinput_inv_enum = +	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text); + + +static const struct snd_kcontrol_new wm8903_snd_controls[] = { + +/* Input PGAs - No TLV since the scale depends on PGA mode */ +SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0, +	   7, 1, 0), +SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0, +	   0, 31, 0), +SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1, +	   6, 1, 0), + +SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0, +	   7, 1, 0), +SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0, +	   0, 31, 0), +SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, +	   6, 1, 0), + +/* ADCs */ +SOC_SINGLE("DRC Switch", WM8903_DRC_0, 15, 1, 0), +SOC_ENUM("DRC Compressor Slope R0", drc_slope_r0), +SOC_ENUM("DRC Compressor Slope R1", drc_slope_r1), +SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8903_DRC_3, 5, 124, 1, +	       drc_tlv_thresh), +SOC_SINGLE_TLV("DRC Volume", WM8903_DRC_3, 0, 30, 1, drc_tlv_amp), +SOC_SINGLE_TLV("DRC Minimum Gain Volume", WM8903_DRC_1, 2, 3, 1, drc_tlv_min), +SOC_SINGLE_TLV("DRC Maximum Gain Volume", WM8903_DRC_1, 0, 3, 0, drc_tlv_max), +SOC_ENUM("DRC Attack Rate", drc_attack), +SOC_ENUM("DRC Decay Rate", drc_decay), +SOC_ENUM("DRC FF Delay", drc_ff_delay), +SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0), +SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0), +SOC_SINGLE_TLV("DRC QR Threashold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max), +SOC_ENUM("DRC QR Decay Rate", drc_qr_decay), +SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0), +SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0), +SOC_ENUM("DRC Smoothing Threashold", drc_smoothing), +SOC_SINGLE_TLV("DRC Startup Volume", WM8903_DRC_0, 6, 18, 0, drc_tlv_startup), + +SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8903_ADC_DIGITAL_VOLUME_LEFT, +		 WM8903_ADC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv), +SOC_ENUM("ADC Companding Mode", adc_companding), +SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0), + +/* DAC */ +SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT, +		 WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv), +SOC_ENUM("DAC Soft Mute Rate", soft_mute), +SOC_ENUM("DAC Mute Mode", mute_mode), +SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0), +SOC_ENUM("DAC De-emphasis", dac_deemphasis), +SOC_SINGLE("DAC Sloping Stopband Filter Switch", +	   WM8903_DAC_DIGITAL_1, 11, 1, 0), +SOC_ENUM("DAC Companding Mode", dac_companding), +SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0), + +/* Headphones */ +SOC_DOUBLE_R("Headphone Switch", +	     WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, +	     8, 1, 1), +SOC_DOUBLE_R("Headphone ZC Switch", +	     WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, +	     6, 1, 0), +SOC_DOUBLE_R_TLV("Headphone Volume", +		 WM8903_ANALOGUE_OUT1_LEFT, WM8903_ANALOGUE_OUT1_RIGHT, +		 0, 63, 0, out_tlv), + +/* Line out */ +SOC_DOUBLE_R("Line Out Switch", +	     WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, +	     8, 1, 1), +SOC_DOUBLE_R("Line Out ZC Switch", +	     WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, +	     6, 1, 0), +SOC_DOUBLE_R_TLV("Line Out Volume", +		 WM8903_ANALOGUE_OUT2_LEFT, WM8903_ANALOGUE_OUT2_RIGHT, +		 0, 63, 0, out_tlv), + +/* Speaker */ +SOC_DOUBLE_R("Speaker Switch", +	     WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 8, 1, 1), +SOC_DOUBLE_R("Speaker ZC Switch", +	     WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, 6, 1, 0), +SOC_DOUBLE_R_TLV("Speaker Volume", +		 WM8903_ANALOGUE_OUT3_LEFT, WM8903_ANALOGUE_OUT3_RIGHT, +		 0, 63, 0, out_tlv), +}; + +static int wm8903_add_controls(struct snd_soc_codec *codec) +{ +	int err, i; + +	for (i = 0; i < ARRAY_SIZE(wm8903_snd_controls); i++) { +		err = snd_ctl_add(codec->card, +				  snd_soc_cnew(&wm8903_snd_controls[i], +					       codec, NULL)); +		if (err < 0) +			return err; +	} + +	return 0; +} + +static const struct snd_kcontrol_new linput_mode_mux = +	SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum); + +static const struct snd_kcontrol_new rinput_mode_mux = +	SOC_DAPM_ENUM("Right Input Mode Mux", rinput_mode_enum); + +static const struct snd_kcontrol_new linput_mux = +	SOC_DAPM_ENUM("Left Input Mux", linput_enum); + +static const struct snd_kcontrol_new linput_inv_mux = +	SOC_DAPM_ENUM("Left Inverting Input Mux", linput_inv_enum); + +static const struct snd_kcontrol_new rinput_mux = +	SOC_DAPM_ENUM("Right Input Mux", rinput_enum); + +static const struct snd_kcontrol_new rinput_inv_mux = +	SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum); + +static const struct snd_kcontrol_new left_output_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0), +SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0), +SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_LEFT_MIX_0, 1, 1, 0), +}; + +static const struct snd_kcontrol_new right_output_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 2, 1, 0), +SOC_DAPM_SINGLE_W("Left Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0), +SOC_DAPM_SINGLE_W("Right Bypass Switch", WM8903_ANALOGUE_RIGHT_MIX_0, 1, 1, 0), +}; + +static const struct snd_kcontrol_new left_speaker_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 2, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, 1, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_LEFT_0, +		1, 1, 0), +}; + +static const struct snd_kcontrol_new right_speaker_mixer[] = { +SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 3, 1, 0), +SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, 2, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, +		1, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8903_ANALOGUE_SPK_MIX_RIGHT_0, +		1, 1, 0), +}; + +static const struct snd_soc_dapm_widget wm8903_dapm_widgets[] = { +SND_SOC_DAPM_INPUT("IN1L"), +SND_SOC_DAPM_INPUT("IN1R"), +SND_SOC_DAPM_INPUT("IN2L"), +SND_SOC_DAPM_INPUT("IN2R"), +SND_SOC_DAPM_INPUT("IN3L"), +SND_SOC_DAPM_INPUT("IN3R"), + +SND_SOC_DAPM_OUTPUT("HPOUTL"), +SND_SOC_DAPM_OUTPUT("HPOUTR"), +SND_SOC_DAPM_OUTPUT("LINEOUTL"), +SND_SOC_DAPM_OUTPUT("LINEOUTR"), +SND_SOC_DAPM_OUTPUT("LOP"), +SND_SOC_DAPM_OUTPUT("LON"), +SND_SOC_DAPM_OUTPUT("ROP"), +SND_SOC_DAPM_OUTPUT("RON"), + +SND_SOC_DAPM_MICBIAS("Mic Bias", WM8903_MIC_BIAS_CONTROL_0, 0, 0), + +SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &linput_mux), +SND_SOC_DAPM_MUX("Left Input Inverting Mux", SND_SOC_NOPM, 0, 0, +		 &linput_inv_mux), +SND_SOC_DAPM_MUX("Left Input Mode Mux", SND_SOC_NOPM, 0, 0, &linput_mode_mux), + +SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &rinput_mux), +SND_SOC_DAPM_MUX("Right Input Inverting Mux", SND_SOC_NOPM, 0, 0, +		 &rinput_inv_mux), +SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux), + +SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0), +SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0), + +SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0), +SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0), + +SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0), +SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0), + +SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0, +		   left_output_mixer, ARRAY_SIZE(left_output_mixer)), +SND_SOC_DAPM_MIXER("Right Output Mixer", WM8903_POWER_MANAGEMENT_1, 0, 0, +		   right_output_mixer, ARRAY_SIZE(right_output_mixer)), + +SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0, +		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)), +SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0, +		   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)), + +SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, +		   1, 0, NULL, 0, wm8903_output_event, +		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | +		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2, +		   0, 0, NULL, 0, wm8903_output_event, +		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | +		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0, +		   NULL, 0, wm8903_output_event, +		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | +		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0, +		   NULL, 0, wm8903_output_event, +		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | +		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0, +		 NULL, 0), +SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0, +		 NULL, 0), + +}; + +static const struct snd_soc_dapm_route intercon[] = { + +	{ "Left Input Mux", "IN1L", "IN1L" }, +	{ "Left Input Mux", "IN2L", "IN2L" }, +	{ "Left Input Mux", "IN3L", "IN3L" }, + +	{ "Left Input Inverting Mux", "IN1L", "IN1L" }, +	{ "Left Input Inverting Mux", "IN2L", "IN2L" }, +	{ "Left Input Inverting Mux", "IN3L", "IN3L" }, + +	{ "Right Input Mux", "IN1R", "IN1R" }, +	{ "Right Input Mux", "IN2R", "IN2R" }, +	{ "Right Input Mux", "IN3R", "IN3R" }, + +	{ "Right Input Inverting Mux", "IN1R", "IN1R" }, +	{ "Right Input Inverting Mux", "IN2R", "IN2R" }, +	{ "Right Input Inverting Mux", "IN3R", "IN3R" }, + +	{ "Left Input Mode Mux", "Single-Ended", "Left Input Inverting Mux" }, +	{ "Left Input Mode Mux", "Differential Line", +	  "Left Input Mux" }, +	{ "Left Input Mode Mux", "Differential Line", +	  "Left Input Inverting Mux" }, +	{ "Left Input Mode Mux", "Differential Mic", +	  "Left Input Mux" }, +	{ "Left Input Mode Mux", "Differential Mic", +	  "Left Input Inverting Mux" }, + +	{ "Right Input Mode Mux", "Single-Ended", +	  "Right Input Inverting Mux" }, +	{ "Right Input Mode Mux", "Differential Line", +	  "Right Input Mux" }, +	{ "Right Input Mode Mux", "Differential Line", +	  "Right Input Inverting Mux" }, +	{ "Right Input Mode Mux", "Differential Mic", +	  "Right Input Mux" }, +	{ "Right Input Mode Mux", "Differential Mic", +	  "Right Input Inverting Mux" }, + +	{ "Left Input PGA", NULL, "Left Input Mode Mux" }, +	{ "Right Input PGA", NULL, "Right Input Mode Mux" }, + +	{ "ADCL", NULL, "Left Input PGA" }, +	{ "ADCR", NULL, "Right Input PGA" }, + +	{ "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" }, +	{ "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" }, +	{ "Left Output Mixer", "DACL Switch", "DACL" }, +	{ "Left Output Mixer", "DACR Switch", "DACR" }, + +	{ "Right Output Mixer", "Left Bypass Switch", "Left Input PGA" }, +	{ "Right Output Mixer", "Right Bypass Switch", "Right Input PGA" }, +	{ "Right Output Mixer", "DACL Switch", "DACL" }, +	{ "Right Output Mixer", "DACR Switch", "DACR" }, + +	{ "Left Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, +	{ "Left Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, +	{ "Left Speaker Mixer", "DACL Switch", "DACL" }, +	{ "Left Speaker Mixer", "DACR Switch", "DACR" }, + +	{ "Right Speaker Mixer", "Left Bypass Switch", "Left Input PGA" }, +	{ "Right Speaker Mixer", "Right Bypass Switch", "Right Input PGA" }, +	{ "Right Speaker Mixer", "DACL Switch", "DACL" }, +	{ "Right Speaker Mixer", "DACR Switch", "DACR" }, + +	{ "Left Line Output PGA", NULL, "Left Output Mixer" }, +	{ "Right Line Output PGA", NULL, "Right Output Mixer" }, + +	{ "Left Headphone Output PGA", NULL, "Left Output Mixer" }, +	{ "Right Headphone Output PGA", NULL, "Right Output Mixer" }, + +	{ "Left Speaker PGA", NULL, "Left Speaker Mixer" }, +	{ "Right Speaker PGA", NULL, "Right Speaker Mixer" }, + +	{ "HPOUTL", NULL, "Left Headphone Output PGA" }, +	{ "HPOUTR", NULL, "Right Headphone Output PGA" }, + +	{ "LINEOUTL", NULL, "Left Line Output PGA" }, +	{ "LINEOUTR", NULL, "Right Line Output PGA" }, + +	{ "LOP", NULL, "Left Speaker PGA" }, +	{ "LON", NULL, "Left Speaker PGA" }, + +	{ "ROP", NULL, "Right Speaker PGA" }, +	{ "RON", NULL, "Right Speaker PGA" }, +}; + +static int wm8903_add_widgets(struct snd_soc_codec *codec) +{ +	snd_soc_dapm_new_controls(codec, wm8903_dapm_widgets, +				  ARRAY_SIZE(wm8903_dapm_widgets)); + +	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + +	snd_soc_dapm_new_widgets(codec); + +	return 0; +} + +static int wm8903_set_bias_level(struct snd_soc_codec *codec, +				 enum snd_soc_bias_level level) +{ +	struct i2c_client *i2c = codec->control_data; +	u16 reg, reg2; + +	switch (level) { +	case SND_SOC_BIAS_ON: +	case SND_SOC_BIAS_PREPARE: +		reg = wm8903_read(codec, WM8903_VMID_CONTROL_0); +		reg &= ~(WM8903_VMID_RES_MASK); +		reg |= WM8903_VMID_RES_50K; +		wm8903_write(codec, WM8903_VMID_CONTROL_0, reg); +		break; + +	case SND_SOC_BIAS_STANDBY: +		if (codec->bias_level == SND_SOC_BIAS_OFF) { +			wm8903_run_sequence(codec, 0); +			wm8903_sync_reg_cache(codec, codec->reg_cache); + +			/* Enable low impedence charge pump output */ +			reg = wm8903_read(codec, +					  WM8903_CONTROL_INTERFACE_TEST_1); +			wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, +				     reg | WM8903_TEST_KEY); +			reg2 = wm8903_read(codec, WM8903_CHARGE_PUMP_TEST_1); +			wm8903_write(codec, WM8903_CHARGE_PUMP_TEST_1, +				     reg2 | WM8903_CP_SW_KELVIN_MODE_MASK); +			wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1, +				     reg); + +			/* By default no bypass paths are enabled so +			 * enable Class W support. +			 */ +			dev_dbg(&i2c->dev, "Enabling Class W\n"); +			wm8903_write(codec, WM8903_CLASS_W_0, reg | +				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V); +		} + +		reg = wm8903_read(codec, WM8903_VMID_CONTROL_0); +		reg &= ~(WM8903_VMID_RES_MASK); +		reg |= WM8903_VMID_RES_250K; +		wm8903_write(codec, WM8903_VMID_CONTROL_0, reg); +		break; + +	case SND_SOC_BIAS_OFF: +		wm8903_run_sequence(codec, 32); +		break; +	} + +	codec->bias_level = level; + +	return 0; +} + +static int wm8903_set_dai_sysclk(struct snd_soc_dai *codec_dai, +				 int clk_id, unsigned int freq, int dir) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct wm8903_priv *wm8903 = codec->private_data; + +	wm8903->sysclk = freq; + +	return 0; +} + +static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai, +			      unsigned int fmt) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1); + +	aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK | +		  WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV); + +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBS_CFS: +		break; +	case SND_SOC_DAIFMT_CBS_CFM: +		aif1 |= WM8903_LRCLK_DIR; +		break; +	case SND_SOC_DAIFMT_CBM_CFM: +		aif1 |= WM8903_LRCLK_DIR | WM8903_BCLK_DIR; +		break; +	case SND_SOC_DAIFMT_CBM_CFS: +		aif1 |= WM8903_BCLK_DIR; +		break; +	default: +		return -EINVAL; +	} + +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_DSP_A: +		aif1 |= 0x3; +		break; +	case SND_SOC_DAIFMT_DSP_B: +		aif1 |= 0x3 | WM8903_AIF_LRCLK_INV; +		break; +	case SND_SOC_DAIFMT_I2S: +		aif1 |= 0x2; +		break; +	case SND_SOC_DAIFMT_RIGHT_J: +		aif1 |= 0x1; +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		break; +	default: +		return -EINVAL; +	} + +	/* Clock inversion */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_DSP_A: +	case SND_SOC_DAIFMT_DSP_B: +		/* frame inversion not valid for DSP modes */ +		switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +		case SND_SOC_DAIFMT_NB_NF: +			break; +		case SND_SOC_DAIFMT_IB_NF: +			aif1 |= WM8903_AIF_BCLK_INV; +			break; +		default: +			return -EINVAL; +		} +		break; +	case SND_SOC_DAIFMT_I2S: +	case SND_SOC_DAIFMT_RIGHT_J: +	case SND_SOC_DAIFMT_LEFT_J: +		switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +		case SND_SOC_DAIFMT_NB_NF: +			break; +		case SND_SOC_DAIFMT_IB_IF: +			aif1 |= WM8903_AIF_BCLK_INV | WM8903_AIF_LRCLK_INV; +			break; +		case SND_SOC_DAIFMT_IB_NF: +			aif1 |= WM8903_AIF_BCLK_INV; +			break; +		case SND_SOC_DAIFMT_NB_IF: +			aif1 |= WM8903_AIF_LRCLK_INV; +			break; +		default: +			return -EINVAL; +		} +		break; +	default: +		return -EINVAL; +	} + +	wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); + +	return 0; +} + +static int wm8903_digital_mute(struct snd_soc_dai *codec_dai, int mute) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	u16 reg; + +	reg = wm8903_read(codec, WM8903_DAC_DIGITAL_1); + +	if (mute) +		reg |= WM8903_DAC_MUTE; +	else +		reg &= ~WM8903_DAC_MUTE; + +	wm8903_write(codec, WM8903_DAC_DIGITAL_1, reg); + +	return 0; +} + +/* Lookup table for CLK_SYS/fs ratio.  256fs or more is recommended + * for optimal performance so we list the lower rates first and match + * on the last match we find. */ +static struct { +	int div; +	int rate; +	int mode; +	int mclk_div; +} clk_sys_ratios[] = { +	{   64, 0x0, 0x0, 1 }, +	{   68, 0x0, 0x1, 1 }, +	{  125, 0x0, 0x2, 1 }, +	{  128, 0x1, 0x0, 1 }, +	{  136, 0x1, 0x1, 1 }, +	{  192, 0x2, 0x0, 1 }, +	{  204, 0x2, 0x1, 1 }, + +	{   64, 0x0, 0x0, 2 }, +	{   68, 0x0, 0x1, 2 }, +	{  125, 0x0, 0x2, 2 }, +	{  128, 0x1, 0x0, 2 }, +	{  136, 0x1, 0x1, 2 }, +	{  192, 0x2, 0x0, 2 }, +	{  204, 0x2, 0x1, 2 }, + +	{  250, 0x2, 0x2, 1 }, +	{  256, 0x3, 0x0, 1 }, +	{  272, 0x3, 0x1, 1 }, +	{  384, 0x4, 0x0, 1 }, +	{  408, 0x4, 0x1, 1 }, +	{  375, 0x4, 0x2, 1 }, +	{  512, 0x5, 0x0, 1 }, +	{  544, 0x5, 0x1, 1 }, +	{  500, 0x5, 0x2, 1 }, +	{  768, 0x6, 0x0, 1 }, +	{  816, 0x6, 0x1, 1 }, +	{  750, 0x6, 0x2, 1 }, +	{ 1024, 0x7, 0x0, 1 }, +	{ 1088, 0x7, 0x1, 1 }, +	{ 1000, 0x7, 0x2, 1 }, +	{ 1408, 0x8, 0x0, 1 }, +	{ 1496, 0x8, 0x1, 1 }, +	{ 1536, 0x9, 0x0, 1 }, +	{ 1632, 0x9, 0x1, 1 }, +	{ 1500, 0x9, 0x2, 1 }, + +	{  250, 0x2, 0x2, 2 }, +	{  256, 0x3, 0x0, 2 }, +	{  272, 0x3, 0x1, 2 }, +	{  384, 0x4, 0x0, 2 }, +	{  408, 0x4, 0x1, 2 }, +	{  375, 0x4, 0x2, 2 }, +	{  512, 0x5, 0x0, 2 }, +	{  544, 0x5, 0x1, 2 }, +	{  500, 0x5, 0x2, 2 }, +	{  768, 0x6, 0x0, 2 }, +	{  816, 0x6, 0x1, 2 }, +	{  750, 0x6, 0x2, 2 }, +	{ 1024, 0x7, 0x0, 2 }, +	{ 1088, 0x7, 0x1, 2 }, +	{ 1000, 0x7, 0x2, 2 }, +	{ 1408, 0x8, 0x0, 2 }, +	{ 1496, 0x8, 0x1, 2 }, +	{ 1536, 0x9, 0x0, 2 }, +	{ 1632, 0x9, 0x1, 2 }, +	{ 1500, 0x9, 0x2, 2 }, +}; + +/* CLK_SYS/BCLK ratios - multiplied by 10 due to .5s */ +static struct { +	int ratio; +	int div; +} bclk_divs[] = { +	{  10,  0 }, +	{  15,  1 }, +	{  20,  2 }, +	{  30,  3 }, +	{  40,  4 }, +	{  50,  5 }, +	{  55,  6 }, +	{  60,  7 }, +	{  80,  8 }, +	{ 100,  9 }, +	{ 110, 10 }, +	{ 120, 11 }, +	{ 160, 12 }, +	{ 200, 13 }, +	{ 220, 14 }, +	{ 240, 15 }, +	{ 250, 16 }, +	{ 300, 17 }, +	{ 320, 18 }, +	{ 440, 19 }, +	{ 480, 20 }, +}; + +/* Sample rates for DSP */ +static struct { +	int rate; +	int value; +} sample_rates[] = { +	{  8000,  0 }, +	{ 11025,  1 }, +	{ 12000,  2 }, +	{ 16000,  3 }, +	{ 22050,  4 }, +	{ 24000,  5 }, +	{ 32000,  6 }, +	{ 44100,  7 }, +	{ 48000,  8 }, +	{ 88200,  9 }, +	{ 96000, 10 }, +	{ 0,      0 }, +}; + +static int wm8903_startup(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	struct wm8903_priv *wm8903 = codec->private_data; +	struct i2c_client *i2c = codec->control_data; +	struct snd_pcm_runtime *master_runtime; + +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		wm8903->playback_active++; +	else +		wm8903->capture_active++; + +	/* The DAI has shared clocks so if we already have a playback or +	 * capture going then constrain this substream to match it. +	 */ +	if (wm8903->master_substream) { +		master_runtime = wm8903->master_substream->runtime; + +		dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n", +			master_runtime->sample_bits, +			master_runtime->rate); + +		snd_pcm_hw_constraint_minmax(substream->runtime, +					     SNDRV_PCM_HW_PARAM_RATE, +					     master_runtime->rate, +					     master_runtime->rate); + +		snd_pcm_hw_constraint_minmax(substream->runtime, +					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS, +					     master_runtime->sample_bits, +					     master_runtime->sample_bits); + +		wm8903->slave_substream = substream; +	} else +		wm8903->master_substream = substream; + +	return 0; +} + +static void wm8903_shutdown(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	struct wm8903_priv *wm8903 = codec->private_data; + +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		wm8903->playback_active--; +	else +		wm8903->capture_active--; + +	if (wm8903->master_substream == substream) +		wm8903->master_substream = wm8903->slave_substream; + +	wm8903->slave_substream = NULL; +} + +static int wm8903_hw_params(struct snd_pcm_substream *substream, +			    struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	struct wm8903_priv *wm8903 = codec->private_data; +	struct i2c_client *i2c = codec->control_data; +	int fs = params_rate(params); +	int bclk; +	int bclk_div; +	int i; +	int dsp_config; +	int clk_config; +	int best_val; +	int cur_val; +	int clk_sys; + +	u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1); +	u16 aif2 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_2); +	u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3); +	u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0); +	u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1); + +	if (substream == wm8903->slave_substream) { +		dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n"); +		return 0; +	} + +	/* Configure sample rate logic for DSP - choose nearest rate */ +	dsp_config = 0; +	best_val = abs(sample_rates[dsp_config].rate - fs); +	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) { +		cur_val = abs(sample_rates[i].rate - fs); +		if (cur_val <= best_val) { +			dsp_config = i; +			best_val = cur_val; +		} +	} + +	/* Constraints should stop us hitting this but let's make sure */ +	if (wm8903->capture_active) +		switch (sample_rates[dsp_config].rate) { +		case 88200: +		case 96000: +			dev_err(&i2c->dev, "%dHz unsupported by ADC\n", +				fs); +			return -EINVAL; + +		default: +			break; +		} + +	dev_dbg(&i2c->dev, "DSP fs = %dHz\n", sample_rates[dsp_config].rate); +	clock1 &= ~WM8903_SAMPLE_RATE_MASK; +	clock1 |= sample_rates[dsp_config].value; + +	aif1 &= ~WM8903_AIF_WL_MASK; +	bclk = 2 * fs; +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S16_LE: +		bclk *= 16; +		break; +	case SNDRV_PCM_FORMAT_S20_3LE: +		bclk *= 20; +		aif1 |= 0x4; +		break; +	case SNDRV_PCM_FORMAT_S24_LE: +		bclk *= 24; +		aif1 |= 0x8; +		break; +	case SNDRV_PCM_FORMAT_S32_LE: +		bclk *= 32; +		aif1 |= 0xc; +		break; +	default: +		return -EINVAL; +	} + +	dev_dbg(&i2c->dev, "MCLK = %dHz, target sample rate = %dHz\n", +		wm8903->sysclk, fs); + +	/* We may not have an MCLK which allows us to generate exactly +	 * the clock we want, particularly with USB derived inputs, so +	 * approximate. +	 */ +	clk_config = 0; +	best_val = abs((wm8903->sysclk / +			(clk_sys_ratios[0].mclk_div * +			 clk_sys_ratios[0].div)) - fs); +	for (i = 1; i < ARRAY_SIZE(clk_sys_ratios); i++) { +		cur_val = abs((wm8903->sysclk / +			       (clk_sys_ratios[i].mclk_div * +				clk_sys_ratios[i].div)) - fs); + +		if (cur_val <= best_val) { +			clk_config = i; +			best_val = cur_val; +		} +	} + +	if (clk_sys_ratios[clk_config].mclk_div == 2) { +		clock0 |= WM8903_MCLKDIV2; +		clk_sys = wm8903->sysclk / 2; +	} else { +		clock0 &= ~WM8903_MCLKDIV2; +		clk_sys = wm8903->sysclk; +	} + +	clock1 &= ~(WM8903_CLK_SYS_RATE_MASK | +		    WM8903_CLK_SYS_MODE_MASK); +	clock1 |= clk_sys_ratios[clk_config].rate << WM8903_CLK_SYS_RATE_SHIFT; +	clock1 |= clk_sys_ratios[clk_config].mode << WM8903_CLK_SYS_MODE_SHIFT; + +	dev_dbg(&i2c->dev, "CLK_SYS_RATE=%x, CLK_SYS_MODE=%x div=%d\n", +		clk_sys_ratios[clk_config].rate, +		clk_sys_ratios[clk_config].mode, +		clk_sys_ratios[clk_config].div); + +	dev_dbg(&i2c->dev, "Actual CLK_SYS = %dHz\n", clk_sys); + +	/* We may not get quite the right frequency if using +	 * approximate clocks so look for the closest match that is +	 * higher than the target (we need to ensure that there enough +	 * BCLKs to clock out the samples). +	 */ +	bclk_div = 0; +	best_val = ((clk_sys * 10) / bclk_divs[0].ratio) - bclk; +	i = 1; +	while (i < ARRAY_SIZE(bclk_divs)) { +		cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk; +		if (cur_val < 0) /* BCLK table is sorted */ +			break; +		bclk_div = i; +		best_val = cur_val; +		i++; +	} + +	aif2 &= ~WM8903_BCLK_DIV_MASK; +	aif3 &= ~WM8903_LRCLK_RATE_MASK; + +	dev_dbg(&i2c->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n", +		bclk_divs[bclk_div].ratio / 10, bclk, +		(clk_sys * 10) / bclk_divs[bclk_div].ratio); + +	aif2 |= bclk_divs[bclk_div].div; +	aif3 |= bclk / fs; + +	wm8903_write(codec, WM8903_CLOCK_RATES_0, clock0); +	wm8903_write(codec, WM8903_CLOCK_RATES_1, clock1); +	wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1); +	wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2); +	wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3); + +	return 0; +} + +#define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ +			       SNDRV_PCM_RATE_11025 |	\ +			       SNDRV_PCM_RATE_16000 |	\ +			       SNDRV_PCM_RATE_22050 |	\ +			       SNDRV_PCM_RATE_32000 |	\ +			       SNDRV_PCM_RATE_44100 |	\ +			       SNDRV_PCM_RATE_48000 |	\ +			       SNDRV_PCM_RATE_88200 |	\ +			       SNDRV_PCM_RATE_96000) + +#define WM8903_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\ +			      SNDRV_PCM_RATE_11025 |	\ +			      SNDRV_PCM_RATE_16000 |	\ +			      SNDRV_PCM_RATE_22050 |	\ +			      SNDRV_PCM_RATE_32000 |	\ +			      SNDRV_PCM_RATE_44100 |	\ +			      SNDRV_PCM_RATE_48000) + +#define WM8903_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ +			SNDRV_PCM_FMTBIT_S20_3LE |\ +			SNDRV_PCM_FMTBIT_S24_LE) + +struct snd_soc_dai wm8903_dai = { +	.name = "WM8903", +	.playback = { +		.stream_name = "Playback", +		.channels_min = 2, +		.channels_max = 2, +		.rates = WM8903_PLAYBACK_RATES, +		.formats = WM8903_FORMATS, +	}, +	.capture = { +		 .stream_name = "Capture", +		 .channels_min = 2, +		 .channels_max = 2, +		 .rates = WM8903_CAPTURE_RATES, +		 .formats = WM8903_FORMATS, +	 }, +	.ops = { +		 .startup = wm8903_startup, +		 .shutdown = wm8903_shutdown, +		 .hw_params = wm8903_hw_params, +	}, +	.dai_ops = { +		 .digital_mute = wm8903_digital_mute, +		 .set_fmt = wm8903_set_dai_fmt, +		 .set_sysclk = wm8903_set_dai_sysclk +	} +}; +EXPORT_SYMBOL_GPL(wm8903_dai); + +static int wm8903_suspend(struct platform_device *pdev, pm_message_t state) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); + +	return 0; +} + +static int wm8903_resume(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; +	struct i2c_client *i2c = codec->control_data; +	int i; +	u16 *reg_cache = codec->reg_cache; +	u16 *tmp_cache = kmemdup(codec->reg_cache, sizeof(wm8903_reg_defaults), +				 GFP_KERNEL); + +	/* Bring the codec back up to standby first to minimise pop/clicks */ +	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); +	wm8903_set_bias_level(codec, codec->suspend_bias_level); + +	/* Sync back everything else */ +	if (tmp_cache) { +		for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++) +			if (tmp_cache[i] != reg_cache[i]) +				wm8903_write(codec, i, tmp_cache[i]); +	} else { +		dev_err(&i2c->dev, "Failed to allocate temporary cache\n"); +	} + +	return 0; +} + +/* + * initialise the WM8903 driver + * register the mixer and dsp interfaces with the kernel + */ +static int wm8903_init(struct snd_soc_device *socdev) +{ +	struct snd_soc_codec *codec = socdev->codec; +	struct i2c_client *i2c = codec->control_data; +	int ret = 0; +	u16 val; + +	val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID); +	if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) { +		dev_err(&i2c->dev, +			"Device with ID register %x is not a WM8903\n", val); +		return -ENODEV; +	} + +	codec->name = "WM8903"; +	codec->owner = THIS_MODULE; +	codec->read = wm8903_read; +	codec->write = wm8903_write; +	codec->bias_level = SND_SOC_BIAS_OFF; +	codec->set_bias_level = wm8903_set_bias_level; +	codec->dai = &wm8903_dai; +	codec->num_dai = 1; +	codec->reg_cache_size = ARRAY_SIZE(wm8903_reg_defaults); +	codec->reg_cache = kmemdup(wm8903_reg_defaults, +				   sizeof(wm8903_reg_defaults), +				   GFP_KERNEL); +	if (codec->reg_cache == NULL) { +		dev_err(&i2c->dev, "Failed to allocate register cache\n"); +		return -ENOMEM; +	} + +	val = wm8903_read(codec, WM8903_REVISION_NUMBER); +	dev_info(&i2c->dev, "WM8903 revision %d\n", +		 val & WM8903_CHIP_REV_MASK); + +	wm8903_reset(codec); + +	/* register pcms */ +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); +	if (ret < 0) { +		dev_err(&i2c->dev, "failed to create pcms\n"); +		goto pcm_err; +	} + +	/* SYSCLK is required for pretty much anything */ +	wm8903_write(codec, WM8903_CLOCK_RATES_2, WM8903_CLK_SYS_ENA); + +	/* power on device */ +	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + +	/* Latch volume update bits */ +	val = wm8903_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT); +	val |= WM8903_ADCVU; +	wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val); +	wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val); + +	val = wm8903_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT); +	val |= WM8903_DACVU; +	wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val); +	wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val); + +	val = wm8903_read(codec, WM8903_ANALOGUE_OUT1_LEFT); +	val |= WM8903_HPOUTVU; +	wm8903_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val); +	wm8903_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val); + +	val = wm8903_read(codec, WM8903_ANALOGUE_OUT2_LEFT); +	val |= WM8903_LINEOUTVU; +	wm8903_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val); +	wm8903_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val); + +	val = wm8903_read(codec, WM8903_ANALOGUE_OUT3_LEFT); +	val |= WM8903_SPKVU; +	wm8903_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val); +	wm8903_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val); + +	/* Enable DAC soft mute by default */ +	val = wm8903_read(codec, WM8903_DAC_DIGITAL_1); +	val |= WM8903_DAC_MUTEMODE; +	wm8903_write(codec, WM8903_DAC_DIGITAL_1, val); + +	wm8903_add_controls(codec); +	wm8903_add_widgets(codec); +	ret = snd_soc_register_card(socdev); +	if (ret < 0) { +		dev_err(&i2c->dev, "wm8903: failed to register card\n"); +		goto card_err; +	} + +	return ret; + +card_err: +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +pcm_err: +	kfree(codec->reg_cache); +	return ret; +} + +static struct snd_soc_device *wm8903_socdev; + +static int wm8903_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id) +{ +	struct snd_soc_device *socdev = wm8903_socdev; +	struct snd_soc_codec *codec = socdev->codec; +	int ret; + +	i2c_set_clientdata(i2c, codec); +	codec->control_data = i2c; + +	ret = wm8903_init(socdev); +	if (ret < 0) +		dev_err(&i2c->dev, "Device initialisation failed\n"); + +	return ret; +} + +static int wm8903_i2c_remove(struct i2c_client *client) +{ +	struct snd_soc_codec *codec = i2c_get_clientdata(client); +	kfree(codec->reg_cache); +	return 0; +} + +/* i2c codec control layer */ +static const struct i2c_device_id wm8903_i2c_id[] = { +       { "wm8903", 0 }, +       { } +}; +MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); + +static struct i2c_driver wm8903_i2c_driver = { +	.driver = { +		.name = "WM8903", +		.owner = THIS_MODULE, +	}, +	.probe    = wm8903_i2c_probe, +	.remove   = wm8903_i2c_remove, +	.id_table = wm8903_i2c_id, +}; + +static int wm8903_probe(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct wm8903_setup_data *setup; +	struct snd_soc_codec *codec; +	struct wm8903_priv *wm8903; +	struct i2c_board_info board_info; +	struct i2c_adapter *adapter; +	struct i2c_client *i2c_client; +	int ret = 0; + +	setup = socdev->codec_data; + +	if (!setup->i2c_address) { +		dev_err(&pdev->dev, "No codec address provided\n"); +		return -ENODEV; +	} + +	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); +	if (codec == NULL) +		return -ENOMEM; + +	wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL); +	if (wm8903 == NULL) { +		ret = -ENOMEM; +		goto err_codec; +	} + +	codec->private_data = wm8903; +	socdev->codec = codec; +	mutex_init(&codec->mutex); +	INIT_LIST_HEAD(&codec->dapm_widgets); +	INIT_LIST_HEAD(&codec->dapm_paths); + +	wm8903_socdev = socdev; + +	codec->hw_write = (hw_write_t)i2c_master_send; +	ret = i2c_add_driver(&wm8903_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		goto err_priv; +	} else { +		memset(&board_info, 0, sizeof(board_info)); +		strlcpy(board_info.type, "wm8903", I2C_NAME_SIZE); +		board_info.addr = setup->i2c_address; + +		adapter = i2c_get_adapter(setup->i2c_bus); +		if (!adapter) { +			dev_err(&pdev->dev, "Can't get I2C bus %d\n", +				setup->i2c_bus); +			ret = -ENODEV; +			goto err_adapter; +		} + +		i2c_client = i2c_new_device(adapter, &board_info); +		i2c_put_adapter(adapter); +		if (i2c_client == NULL) { +			dev_err(&pdev->dev, +				"I2C driver registration failed\n"); +			ret = -ENODEV; +			goto err_adapter; +		} +	} + +	return ret; + +err_adapter: +	i2c_del_driver(&wm8903_i2c_driver); +err_priv: +	kfree(codec->private_data); +err_codec: +	kfree(codec); +	return ret; +} + +/* power down chip */ +static int wm8903_remove(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	if (codec->control_data) +		wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); + +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +	i2c_unregister_device(socdev->codec->control_data); +	i2c_del_driver(&wm8903_i2c_driver); +	kfree(codec->private_data); +	kfree(codec); + +	return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8903 = { +	.probe = 	wm8903_probe, +	.remove = 	wm8903_remove, +	.suspend = 	wm8903_suspend, +	.resume =	wm8903_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8903); + +MODULE_DESCRIPTION("ASoC WM8903 driver"); +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.cm>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h new file mode 100644 index 0000000..cec622f --- /dev/null +++ b/sound/soc/codecs/wm8903.h @@ -0,0 +1,1463 @@ +/* + * wm8903.h - WM8903 audio codec interface + * + * Copyright 2008 Wolfson Microelectronics PLC. + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + */ + +#ifndef _WM8903_H +#define _WM8903_H + +#include <linux/i2c.h> + +extern struct snd_soc_dai wm8903_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8903; + +struct wm8903_setup_data { +	int i2c_bus; +	int i2c_address; +}; + +#define WM8903_MCLK_DIV_2 1 +#define WM8903_CLK_SYS    2 +#define WM8903_BCLK       3 +#define WM8903_LRCLK      4 + +/* + * Register values. + */ +#define WM8903_SW_RESET_AND_ID                  0x00 +#define WM8903_REVISION_NUMBER                  0x01 +#define WM8903_BIAS_CONTROL_0                   0x04 +#define WM8903_VMID_CONTROL_0                   0x05 +#define WM8903_MIC_BIAS_CONTROL_0               0x06 +#define WM8903_ANALOGUE_DAC_0                   0x08 +#define WM8903_ANALOGUE_ADC_0                   0x0A +#define WM8903_POWER_MANAGEMENT_0               0x0C +#define WM8903_POWER_MANAGEMENT_1               0x0D +#define WM8903_POWER_MANAGEMENT_2               0x0E +#define WM8903_POWER_MANAGEMENT_3               0x0F +#define WM8903_POWER_MANAGEMENT_4               0x10 +#define WM8903_POWER_MANAGEMENT_5               0x11 +#define WM8903_POWER_MANAGEMENT_6               0x12 +#define WM8903_CLOCK_RATES_0                    0x14 +#define WM8903_CLOCK_RATES_1                    0x15 +#define WM8903_CLOCK_RATES_2                    0x16 +#define WM8903_AUDIO_INTERFACE_0                0x18 +#define WM8903_AUDIO_INTERFACE_1                0x19 +#define WM8903_AUDIO_INTERFACE_2                0x1A +#define WM8903_AUDIO_INTERFACE_3                0x1B +#define WM8903_DAC_DIGITAL_VOLUME_LEFT          0x1E +#define WM8903_DAC_DIGITAL_VOLUME_RIGHT         0x1F +#define WM8903_DAC_DIGITAL_0                    0x20 +#define WM8903_DAC_DIGITAL_1                    0x21 +#define WM8903_ADC_DIGITAL_VOLUME_LEFT          0x24 +#define WM8903_ADC_DIGITAL_VOLUME_RIGHT         0x25 +#define WM8903_ADC_DIGITAL_0                    0x26 +#define WM8903_DIGITAL_MICROPHONE_0             0x27 +#define WM8903_DRC_0                            0x28 +#define WM8903_DRC_1                            0x29 +#define WM8903_DRC_2                            0x2A +#define WM8903_DRC_3                            0x2B +#define WM8903_ANALOGUE_LEFT_INPUT_0            0x2C +#define WM8903_ANALOGUE_RIGHT_INPUT_0           0x2D +#define WM8903_ANALOGUE_LEFT_INPUT_1            0x2E +#define WM8903_ANALOGUE_RIGHT_INPUT_1           0x2F +#define WM8903_ANALOGUE_LEFT_MIX_0              0x32 +#define WM8903_ANALOGUE_RIGHT_MIX_0             0x33 +#define WM8903_ANALOGUE_SPK_MIX_LEFT_0          0x34 +#define WM8903_ANALOGUE_SPK_MIX_LEFT_1          0x35 +#define WM8903_ANALOGUE_SPK_MIX_RIGHT_0         0x36 +#define WM8903_ANALOGUE_SPK_MIX_RIGHT_1         0x37 +#define WM8903_ANALOGUE_OUT1_LEFT               0x39 +#define WM8903_ANALOGUE_OUT1_RIGHT              0x3A +#define WM8903_ANALOGUE_OUT2_LEFT               0x3B +#define WM8903_ANALOGUE_OUT2_RIGHT              0x3C +#define WM8903_ANALOGUE_OUT3_LEFT               0x3E +#define WM8903_ANALOGUE_OUT3_RIGHT              0x3F +#define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0    0x41 +#define WM8903_DC_SERVO_0                       0x43 +#define WM8903_DC_SERVO_2                       0x45 +#define WM8903_ANALOGUE_HP_0                    0x5A +#define WM8903_ANALOGUE_LINEOUT_0               0x5E +#define WM8903_CHARGE_PUMP_0                    0x62 +#define WM8903_CLASS_W_0                        0x68 +#define WM8903_WRITE_SEQUENCER_0                0x6C +#define WM8903_WRITE_SEQUENCER_1                0x6D +#define WM8903_WRITE_SEQUENCER_2                0x6E +#define WM8903_WRITE_SEQUENCER_3                0x6F +#define WM8903_WRITE_SEQUENCER_4                0x70 +#define WM8903_CONTROL_INTERFACE                0x72 +#define WM8903_GPIO_CONTROL_1                   0x74 +#define WM8903_GPIO_CONTROL_2                   0x75 +#define WM8903_GPIO_CONTROL_3                   0x76 +#define WM8903_GPIO_CONTROL_4                   0x77 +#define WM8903_GPIO_CONTROL_5                   0x78 +#define WM8903_INTERRUPT_STATUS_1               0x79 +#define WM8903_INTERRUPT_STATUS_1_MASK          0x7A +#define WM8903_INTERRUPT_POLARITY_1             0x7B +#define WM8903_INTERRUPT_CONTROL                0x7E +#define WM8903_CONTROL_INTERFACE_TEST_1         0x81 +#define WM8903_CHARGE_PUMP_TEST_1               0x95 +#define WM8903_CLOCK_RATE_TEST_4                0xA4 +#define WM8903_ANALOGUE_OUTPUT_BIAS_0           0xAC + +#define WM8903_REGISTER_COUNT                   75 +#define WM8903_MAX_REGISTER                     0xAC + +/* + * Field Definitions. + */ + +/* + * R0 (0x00) - SW Reset and ID + */ +#define WM8903_SW_RESET_DEV_ID1_MASK            0xFFFF  /* SW_RESET_DEV_ID1 - [15:0] */ +#define WM8903_SW_RESET_DEV_ID1_SHIFT                0  /* SW_RESET_DEV_ID1 - [15:0] */ +#define WM8903_SW_RESET_DEV_ID1_WIDTH               16  /* SW_RESET_DEV_ID1 - [15:0] */ + +/* + * R1 (0x01) - Revision Number + */ +#define WM8903_CHIP_REV_MASK                    0x000F  /* CHIP_REV - [3:0] */ +#define WM8903_CHIP_REV_SHIFT                        0  /* CHIP_REV - [3:0] */ +#define WM8903_CHIP_REV_WIDTH                        4  /* CHIP_REV - [3:0] */ + +/* + * R4 (0x04) - Bias Control 0 + */ +#define WM8903_POBCTRL                          0x0010  /* POBCTRL */ +#define WM8903_POBCTRL_MASK                     0x0010  /* POBCTRL */ +#define WM8903_POBCTRL_SHIFT                         4  /* POBCTRL */ +#define WM8903_POBCTRL_WIDTH                         1  /* POBCTRL */ +#define WM8903_ISEL_MASK                        0x000C  /* ISEL - [3:2] */ +#define WM8903_ISEL_SHIFT                            2  /* ISEL - [3:2] */ +#define WM8903_ISEL_WIDTH                            2  /* ISEL - [3:2] */ +#define WM8903_STARTUP_BIAS_ENA                 0x0002  /* STARTUP_BIAS_ENA */ +#define WM8903_STARTUP_BIAS_ENA_MASK            0x0002  /* STARTUP_BIAS_ENA */ +#define WM8903_STARTUP_BIAS_ENA_SHIFT                1  /* STARTUP_BIAS_ENA */ +#define WM8903_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */ +#define WM8903_BIAS_ENA                         0x0001  /* BIAS_ENA */ +#define WM8903_BIAS_ENA_MASK                    0x0001  /* BIAS_ENA */ +#define WM8903_BIAS_ENA_SHIFT                        0  /* BIAS_ENA */ +#define WM8903_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */ + +/* + * R5 (0x05) - VMID Control 0 + */ +#define WM8903_VMID_TIE_ENA                     0x0080  /* VMID_TIE_ENA */ +#define WM8903_VMID_TIE_ENA_MASK                0x0080  /* VMID_TIE_ENA */ +#define WM8903_VMID_TIE_ENA_SHIFT                    7  /* VMID_TIE_ENA */ +#define WM8903_VMID_TIE_ENA_WIDTH                    1  /* VMID_TIE_ENA */ +#define WM8903_BUFIO_ENA                        0x0040  /* BUFIO_ENA */ +#define WM8903_BUFIO_ENA_MASK                   0x0040  /* BUFIO_ENA */ +#define WM8903_BUFIO_ENA_SHIFT                       6  /* BUFIO_ENA */ +#define WM8903_BUFIO_ENA_WIDTH                       1  /* BUFIO_ENA */ +#define WM8903_VMID_IO_ENA                      0x0020  /* VMID_IO_ENA */ +#define WM8903_VMID_IO_ENA_MASK                 0x0020  /* VMID_IO_ENA */ +#define WM8903_VMID_IO_ENA_SHIFT                     5  /* VMID_IO_ENA */ +#define WM8903_VMID_IO_ENA_WIDTH                     1  /* VMID_IO_ENA */ +#define WM8903_VMID_SOFT_MASK                   0x0018  /* VMID_SOFT - [4:3] */ +#define WM8903_VMID_SOFT_SHIFT                       3  /* VMID_SOFT - [4:3] */ +#define WM8903_VMID_SOFT_WIDTH                       2  /* VMID_SOFT - [4:3] */ +#define WM8903_VMID_RES_MASK                    0x0006  /* VMID_RES - [2:1] */ +#define WM8903_VMID_RES_SHIFT                        1  /* VMID_RES - [2:1] */ +#define WM8903_VMID_RES_WIDTH                        2  /* VMID_RES - [2:1] */ +#define WM8903_VMID_BUF_ENA                     0x0001  /* VMID_BUF_ENA */ +#define WM8903_VMID_BUF_ENA_MASK                0x0001  /* VMID_BUF_ENA */ +#define WM8903_VMID_BUF_ENA_SHIFT                    0  /* VMID_BUF_ENA */ +#define WM8903_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */ + +#define WM8903_VMID_RES_50K                          2 +#define WM8903_VMID_RES_250K                         3 +#define WM8903_VMID_RES_5K                           4 + +/* + * R6 (0x06) - Mic Bias Control 0 + */ +#define WM8903_MICDET_HYST_ENA                  0x0080  /* MICDET_HYST_ENA */ +#define WM8903_MICDET_HYST_ENA_MASK             0x0080  /* MICDET_HYST_ENA */ +#define WM8903_MICDET_HYST_ENA_SHIFT                 7  /* MICDET_HYST_ENA */ +#define WM8903_MICDET_HYST_ENA_WIDTH                 1  /* MICDET_HYST_ENA */ +#define WM8903_MICDET_THR_MASK                  0x0070  /* MICDET_THR - [6:4] */ +#define WM8903_MICDET_THR_SHIFT                      4  /* MICDET_THR - [6:4] */ +#define WM8903_MICDET_THR_WIDTH                      3  /* MICDET_THR - [6:4] */ +#define WM8903_MICSHORT_THR_MASK                0x000C  /* MICSHORT_THR - [3:2] */ +#define WM8903_MICSHORT_THR_SHIFT                    2  /* MICSHORT_THR - [3:2] */ +#define WM8903_MICSHORT_THR_WIDTH                    2  /* MICSHORT_THR - [3:2] */ +#define WM8903_MICDET_ENA                       0x0002  /* MICDET_ENA */ +#define WM8903_MICDET_ENA_MASK                  0x0002  /* MICDET_ENA */ +#define WM8903_MICDET_ENA_SHIFT                      1  /* MICDET_ENA */ +#define WM8903_MICDET_ENA_WIDTH                      1  /* MICDET_ENA */ +#define WM8903_MICBIAS_ENA                      0x0001  /* MICBIAS_ENA */ +#define WM8903_MICBIAS_ENA_MASK                 0x0001  /* MICBIAS_ENA */ +#define WM8903_MICBIAS_ENA_SHIFT                     0  /* MICBIAS_ENA */ +#define WM8903_MICBIAS_ENA_WIDTH                     1  /* MICBIAS_ENA */ + +/* + * R8 (0x08) - Analogue DAC 0 + */ +#define WM8903_DACBIAS_SEL_MASK                 0x0018  /* DACBIAS_SEL - [4:3] */ +#define WM8903_DACBIAS_SEL_SHIFT                     3  /* DACBIAS_SEL - [4:3] */ +#define WM8903_DACBIAS_SEL_WIDTH                     2  /* DACBIAS_SEL - [4:3] */ +#define WM8903_DACVMID_BIAS_SEL_MASK            0x0006  /* DACVMID_BIAS_SEL - [2:1] */ +#define WM8903_DACVMID_BIAS_SEL_SHIFT                1  /* DACVMID_BIAS_SEL - [2:1] */ +#define WM8903_DACVMID_BIAS_SEL_WIDTH                2  /* DACVMID_BIAS_SEL - [2:1] */ + +/* + * R10 (0x0A) - Analogue ADC 0 + */ +#define WM8903_ADC_OSR128                       0x0001  /* ADC_OSR128 */ +#define WM8903_ADC_OSR128_MASK                  0x0001  /* ADC_OSR128 */ +#define WM8903_ADC_OSR128_SHIFT                      0  /* ADC_OSR128 */ +#define WM8903_ADC_OSR128_WIDTH                      1  /* ADC_OSR128 */ + +/* + * R12 (0x0C) - Power Management 0 + */ +#define WM8903_INL_ENA                          0x0002  /* INL_ENA */ +#define WM8903_INL_ENA_MASK                     0x0002  /* INL_ENA */ +#define WM8903_INL_ENA_SHIFT                         1  /* INL_ENA */ +#define WM8903_INL_ENA_WIDTH                         1  /* INL_ENA */ +#define WM8903_INR_ENA                          0x0001  /* INR_ENA */ +#define WM8903_INR_ENA_MASK                     0x0001  /* INR_ENA */ +#define WM8903_INR_ENA_SHIFT                         0  /* INR_ENA */ +#define WM8903_INR_ENA_WIDTH                         1  /* INR_ENA */ + +/* + * R13 (0x0D) - Power Management 1 + */ +#define WM8903_MIXOUTL_ENA                      0x0002  /* MIXOUTL_ENA */ +#define WM8903_MIXOUTL_ENA_MASK                 0x0002  /* MIXOUTL_ENA */ +#define WM8903_MIXOUTL_ENA_SHIFT                     1  /* MIXOUTL_ENA */ +#define WM8903_MIXOUTL_ENA_WIDTH                     1  /* MIXOUTL_ENA */ +#define WM8903_MIXOUTR_ENA                      0x0001  /* MIXOUTR_ENA */ +#define WM8903_MIXOUTR_ENA_MASK                 0x0001  /* MIXOUTR_ENA */ +#define WM8903_MIXOUTR_ENA_SHIFT                     0  /* MIXOUTR_ENA */ +#define WM8903_MIXOUTR_ENA_WIDTH                     1  /* MIXOUTR_ENA */ + +/* + * R14 (0x0E) - Power Management 2 + */ +#define WM8903_HPL_PGA_ENA                      0x0002  /* HPL_PGA_ENA */ +#define WM8903_HPL_PGA_ENA_MASK                 0x0002  /* HPL_PGA_ENA */ +#define WM8903_HPL_PGA_ENA_SHIFT                     1  /* HPL_PGA_ENA */ +#define WM8903_HPL_PGA_ENA_WIDTH                     1  /* HPL_PGA_ENA */ +#define WM8903_HPR_PGA_ENA                      0x0001  /* HPR_PGA_ENA */ +#define WM8903_HPR_PGA_ENA_MASK                 0x0001  /* HPR_PGA_ENA */ +#define WM8903_HPR_PGA_ENA_SHIFT                     0  /* HPR_PGA_ENA */ +#define WM8903_HPR_PGA_ENA_WIDTH                     1  /* HPR_PGA_ENA */ + +/* + * R15 (0x0F) - Power Management 3 + */ +#define WM8903_LINEOUTL_PGA_ENA                 0x0002  /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTL_PGA_ENA_MASK            0x0002  /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTL_PGA_ENA_SHIFT                1  /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTL_PGA_ENA_WIDTH                1  /* LINEOUTL_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA                 0x0001  /* LINEOUTR_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA_MASK            0x0001  /* LINEOUTR_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA_SHIFT                0  /* LINEOUTR_PGA_ENA */ +#define WM8903_LINEOUTR_PGA_ENA_WIDTH                1  /* LINEOUTR_PGA_ENA */ + +/* + * R16 (0x10) - Power Management 4 + */ +#define WM8903_MIXSPKL_ENA                      0x0002  /* MIXSPKL_ENA */ +#define WM8903_MIXSPKL_ENA_MASK                 0x0002  /* MIXSPKL_ENA */ +#define WM8903_MIXSPKL_ENA_SHIFT                     1  /* MIXSPKL_ENA */ +#define WM8903_MIXSPKL_ENA_WIDTH                     1  /* MIXSPKL_ENA */ +#define WM8903_MIXSPKR_ENA                      0x0001  /* MIXSPKR_ENA */ +#define WM8903_MIXSPKR_ENA_MASK                 0x0001  /* MIXSPKR_ENA */ +#define WM8903_MIXSPKR_ENA_SHIFT                     0  /* MIXSPKR_ENA */ +#define WM8903_MIXSPKR_ENA_WIDTH                     1  /* MIXSPKR_ENA */ + +/* + * R17 (0x11) - Power Management 5 + */ +#define WM8903_SPKL_ENA                         0x0002  /* SPKL_ENA */ +#define WM8903_SPKL_ENA_MASK                    0x0002  /* SPKL_ENA */ +#define WM8903_SPKL_ENA_SHIFT                        1  /* SPKL_ENA */ +#define WM8903_SPKL_ENA_WIDTH                        1  /* SPKL_ENA */ +#define WM8903_SPKR_ENA                         0x0001  /* SPKR_ENA */ +#define WM8903_SPKR_ENA_MASK                    0x0001  /* SPKR_ENA */ +#define WM8903_SPKR_ENA_SHIFT                        0  /* SPKR_ENA */ +#define WM8903_SPKR_ENA_WIDTH                        1  /* SPKR_ENA */ + +/* + * R18 (0x12) - Power Management 6 + */ +#define WM8903_DACL_ENA                         0x0008  /* DACL_ENA */ +#define WM8903_DACL_ENA_MASK                    0x0008  /* DACL_ENA */ +#define WM8903_DACL_ENA_SHIFT                        3  /* DACL_ENA */ +#define WM8903_DACL_ENA_WIDTH                        1  /* DACL_ENA */ +#define WM8903_DACR_ENA                         0x0004  /* DACR_ENA */ +#define WM8903_DACR_ENA_MASK                    0x0004  /* DACR_ENA */ +#define WM8903_DACR_ENA_SHIFT                        2  /* DACR_ENA */ +#define WM8903_DACR_ENA_WIDTH                        1  /* DACR_ENA */ +#define WM8903_ADCL_ENA                         0x0002  /* ADCL_ENA */ +#define WM8903_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */ +#define WM8903_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */ +#define WM8903_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */ +#define WM8903_ADCR_ENA                         0x0001  /* ADCR_ENA */ +#define WM8903_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */ +#define WM8903_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */ +#define WM8903_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */ + +/* + * R20 (0x14) - Clock Rates 0 + */ +#define WM8903_MCLKDIV2                         0x0001  /* MCLKDIV2 */ +#define WM8903_MCLKDIV2_MASK                    0x0001  /* MCLKDIV2 */ +#define WM8903_MCLKDIV2_SHIFT                        0  /* MCLKDIV2 */ +#define WM8903_MCLKDIV2_WIDTH                        1  /* MCLKDIV2 */ + +/* + * R21 (0x15) - Clock Rates 1 + */ +#define WM8903_CLK_SYS_RATE_MASK                0x3C00  /* CLK_SYS_RATE - [13:10] */ +#define WM8903_CLK_SYS_RATE_SHIFT                   10  /* CLK_SYS_RATE - [13:10] */ +#define WM8903_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [13:10] */ +#define WM8903_CLK_SYS_MODE_MASK                0x0300  /* CLK_SYS_MODE - [9:8] */ +#define WM8903_CLK_SYS_MODE_SHIFT                    8  /* CLK_SYS_MODE - [9:8] */ +#define WM8903_CLK_SYS_MODE_WIDTH                    2  /* CLK_SYS_MODE - [9:8] */ +#define WM8903_SAMPLE_RATE_MASK                 0x000F  /* SAMPLE_RATE - [3:0] */ +#define WM8903_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [3:0] */ +#define WM8903_SAMPLE_RATE_WIDTH                     4  /* SAMPLE_RATE - [3:0] */ + +/* + * R22 (0x16) - Clock Rates 2 + */ +#define WM8903_CLK_SYS_ENA                      0x0004  /* CLK_SYS_ENA */ +#define WM8903_CLK_SYS_ENA_MASK                 0x0004  /* CLK_SYS_ENA */ +#define WM8903_CLK_SYS_ENA_SHIFT                     2  /* CLK_SYS_ENA */ +#define WM8903_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */ +#define WM8903_CLK_DSP_ENA                      0x0002  /* CLK_DSP_ENA */ +#define WM8903_CLK_DSP_ENA_MASK                 0x0002  /* CLK_DSP_ENA */ +#define WM8903_CLK_DSP_ENA_SHIFT                     1  /* CLK_DSP_ENA */ +#define WM8903_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */ +#define WM8903_TO_ENA                           0x0001  /* TO_ENA */ +#define WM8903_TO_ENA_MASK                      0x0001  /* TO_ENA */ +#define WM8903_TO_ENA_SHIFT                          0  /* TO_ENA */ +#define WM8903_TO_ENA_WIDTH                          1  /* TO_ENA */ + +/* + * R24 (0x18) - Audio Interface 0 + */ +#define WM8903_DACL_DATINV                      0x1000  /* DACL_DATINV */ +#define WM8903_DACL_DATINV_MASK                 0x1000  /* DACL_DATINV */ +#define WM8903_DACL_DATINV_SHIFT                    12  /* DACL_DATINV */ +#define WM8903_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */ +#define WM8903_DACR_DATINV                      0x0800  /* DACR_DATINV */ +#define WM8903_DACR_DATINV_MASK                 0x0800  /* DACR_DATINV */ +#define WM8903_DACR_DATINV_SHIFT                    11  /* DACR_DATINV */ +#define WM8903_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */ +#define WM8903_DAC_BOOST_MASK                   0x0600  /* DAC_BOOST - [10:9] */ +#define WM8903_DAC_BOOST_SHIFT                       9  /* DAC_BOOST - [10:9] */ +#define WM8903_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [10:9] */ +#define WM8903_LOOPBACK                         0x0100  /* LOOPBACK */ +#define WM8903_LOOPBACK_MASK                    0x0100  /* LOOPBACK */ +#define WM8903_LOOPBACK_SHIFT                        8  /* LOOPBACK */ +#define WM8903_LOOPBACK_WIDTH                        1  /* LOOPBACK */ +#define WM8903_AIFADCL_SRC                      0x0080  /* AIFADCL_SRC */ +#define WM8903_AIFADCL_SRC_MASK                 0x0080  /* AIFADCL_SRC */ +#define WM8903_AIFADCL_SRC_SHIFT                     7  /* AIFADCL_SRC */ +#define WM8903_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */ +#define WM8903_AIFADCR_SRC                      0x0040  /* AIFADCR_SRC */ +#define WM8903_AIFADCR_SRC_MASK                 0x0040  /* AIFADCR_SRC */ +#define WM8903_AIFADCR_SRC_SHIFT                     6  /* AIFADCR_SRC */ +#define WM8903_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */ +#define WM8903_AIFDACL_SRC                      0x0020  /* AIFDACL_SRC */ +#define WM8903_AIFDACL_SRC_MASK                 0x0020  /* AIFDACL_SRC */ +#define WM8903_AIFDACL_SRC_SHIFT                     5  /* AIFDACL_SRC */ +#define WM8903_AIFDACL_SRC_WIDTH                     1  /* AIFDACL_SRC */ +#define WM8903_AIFDACR_SRC                      0x0010  /* AIFDACR_SRC */ +#define WM8903_AIFDACR_SRC_MASK                 0x0010  /* AIFDACR_SRC */ +#define WM8903_AIFDACR_SRC_SHIFT                     4  /* AIFDACR_SRC */ +#define WM8903_AIFDACR_SRC_WIDTH                     1  /* AIFDACR_SRC */ +#define WM8903_ADC_COMP                         0x0008  /* ADC_COMP */ +#define WM8903_ADC_COMP_MASK                    0x0008  /* ADC_COMP */ +#define WM8903_ADC_COMP_SHIFT                        3  /* ADC_COMP */ +#define WM8903_ADC_COMP_WIDTH                        1  /* ADC_COMP */ +#define WM8903_ADC_COMPMODE                     0x0004  /* ADC_COMPMODE */ +#define WM8903_ADC_COMPMODE_MASK                0x0004  /* ADC_COMPMODE */ +#define WM8903_ADC_COMPMODE_SHIFT                    2  /* ADC_COMPMODE */ +#define WM8903_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */ +#define WM8903_DAC_COMP                         0x0002  /* DAC_COMP */ +#define WM8903_DAC_COMP_MASK                    0x0002  /* DAC_COMP */ +#define WM8903_DAC_COMP_SHIFT                        1  /* DAC_COMP */ +#define WM8903_DAC_COMP_WIDTH                        1  /* DAC_COMP */ +#define WM8903_DAC_COMPMODE                     0x0001  /* DAC_COMPMODE */ +#define WM8903_DAC_COMPMODE_MASK                0x0001  /* DAC_COMPMODE */ +#define WM8903_DAC_COMPMODE_SHIFT                    0  /* DAC_COMPMODE */ +#define WM8903_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */ + +/* + * R25 (0x19) - Audio Interface 1 + */ +#define WM8903_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */ +#define WM8903_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */ +#define WM8903_AIFADC_TDM                       0x0800  /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_MASK                  0x0800  /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_SHIFT                     11  /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */ +#define WM8903_AIFADC_TDM_CHAN                  0x0400  /* AIFADC_TDM_CHAN */ +#define WM8903_AIFADC_TDM_CHAN_MASK             0x0400  /* AIFADC_TDM_CHAN */ +#define WM8903_AIFADC_TDM_CHAN_SHIFT                10  /* AIFADC_TDM_CHAN */ +#define WM8903_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */ +#define WM8903_LRCLK_DIR                        0x0200  /* LRCLK_DIR */ +#define WM8903_LRCLK_DIR_MASK                   0x0200  /* LRCLK_DIR */ +#define WM8903_LRCLK_DIR_SHIFT                       9  /* LRCLK_DIR */ +#define WM8903_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */ +#define WM8903_AIF_BCLK_INV                     0x0080  /* AIF_BCLK_INV */ +#define WM8903_AIF_BCLK_INV_MASK                0x0080  /* AIF_BCLK_INV */ +#define WM8903_AIF_BCLK_INV_SHIFT                    7  /* AIF_BCLK_INV */ +#define WM8903_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */ +#define WM8903_BCLK_DIR                         0x0040  /* BCLK_DIR */ +#define WM8903_BCLK_DIR_MASK                    0x0040  /* BCLK_DIR */ +#define WM8903_BCLK_DIR_SHIFT                        6  /* BCLK_DIR */ +#define WM8903_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */ +#define WM8903_AIF_LRCLK_INV                    0x0010  /* AIF_LRCLK_INV */ +#define WM8903_AIF_LRCLK_INV_MASK               0x0010  /* AIF_LRCLK_INV */ +#define WM8903_AIF_LRCLK_INV_SHIFT                   4  /* AIF_LRCLK_INV */ +#define WM8903_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */ +#define WM8903_AIF_WL_MASK                      0x000C  /* AIF_WL - [3:2] */ +#define WM8903_AIF_WL_SHIFT                          2  /* AIF_WL - [3:2] */ +#define WM8903_AIF_WL_WIDTH                          2  /* AIF_WL - [3:2] */ +#define WM8903_AIF_FMT_MASK                     0x0003  /* AIF_FMT - [1:0] */ +#define WM8903_AIF_FMT_SHIFT                         0  /* AIF_FMT - [1:0] */ +#define WM8903_AIF_FMT_WIDTH                         2  /* AIF_FMT - [1:0] */ + +/* + * R26 (0x1A) - Audio Interface 2 + */ +#define WM8903_BCLK_DIV_MASK                    0x001F  /* BCLK_DIV - [4:0] */ +#define WM8903_BCLK_DIV_SHIFT                        0  /* BCLK_DIV - [4:0] */ +#define WM8903_BCLK_DIV_WIDTH                        5  /* BCLK_DIV - [4:0] */ + +/* + * R27 (0x1B) - Audio Interface 3 + */ +#define WM8903_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */ +#define WM8903_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */ +#define WM8903_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */ + +/* + * R30 (0x1E) - DAC Digital Volume Left + */ +#define WM8903_DACVU                            0x0100  /* DACVU */ +#define WM8903_DACVU_MASK                       0x0100  /* DACVU */ +#define WM8903_DACVU_SHIFT                           8  /* DACVU */ +#define WM8903_DACVU_WIDTH                           1  /* DACVU */ +#define WM8903_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */ +#define WM8903_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */ +#define WM8903_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */ + +/* + * R31 (0x1F) - DAC Digital Volume Right + */ +#define WM8903_DACVU                            0x0100  /* DACVU */ +#define WM8903_DACVU_MASK                       0x0100  /* DACVU */ +#define WM8903_DACVU_SHIFT                           8  /* DACVU */ +#define WM8903_DACVU_WIDTH                           1  /* DACVU */ +#define WM8903_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */ +#define WM8903_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */ +#define WM8903_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */ + +/* + * R32 (0x20) - DAC Digital 0 + */ +#define WM8903_ADCL_DAC_SVOL_MASK               0x0F00  /* ADCL_DAC_SVOL - [11:8] */ +#define WM8903_ADCL_DAC_SVOL_SHIFT                   8  /* ADCL_DAC_SVOL - [11:8] */ +#define WM8903_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [11:8] */ +#define WM8903_ADCR_DAC_SVOL_MASK               0x00F0  /* ADCR_DAC_SVOL - [7:4] */ +#define WM8903_ADCR_DAC_SVOL_SHIFT                   4  /* ADCR_DAC_SVOL - [7:4] */ +#define WM8903_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [7:4] */ +#define WM8903_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */ +#define WM8903_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */ +#define WM8903_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */ +#define WM8903_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */ +#define WM8903_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */ +#define WM8903_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */ + +/* + * R33 (0x21) - DAC Digital 1 + */ +#define WM8903_DAC_MONO                         0x1000  /* DAC_MONO */ +#define WM8903_DAC_MONO_MASK                    0x1000  /* DAC_MONO */ +#define WM8903_DAC_MONO_SHIFT                       12  /* DAC_MONO */ +#define WM8903_DAC_MONO_WIDTH                        1  /* DAC_MONO */ +#define WM8903_DAC_SB_FILT                      0x0800  /* DAC_SB_FILT */ +#define WM8903_DAC_SB_FILT_MASK                 0x0800  /* DAC_SB_FILT */ +#define WM8903_DAC_SB_FILT_SHIFT                    11  /* DAC_SB_FILT */ +#define WM8903_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */ +#define WM8903_DAC_MUTERATE                     0x0400  /* DAC_MUTERATE */ +#define WM8903_DAC_MUTERATE_MASK                0x0400  /* DAC_MUTERATE */ +#define WM8903_DAC_MUTERATE_SHIFT                   10  /* DAC_MUTERATE */ +#define WM8903_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */ +#define WM8903_DAC_MUTEMODE                     0x0200  /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTEMODE_MASK                0x0200  /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTEMODE_SHIFT                    9  /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTEMODE_WIDTH                    1  /* DAC_MUTEMODE */ +#define WM8903_DAC_MUTE                         0x0008  /* DAC_MUTE */ +#define WM8903_DAC_MUTE_MASK                    0x0008  /* DAC_MUTE */ +#define WM8903_DAC_MUTE_SHIFT                        3  /* DAC_MUTE */ +#define WM8903_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */ +#define WM8903_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */ +#define WM8903_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */ +#define WM8903_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */ + +/* + * R36 (0x24) - ADC Digital Volume Left + */ +#define WM8903_ADCVU                            0x0100  /* ADCVU */ +#define WM8903_ADCVU_MASK                       0x0100  /* ADCVU */ +#define WM8903_ADCVU_SHIFT                           8  /* ADCVU */ +#define WM8903_ADCVU_WIDTH                           1  /* ADCVU */ +#define WM8903_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */ +#define WM8903_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */ +#define WM8903_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */ + +/* + * R37 (0x25) - ADC Digital Volume Right + */ +#define WM8903_ADCVU                            0x0100  /* ADCVU */ +#define WM8903_ADCVU_MASK                       0x0100  /* ADCVU */ +#define WM8903_ADCVU_SHIFT                           8  /* ADCVU */ +#define WM8903_ADCVU_WIDTH                           1  /* ADCVU */ +#define WM8903_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */ +#define WM8903_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */ +#define WM8903_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */ + +/* + * R38 (0x26) - ADC Digital 0 + */ +#define WM8903_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */ +#define WM8903_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */ +#define WM8903_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */ +#define WM8903_ADC_HPF_ENA                      0x0010  /* ADC_HPF_ENA */ +#define WM8903_ADC_HPF_ENA_MASK                 0x0010  /* ADC_HPF_ENA */ +#define WM8903_ADC_HPF_ENA_SHIFT                     4  /* ADC_HPF_ENA */ +#define WM8903_ADC_HPF_ENA_WIDTH                     1  /* ADC_HPF_ENA */ +#define WM8903_ADCL_DATINV                      0x0002  /* ADCL_DATINV */ +#define WM8903_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */ +#define WM8903_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */ +#define WM8903_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */ +#define WM8903_ADCR_DATINV                      0x0001  /* ADCR_DATINV */ +#define WM8903_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */ +#define WM8903_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */ +#define WM8903_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */ + +/* + * R39 (0x27) - Digital Microphone 0 + */ +#define WM8903_DIGMIC_MODE_SEL                  0x0100  /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_MODE_SEL_MASK             0x0100  /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_MODE_SEL_SHIFT                 8  /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_MODE_SEL_WIDTH                 1  /* DIGMIC_MODE_SEL */ +#define WM8903_DIGMIC_CLK_SEL_L_MASK            0x00C0  /* DIGMIC_CLK_SEL_L - [7:6] */ +#define WM8903_DIGMIC_CLK_SEL_L_SHIFT                6  /* DIGMIC_CLK_SEL_L - [7:6] */ +#define WM8903_DIGMIC_CLK_SEL_L_WIDTH                2  /* DIGMIC_CLK_SEL_L - [7:6] */ +#define WM8903_DIGMIC_CLK_SEL_R_MASK            0x0030  /* DIGMIC_CLK_SEL_R - [5:4] */ +#define WM8903_DIGMIC_CLK_SEL_R_SHIFT                4  /* DIGMIC_CLK_SEL_R - [5:4] */ +#define WM8903_DIGMIC_CLK_SEL_R_WIDTH                2  /* DIGMIC_CLK_SEL_R - [5:4] */ +#define WM8903_DIGMIC_CLK_SEL_RT_MASK           0x000C  /* DIGMIC_CLK_SEL_RT - [3:2] */ +#define WM8903_DIGMIC_CLK_SEL_RT_SHIFT               2  /* DIGMIC_CLK_SEL_RT - [3:2] */ +#define WM8903_DIGMIC_CLK_SEL_RT_WIDTH               2  /* DIGMIC_CLK_SEL_RT - [3:2] */ +#define WM8903_DIGMIC_CLK_SEL_MASK              0x0003  /* DIGMIC_CLK_SEL - [1:0] */ +#define WM8903_DIGMIC_CLK_SEL_SHIFT                  0  /* DIGMIC_CLK_SEL - [1:0] */ +#define WM8903_DIGMIC_CLK_SEL_WIDTH                  2  /* DIGMIC_CLK_SEL - [1:0] */ + +/* + * R40 (0x28) - DRC 0 + */ +#define WM8903_DRC_ENA                          0x8000  /* DRC_ENA */ +#define WM8903_DRC_ENA_MASK                     0x8000  /* DRC_ENA */ +#define WM8903_DRC_ENA_SHIFT                        15  /* DRC_ENA */ +#define WM8903_DRC_ENA_WIDTH                         1  /* DRC_ENA */ +#define WM8903_DRC_THRESH_HYST_MASK             0x1800  /* DRC_THRESH_HYST - [12:11] */ +#define WM8903_DRC_THRESH_HYST_SHIFT                11  /* DRC_THRESH_HYST - [12:11] */ +#define WM8903_DRC_THRESH_HYST_WIDTH                 2  /* DRC_THRESH_HYST - [12:11] */ +#define WM8903_DRC_STARTUP_GAIN_MASK            0x07C0  /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8903_DRC_STARTUP_GAIN_SHIFT                6  /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8903_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [10:6] */ +#define WM8903_DRC_FF_DELAY                     0x0020  /* DRC_FF_DELAY */ +#define WM8903_DRC_FF_DELAY_MASK                0x0020  /* DRC_FF_DELAY */ +#define WM8903_DRC_FF_DELAY_SHIFT                    5  /* DRC_FF_DELAY */ +#define WM8903_DRC_FF_DELAY_WIDTH                    1  /* DRC_FF_DELAY */ +#define WM8903_DRC_SMOOTH_ENA                   0x0008  /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_SMOOTH_ENA_MASK              0x0008  /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_SMOOTH_ENA_SHIFT                  3  /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_SMOOTH_ENA_WIDTH                  1  /* DRC_SMOOTH_ENA */ +#define WM8903_DRC_QR_ENA                       0x0004  /* DRC_QR_ENA */ +#define WM8903_DRC_QR_ENA_MASK                  0x0004  /* DRC_QR_ENA */ +#define WM8903_DRC_QR_ENA_SHIFT                      2  /* DRC_QR_ENA */ +#define WM8903_DRC_QR_ENA_WIDTH                      1  /* DRC_QR_ENA */ +#define WM8903_DRC_ANTICLIP_ENA                 0x0002  /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_ANTICLIP_ENA_MASK            0x0002  /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_ANTICLIP_ENA_SHIFT                1  /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_ANTICLIP_ENA_WIDTH                1  /* DRC_ANTICLIP_ENA */ +#define WM8903_DRC_HYST_ENA                     0x0001  /* DRC_HYST_ENA */ +#define WM8903_DRC_HYST_ENA_MASK                0x0001  /* DRC_HYST_ENA */ +#define WM8903_DRC_HYST_ENA_SHIFT                    0  /* DRC_HYST_ENA */ +#define WM8903_DRC_HYST_ENA_WIDTH                    1  /* DRC_HYST_ENA */ + +/* + * R41 (0x29) - DRC 1 + */ +#define WM8903_DRC_ATTACK_RATE_MASK             0xF000  /* DRC_ATTACK_RATE - [15:12] */ +#define WM8903_DRC_ATTACK_RATE_SHIFT                12  /* DRC_ATTACK_RATE - [15:12] */ +#define WM8903_DRC_ATTACK_RATE_WIDTH                 4  /* DRC_ATTACK_RATE - [15:12] */ +#define WM8903_DRC_DECAY_RATE_MASK              0x0F00  /* DRC_DECAY_RATE - [11:8] */ +#define WM8903_DRC_DECAY_RATE_SHIFT                  8  /* DRC_DECAY_RATE - [11:8] */ +#define WM8903_DRC_DECAY_RATE_WIDTH                  4  /* DRC_DECAY_RATE - [11:8] */ +#define WM8903_DRC_THRESH_QR_MASK               0x00C0  /* DRC_THRESH_QR - [7:6] */ +#define WM8903_DRC_THRESH_QR_SHIFT                   6  /* DRC_THRESH_QR - [7:6] */ +#define WM8903_DRC_THRESH_QR_WIDTH                   2  /* DRC_THRESH_QR - [7:6] */ +#define WM8903_DRC_RATE_QR_MASK                 0x0030  /* DRC_RATE_QR - [5:4] */ +#define WM8903_DRC_RATE_QR_SHIFT                     4  /* DRC_RATE_QR - [5:4] */ +#define WM8903_DRC_RATE_QR_WIDTH                     2  /* DRC_RATE_QR - [5:4] */ +#define WM8903_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */ +#define WM8903_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */ +#define WM8903_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */ +#define WM8903_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */ +#define WM8903_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */ +#define WM8903_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */ + +/* + * R42 (0x2A) - DRC 2 + */ +#define WM8903_DRC_R0_SLOPE_COMP_MASK           0x0038  /* DRC_R0_SLOPE_COMP - [5:3] */ +#define WM8903_DRC_R0_SLOPE_COMP_SHIFT               3  /* DRC_R0_SLOPE_COMP - [5:3] */ +#define WM8903_DRC_R0_SLOPE_COMP_WIDTH               3  /* DRC_R0_SLOPE_COMP - [5:3] */ +#define WM8903_DRC_R1_SLOPE_COMP_MASK           0x0007  /* DRC_R1_SLOPE_COMP - [2:0] */ +#define WM8903_DRC_R1_SLOPE_COMP_SHIFT               0  /* DRC_R1_SLOPE_COMP - [2:0] */ +#define WM8903_DRC_R1_SLOPE_COMP_WIDTH               3  /* DRC_R1_SLOPE_COMP - [2:0] */ + +/* + * R43 (0x2B) - DRC 3 + */ +#define WM8903_DRC_THRESH_COMP_MASK             0x07E0  /* DRC_THRESH_COMP - [10:5] */ +#define WM8903_DRC_THRESH_COMP_SHIFT                 5  /* DRC_THRESH_COMP - [10:5] */ +#define WM8903_DRC_THRESH_COMP_WIDTH                 6  /* DRC_THRESH_COMP - [10:5] */ +#define WM8903_DRC_AMP_COMP_MASK                0x001F  /* DRC_AMP_COMP - [4:0] */ +#define WM8903_DRC_AMP_COMP_SHIFT                    0  /* DRC_AMP_COMP - [4:0] */ +#define WM8903_DRC_AMP_COMP_WIDTH                    5  /* DRC_AMP_COMP - [4:0] */ + +/* + * R44 (0x2C) - Analogue Left Input 0 + */ +#define WM8903_LINMUTE                          0x0080  /* LINMUTE */ +#define WM8903_LINMUTE_MASK                     0x0080  /* LINMUTE */ +#define WM8903_LINMUTE_SHIFT                         7  /* LINMUTE */ +#define WM8903_LINMUTE_WIDTH                         1  /* LINMUTE */ +#define WM8903_LIN_VOL_MASK                     0x001F  /* LIN_VOL - [4:0] */ +#define WM8903_LIN_VOL_SHIFT                         0  /* LIN_VOL - [4:0] */ +#define WM8903_LIN_VOL_WIDTH                         5  /* LIN_VOL - [4:0] */ + +/* + * R45 (0x2D) - Analogue Right Input 0 + */ +#define WM8903_RINMUTE                          0x0080  /* RINMUTE */ +#define WM8903_RINMUTE_MASK                     0x0080  /* RINMUTE */ +#define WM8903_RINMUTE_SHIFT                         7  /* RINMUTE */ +#define WM8903_RINMUTE_WIDTH                         1  /* RINMUTE */ +#define WM8903_RIN_VOL_MASK                     0x001F  /* RIN_VOL - [4:0] */ +#define WM8903_RIN_VOL_SHIFT                         0  /* RIN_VOL - [4:0] */ +#define WM8903_RIN_VOL_WIDTH                         5  /* RIN_VOL - [4:0] */ + +/* + * R46 (0x2E) - Analogue Left Input 1 + */ +#define WM8903_INL_CM_ENA                       0x0040  /* INL_CM_ENA */ +#define WM8903_INL_CM_ENA_MASK                  0x0040  /* INL_CM_ENA */ +#define WM8903_INL_CM_ENA_SHIFT                      6  /* INL_CM_ENA */ +#define WM8903_INL_CM_ENA_WIDTH                      1  /* INL_CM_ENA */ +#define WM8903_L_IP_SEL_N_MASK                  0x0030  /* L_IP_SEL_N - [5:4] */ +#define WM8903_L_IP_SEL_N_SHIFT                      4  /* L_IP_SEL_N - [5:4] */ +#define WM8903_L_IP_SEL_N_WIDTH                      2  /* L_IP_SEL_N - [5:4] */ +#define WM8903_L_IP_SEL_P_MASK                  0x000C  /* L_IP_SEL_P - [3:2] */ +#define WM8903_L_IP_SEL_P_SHIFT                      2  /* L_IP_SEL_P - [3:2] */ +#define WM8903_L_IP_SEL_P_WIDTH                      2  /* L_IP_SEL_P - [3:2] */ +#define WM8903_L_MODE_MASK                      0x0003  /* L_MODE - [1:0] */ +#define WM8903_L_MODE_SHIFT                          0  /* L_MODE - [1:0] */ +#define WM8903_L_MODE_WIDTH                          2  /* L_MODE - [1:0] */ + +/* + * R47 (0x2F) - Analogue Right Input 1 + */ +#define WM8903_INR_CM_ENA                       0x0040  /* INR_CM_ENA */ +#define WM8903_INR_CM_ENA_MASK                  0x0040  /* INR_CM_ENA */ +#define WM8903_INR_CM_ENA_SHIFT                      6  /* INR_CM_ENA */ +#define WM8903_INR_CM_ENA_WIDTH                      1  /* INR_CM_ENA */ +#define WM8903_R_IP_SEL_N_MASK                  0x0030  /* R_IP_SEL_N - [5:4] */ +#define WM8903_R_IP_SEL_N_SHIFT                      4  /* R_IP_SEL_N - [5:4] */ +#define WM8903_R_IP_SEL_N_WIDTH                      2  /* R_IP_SEL_N - [5:4] */ +#define WM8903_R_IP_SEL_P_MASK                  0x000C  /* R_IP_SEL_P - [3:2] */ +#define WM8903_R_IP_SEL_P_SHIFT                      2  /* R_IP_SEL_P - [3:2] */ +#define WM8903_R_IP_SEL_P_WIDTH                      2  /* R_IP_SEL_P - [3:2] */ +#define WM8903_R_MODE_MASK                      0x0003  /* R_MODE - [1:0] */ +#define WM8903_R_MODE_SHIFT                          0  /* R_MODE - [1:0] */ +#define WM8903_R_MODE_WIDTH                          2  /* R_MODE - [1:0] */ + +/* + * R50 (0x32) - Analogue Left Mix 0 + */ +#define WM8903_DACL_TO_MIXOUTL                  0x0008  /* DACL_TO_MIXOUTL */ +#define WM8903_DACL_TO_MIXOUTL_MASK             0x0008  /* DACL_TO_MIXOUTL */ +#define WM8903_DACL_TO_MIXOUTL_SHIFT                 3  /* DACL_TO_MIXOUTL */ +#define WM8903_DACL_TO_MIXOUTL_WIDTH                 1  /* DACL_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL                  0x0004  /* DACR_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL_MASK             0x0004  /* DACR_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL_SHIFT                 2  /* DACR_TO_MIXOUTL */ +#define WM8903_DACR_TO_MIXOUTL_WIDTH                 1  /* DACR_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL               0x0002  /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL_MASK          0x0002  /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL_SHIFT              1  /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSL_TO_MIXOUTL_WIDTH              1  /* BYPASSL_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL               0x0001  /* BYPASSR_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL_MASK          0x0001  /* BYPASSR_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL_SHIFT              0  /* BYPASSR_TO_MIXOUTL */ +#define WM8903_BYPASSR_TO_MIXOUTL_WIDTH              1  /* BYPASSR_TO_MIXOUTL */ + +/* + * R51 (0x33) - Analogue Right Mix 0 + */ +#define WM8903_DACL_TO_MIXOUTR                  0x0008  /* DACL_TO_MIXOUTR */ +#define WM8903_DACL_TO_MIXOUTR_MASK             0x0008  /* DACL_TO_MIXOUTR */ +#define WM8903_DACL_TO_MIXOUTR_SHIFT                 3  /* DACL_TO_MIXOUTR */ +#define WM8903_DACL_TO_MIXOUTR_WIDTH                 1  /* DACL_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR                  0x0004  /* DACR_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR_MASK             0x0004  /* DACR_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR_SHIFT                 2  /* DACR_TO_MIXOUTR */ +#define WM8903_DACR_TO_MIXOUTR_WIDTH                 1  /* DACR_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR               0x0002  /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR_MASK          0x0002  /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR_SHIFT              1  /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSL_TO_MIXOUTR_WIDTH              1  /* BYPASSL_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR               0x0001  /* BYPASSR_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR_MASK          0x0001  /* BYPASSR_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR_SHIFT              0  /* BYPASSR_TO_MIXOUTR */ +#define WM8903_BYPASSR_TO_MIXOUTR_WIDTH              1  /* BYPASSR_TO_MIXOUTR */ + +/* + * R52 (0x34) - Analogue Spk Mix Left 0 + */ +#define WM8903_DACL_TO_MIXSPKL                  0x0008  /* DACL_TO_MIXSPKL */ +#define WM8903_DACL_TO_MIXSPKL_MASK             0x0008  /* DACL_TO_MIXSPKL */ +#define WM8903_DACL_TO_MIXSPKL_SHIFT                 3  /* DACL_TO_MIXSPKL */ +#define WM8903_DACL_TO_MIXSPKL_WIDTH                 1  /* DACL_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL                  0x0004  /* DACR_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL_MASK             0x0004  /* DACR_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL_SHIFT                 2  /* DACR_TO_MIXSPKL */ +#define WM8903_DACR_TO_MIXSPKL_WIDTH                 1  /* DACR_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL               0x0002  /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL_MASK          0x0002  /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL_SHIFT              1  /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSL_TO_MIXSPKL_WIDTH              1  /* BYPASSL_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL               0x0001  /* BYPASSR_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL_MASK          0x0001  /* BYPASSR_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL_SHIFT              0  /* BYPASSR_TO_MIXSPKL */ +#define WM8903_BYPASSR_TO_MIXSPKL_WIDTH              1  /* BYPASSR_TO_MIXSPKL */ + +/* + * R53 (0x35) - Analogue Spk Mix Left 1 + */ +#define WM8903_DACL_MIXSPKL_VOL                 0x0008  /* DACL_MIXSPKL_VOL */ +#define WM8903_DACL_MIXSPKL_VOL_MASK            0x0008  /* DACL_MIXSPKL_VOL */ +#define WM8903_DACL_MIXSPKL_VOL_SHIFT                3  /* DACL_MIXSPKL_VOL */ +#define WM8903_DACL_MIXSPKL_VOL_WIDTH                1  /* DACL_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL                 0x0004  /* DACR_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL_MASK            0x0004  /* DACR_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL_SHIFT                2  /* DACR_MIXSPKL_VOL */ +#define WM8903_DACR_MIXSPKL_VOL_WIDTH                1  /* DACR_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL              0x0002  /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL_MASK         0x0002  /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL_SHIFT             1  /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSL_MIXSPKL_VOL_WIDTH             1  /* BYPASSL_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL              0x0001  /* BYPASSR_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL_MASK         0x0001  /* BYPASSR_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL_SHIFT             0  /* BYPASSR_MIXSPKL_VOL */ +#define WM8903_BYPASSR_MIXSPKL_VOL_WIDTH             1  /* BYPASSR_MIXSPKL_VOL */ + +/* + * R54 (0x36) - Analogue Spk Mix Right 0 + */ +#define WM8903_DACL_TO_MIXSPKR                  0x0008  /* DACL_TO_MIXSPKR */ +#define WM8903_DACL_TO_MIXSPKR_MASK             0x0008  /* DACL_TO_MIXSPKR */ +#define WM8903_DACL_TO_MIXSPKR_SHIFT                 3  /* DACL_TO_MIXSPKR */ +#define WM8903_DACL_TO_MIXSPKR_WIDTH                 1  /* DACL_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR                  0x0004  /* DACR_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR_MASK             0x0004  /* DACR_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR_SHIFT                 2  /* DACR_TO_MIXSPKR */ +#define WM8903_DACR_TO_MIXSPKR_WIDTH                 1  /* DACR_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR               0x0002  /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR_MASK          0x0002  /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR_SHIFT              1  /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSL_TO_MIXSPKR_WIDTH              1  /* BYPASSL_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR               0x0001  /* BYPASSR_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR_MASK          0x0001  /* BYPASSR_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR_SHIFT              0  /* BYPASSR_TO_MIXSPKR */ +#define WM8903_BYPASSR_TO_MIXSPKR_WIDTH              1  /* BYPASSR_TO_MIXSPKR */ + +/* + * R55 (0x37) - Analogue Spk Mix Right 1 + */ +#define WM8903_DACL_MIXSPKR_VOL                 0x0008  /* DACL_MIXSPKR_VOL */ +#define WM8903_DACL_MIXSPKR_VOL_MASK            0x0008  /* DACL_MIXSPKR_VOL */ +#define WM8903_DACL_MIXSPKR_VOL_SHIFT                3  /* DACL_MIXSPKR_VOL */ +#define WM8903_DACL_MIXSPKR_VOL_WIDTH                1  /* DACL_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL                 0x0004  /* DACR_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL_MASK            0x0004  /* DACR_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL_SHIFT                2  /* DACR_MIXSPKR_VOL */ +#define WM8903_DACR_MIXSPKR_VOL_WIDTH                1  /* DACR_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL              0x0002  /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL_MASK         0x0002  /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL_SHIFT             1  /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSL_MIXSPKR_VOL_WIDTH             1  /* BYPASSL_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL              0x0001  /* BYPASSR_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL_MASK         0x0001  /* BYPASSR_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL_SHIFT             0  /* BYPASSR_MIXSPKR_VOL */ +#define WM8903_BYPASSR_MIXSPKR_VOL_WIDTH             1  /* BYPASSR_MIXSPKR_VOL */ + +/* + * R57 (0x39) - Analogue OUT1 Left + */ +#define WM8903_HPL_MUTE                         0x0100  /* HPL_MUTE */ +#define WM8903_HPL_MUTE_MASK                    0x0100  /* HPL_MUTE */ +#define WM8903_HPL_MUTE_SHIFT                        8  /* HPL_MUTE */ +#define WM8903_HPL_MUTE_WIDTH                        1  /* HPL_MUTE */ +#define WM8903_HPOUTVU                          0x0080  /* HPOUTVU */ +#define WM8903_HPOUTVU_MASK                     0x0080  /* HPOUTVU */ +#define WM8903_HPOUTVU_SHIFT                         7  /* HPOUTVU */ +#define WM8903_HPOUTVU_WIDTH                         1  /* HPOUTVU */ +#define WM8903_HPOUTLZC                         0x0040  /* HPOUTLZC */ +#define WM8903_HPOUTLZC_MASK                    0x0040  /* HPOUTLZC */ +#define WM8903_HPOUTLZC_SHIFT                        6  /* HPOUTLZC */ +#define WM8903_HPOUTLZC_WIDTH                        1  /* HPOUTLZC */ +#define WM8903_HPOUTL_VOL_MASK                  0x003F  /* HPOUTL_VOL - [5:0] */ +#define WM8903_HPOUTL_VOL_SHIFT                      0  /* HPOUTL_VOL - [5:0] */ +#define WM8903_HPOUTL_VOL_WIDTH                      6  /* HPOUTL_VOL - [5:0] */ + +/* + * R58 (0x3A) - Analogue OUT1 Right + */ +#define WM8903_HPR_MUTE                         0x0100  /* HPR_MUTE */ +#define WM8903_HPR_MUTE_MASK                    0x0100  /* HPR_MUTE */ +#define WM8903_HPR_MUTE_SHIFT                        8  /* HPR_MUTE */ +#define WM8903_HPR_MUTE_WIDTH                        1  /* HPR_MUTE */ +#define WM8903_HPOUTVU                          0x0080  /* HPOUTVU */ +#define WM8903_HPOUTVU_MASK                     0x0080  /* HPOUTVU */ +#define WM8903_HPOUTVU_SHIFT                         7  /* HPOUTVU */ +#define WM8903_HPOUTVU_WIDTH                         1  /* HPOUTVU */ +#define WM8903_HPOUTRZC                         0x0040  /* HPOUTRZC */ +#define WM8903_HPOUTRZC_MASK                    0x0040  /* HPOUTRZC */ +#define WM8903_HPOUTRZC_SHIFT                        6  /* HPOUTRZC */ +#define WM8903_HPOUTRZC_WIDTH                        1  /* HPOUTRZC */ +#define WM8903_HPOUTR_VOL_MASK                  0x003F  /* HPOUTR_VOL - [5:0] */ +#define WM8903_HPOUTR_VOL_SHIFT                      0  /* HPOUTR_VOL - [5:0] */ +#define WM8903_HPOUTR_VOL_WIDTH                      6  /* HPOUTR_VOL - [5:0] */ + +/* + * R59 (0x3B) - Analogue OUT2 Left + */ +#define WM8903_LINEOUTL_MUTE                    0x0100  /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTL_MUTE_MASK               0x0100  /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTL_MUTE_SHIFT                   8  /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTL_MUTE_WIDTH                   1  /* LINEOUTL_MUTE */ +#define WM8903_LINEOUTVU                        0x0080  /* LINEOUTVU */ +#define WM8903_LINEOUTVU_MASK                   0x0080  /* LINEOUTVU */ +#define WM8903_LINEOUTVU_SHIFT                       7  /* LINEOUTVU */ +#define WM8903_LINEOUTVU_WIDTH                       1  /* LINEOUTVU */ +#define WM8903_LINEOUTLZC                       0x0040  /* LINEOUTLZC */ +#define WM8903_LINEOUTLZC_MASK                  0x0040  /* LINEOUTLZC */ +#define WM8903_LINEOUTLZC_SHIFT                      6  /* LINEOUTLZC */ +#define WM8903_LINEOUTLZC_WIDTH                      1  /* LINEOUTLZC */ +#define WM8903_LINEOUTL_VOL_MASK                0x003F  /* LINEOUTL_VOL - [5:0] */ +#define WM8903_LINEOUTL_VOL_SHIFT                    0  /* LINEOUTL_VOL - [5:0] */ +#define WM8903_LINEOUTL_VOL_WIDTH                    6  /* LINEOUTL_VOL - [5:0] */ + +/* + * R60 (0x3C) - Analogue OUT2 Right + */ +#define WM8903_LINEOUTR_MUTE                    0x0100  /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTR_MUTE_MASK               0x0100  /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTR_MUTE_SHIFT                   8  /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTR_MUTE_WIDTH                   1  /* LINEOUTR_MUTE */ +#define WM8903_LINEOUTVU                        0x0080  /* LINEOUTVU */ +#define WM8903_LINEOUTVU_MASK                   0x0080  /* LINEOUTVU */ +#define WM8903_LINEOUTVU_SHIFT                       7  /* LINEOUTVU */ +#define WM8903_LINEOUTVU_WIDTH                       1  /* LINEOUTVU */ +#define WM8903_LINEOUTRZC                       0x0040  /* LINEOUTRZC */ +#define WM8903_LINEOUTRZC_MASK                  0x0040  /* LINEOUTRZC */ +#define WM8903_LINEOUTRZC_SHIFT                      6  /* LINEOUTRZC */ +#define WM8903_LINEOUTRZC_WIDTH                      1  /* LINEOUTRZC */ +#define WM8903_LINEOUTR_VOL_MASK                0x003F  /* LINEOUTR_VOL - [5:0] */ +#define WM8903_LINEOUTR_VOL_SHIFT                    0  /* LINEOUTR_VOL - [5:0] */ +#define WM8903_LINEOUTR_VOL_WIDTH                    6  /* LINEOUTR_VOL - [5:0] */ + +/* + * R62 (0x3E) - Analogue OUT3 Left + */ +#define WM8903_SPKL_MUTE                        0x0100  /* SPKL_MUTE */ +#define WM8903_SPKL_MUTE_MASK                   0x0100  /* SPKL_MUTE */ +#define WM8903_SPKL_MUTE_SHIFT                       8  /* SPKL_MUTE */ +#define WM8903_SPKL_MUTE_WIDTH                       1  /* SPKL_MUTE */ +#define WM8903_SPKVU                            0x0080  /* SPKVU */ +#define WM8903_SPKVU_MASK                       0x0080  /* SPKVU */ +#define WM8903_SPKVU_SHIFT                           7  /* SPKVU */ +#define WM8903_SPKVU_WIDTH                           1  /* SPKVU */ +#define WM8903_SPKLZC                           0x0040  /* SPKLZC */ +#define WM8903_SPKLZC_MASK                      0x0040  /* SPKLZC */ +#define WM8903_SPKLZC_SHIFT                          6  /* SPKLZC */ +#define WM8903_SPKLZC_WIDTH                          1  /* SPKLZC */ +#define WM8903_SPKL_VOL_MASK                    0x003F  /* SPKL_VOL - [5:0] */ +#define WM8903_SPKL_VOL_SHIFT                        0  /* SPKL_VOL - [5:0] */ +#define WM8903_SPKL_VOL_WIDTH                        6  /* SPKL_VOL - [5:0] */ + +/* + * R63 (0x3F) - Analogue OUT3 Right + */ +#define WM8903_SPKR_MUTE                        0x0100  /* SPKR_MUTE */ +#define WM8903_SPKR_MUTE_MASK                   0x0100  /* SPKR_MUTE */ +#define WM8903_SPKR_MUTE_SHIFT                       8  /* SPKR_MUTE */ +#define WM8903_SPKR_MUTE_WIDTH                       1  /* SPKR_MUTE */ +#define WM8903_SPKVU                            0x0080  /* SPKVU */ +#define WM8903_SPKVU_MASK                       0x0080  /* SPKVU */ +#define WM8903_SPKVU_SHIFT                           7  /* SPKVU */ +#define WM8903_SPKVU_WIDTH                           1  /* SPKVU */ +#define WM8903_SPKRZC                           0x0040  /* SPKRZC */ +#define WM8903_SPKRZC_MASK                      0x0040  /* SPKRZC */ +#define WM8903_SPKRZC_SHIFT                          6  /* SPKRZC */ +#define WM8903_SPKRZC_WIDTH                          1  /* SPKRZC */ +#define WM8903_SPKR_VOL_MASK                    0x003F  /* SPKR_VOL - [5:0] */ +#define WM8903_SPKR_VOL_SHIFT                        0  /* SPKR_VOL - [5:0] */ +#define WM8903_SPKR_VOL_WIDTH                        6  /* SPKR_VOL - [5:0] */ + +/* + * R65 (0x41) - Analogue SPK Output Control 0 + */ +#define WM8903_SPK_DISCHARGE                    0x0002  /* SPK_DISCHARGE */ +#define WM8903_SPK_DISCHARGE_MASK               0x0002  /* SPK_DISCHARGE */ +#define WM8903_SPK_DISCHARGE_SHIFT                   1  /* SPK_DISCHARGE */ +#define WM8903_SPK_DISCHARGE_WIDTH                   1  /* SPK_DISCHARGE */ +#define WM8903_VROI                             0x0001  /* VROI */ +#define WM8903_VROI_MASK                        0x0001  /* VROI */ +#define WM8903_VROI_SHIFT                            0  /* VROI */ +#define WM8903_VROI_WIDTH                            1  /* VROI */ + +/* + * R67 (0x43) - DC Servo 0 + */ +#define WM8903_DCS_MASTER_ENA                   0x0010  /* DCS_MASTER_ENA */ +#define WM8903_DCS_MASTER_ENA_MASK              0x0010  /* DCS_MASTER_ENA */ +#define WM8903_DCS_MASTER_ENA_SHIFT                  4  /* DCS_MASTER_ENA */ +#define WM8903_DCS_MASTER_ENA_WIDTH                  1  /* DCS_MASTER_ENA */ +#define WM8903_DCS_ENA_MASK                     0x000F  /* DCS_ENA - [3:0] */ +#define WM8903_DCS_ENA_SHIFT                         0  /* DCS_ENA - [3:0] */ +#define WM8903_DCS_ENA_WIDTH                         4  /* DCS_ENA - [3:0] */ + +/* + * R69 (0x45) - DC Servo 2 + */ +#define WM8903_DCS_MODE_MASK                    0x0003  /* DCS_MODE - [1:0] */ +#define WM8903_DCS_MODE_SHIFT                        0  /* DCS_MODE - [1:0] */ +#define WM8903_DCS_MODE_WIDTH                        2  /* DCS_MODE - [1:0] */ + +/* + * R90 (0x5A) - Analogue HP 0 + */ +#define WM8903_HPL_RMV_SHORT                    0x0080  /* HPL_RMV_SHORT */ +#define WM8903_HPL_RMV_SHORT_MASK               0x0080  /* HPL_RMV_SHORT */ +#define WM8903_HPL_RMV_SHORT_SHIFT                   7  /* HPL_RMV_SHORT */ +#define WM8903_HPL_RMV_SHORT_WIDTH                   1  /* HPL_RMV_SHORT */ +#define WM8903_HPL_ENA_OUTP                     0x0040  /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_OUTP_MASK                0x0040  /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_OUTP_SHIFT                    6  /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_OUTP_WIDTH                    1  /* HPL_ENA_OUTP */ +#define WM8903_HPL_ENA_DLY                      0x0020  /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA_DLY_MASK                 0x0020  /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA_DLY_SHIFT                     5  /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA_DLY_WIDTH                     1  /* HPL_ENA_DLY */ +#define WM8903_HPL_ENA                          0x0010  /* HPL_ENA */ +#define WM8903_HPL_ENA_MASK                     0x0010  /* HPL_ENA */ +#define WM8903_HPL_ENA_SHIFT                         4  /* HPL_ENA */ +#define WM8903_HPL_ENA_WIDTH                         1  /* HPL_ENA */ +#define WM8903_HPR_RMV_SHORT                    0x0008  /* HPR_RMV_SHORT */ +#define WM8903_HPR_RMV_SHORT_MASK               0x0008  /* HPR_RMV_SHORT */ +#define WM8903_HPR_RMV_SHORT_SHIFT                   3  /* HPR_RMV_SHORT */ +#define WM8903_HPR_RMV_SHORT_WIDTH                   1  /* HPR_RMV_SHORT */ +#define WM8903_HPR_ENA_OUTP                     0x0004  /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_OUTP_MASK                0x0004  /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_OUTP_SHIFT                    2  /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_OUTP_WIDTH                    1  /* HPR_ENA_OUTP */ +#define WM8903_HPR_ENA_DLY                      0x0002  /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA_DLY_MASK                 0x0002  /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA_DLY_SHIFT                     1  /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA_DLY_WIDTH                     1  /* HPR_ENA_DLY */ +#define WM8903_HPR_ENA                          0x0001  /* HPR_ENA */ +#define WM8903_HPR_ENA_MASK                     0x0001  /* HPR_ENA */ +#define WM8903_HPR_ENA_SHIFT                         0  /* HPR_ENA */ +#define WM8903_HPR_ENA_WIDTH                         1  /* HPR_ENA */ + +/* + * R94 (0x5E) - Analogue Lineout 0 + */ +#define WM8903_LINEOUTL_RMV_SHORT               0x0080  /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_RMV_SHORT_MASK          0x0080  /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_RMV_SHORT_SHIFT              7  /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_RMV_SHORT_WIDTH              1  /* LINEOUTL_RMV_SHORT */ +#define WM8903_LINEOUTL_ENA_OUTP                0x0040  /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_OUTP_MASK           0x0040  /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_OUTP_SHIFT               6  /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_OUTP_WIDTH               1  /* LINEOUTL_ENA_OUTP */ +#define WM8903_LINEOUTL_ENA_DLY                 0x0020  /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA_DLY_MASK            0x0020  /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA_DLY_SHIFT                5  /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA_DLY_WIDTH                1  /* LINEOUTL_ENA_DLY */ +#define WM8903_LINEOUTL_ENA                     0x0010  /* LINEOUTL_ENA */ +#define WM8903_LINEOUTL_ENA_MASK                0x0010  /* LINEOUTL_ENA */ +#define WM8903_LINEOUTL_ENA_SHIFT                    4  /* LINEOUTL_ENA */ +#define WM8903_LINEOUTL_ENA_WIDTH                    1  /* LINEOUTL_ENA */ +#define WM8903_LINEOUTR_RMV_SHORT               0x0008  /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_RMV_SHORT_MASK          0x0008  /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_RMV_SHORT_SHIFT              3  /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_RMV_SHORT_WIDTH              1  /* LINEOUTR_RMV_SHORT */ +#define WM8903_LINEOUTR_ENA_OUTP                0x0004  /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_OUTP_MASK           0x0004  /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_OUTP_SHIFT               2  /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_OUTP_WIDTH               1  /* LINEOUTR_ENA_OUTP */ +#define WM8903_LINEOUTR_ENA_DLY                 0x0002  /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA_DLY_MASK            0x0002  /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA_DLY_SHIFT                1  /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA_DLY_WIDTH                1  /* LINEOUTR_ENA_DLY */ +#define WM8903_LINEOUTR_ENA                     0x0001  /* LINEOUTR_ENA */ +#define WM8903_LINEOUTR_ENA_MASK                0x0001  /* LINEOUTR_ENA */ +#define WM8903_LINEOUTR_ENA_SHIFT                    0  /* LINEOUTR_ENA */ +#define WM8903_LINEOUTR_ENA_WIDTH                    1  /* LINEOUTR_ENA */ + +/* + * R98 (0x62) - Charge Pump 0 + */ +#define WM8903_CP_ENA                           0x0001  /* CP_ENA */ +#define WM8903_CP_ENA_MASK                      0x0001  /* CP_ENA */ +#define WM8903_CP_ENA_SHIFT                          0  /* CP_ENA */ +#define WM8903_CP_ENA_WIDTH                          1  /* CP_ENA */ + +/* + * R104 (0x68) - Class W 0 + */ +#define WM8903_CP_DYN_FREQ                      0x0002  /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_FREQ_MASK                 0x0002  /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_FREQ_SHIFT                     1  /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_FREQ_WIDTH                     1  /* CP_DYN_FREQ */ +#define WM8903_CP_DYN_V                         0x0001  /* CP_DYN_V */ +#define WM8903_CP_DYN_V_MASK                    0x0001  /* CP_DYN_V */ +#define WM8903_CP_DYN_V_SHIFT                        0  /* CP_DYN_V */ +#define WM8903_CP_DYN_V_WIDTH                        1  /* CP_DYN_V */ + +/* + * R108 (0x6C) - Write Sequencer 0 + */ +#define WM8903_WSEQ_ENA                         0x0100  /* WSEQ_ENA */ +#define WM8903_WSEQ_ENA_MASK                    0x0100  /* WSEQ_ENA */ +#define WM8903_WSEQ_ENA_SHIFT                        8  /* WSEQ_ENA */ +#define WM8903_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */ +#define WM8903_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8903_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */ +#define WM8903_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */ + +/* + * R109 (0x6D) - Write Sequencer 1 + */ +#define WM8903_WSEQ_DATA_WIDTH_MASK             0x7000  /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8903_WSEQ_DATA_WIDTH_SHIFT                12  /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8903_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [14:12] */ +#define WM8903_WSEQ_DATA_START_MASK             0x0F00  /* WSEQ_DATA_START - [11:8] */ +#define WM8903_WSEQ_DATA_START_SHIFT                 8  /* WSEQ_DATA_START - [11:8] */ +#define WM8903_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [11:8] */ +#define WM8903_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */ +#define WM8903_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */ +#define WM8903_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */ + +/* + * R110 (0x6E) - Write Sequencer 2 + */ +#define WM8903_WSEQ_EOS                         0x4000  /* WSEQ_EOS */ +#define WM8903_WSEQ_EOS_MASK                    0x4000  /* WSEQ_EOS */ +#define WM8903_WSEQ_EOS_SHIFT                       14  /* WSEQ_EOS */ +#define WM8903_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */ +#define WM8903_WSEQ_DELAY_MASK                  0x0F00  /* WSEQ_DELAY - [11:8] */ +#define WM8903_WSEQ_DELAY_SHIFT                      8  /* WSEQ_DELAY - [11:8] */ +#define WM8903_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [11:8] */ +#define WM8903_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */ +#define WM8903_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */ +#define WM8903_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */ + +/* + * R111 (0x6F) - Write Sequencer 3 + */ +#define WM8903_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */ +#define WM8903_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */ +#define WM8903_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */ +#define WM8903_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */ +#define WM8903_WSEQ_START                       0x0100  /* WSEQ_START */ +#define WM8903_WSEQ_START_MASK                  0x0100  /* WSEQ_START */ +#define WM8903_WSEQ_START_SHIFT                      8  /* WSEQ_START */ +#define WM8903_WSEQ_START_WIDTH                      1  /* WSEQ_START */ +#define WM8903_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */ +#define WM8903_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */ +#define WM8903_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */ + +/* + * R112 (0x70) - Write Sequencer 4 + */ +#define WM8903_WSEQ_CURRENT_INDEX_MASK          0x03F0  /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8903_WSEQ_CURRENT_INDEX_SHIFT              4  /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8903_WSEQ_CURRENT_INDEX_WIDTH              6  /* WSEQ_CURRENT_INDEX - [9:4] */ +#define WM8903_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */ +#define WM8903_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */ +#define WM8903_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */ +#define WM8903_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */ + +/* + * R114 (0x72) - Control Interface + */ +#define WM8903_MASK_WRITE_ENA                   0x0001  /* MASK_WRITE_ENA */ +#define WM8903_MASK_WRITE_ENA_MASK              0x0001  /* MASK_WRITE_ENA */ +#define WM8903_MASK_WRITE_ENA_SHIFT                  0  /* MASK_WRITE_ENA */ +#define WM8903_MASK_WRITE_ENA_WIDTH                  1  /* MASK_WRITE_ENA */ + +/* + * R116 (0x74) - GPIO Control 1 + */ +#define WM8903_GP1_FN_MASK                      0x1F00  /* GP1_FN - [12:8] */ +#define WM8903_GP1_FN_SHIFT                          8  /* GP1_FN - [12:8] */ +#define WM8903_GP1_FN_WIDTH                          5  /* GP1_FN - [12:8] */ +#define WM8903_GP1_DIR                          0x0080  /* GP1_DIR */ +#define WM8903_GP1_DIR_MASK                     0x0080  /* GP1_DIR */ +#define WM8903_GP1_DIR_SHIFT                         7  /* GP1_DIR */ +#define WM8903_GP1_DIR_WIDTH                         1  /* GP1_DIR */ +#define WM8903_GP1_OP_CFG                       0x0040  /* GP1_OP_CFG */ +#define WM8903_GP1_OP_CFG_MASK                  0x0040  /* GP1_OP_CFG */ +#define WM8903_GP1_OP_CFG_SHIFT                      6  /* GP1_OP_CFG */ +#define WM8903_GP1_OP_CFG_WIDTH                      1  /* GP1_OP_CFG */ +#define WM8903_GP1_IP_CFG                       0x0020  /* GP1_IP_CFG */ +#define WM8903_GP1_IP_CFG_MASK                  0x0020  /* GP1_IP_CFG */ +#define WM8903_GP1_IP_CFG_SHIFT                      5  /* GP1_IP_CFG */ +#define WM8903_GP1_IP_CFG_WIDTH                      1  /* GP1_IP_CFG */ +#define WM8903_GP1_LVL                          0x0010  /* GP1_LVL */ +#define WM8903_GP1_LVL_MASK                     0x0010  /* GP1_LVL */ +#define WM8903_GP1_LVL_SHIFT                         4  /* GP1_LVL */ +#define WM8903_GP1_LVL_WIDTH                         1  /* GP1_LVL */ +#define WM8903_GP1_PD                           0x0008  /* GP1_PD */ +#define WM8903_GP1_PD_MASK                      0x0008  /* GP1_PD */ +#define WM8903_GP1_PD_SHIFT                          3  /* GP1_PD */ +#define WM8903_GP1_PD_WIDTH                          1  /* GP1_PD */ +#define WM8903_GP1_PU                           0x0004  /* GP1_PU */ +#define WM8903_GP1_PU_MASK                      0x0004  /* GP1_PU */ +#define WM8903_GP1_PU_SHIFT                          2  /* GP1_PU */ +#define WM8903_GP1_PU_WIDTH                          1  /* GP1_PU */ +#define WM8903_GP1_INTMODE                      0x0002  /* GP1_INTMODE */ +#define WM8903_GP1_INTMODE_MASK                 0x0002  /* GP1_INTMODE */ +#define WM8903_GP1_INTMODE_SHIFT                     1  /* GP1_INTMODE */ +#define WM8903_GP1_INTMODE_WIDTH                     1  /* GP1_INTMODE */ +#define WM8903_GP1_DB                           0x0001  /* GP1_DB */ +#define WM8903_GP1_DB_MASK                      0x0001  /* GP1_DB */ +#define WM8903_GP1_DB_SHIFT                          0  /* GP1_DB */ +#define WM8903_GP1_DB_WIDTH                          1  /* GP1_DB */ + +/* + * R117 (0x75) - GPIO Control 2 + */ +#define WM8903_GP2_FN_MASK                      0x1F00  /* GP2_FN - [12:8] */ +#define WM8903_GP2_FN_SHIFT                          8  /* GP2_FN - [12:8] */ +#define WM8903_GP2_FN_WIDTH                          5  /* GP2_FN - [12:8] */ +#define WM8903_GP2_DIR                          0x0080  /* GP2_DIR */ +#define WM8903_GP2_DIR_MASK                     0x0080  /* GP2_DIR */ +#define WM8903_GP2_DIR_SHIFT                         7  /* GP2_DIR */ +#define WM8903_GP2_DIR_WIDTH                         1  /* GP2_DIR */ +#define WM8903_GP2_OP_CFG                       0x0040  /* GP2_OP_CFG */ +#define WM8903_GP2_OP_CFG_MASK                  0x0040  /* GP2_OP_CFG */ +#define WM8903_GP2_OP_CFG_SHIFT                      6  /* GP2_OP_CFG */ +#define WM8903_GP2_OP_CFG_WIDTH                      1  /* GP2_OP_CFG */ +#define WM8903_GP2_IP_CFG                       0x0020  /* GP2_IP_CFG */ +#define WM8903_GP2_IP_CFG_MASK                  0x0020  /* GP2_IP_CFG */ +#define WM8903_GP2_IP_CFG_SHIFT                      5  /* GP2_IP_CFG */ +#define WM8903_GP2_IP_CFG_WIDTH                      1  /* GP2_IP_CFG */ +#define WM8903_GP2_LVL                          0x0010  /* GP2_LVL */ +#define WM8903_GP2_LVL_MASK                     0x0010  /* GP2_LVL */ +#define WM8903_GP2_LVL_SHIFT                         4  /* GP2_LVL */ +#define WM8903_GP2_LVL_WIDTH                         1  /* GP2_LVL */ +#define WM8903_GP2_PD                           0x0008  /* GP2_PD */ +#define WM8903_GP2_PD_MASK                      0x0008  /* GP2_PD */ +#define WM8903_GP2_PD_SHIFT                          3  /* GP2_PD */ +#define WM8903_GP2_PD_WIDTH                          1  /* GP2_PD */ +#define WM8903_GP2_PU                           0x0004  /* GP2_PU */ +#define WM8903_GP2_PU_MASK                      0x0004  /* GP2_PU */ +#define WM8903_GP2_PU_SHIFT                          2  /* GP2_PU */ +#define WM8903_GP2_PU_WIDTH                          1  /* GP2_PU */ +#define WM8903_GP2_INTMODE                      0x0002  /* GP2_INTMODE */ +#define WM8903_GP2_INTMODE_MASK                 0x0002  /* GP2_INTMODE */ +#define WM8903_GP2_INTMODE_SHIFT                     1  /* GP2_INTMODE */ +#define WM8903_GP2_INTMODE_WIDTH                     1  /* GP2_INTMODE */ +#define WM8903_GP2_DB                           0x0001  /* GP2_DB */ +#define WM8903_GP2_DB_MASK                      0x0001  /* GP2_DB */ +#define WM8903_GP2_DB_SHIFT                          0  /* GP2_DB */ +#define WM8903_GP2_DB_WIDTH                          1  /* GP2_DB */ + +/* + * R118 (0x76) - GPIO Control 3 + */ +#define WM8903_GP3_FN_MASK                      0x1F00  /* GP3_FN - [12:8] */ +#define WM8903_GP3_FN_SHIFT                          8  /* GP3_FN - [12:8] */ +#define WM8903_GP3_FN_WIDTH                          5  /* GP3_FN - [12:8] */ +#define WM8903_GP3_DIR                          0x0080  /* GP3_DIR */ +#define WM8903_GP3_DIR_MASK                     0x0080  /* GP3_DIR */ +#define WM8903_GP3_DIR_SHIFT                         7  /* GP3_DIR */ +#define WM8903_GP3_DIR_WIDTH                         1  /* GP3_DIR */ +#define WM8903_GP3_OP_CFG                       0x0040  /* GP3_OP_CFG */ +#define WM8903_GP3_OP_CFG_MASK                  0x0040  /* GP3_OP_CFG */ +#define WM8903_GP3_OP_CFG_SHIFT                      6  /* GP3_OP_CFG */ +#define WM8903_GP3_OP_CFG_WIDTH                      1  /* GP3_OP_CFG */ +#define WM8903_GP3_IP_CFG                       0x0020  /* GP3_IP_CFG */ +#define WM8903_GP3_IP_CFG_MASK                  0x0020  /* GP3_IP_CFG */ +#define WM8903_GP3_IP_CFG_SHIFT                      5  /* GP3_IP_CFG */ +#define WM8903_GP3_IP_CFG_WIDTH                      1  /* GP3_IP_CFG */ +#define WM8903_GP3_LVL                          0x0010  /* GP3_LVL */ +#define WM8903_GP3_LVL_MASK                     0x0010  /* GP3_LVL */ +#define WM8903_GP3_LVL_SHIFT                         4  /* GP3_LVL */ +#define WM8903_GP3_LVL_WIDTH                         1  /* GP3_LVL */ +#define WM8903_GP3_PD                           0x0008  /* GP3_PD */ +#define WM8903_GP3_PD_MASK                      0x0008  /* GP3_PD */ +#define WM8903_GP3_PD_SHIFT                          3  /* GP3_PD */ +#define WM8903_GP3_PD_WIDTH                          1  /* GP3_PD */ +#define WM8903_GP3_PU                           0x0004  /* GP3_PU */ +#define WM8903_GP3_PU_MASK                      0x0004  /* GP3_PU */ +#define WM8903_GP3_PU_SHIFT                          2  /* GP3_PU */ +#define WM8903_GP3_PU_WIDTH                          1  /* GP3_PU */ +#define WM8903_GP3_INTMODE                      0x0002  /* GP3_INTMODE */ +#define WM8903_GP3_INTMODE_MASK                 0x0002  /* GP3_INTMODE */ +#define WM8903_GP3_INTMODE_SHIFT                     1  /* GP3_INTMODE */ +#define WM8903_GP3_INTMODE_WIDTH                     1  /* GP3_INTMODE */ +#define WM8903_GP3_DB                           0x0001  /* GP3_DB */ +#define WM8903_GP3_DB_MASK                      0x0001  /* GP3_DB */ +#define WM8903_GP3_DB_SHIFT                          0  /* GP3_DB */ +#define WM8903_GP3_DB_WIDTH                          1  /* GP3_DB */ + +/* + * R119 (0x77) - GPIO Control 4 + */ +#define WM8903_GP4_FN_MASK                      0x1F00  /* GP4_FN - [12:8] */ +#define WM8903_GP4_FN_SHIFT                          8  /* GP4_FN - [12:8] */ +#define WM8903_GP4_FN_WIDTH                          5  /* GP4_FN - [12:8] */ +#define WM8903_GP4_DIR                          0x0080  /* GP4_DIR */ +#define WM8903_GP4_DIR_MASK                     0x0080  /* GP4_DIR */ +#define WM8903_GP4_DIR_SHIFT                         7  /* GP4_DIR */ +#define WM8903_GP4_DIR_WIDTH                         1  /* GP4_DIR */ +#define WM8903_GP4_OP_CFG                       0x0040  /* GP4_OP_CFG */ +#define WM8903_GP4_OP_CFG_MASK                  0x0040  /* GP4_OP_CFG */ +#define WM8903_GP4_OP_CFG_SHIFT                      6  /* GP4_OP_CFG */ +#define WM8903_GP4_OP_CFG_WIDTH                      1  /* GP4_OP_CFG */ +#define WM8903_GP4_IP_CFG                       0x0020  /* GP4_IP_CFG */ +#define WM8903_GP4_IP_CFG_MASK                  0x0020  /* GP4_IP_CFG */ +#define WM8903_GP4_IP_CFG_SHIFT                      5  /* GP4_IP_CFG */ +#define WM8903_GP4_IP_CFG_WIDTH                      1  /* GP4_IP_CFG */ +#define WM8903_GP4_LVL                          0x0010  /* GP4_LVL */ +#define WM8903_GP4_LVL_MASK                     0x0010  /* GP4_LVL */ +#define WM8903_GP4_LVL_SHIFT                         4  /* GP4_LVL */ +#define WM8903_GP4_LVL_WIDTH                         1  /* GP4_LVL */ +#define WM8903_GP4_PD                           0x0008  /* GP4_PD */ +#define WM8903_GP4_PD_MASK                      0x0008  /* GP4_PD */ +#define WM8903_GP4_PD_SHIFT                          3  /* GP4_PD */ +#define WM8903_GP4_PD_WIDTH                          1  /* GP4_PD */ +#define WM8903_GP4_PU                           0x0004  /* GP4_PU */ +#define WM8903_GP4_PU_MASK                      0x0004  /* GP4_PU */ +#define WM8903_GP4_PU_SHIFT                          2  /* GP4_PU */ +#define WM8903_GP4_PU_WIDTH                          1  /* GP4_PU */ +#define WM8903_GP4_INTMODE                      0x0002  /* GP4_INTMODE */ +#define WM8903_GP4_INTMODE_MASK                 0x0002  /* GP4_INTMODE */ +#define WM8903_GP4_INTMODE_SHIFT                     1  /* GP4_INTMODE */ +#define WM8903_GP4_INTMODE_WIDTH                     1  /* GP4_INTMODE */ +#define WM8903_GP4_DB                           0x0001  /* GP4_DB */ +#define WM8903_GP4_DB_MASK                      0x0001  /* GP4_DB */ +#define WM8903_GP4_DB_SHIFT                          0  /* GP4_DB */ +#define WM8903_GP4_DB_WIDTH                          1  /* GP4_DB */ + +/* + * R120 (0x78) - GPIO Control 5 + */ +#define WM8903_GP5_FN_MASK                      0x1F00  /* GP5_FN - [12:8] */ +#define WM8903_GP5_FN_SHIFT                          8  /* GP5_FN - [12:8] */ +#define WM8903_GP5_FN_WIDTH                          5  /* GP5_FN - [12:8] */ +#define WM8903_GP5_DIR                          0x0080  /* GP5_DIR */ +#define WM8903_GP5_DIR_MASK                     0x0080  /* GP5_DIR */ +#define WM8903_GP5_DIR_SHIFT                         7  /* GP5_DIR */ +#define WM8903_GP5_DIR_WIDTH                         1  /* GP5_DIR */ +#define WM8903_GP5_OP_CFG                       0x0040  /* GP5_OP_CFG */ +#define WM8903_GP5_OP_CFG_MASK                  0x0040  /* GP5_OP_CFG */ +#define WM8903_GP5_OP_CFG_SHIFT                      6  /* GP5_OP_CFG */ +#define WM8903_GP5_OP_CFG_WIDTH                      1  /* GP5_OP_CFG */ +#define WM8903_GP5_IP_CFG                       0x0020  /* GP5_IP_CFG */ +#define WM8903_GP5_IP_CFG_MASK                  0x0020  /* GP5_IP_CFG */ +#define WM8903_GP5_IP_CFG_SHIFT                      5  /* GP5_IP_CFG */ +#define WM8903_GP5_IP_CFG_WIDTH                      1  /* GP5_IP_CFG */ +#define WM8903_GP5_LVL                          0x0010  /* GP5_LVL */ +#define WM8903_GP5_LVL_MASK                     0x0010  /* GP5_LVL */ +#define WM8903_GP5_LVL_SHIFT                         4  /* GP5_LVL */ +#define WM8903_GP5_LVL_WIDTH                         1  /* GP5_LVL */ +#define WM8903_GP5_PD                           0x0008  /* GP5_PD */ +#define WM8903_GP5_PD_MASK                      0x0008  /* GP5_PD */ +#define WM8903_GP5_PD_SHIFT                          3  /* GP5_PD */ +#define WM8903_GP5_PD_WIDTH                          1  /* GP5_PD */ +#define WM8903_GP5_PU                           0x0004  /* GP5_PU */ +#define WM8903_GP5_PU_MASK                      0x0004  /* GP5_PU */ +#define WM8903_GP5_PU_SHIFT                          2  /* GP5_PU */ +#define WM8903_GP5_PU_WIDTH                          1  /* GP5_PU */ +#define WM8903_GP5_INTMODE                      0x0002  /* GP5_INTMODE */ +#define WM8903_GP5_INTMODE_MASK                 0x0002  /* GP5_INTMODE */ +#define WM8903_GP5_INTMODE_SHIFT                     1  /* GP5_INTMODE */ +#define WM8903_GP5_INTMODE_WIDTH                     1  /* GP5_INTMODE */ +#define WM8903_GP5_DB                           0x0001  /* GP5_DB */ +#define WM8903_GP5_DB_MASK                      0x0001  /* GP5_DB */ +#define WM8903_GP5_DB_SHIFT                          0  /* GP5_DB */ +#define WM8903_GP5_DB_WIDTH                          1  /* GP5_DB */ + +/* + * R121 (0x79) - Interrupt Status 1 + */ +#define WM8903_MICSHRT_EINT                     0x8000  /* MICSHRT_EINT */ +#define WM8903_MICSHRT_EINT_MASK                0x8000  /* MICSHRT_EINT */ +#define WM8903_MICSHRT_EINT_SHIFT                   15  /* MICSHRT_EINT */ +#define WM8903_MICSHRT_EINT_WIDTH                    1  /* MICSHRT_EINT */ +#define WM8903_MICDET_EINT                      0x4000  /* MICDET_EINT */ +#define WM8903_MICDET_EINT_MASK                 0x4000  /* MICDET_EINT */ +#define WM8903_MICDET_EINT_SHIFT                    14  /* MICDET_EINT */ +#define WM8903_MICDET_EINT_WIDTH                     1  /* MICDET_EINT */ +#define WM8903_WSEQ_BUSY_EINT                   0x2000  /* WSEQ_BUSY_EINT */ +#define WM8903_WSEQ_BUSY_EINT_MASK              0x2000  /* WSEQ_BUSY_EINT */ +#define WM8903_WSEQ_BUSY_EINT_SHIFT                 13  /* WSEQ_BUSY_EINT */ +#define WM8903_WSEQ_BUSY_EINT_WIDTH                  1  /* WSEQ_BUSY_EINT */ +#define WM8903_GP5_EINT                         0x0010  /* GP5_EINT */ +#define WM8903_GP5_EINT_MASK                    0x0010  /* GP5_EINT */ +#define WM8903_GP5_EINT_SHIFT                        4  /* GP5_EINT */ +#define WM8903_GP5_EINT_WIDTH                        1  /* GP5_EINT */ +#define WM8903_GP4_EINT                         0x0008  /* GP4_EINT */ +#define WM8903_GP4_EINT_MASK                    0x0008  /* GP4_EINT */ +#define WM8903_GP4_EINT_SHIFT                        3  /* GP4_EINT */ +#define WM8903_GP4_EINT_WIDTH                        1  /* GP4_EINT */ +#define WM8903_GP3_EINT                         0x0004  /* GP3_EINT */ +#define WM8903_GP3_EINT_MASK                    0x0004  /* GP3_EINT */ +#define WM8903_GP3_EINT_SHIFT                        2  /* GP3_EINT */ +#define WM8903_GP3_EINT_WIDTH                        1  /* GP3_EINT */ +#define WM8903_GP2_EINT                         0x0002  /* GP2_EINT */ +#define WM8903_GP2_EINT_MASK                    0x0002  /* GP2_EINT */ +#define WM8903_GP2_EINT_SHIFT                        1  /* GP2_EINT */ +#define WM8903_GP2_EINT_WIDTH                        1  /* GP2_EINT */ +#define WM8903_GP1_EINT                         0x0001  /* GP1_EINT */ +#define WM8903_GP1_EINT_MASK                    0x0001  /* GP1_EINT */ +#define WM8903_GP1_EINT_SHIFT                        0  /* GP1_EINT */ +#define WM8903_GP1_EINT_WIDTH                        1  /* GP1_EINT */ + +/* + * R122 (0x7A) - Interrupt Status 1 Mask + */ +#define WM8903_IM_MICSHRT_EINT                  0x8000  /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICSHRT_EINT_MASK             0x8000  /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICSHRT_EINT_SHIFT                15  /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICSHRT_EINT_WIDTH                 1  /* IM_MICSHRT_EINT */ +#define WM8903_IM_MICDET_EINT                   0x4000  /* IM_MICDET_EINT */ +#define WM8903_IM_MICDET_EINT_MASK              0x4000  /* IM_MICDET_EINT */ +#define WM8903_IM_MICDET_EINT_SHIFT                 14  /* IM_MICDET_EINT */ +#define WM8903_IM_MICDET_EINT_WIDTH                  1  /* IM_MICDET_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT                0x2000  /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT_MASK           0x2000  /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT_SHIFT              13  /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_WSEQ_BUSY_EINT_WIDTH               1  /* IM_WSEQ_BUSY_EINT */ +#define WM8903_IM_GP5_EINT                      0x0010  /* IM_GP5_EINT */ +#define WM8903_IM_GP5_EINT_MASK                 0x0010  /* IM_GP5_EINT */ +#define WM8903_IM_GP5_EINT_SHIFT                     4  /* IM_GP5_EINT */ +#define WM8903_IM_GP5_EINT_WIDTH                     1  /* IM_GP5_EINT */ +#define WM8903_IM_GP4_EINT                      0x0008  /* IM_GP4_EINT */ +#define WM8903_IM_GP4_EINT_MASK                 0x0008  /* IM_GP4_EINT */ +#define WM8903_IM_GP4_EINT_SHIFT                     3  /* IM_GP4_EINT */ +#define WM8903_IM_GP4_EINT_WIDTH                     1  /* IM_GP4_EINT */ +#define WM8903_IM_GP3_EINT                      0x0004  /* IM_GP3_EINT */ +#define WM8903_IM_GP3_EINT_MASK                 0x0004  /* IM_GP3_EINT */ +#define WM8903_IM_GP3_EINT_SHIFT                     2  /* IM_GP3_EINT */ +#define WM8903_IM_GP3_EINT_WIDTH                     1  /* IM_GP3_EINT */ +#define WM8903_IM_GP2_EINT                      0x0002  /* IM_GP2_EINT */ +#define WM8903_IM_GP2_EINT_MASK                 0x0002  /* IM_GP2_EINT */ +#define WM8903_IM_GP2_EINT_SHIFT                     1  /* IM_GP2_EINT */ +#define WM8903_IM_GP2_EINT_WIDTH                     1  /* IM_GP2_EINT */ +#define WM8903_IM_GP1_EINT                      0x0001  /* IM_GP1_EINT */ +#define WM8903_IM_GP1_EINT_MASK                 0x0001  /* IM_GP1_EINT */ +#define WM8903_IM_GP1_EINT_SHIFT                     0  /* IM_GP1_EINT */ +#define WM8903_IM_GP1_EINT_WIDTH                     1  /* IM_GP1_EINT */ + +/* + * R123 (0x7B) - Interrupt Polarity 1 + */ +#define WM8903_MICSHRT_INV                      0x8000  /* MICSHRT_INV */ +#define WM8903_MICSHRT_INV_MASK                 0x8000  /* MICSHRT_INV */ +#define WM8903_MICSHRT_INV_SHIFT                    15  /* MICSHRT_INV */ +#define WM8903_MICSHRT_INV_WIDTH                     1  /* MICSHRT_INV */ +#define WM8903_MICDET_INV                       0x4000  /* MICDET_INV */ +#define WM8903_MICDET_INV_MASK                  0x4000  /* MICDET_INV */ +#define WM8903_MICDET_INV_SHIFT                     14  /* MICDET_INV */ +#define WM8903_MICDET_INV_WIDTH                      1  /* MICDET_INV */ + +/* + * R126 (0x7E) - Interrupt Control + */ +#define WM8903_IRQ_POL                          0x0001  /* IRQ_POL */ +#define WM8903_IRQ_POL_MASK                     0x0001  /* IRQ_POL */ +#define WM8903_IRQ_POL_SHIFT                         0  /* IRQ_POL */ +#define WM8903_IRQ_POL_WIDTH                         1  /* IRQ_POL */ + +/* + * R129 (0x81) - Control Interface Test 1 + */ +#define WM8903_USER_KEY                         0x0002  /* USER_KEY */ +#define WM8903_USER_KEY_MASK                    0x0002  /* USER_KEY */ +#define WM8903_USER_KEY_SHIFT                        1  /* USER_KEY */ +#define WM8903_USER_KEY_WIDTH                        1  /* USER_KEY */ +#define WM8903_TEST_KEY                         0x0001  /* TEST_KEY */ +#define WM8903_TEST_KEY_MASK                    0x0001  /* TEST_KEY */ +#define WM8903_TEST_KEY_SHIFT                        0  /* TEST_KEY */ +#define WM8903_TEST_KEY_WIDTH                        1  /* TEST_KEY */ + +/* + * R149 (0x95) - Charge Pump Test 1 + */ +#define WM8903_CP_SW_KELVIN_MODE_MASK           0x0006  /* CP_SW_KELVIN_MODE - [2:1] */ +#define WM8903_CP_SW_KELVIN_MODE_SHIFT               1  /* CP_SW_KELVIN_MODE - [2:1] */ +#define WM8903_CP_SW_KELVIN_MODE_WIDTH               2  /* CP_SW_KELVIN_MODE - [2:1] */ + +/* + * R164 (0xA4) - Clock Rate Test 4 + */ +#define WM8903_ADC_DIG_MIC                      0x0200  /* ADC_DIG_MIC */ +#define WM8903_ADC_DIG_MIC_MASK                 0x0200  /* ADC_DIG_MIC */ +#define WM8903_ADC_DIG_MIC_SHIFT                     9  /* ADC_DIG_MIC */ +#define WM8903_ADC_DIG_MIC_WIDTH                     1  /* ADC_DIG_MIC */ + +/* + * R172 (0xAC) - Analogue Output Bias 0 + */ +#define WM8903_PGA_BIAS_MASK                    0x0070  /* PGA_BIAS - [6:4] */ +#define WM8903_PGA_BIAS_SHIFT                        4  /* PGA_BIAS - [6:4] */ +#define WM8903_PGA_BIAS_WIDTH                        3  /* PGA_BIAS - [6:4] */ + +#endif diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c new file mode 100644 index 0000000..974a4cd --- /dev/null +++ b/sound/soc/codecs/wm8971.c @@ -0,0 +1,942 @@ +/* + * wm8971.c  --  WM8971 ALSA SoC Audio driver + * + * Copyright 2005 Lab126, Inc. + * + * Author: Kenneth Kiraly <kiraly@lab126.com> + * + * Based on wm8753.c by Liam Girdwood + * + *  This program is free software; you can redistribute  it and/or modify it + *  under  the terms of  the GNU General  Public License as published by the + *  Free Software Foundation;  either version 2 of the  License, or (at your + *  option) any later version. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/initval.h> + +#include "wm8971.h" + +#define AUDIO_NAME "wm8971" +#define WM8971_VERSION "0.9" + +#define	WM8971_REG_COUNT		43 + +static struct workqueue_struct *wm8971_workq = NULL; + +/* codec private data */ +struct wm8971_priv { +	unsigned int sysclk; +}; + +/* + * wm8971 register cache + * We can't read the WM8971 register space when we + * are using 2 wire for device control, so we cache them instead. + */ +static const u16 wm8971_reg[] = { +	0x0097, 0x0097, 0x0079, 0x0079,  /*  0 */ +	0x0000, 0x0008, 0x0000, 0x000a,  /*  4 */ +	0x0000, 0x0000, 0x00ff, 0x00ff,  /*  8 */ +	0x000f, 0x000f, 0x0000, 0x0000,  /* 12 */ +	0x0000, 0x007b, 0x0000, 0x0032,  /* 16 */ +	0x0000, 0x00c3, 0x00c3, 0x00c0,  /* 20 */ +	0x0000, 0x0000, 0x0000, 0x0000,  /* 24 */ +	0x0000, 0x0000, 0x0000, 0x0000,  /* 28 */ +	0x0000, 0x0000, 0x0050, 0x0050,  /* 32 */ +	0x0050, 0x0050, 0x0050, 0x0050,  /* 36 */ +	0x0079, 0x0079, 0x0079,          /* 40 */ +}; + +static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec, +	unsigned int reg) +{ +	u16 *cache = codec->reg_cache; +	if (reg < WM8971_REG_COUNT) +		return cache[reg]; + +	return -1; +} + +static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec, +	unsigned int reg, unsigned int value) +{ +	u16 *cache = codec->reg_cache; +	if (reg < WM8971_REG_COUNT) +		cache[reg] = value; +} + +static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg, +	unsigned int value) +{ +	u8 data[2]; + +	/* data is +	 *   D15..D9 WM8753 register offset +	 *   D8...D0 register data +	 */ +	data[0] = (reg << 1) | ((value >> 8) & 0x0001); +	data[1] = value & 0x00ff; + +	wm8971_write_reg_cache (codec, reg, value); +	if (codec->hw_write(codec->control_data, data, 2) == 2) +		return 0; +	else +		return -EIO; +} + +#define wm8971_reset(c)	wm8971_write(c, WM8971_RESET, 0) + +/* WM8971 Controls */ +static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" }; +static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz", +	"200Hz @ 48kHz" }; +static const char *wm8971_treble[] = { "8kHz", "4kHz" }; +static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" }; +static const char *wm8971_ng_type[] = { "Constant PGA Gain", +	"Mute ADC Output" }; +static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; +static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)", +	"Mono (Right)", "Digital Mono"}; +static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" }; +static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA", +	"Differential"}; +static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA", +	"Differential"}; +static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"}; +static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"}; +static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert", +	"L + R Invert"}; + +static const struct soc_enum wm8971_enum[] = { +	SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass),	/* 0 */ +	SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter), +	SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble), +	SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func), +	SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type),    /* 4 */ +	SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp), +	SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux), +	SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase), +	SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */ +	SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux), +	SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel), +	SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel), +	SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol),    /* 12 */ +	SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux), +}; + +static const struct snd_kcontrol_new wm8971_snd_controls[] = { +	SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0), +	SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL, +		     6, 1, 0), +	SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1), + +	SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V, +		WM8971_ROUT1V, 7, 1, 0), +	SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V, +		WM8971_ROUT2V, 7, 1, 0), +	SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0), + +	SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0), + +	SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1, +		WM8971_LOUTM2, 4, 7, 1), +	SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1, +		WM8971_ROUTM2, 4, 7, 1), +	SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1, +		WM8971_MOUTM2, 4, 7, 1), + +	SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V, +		WM8971_ROUT1V, 0, 127, 0), +	SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V, +		WM8971_ROUT2V, 0, 127, 0), + +	SOC_ENUM("Bass Boost", wm8971_enum[0]), +	SOC_ENUM("Bass Filter", wm8971_enum[1]), +	SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1), + +	SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0), +	SOC_ENUM("Treble Cut-off", wm8971_enum[2]), + +	SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1), + +	SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0), +	SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0), + +	SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0), +	SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0), +	SOC_ENUM("ALC Capture Function", wm8971_enum[3]), +	SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0), +	SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0), +	SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0), +	SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0), +	SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0), +	SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]), +	SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0), + +	SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0), +	SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0), + +	SOC_ENUM("Playback De-emphasis", wm8971_enum[5]), +	SOC_ENUM("Playback Function", wm8971_enum[6]), +	SOC_ENUM("Playback Phase", wm8971_enum[7]), + +	SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0), +}; + +/* add non-DAPM controls */ +static int wm8971_add_controls(struct snd_soc_codec *codec) +{ +	int err, i; + +	for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) { +		err = snd_ctl_add(codec->card, +				snd_soc_cnew(&wm8971_snd_controls[i], +					     codec, NULL)); +		if (err < 0) +			return err; +	} +	return 0; +} + +/* + * DAPM Controls + */ + +/* Left Mixer */ +static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = { +SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0), +}; + +/* Right Mixer */ +static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0), +}; + +/* Mono Mixer */ +static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = { +SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0), +SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0), +SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0), +SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0), +}; + +/* Left Line Mux */ +static const struct snd_kcontrol_new wm8971_left_line_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[8]); + +/* Right Line Mux */ +static const struct snd_kcontrol_new wm8971_right_line_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[9]); + +/* Left PGA Mux */ +static const struct snd_kcontrol_new wm8971_left_pga_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[10]); + +/* Right PGA Mux */ +static const struct snd_kcontrol_new wm8971_right_pga_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[11]); + +/* Mono ADC Mux */ +static const struct snd_kcontrol_new wm8971_monomux_controls = +SOC_DAPM_ENUM("Route", wm8971_enum[13]); + +static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = { +	SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0, +		&wm8971_left_mixer_controls[0], +		ARRAY_SIZE(wm8971_left_mixer_controls)), +	SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0, +		&wm8971_right_mixer_controls[0], +		ARRAY_SIZE(wm8971_right_mixer_controls)), +	SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0, +		&wm8971_mono_mixer_controls[0], +		ARRAY_SIZE(wm8971_mono_mixer_controls)), + +	SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0), +	SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0), +	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0), +	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0), +	SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0), + +	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0), +	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0), +	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0), + +	SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0, +		&wm8971_left_pga_controls), +	SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0, +		&wm8971_right_pga_controls), +	SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, +		&wm8971_left_line_controls), +	SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, +		&wm8971_right_line_controls), + +	SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, +		&wm8971_monomux_controls), +	SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, +		&wm8971_monomux_controls), + +	SND_SOC_DAPM_OUTPUT("LOUT1"), +	SND_SOC_DAPM_OUTPUT("ROUT1"), +	SND_SOC_DAPM_OUTPUT("LOUT2"), +	SND_SOC_DAPM_OUTPUT("ROUT2"), +	SND_SOC_DAPM_OUTPUT("MONO"), + +	SND_SOC_DAPM_INPUT("LINPUT1"), +	SND_SOC_DAPM_INPUT("RINPUT1"), +	SND_SOC_DAPM_INPUT("MIC"), +}; + +static const struct snd_soc_dapm_route audio_map[] = { +	/* left mixer */ +	{"Left Mixer", "Playback Switch", "Left DAC"}, +	{"Left Mixer", "Left Bypass Switch", "Left Line Mux"}, +	{"Left Mixer", "Right Playback Switch", "Right DAC"}, +	{"Left Mixer", "Right Bypass Switch", "Right Line Mux"}, + +	/* right mixer */ +	{"Right Mixer", "Left Playback Switch", "Left DAC"}, +	{"Right Mixer", "Left Bypass Switch", "Left Line Mux"}, +	{"Right Mixer", "Playback Switch", "Right DAC"}, +	{"Right Mixer", "Right Bypass Switch", "Right Line Mux"}, + +	/* left out 1 */ +	{"Left Out 1", NULL, "Left Mixer"}, +	{"LOUT1", NULL, "Left Out 1"}, + +	/* left out 2 */ +	{"Left Out 2", NULL, "Left Mixer"}, +	{"LOUT2", NULL, "Left Out 2"}, + +	/* right out 1 */ +	{"Right Out 1", NULL, "Right Mixer"}, +	{"ROUT1", NULL, "Right Out 1"}, + +	/* right out 2 */ +	{"Right Out 2", NULL, "Right Mixer"}, +	{"ROUT2", NULL, "Right Out 2"}, + +	/* mono mixer */ +	{"Mono Mixer", "Left Playback Switch", "Left DAC"}, +	{"Mono Mixer", "Left Bypass Switch", "Left Line Mux"}, +	{"Mono Mixer", "Right Playback Switch", "Right DAC"}, +	{"Mono Mixer", "Right Bypass Switch", "Right Line Mux"}, + +	/* mono out */ +	{"Mono Out", NULL, "Mono Mixer"}, +	{"MONO1", NULL, "Mono Out"}, + +	/* Left Line Mux */ +	{"Left Line Mux", "Line", "LINPUT1"}, +	{"Left Line Mux", "PGA", "Left PGA Mux"}, +	{"Left Line Mux", "Differential", "Differential Mux"}, + +	/* Right Line Mux */ +	{"Right Line Mux", "Line", "RINPUT1"}, +	{"Right Line Mux", "Mic", "MIC"}, +	{"Right Line Mux", "PGA", "Right PGA Mux"}, +	{"Right Line Mux", "Differential", "Differential Mux"}, + +	/* Left PGA Mux */ +	{"Left PGA Mux", "Line", "LINPUT1"}, +	{"Left PGA Mux", "Differential", "Differential Mux"}, + +	/* Right PGA Mux */ +	{"Right PGA Mux", "Line", "RINPUT1"}, +	{"Right PGA Mux", "Differential", "Differential Mux"}, + +	/* Differential Mux */ +	{"Differential Mux", "Line", "LINPUT1"}, +	{"Differential Mux", "Line", "RINPUT1"}, + +	/* Left ADC Mux */ +	{"Left ADC Mux", "Stereo", "Left PGA Mux"}, +	{"Left ADC Mux", "Mono (Left)", "Left PGA Mux"}, +	{"Left ADC Mux", "Digital Mono", "Left PGA Mux"}, + +	/* Right ADC Mux */ +	{"Right ADC Mux", "Stereo", "Right PGA Mux"}, +	{"Right ADC Mux", "Mono (Right)", "Right PGA Mux"}, +	{"Right ADC Mux", "Digital Mono", "Right PGA Mux"}, + +	/* ADC */ +	{"Left ADC", NULL, "Left ADC Mux"}, +	{"Right ADC", NULL, "Right ADC Mux"}, +}; + +static int wm8971_add_widgets(struct snd_soc_codec *codec) +{ +	snd_soc_dapm_new_controls(codec, wm8971_dapm_widgets, +				  ARRAY_SIZE(wm8971_dapm_widgets)); + +	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + +	snd_soc_dapm_new_widgets(codec); + +	return 0; +} + +struct _coeff_div { +	u32 mclk; +	u32 rate; +	u16 fs; +	u8 sr:5; +	u8 usb:1; +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { +	/* 8k */ +	{12288000, 8000, 1536, 0x6, 0x0}, +	{11289600, 8000, 1408, 0x16, 0x0}, +	{18432000, 8000, 2304, 0x7, 0x0}, +	{16934400, 8000, 2112, 0x17, 0x0}, +	{12000000, 8000, 1500, 0x6, 0x1}, + +	/* 11.025k */ +	{11289600, 11025, 1024, 0x18, 0x0}, +	{16934400, 11025, 1536, 0x19, 0x0}, +	{12000000, 11025, 1088, 0x19, 0x1}, + +	/* 16k */ +	{12288000, 16000, 768, 0xa, 0x0}, +	{18432000, 16000, 1152, 0xb, 0x0}, +	{12000000, 16000, 750, 0xa, 0x1}, + +	/* 22.05k */ +	{11289600, 22050, 512, 0x1a, 0x0}, +	{16934400, 22050, 768, 0x1b, 0x0}, +	{12000000, 22050, 544, 0x1b, 0x1}, + +	/* 32k */ +	{12288000, 32000, 384, 0xc, 0x0}, +	{18432000, 32000, 576, 0xd, 0x0}, +	{12000000, 32000, 375, 0xa, 0x1}, + +	/* 44.1k */ +	{11289600, 44100, 256, 0x10, 0x0}, +	{16934400, 44100, 384, 0x11, 0x0}, +	{12000000, 44100, 272, 0x11, 0x1}, + +	/* 48k */ +	{12288000, 48000, 256, 0x0, 0x0}, +	{18432000, 48000, 384, 0x1, 0x0}, +	{12000000, 48000, 250, 0x0, 0x1}, + +	/* 88.2k */ +	{11289600, 88200, 128, 0x1e, 0x0}, +	{16934400, 88200, 192, 0x1f, 0x0}, +	{12000000, 88200, 136, 0x1f, 0x1}, + +	/* 96k */ +	{12288000, 96000, 128, 0xe, 0x0}, +	{18432000, 96000, 192, 0xf, 0x0}, +	{12000000, 96000, 125, 0xe, 0x1}, +}; + +static int get_coeff(int mclk, int rate) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) { +		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) +			return i; +	} +	return -EINVAL; +} + +static int wm8971_set_dai_sysclk(struct snd_soc_dai *codec_dai, +		int clk_id, unsigned int freq, int dir) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	struct wm8971_priv *wm8971 = codec->private_data; + +	switch (freq) { +	case 11289600: +	case 12000000: +	case 12288000: +	case 16934400: +	case 18432000: +		wm8971->sysclk = freq; +		return 0; +	} +	return -EINVAL; +} + +static int wm8971_set_dai_fmt(struct snd_soc_dai *codec_dai, +		unsigned int fmt) +{ +	struct snd_soc_codec *codec = codec_dai->codec; +	u16 iface = 0; + +	/* set master/slave audio interface */ +	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { +	case SND_SOC_DAIFMT_CBM_CFM: +		iface = 0x0040; +		break; +	case SND_SOC_DAIFMT_CBS_CFS: +		break; +	default: +		return -EINVAL; +	} + +	/* interface format */ +	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { +	case SND_SOC_DAIFMT_I2S: +		iface |= 0x0002; +		break; +	case SND_SOC_DAIFMT_RIGHT_J: +		break; +	case SND_SOC_DAIFMT_LEFT_J: +		iface |= 0x0001; +		break; +	case SND_SOC_DAIFMT_DSP_A: +		iface |= 0x0003; +		break; +	case SND_SOC_DAIFMT_DSP_B: +		iface |= 0x0013; +		break; +	default: +		return -EINVAL; +	} + +	/* clock inversion */ +	switch (fmt & SND_SOC_DAIFMT_INV_MASK) { +	case SND_SOC_DAIFMT_NB_NF: +		break; +	case SND_SOC_DAIFMT_IB_IF: +		iface |= 0x0090; +		break; +	case SND_SOC_DAIFMT_IB_NF: +		iface |= 0x0080; +		break; +	case SND_SOC_DAIFMT_NB_IF: +		iface |= 0x0010; +		break; +	default: +		return -EINVAL; +	} + +	wm8971_write(codec, WM8971_IFACE, iface); +	return 0; +} + +static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream, +	struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	struct wm8971_priv *wm8971 = codec->private_data; +	u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3; +	u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0; +	int coeff = get_coeff(wm8971->sysclk, params_rate(params)); + +	/* bit size */ +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S16_LE: +		break; +	case SNDRV_PCM_FORMAT_S20_3LE: +		iface |= 0x0004; +		break; +	case SNDRV_PCM_FORMAT_S24_LE: +		iface |= 0x0008; +		break; +	case SNDRV_PCM_FORMAT_S32_LE: +		iface |= 0x000c; +		break; +	} + +	/* set iface & srate */ +	wm8971_write(codec, WM8971_IFACE, iface); +	if (coeff >= 0) +		wm8971_write(codec, WM8971_SRATE, srate | +			(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb); + +	return 0; +} + +static int wm8971_mute(struct snd_soc_dai *dai, int mute) +{ +	struct snd_soc_codec *codec = dai->codec; +	u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7; + +	if (mute) +		wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8); +	else +		wm8971_write(codec, WM8971_ADCDAC, mute_reg); +	return 0; +} + +static int wm8971_set_bias_level(struct snd_soc_codec *codec, +	enum snd_soc_bias_level level) +{ +	u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e; + +	switch (level) { +	case SND_SOC_BIAS_ON: +		/* set vmid to 50k and unmute dac */ +		wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1); +		break; +	case SND_SOC_BIAS_PREPARE: +		break; +	case SND_SOC_BIAS_STANDBY: +		/* mute dac and set vmid to 500k, enable VREF */ +		wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140); +		break; +	case SND_SOC_BIAS_OFF: +		wm8971_write(codec, WM8971_PWR1, 0x0001); +		break; +	} +	codec->bias_level = level; +	return 0; +} + +#define WM8971_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ +		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \ +		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ +	SNDRV_PCM_FMTBIT_S24_LE) + +struct snd_soc_dai wm8971_dai = { +	.name = "WM8971", +	.playback = { +		.stream_name = "Playback", +		.channels_min = 1, +		.channels_max = 2, +		.rates = WM8971_RATES, +		.formats = WM8971_FORMATS,}, +	.capture = { +		.stream_name = "Capture", +		.channels_min = 1, +		.channels_max = 2, +		.rates = WM8971_RATES, +		.formats = WM8971_FORMATS,}, +	.ops = { +		.hw_params = wm8971_pcm_hw_params, +	}, +	.dai_ops = { +		.digital_mute = wm8971_mute, +		.set_fmt = wm8971_set_dai_fmt, +		.set_sysclk = wm8971_set_dai_sysclk, +	}, +}; +EXPORT_SYMBOL_GPL(wm8971_dai); + +static void wm8971_work(struct work_struct *work) +{ +	struct snd_soc_codec *codec = +		container_of(work, struct snd_soc_codec, delayed_work.work); +	wm8971_set_bias_level(codec, codec->bias_level); +} + +static int wm8971_suspend(struct platform_device *pdev, pm_message_t state) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); +	return 0; +} + +static int wm8971_resume(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; +	int i; +	u8 data[2]; +	u16 *cache = codec->reg_cache; +	u16 reg; + +	/* Sync reg_cache with the hardware */ +	for (i = 0; i < ARRAY_SIZE(wm8971_reg); i++) { +		if (i + 1 == WM8971_RESET) +			continue; +		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); +		data[1] = cache[i] & 0x00ff; +		codec->hw_write(codec->control_data, data, 2); +	} + +	wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + +	/* charge wm8971 caps */ +	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { +		reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e; +		wm8971_write(codec, WM8971_PWR1, reg | 0x01c0); +		codec->bias_level = SND_SOC_BIAS_ON; +		queue_delayed_work(wm8971_workq, &codec->delayed_work, +			msecs_to_jiffies(1000)); +	} + +	return 0; +} + +static int wm8971_init(struct snd_soc_device *socdev) +{ +	struct snd_soc_codec *codec = socdev->codec; +	int reg, ret = 0; + +	codec->name = "WM8971"; +	codec->owner = THIS_MODULE; +	codec->read = wm8971_read_reg_cache; +	codec->write = wm8971_write; +	codec->set_bias_level = wm8971_set_bias_level; +	codec->dai = &wm8971_dai; +	codec->reg_cache_size = ARRAY_SIZE(wm8971_reg); +	codec->num_dai = 1; +	codec->reg_cache = kmemdup(wm8971_reg, sizeof(wm8971_reg), GFP_KERNEL); + +	if (codec->reg_cache == NULL) +		return -ENOMEM; + +	wm8971_reset(codec); + +	/* register pcms */ +	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); +	if (ret < 0) { +		printk(KERN_ERR "wm8971: failed to create pcms\n"); +		goto pcm_err; +	} + +	/* charge output caps - set vmid to 5k for quick power up */ +	reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e; +	wm8971_write(codec, WM8971_PWR1, reg | 0x01c0); +	codec->bias_level = SND_SOC_BIAS_STANDBY; +	queue_delayed_work(wm8971_workq, &codec->delayed_work, +		msecs_to_jiffies(1000)); + +	/* set the update bits */ +	reg = wm8971_read_reg_cache(codec, WM8971_LDAC); +	wm8971_write(codec, WM8971_LDAC, reg | 0x0100); +	reg = wm8971_read_reg_cache(codec, WM8971_RDAC); +	wm8971_write(codec, WM8971_RDAC, reg | 0x0100); + +	reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V); +	wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100); +	reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V); +	wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100); + +	reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V); +	wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100); +	reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V); +	wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100); + +	reg = wm8971_read_reg_cache(codec, WM8971_LINVOL); +	wm8971_write(codec, WM8971_LINVOL, reg | 0x0100); +	reg = wm8971_read_reg_cache(codec, WM8971_RINVOL); +	wm8971_write(codec, WM8971_RINVOL, reg | 0x0100); + +	wm8971_add_controls(codec); +	wm8971_add_widgets(codec); +	ret = snd_soc_register_card(socdev); +	if (ret < 0) { +		printk(KERN_ERR "wm8971: failed to register card\n"); +		goto card_err; +	} +	return ret; + +card_err: +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +pcm_err: +	kfree(codec->reg_cache); +	return ret; +} + +/* If the i2c layer weren't so broken, we could pass this kind of data +   around */ +static struct snd_soc_device *wm8971_socdev; + +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) + +static int wm8971_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id) +{ +	struct snd_soc_device *socdev = wm8971_socdev; +	struct snd_soc_codec *codec = socdev->codec; +	int ret; + +	i2c_set_clientdata(i2c, codec); + +	codec->control_data = i2c; + +	ret = wm8971_init(socdev); +	if (ret < 0) +		pr_err("failed to initialise WM8971\n"); + +	return ret; +} + +static int wm8971_i2c_remove(struct i2c_client *client) +{ +	struct snd_soc_codec *codec = i2c_get_clientdata(client); +	kfree(codec->reg_cache); +	return 0; +} + +static const struct i2c_device_id wm8971_i2c_id[] = { +	{ "wm8971", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id); + +static struct i2c_driver wm8971_i2c_driver = { +	.driver = { +		.name = "WM8971 I2C Codec", +		.owner = THIS_MODULE, +	}, +	.probe    = wm8971_i2c_probe, +	.remove   = wm8971_i2c_remove, +	.id_table = wm8971_i2c_id, +}; + +static int wm8971_add_i2c_device(struct platform_device *pdev, +				 const struct wm8971_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&wm8971_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "wm8971", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&wm8971_i2c_driver); +	return -ENODEV; +} + +#endif + +static int wm8971_probe(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct wm8971_setup_data *setup; +	struct snd_soc_codec *codec; +	struct wm8971_priv *wm8971; +	int ret = 0; + +	pr_info("WM8971 Audio Codec %s", WM8971_VERSION); + +	setup = socdev->codec_data; +	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); +	if (codec == NULL) +		return -ENOMEM; + +	wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL); +	if (wm8971 == NULL) { +		kfree(codec); +		return -ENOMEM; +	} + +	codec->private_data = wm8971; +	socdev->codec = codec; +	mutex_init(&codec->mutex); +	INIT_LIST_HEAD(&codec->dapm_widgets); +	INIT_LIST_HEAD(&codec->dapm_paths); +	wm8971_socdev = socdev; + +	INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work); +	wm8971_workq = create_workqueue("wm8971"); +	if (wm8971_workq == NULL) { +		kfree(codec->private_data); +		kfree(codec); +		return -ENOMEM; +	} + +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +	if (setup->i2c_address) { +		codec->hw_write = (hw_write_t)i2c_master_send; +		ret = wm8971_add_i2c_device(pdev, setup); +	} +#endif +	/* Add other interfaces here */ + +	if (ret != 0) { +		destroy_workqueue(wm8971_workq); +		kfree(codec->private_data); +		kfree(codec); +	} + +	return ret; +} + +/* power down chip */ +static int wm8971_remove(struct platform_device *pdev) +{ +	struct snd_soc_device *socdev = platform_get_drvdata(pdev); +	struct snd_soc_codec *codec = socdev->codec; + +	if (codec->control_data) +		wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); +	if (wm8971_workq) +		destroy_workqueue(wm8971_workq); +	snd_soc_free_pcms(socdev); +	snd_soc_dapm_free(socdev); +#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data); +	i2c_del_driver(&wm8971_i2c_driver); +#endif +	kfree(codec->private_data); +	kfree(codec); + +	return 0; +} + +struct snd_soc_codec_device soc_codec_dev_wm8971 = { +	.probe = 	wm8971_probe, +	.remove = 	wm8971_remove, +	.suspend = 	wm8971_suspend, +	.resume =	wm8971_resume, +}; + +EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971); + +MODULE_DESCRIPTION("ASoC WM8971 driver"); +MODULE_AUTHOR("Lab126"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8971.h b/sound/soc/codecs/wm8971.h new file mode 100644 index 0000000..ef4f08f --- /dev/null +++ b/sound/soc/codecs/wm8971.h @@ -0,0 +1,64 @@ +/* + * wm8971.h  --  audio driver for WM8971 + * + * Copyright 2005 Lab126, Inc. + * + * Author: Kenneth Kiraly <kiraly@lab126.com> + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + * + */ + +#ifndef _WM8971_H +#define _WM8971_H + +#define WM8971_LINVOL	0x00 +#define WM8971_RINVOL	0x01 +#define WM8971_LOUT1V	0x02 +#define WM8971_ROUT1V	0x03 +#define WM8971_ADCDAC	0x05 +#define WM8971_IFACE	0x07 +#define WM8971_SRATE	0x08 +#define WM8971_LDAC		0x0a +#define WM8971_RDAC		0x0b +#define WM8971_BASS		0x0c +#define WM8971_TREBLE	0x0d +#define WM8971_RESET	0x0f +#define WM8971_ALC1		0x11 +#define	WM8971_ALC2		0x12 +#define	WM8971_ALC3		0x13 +#define WM8971_NGATE	0x14 +#define WM8971_LADC		0x15 +#define WM8971_RADC		0x16 +#define	WM8971_ADCTL1	0x17 +#define	WM8971_ADCTL2	0x18 +#define WM8971_PWR1		0x19 +#define WM8971_PWR2		0x1a +#define	WM8971_ADCTL3	0x1b +#define WM8971_ADCIN	0x1f +#define	WM8971_LADCIN	0x20 +#define	WM8971_RADCIN	0x21 +#define WM8971_LOUTM1	0x22 +#define WM8971_LOUTM2	0x23 +#define WM8971_ROUTM1	0x24 +#define WM8971_ROUTM2	0x25 +#define WM8971_MOUTM1	0x26 +#define WM8971_MOUTM2	0x27 +#define WM8971_LOUT2V	0x28 +#define WM8971_ROUT2V	0x29 +#define WM8971_MOUTV	0x2A + +#define WM8971_SYSCLK	0 + +struct wm8971_setup_data { +	int i2c_bus; +	unsigned short i2c_address; +}; + +extern struct snd_soc_dai wm8971_dai; +extern struct snd_soc_codec_device soc_codec_dev_wm8971; + +#endif diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index dd995ef..63410d7 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1477,81 +1477,86 @@ static struct snd_soc_device *wm8990_socdev;   *    low  = 0x34   *    high = 0x36   */ -static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END }; -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static struct i2c_driver wm8990_i2c_driver; -static struct i2c_client client_template; - -static int wm8990_codec_probe(struct i2c_adapter *adap, int addr, int kind) +static int wm8990_i2c_probe(struct i2c_client *i2c, +			    const struct i2c_device_id *id)  {  	struct snd_soc_device *socdev = wm8990_socdev; -	struct wm8990_setup_data *setup = socdev->codec_data;  	struct snd_soc_codec *codec = socdev->codec; -	struct i2c_client *i2c;  	int ret; -	if (addr != setup->i2c_address) -		return -ENODEV; - -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c =  kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; -  	i2c_set_clientdata(i2c, codec);  	codec->control_data = i2c; -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		pr_err("failed to attach codec at addr %x\n", addr); -		goto err; -	} -  	ret = wm8990_init(socdev); -	if (ret < 0) { +	if (ret < 0)  		pr_err("failed to initialise WM8990\n"); -		goto err; -	} -	return ret; -err: -	kfree(i2c);  	return ret;  } -static int wm8990_i2c_detach(struct i2c_client *client) +static int wm8990_i2c_remove(struct i2c_client *client)  {  	struct snd_soc_codec *codec = i2c_get_clientdata(client); -	i2c_detach_client(client);  	kfree(codec->reg_cache); -	kfree(client);  	return 0;  } -static int wm8990_i2c_attach(struct i2c_adapter *adap) -{ -	return i2c_probe(adap, &addr_data, wm8990_codec_probe); -} +static const struct i2c_device_id wm8990_i2c_id[] = { +	{ "wm8990", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id);  static struct i2c_driver wm8990_i2c_driver = {  	.driver = {  		.name = "WM8990 I2C Codec",  		.owner = THIS_MODULE,  	}, -	.attach_adapter = wm8990_i2c_attach, -	.detach_client =  wm8990_i2c_detach, -	.command =        NULL, +	.probe =    wm8990_i2c_probe, +	.remove =   wm8990_i2c_remove, +	.id_table = wm8990_i2c_id,  }; -static struct i2c_client client_template = { -	.name =   "WM8990", -	.driver = &wm8990_i2c_driver, -}; +static int wm8990_add_i2c_device(struct platform_device *pdev, +				 const struct wm8990_setup_data *setup) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&wm8990_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add i2c driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = setup->i2c_address; +	strlcpy(info.type, "wm8990", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(setup->i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", +			setup->i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	return 0; + +err_driver: +	i2c_del_driver(&wm8990_i2c_driver); +	return -ENODEV; +}  #endif  static int wm8990_probe(struct platform_device *pdev) @@ -1560,7 +1565,7 @@ static int wm8990_probe(struct platform_device *pdev)  	struct wm8990_setup_data *setup;  	struct snd_soc_codec *codec;  	struct wm8990_priv *wm8990; -	int ret = 0; +	int ret;  	pr_info("WM8990 Audio Codec %s\n", WM8990_VERSION); @@ -1582,16 +1587,13 @@ static int wm8990_probe(struct platform_device *pdev)  	INIT_LIST_HEAD(&codec->dapm_paths);  	wm8990_socdev = socdev; +	ret = -ENODEV; +  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)  	if (setup->i2c_address) { -		normal_i2c[0] = setup->i2c_address;  		codec->hw_write = (hw_write_t)i2c_master_send; -		ret = i2c_add_driver(&wm8990_i2c_driver); -		if (ret != 0) -			printk(KERN_ERR "can't add i2c driver"); +		ret = wm8990_add_i2c_device(pdev, setup);  	} -#else -		/* Add other interfaces here */  #endif  	if (ret != 0) { @@ -1612,6 +1614,7 @@ static int wm8990_remove(struct platform_device *pdev)  	snd_soc_free_pcms(socdev);  	snd_soc_dapm_free(socdev);  #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +	i2c_unregister_device(codec->control_data);  	i2c_del_driver(&wm8990_i2c_driver);  #endif  	kfree(codec->private_data); diff --git a/sound/soc/codecs/wm8990.h b/sound/soc/codecs/wm8990.h index 0a08325..0e192f3 100644 --- a/sound/soc/codecs/wm8990.h +++ b/sound/soc/codecs/wm8990.h @@ -827,6 +827,7 @@  #define WM8990_AINRMUX_PWR_BIT			3  struct wm8990_setup_data { +	unsigned i2c_bus;  	unsigned short i2c_address;  }; diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 38d1fe0..441d058 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -419,8 +419,12 @@ SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),  SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),  SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),  SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1), -SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1), -SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1), +SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0), +SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0), +SND_SOC_DAPM_ADC("Left HiFi ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Right HiFi ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Left Voice ADC", "Left Voice Capture", SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_ADC("Right Voice ADC", "Right Voice Capture", SND_SOC_NOPM, 0, 0),  SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),  SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),  SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0), @@ -583,9 +587,13 @@ static const struct snd_soc_dapm_route audio_map[] = {  	/* left ADC */  	{"Left ADC", NULL, "Left Capture Source"}, +	{"Left Voice ADC", NULL, "Left ADC"}, +	{"Left HiFi ADC", NULL, "Left ADC"},  	/* right ADC */  	{"Right ADC", NULL, "Right Capture Source"}, +	{"Right Voice ADC", NULL, "Right ADC"}, +	{"Right HiFi ADC", NULL, "Right ADC"},  	/* mic */  	{"Mic A Pre Amp", NULL, "Mic A Source"}, @@ -949,17 +957,17 @@ static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,  static void wm9713_voiceshutdown(struct snd_pcm_substream *substream)  { -    struct snd_soc_pcm_runtime *rtd = substream->private_data; -    struct snd_soc_device *socdev = rtd->socdev; -    struct snd_soc_codec *codec = socdev->codec; -    u16 status; - -    /* Gracefully shut down the voice interface. */ -    status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000; -    ac97_write(codec, AC97_HANDSET_RATE, 0x0280); -    schedule_timeout_interruptible(msecs_to_jiffies(1)); -    ac97_write(codec, AC97_HANDSET_RATE, 0x0F80); -    ac97_write(codec, AC97_EXTENDED_MID, status); +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_device *socdev = rtd->socdev; +	struct snd_soc_codec *codec = socdev->codec; +	u16 status; + +	/* Gracefully shut down the voice interface. */ +	status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000; +	ac97_write(codec, AC97_HANDSET_RATE, 0x0280); +	schedule_timeout_interruptible(msecs_to_jiffies(1)); +	ac97_write(codec, AC97_HANDSET_RATE, 0x0F80); +	ac97_write(codec, AC97_EXTENDED_MID, status);  }  static int ac97_hifi_prepare(struct snd_pcm_substream *substream) diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 65fdbd8..9e6062c 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -1,7 +1,7 @@  /*   * ASoC driver for TI DAVINCI EVM platform   * - * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com> + * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>   * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>   *   * This program is free software; you can redistribute it and/or modify @@ -136,6 +136,7 @@ static struct snd_soc_machine snd_soc_machine_evm = {  /* evm audio private data */  static struct aic3x_setup_data evm_aic3x_setup = { +	.i2c_bus = 0,  	.i2c_address = 0x1b,  }; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 5ebf1ff..abb5fed 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -1,7 +1,7 @@  /*   * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor   * - * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com> + * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>   * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>   *   * This program is free software; you can redistribute it and/or modify @@ -256,7 +256,7 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,  		mcbsp_word_length = DAVINCI_MCBSP_WORD_32;  		break;  	default: -		printk(KERN_WARNING "davinci-i2s: unsupported PCM format"); +		printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");  		return -EINVAL;  	} diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h index c5b0918..241648c 100644 --- a/sound/soc/davinci/davinci-i2s.h +++ b/sound/soc/davinci/davinci-i2s.h @@ -1,7 +1,7 @@  /*   * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor   * - * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com> + * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>   * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>   *   * This program is free software; you can redistribute it and/or modify diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index 6a5e56a..76feaa6 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -1,7 +1,7 @@  /*   * ALSA PCM interface for the TI DAVINCI processor   * - * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com> + * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>   * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>   *   * This program is free software; you can redistribute it and/or modify diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 8d6a45e..62cb4eb 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -1,7 +1,7 @@  /*   * ALSA PCM interface for the TI DAVINCI processor   * - * Author:      Vladimir Barinov, <vbarinov@ru.mvista.com> + * Author:      Vladimir Barinov, <vbarinov@embeddedalley.com>   * Copyright:   (C) 2007 MontaVista Software, Inc., <source@mvista.com>   *   * This program is free software; you can redistribute it and/or modify diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3368ace..bba9546 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,3 +1,6 @@ +config SND_SOC_OF_SIMPLE +	tristate +  config SND_SOC_MPC8610  	bool "ALSA SoC support for the MPC8610 SOC"  	depends on MPC8610_HPCD @@ -14,3 +17,10 @@ config SND_SOC_MPC8610_HPCD  	default y if MPC8610_HPCD  	help  	  Say Y if you want to enable audio on the Freescale MPC8610 HPCD. + +config SND_SOC_MPC5200_I2S +	tristate "Freescale MPC5200 PSC in I2S mode driver" +	select SND_SOC_OF_SIMPLE +	depends on SND_SOC && PPC_MPC52xx +	help +	  Say Y here to support the MPC5200 PSCs in I2S mode. diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 62f680a..035da4a 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -1,6 +1,11 @@ +# Simple machine driver that extracts configuration from the OF device tree +obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o +  # MPC8610 HPCD Machine Support  obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o  # MPC8610 Platform Support  obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o +obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o + diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c new file mode 100644 index 0000000..8692329 --- /dev/null +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -0,0 +1,884 @@ +/* + * Freescale MPC5200 PSC in I2S mode + * ALSA SoC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2008 Secret Lab Technologies Ltd. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/dma-mapping.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/soc.h> +#include <sound/soc-of-simple.h> + +#include <sysdev/bestcomm/bestcomm.h> +#include <sysdev/bestcomm/gen_bd.h> +#include <asm/mpc52xx_psc.h> + +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); +MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver"); +MODULE_LICENSE("GPL"); + +/** + * PSC_I2S_RATES: sample rates supported by the I2S + * + * This driver currently only supports the PSC running in I2S slave mode, + * which means the codec determines the sample rate.  Therefore, we tell + * ALSA that we support all rates and let the codec driver decide what rates + * are really supported. + */ +#define PSC_I2S_RATES (SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_192000 | \ +			SNDRV_PCM_RATE_CONTINUOUS) + +/** + * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode + */ +#define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \ +			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \ +			 SNDRV_PCM_FMTBIT_S32_BE) + +/** + * psc_i2s_stream - Data specific to a single stream (playback or capture) + * @active:		flag indicating if the stream is active + * @psc_i2s:		pointer back to parent psc_i2s data structure + * @bcom_task:		bestcomm task structure + * @irq:		irq number for bestcomm task + * @period_start:	physical address of start of DMA region + * @period_end:		physical address of end of DMA region + * @period_next_pt:	physical address of next DMA buffer to enqueue + * @period_bytes:	size of DMA period in bytes + */ +struct psc_i2s_stream { +	int active; +	struct psc_i2s *psc_i2s; +	struct bcom_task *bcom_task; +	int irq; +	struct snd_pcm_substream *stream; +	dma_addr_t period_start; +	dma_addr_t period_end; +	dma_addr_t period_next_pt; +	dma_addr_t period_current_pt; +	int period_bytes; +}; + +/** + * psc_i2s - Private driver data + * @name: short name for this device ("PSC0", "PSC1", etc) + * @psc_regs: pointer to the PSC's registers + * @fifo_regs: pointer to the PSC's FIFO registers + * @irq: IRQ of this PSC + * @dev: struct device pointer + * @dai: the CPU DAI for this device + * @sicr: Base value used in serial interface control register; mode is ORed + *        with this value. + * @playback: Playback stream context data + * @capture: Capture stream context data + */ +struct psc_i2s { +	char name[32]; +	struct mpc52xx_psc __iomem *psc_regs; +	struct mpc52xx_psc_fifo __iomem *fifo_regs; +	unsigned int irq; +	struct device *dev; +	struct snd_soc_dai dai; +	spinlock_t lock; +	u32 sicr; + +	/* per-stream data */ +	struct psc_i2s_stream playback; +	struct psc_i2s_stream capture; + +	/* Statistics */ +	struct { +		int overrun_count; +		int underrun_count; +	} stats; +}; + +/* + * Interrupt handlers + */ +static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s) +{ +	struct psc_i2s *psc_i2s = _psc_i2s; +	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; +	u16 isr; + +	isr = in_be16(®s->mpc52xx_psc_isr); + +	/* Playback underrun error */ +	if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP)) +		psc_i2s->stats.underrun_count++; + +	/* Capture overrun error */ +	if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR)) +		psc_i2s->stats.overrun_count++; + +	out_8(®s->command, 4 << 4);	/* reset the error status */ + +	return IRQ_HANDLED; +} + +/** + * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer + * @s: pointer to stream private data structure + * + * Enqueues another audio period buffer into the bestcomm queue. + * + * Note: The routine must only be called when there is space available in + * the queue.  Otherwise the enqueue will fail and the audio ring buffer + * will get out of sync + */ +static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s) +{ +	struct bcom_bd *bd; + +	/* Prepare and enqueue the next buffer descriptor */ +	bd = bcom_prepare_next_buffer(s->bcom_task); +	bd->status = s->period_bytes; +	bd->data[0] = s->period_next_pt; +	bcom_submit_next_buffer(s->bcom_task, NULL); + +	/* Update for next period */ +	s->period_next_pt += s->period_bytes; +	if (s->period_next_pt >= s->period_end) +		s->period_next_pt = s->period_start; +} + +/* Bestcomm DMA irq handler */ +static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream) +{ +	struct psc_i2s_stream *s = _psc_i2s_stream; + +	/* For each finished period, dequeue the completed period buffer +	 * and enqueue a new one in it's place. */ +	while (bcom_buffer_done(s->bcom_task)) { +		bcom_retrieve_buffer(s->bcom_task, NULL, NULL); +		s->period_current_pt += s->period_bytes; +		if (s->period_current_pt >= s->period_end) +			s->period_current_pt = s->period_start; +		psc_i2s_bcom_enqueue_next_buffer(s); +		bcom_enable(s->bcom_task); +	} + +	/* If the stream is active, then also inform the PCM middle layer +	 * of the period finished event. */ +	if (s->active) +		snd_pcm_period_elapsed(s->stream); + +	return IRQ_HANDLED; +} + +/** + * psc_i2s_startup: create a new substream + * + * This is the first function called when a stream is opened. + * + * If this is the first stream open, then grab the IRQ and program most of + * the PSC registers. + */ +static int psc_i2s_startup(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; +	int rc; + +	dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream); + +	if (!psc_i2s->playback.active && +	    !psc_i2s->capture.active) { +		/* Setup the IRQs */ +		rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED, +				 "psc-i2s-status", psc_i2s); +		rc |= request_irq(psc_i2s->capture.irq, +				  &psc_i2s_bcom_irq, IRQF_SHARED, +				  "psc-i2s-capture", &psc_i2s->capture); +		rc |= request_irq(psc_i2s->playback.irq, +				  &psc_i2s_bcom_irq, IRQF_SHARED, +				  "psc-i2s-playback", &psc_i2s->playback); +		if (rc) { +			free_irq(psc_i2s->irq, psc_i2s); +			free_irq(psc_i2s->capture.irq, +				 &psc_i2s->capture); +			free_irq(psc_i2s->playback.irq, +				 &psc_i2s->playback); +			return -ENODEV; +		} +	} + +	return 0; +} + +static int psc_i2s_hw_params(struct snd_pcm_substream *substream, +				 struct snd_pcm_hw_params *params) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; +	u32 mode; + +	dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i" +		" periods=%i buffer_size=%i  buffer_bytes=%i\n", +		__func__, substream, params_period_size(params), +		params_period_bytes(params), params_periods(params), +		params_buffer_size(params), params_buffer_bytes(params)); + +	switch (params_format(params)) { +	case SNDRV_PCM_FORMAT_S8: +		mode = MPC52xx_PSC_SICR_SIM_CODEC_8; +		break; +	case SNDRV_PCM_FORMAT_S16_BE: +		mode = MPC52xx_PSC_SICR_SIM_CODEC_16; +		break; +	case SNDRV_PCM_FORMAT_S24_BE: +		mode = MPC52xx_PSC_SICR_SIM_CODEC_24; +		break; +	case SNDRV_PCM_FORMAT_S32_BE: +		mode = MPC52xx_PSC_SICR_SIM_CODEC_32; +		break; +	default: +		dev_dbg(psc_i2s->dev, "invalid format\n"); +		return -EINVAL; +	} +	out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode); + +	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + +	return 0; +} + +static int psc_i2s_hw_free(struct snd_pcm_substream *substream) +{ +	snd_pcm_set_runtime_buffer(substream, NULL); +	return 0; +} + +/** + * psc_i2s_trigger: start and stop the DMA transfer. + * + * This function is called by ALSA to start, stop, pause, and resume the DMA + * transfer of data. + */ +static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct psc_i2s_stream *s; +	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs; +	u16 imr; +	u8 psc_cmd; +	long flags; + +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +		s = &psc_i2s->capture; +	else +		s = &psc_i2s->playback; + +	dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)" +		" stream_id=%i\n", +		substream, cmd, substream->pstr->stream); + +	switch (cmd) { +	case SNDRV_PCM_TRIGGER_START: +		s->period_bytes = frames_to_bytes(runtime, +						  runtime->period_size); +		s->period_start = virt_to_phys(runtime->dma_area); +		s->period_end = s->period_start + +				(s->period_bytes * runtime->periods); +		s->period_next_pt = s->period_start; +		s->period_current_pt = s->period_start; +		s->active = 1; + +		/* First; reset everything */ +		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { +			out_8(®s->command, MPC52xx_PSC_RST_RX); +			out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); +		} else { +			out_8(®s->command, MPC52xx_PSC_RST_TX); +			out_8(®s->command, MPC52xx_PSC_RST_ERR_STAT); +		} + +		/* Next, fill up the bestcomm bd queue and enable DMA. +		 * This will begin filling the PSC's fifo. */ +		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +			bcom_gen_bd_rx_reset(s->bcom_task); +		else +			bcom_gen_bd_tx_reset(s->bcom_task); +		while (!bcom_queue_full(s->bcom_task)) +			psc_i2s_bcom_enqueue_next_buffer(s); +		bcom_enable(s->bcom_task); + +		/* Due to errata in the i2s mode; need to line up enabling +		 * the transmitter with a transition on the frame sync +		 * line */ + +		spin_lock_irqsave(&psc_i2s->lock, flags); +		/* first make sure it is low */ +		while ((in_8(®s->ipcr_acr.ipcr) & 0x80) != 0) +			; +		/* then wait for the transition to high */ +		while ((in_8(®s->ipcr_acr.ipcr) & 0x80) == 0) +			; +		/* Finally, enable the PSC. +		 * Receiver must always be enabled; even when we only want +		 * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */ +		psc_cmd = MPC52xx_PSC_RX_ENABLE; +		if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) +			psc_cmd |= MPC52xx_PSC_TX_ENABLE; +		out_8(®s->command, psc_cmd); +		spin_unlock_irqrestore(&psc_i2s->lock, flags); + +		break; + +	case SNDRV_PCM_TRIGGER_STOP: +		/* Turn off the PSC */ +		s->active = 0; +		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) { +			if (!psc_i2s->playback.active) { +				out_8(®s->command, 2 << 4);	/* reset rx */ +				out_8(®s->command, 3 << 4);	/* reset tx */ +				out_8(®s->command, 4 << 4);	/* reset err */ +			} +		} else { +			out_8(®s->command, 3 << 4);	/* reset tx */ +			out_8(®s->command, 4 << 4);	/* reset err */ +			if (!psc_i2s->capture.active) +				out_8(®s->command, 2 << 4);	/* reset rx */ +		} + +		bcom_disable(s->bcom_task); +		while (!bcom_queue_empty(s->bcom_task)) +			bcom_retrieve_buffer(s->bcom_task, NULL, NULL); + +		break; + +	default: +		dev_dbg(psc_i2s->dev, "invalid command\n"); +		return -EINVAL; +	} + +	/* Update interrupt enable settings */ +	imr = 0; +	if (psc_i2s->playback.active) +		imr |= MPC52xx_PSC_IMR_TXEMP; +	if (psc_i2s->capture.active) +		imr |= MPC52xx_PSC_IMR_ORERR; +	out_be16(®s->isr_imr.imr, imr); + +	return 0; +} + +/** + * psc_i2s_shutdown: shutdown the data transfer on a stream + * + * Shutdown the PSC if there are no other substreams open. + */ +static void psc_i2s_shutdown(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; + +	dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream); + +	/* +	 * If this is the last active substream, disable the PSC and release +	 * the IRQ. +	 */ +	if (!psc_i2s->playback.active && +	    !psc_i2s->capture.active) { + +		/* Disable all interrupts and reset the PSC */ +		out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); +		out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */ +		out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */ +		out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ +		out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + +		/* Release irqs */ +		free_irq(psc_i2s->irq, psc_i2s); +		free_irq(psc_i2s->capture.irq, &psc_i2s->capture); +		free_irq(psc_i2s->playback.irq, &psc_i2s->playback); +	} +} + +/** + * psc_i2s_set_sysclk: set the clock frequency and direction + * + * This function is called by the machine driver to tell us what the clock + * frequency and direction are. + * + * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN), + * and we don't care about the frequency.  Return an error if the direction + * is not SND_SOC_CLOCK_IN. + * + * @clk_id: reserved, should be zero + * @freq: the frequency of the given clock ID, currently ignored + * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master) + */ +static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, +			      int clk_id, unsigned int freq, int dir) +{ +	struct psc_i2s *psc_i2s = cpu_dai->private_data; +	dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n", +				cpu_dai, dir); +	return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL; +} + +/** + * psc_i2s_set_fmt: set the serial format. + * + * This function is called by the machine driver to tell us what serial + * format to use. + * + * This driver only supports I2S mode.  Return an error if the format is + * not SND_SOC_DAIFMT_I2S. + * + * @format: one of SND_SOC_DAIFMT_xxx + */ +static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format) +{ +	struct psc_i2s *psc_i2s = cpu_dai->private_data; +	dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n", +				cpu_dai, format); +	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL; +} + +/* --------------------------------------------------------------------- + * ALSA SoC Bindings + * + * - Digital Audio Interface (DAI) template + * - create/destroy dai hooks + */ + +/** + * psc_i2s_dai_template: template CPU Digital Audio Interface + */ +static struct snd_soc_dai psc_i2s_dai_template = { +	.type = SND_SOC_DAI_I2S, +	.playback = { +		.channels_min = 2, +		.channels_max = 2, +		.rates = PSC_I2S_RATES, +		.formats = PSC_I2S_FORMATS, +	}, +	.capture = { +		.channels_min = 2, +		.channels_max = 2, +		.rates = PSC_I2S_RATES, +		.formats = PSC_I2S_FORMATS, +	}, +	.ops = { +		.startup = psc_i2s_startup, +		.hw_params = psc_i2s_hw_params, +		.hw_free = psc_i2s_hw_free, +		.shutdown = psc_i2s_shutdown, +		.trigger = psc_i2s_trigger, +	}, +	.dai_ops = { +		.set_sysclk = psc_i2s_set_sysclk, +		.set_fmt = psc_i2s_set_fmt, +	}, +}; + +/* --------------------------------------------------------------------- + * The PSC I2S 'ASoC platform' driver + * + * Can be referenced by an 'ASoC machine' driver + * This driver only deals with the audio bus; it doesn't have any + * interaction with the attached codec + */ + +static const struct snd_pcm_hardware psc_i2s_pcm_hardware = { +	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | +		SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER, +	.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | +		   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE, +	.rate_min = 8000, +	.rate_max = 48000, +	.channels_min = 2, +	.channels_max = 2, +	.period_bytes_max	= 1024 * 1024, +	.period_bytes_min	= 32, +	.periods_min		= 2, +	.periods_max		= 256, +	.buffer_bytes_max	= 2 * 1024 * 1024, +	.fifo_size		= 0, +}; + +static int psc_i2s_pcm_open(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; +	struct psc_i2s_stream *s; + +	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream); + +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +		s = &psc_i2s->capture; +	else +		s = &psc_i2s->playback; + +	snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware); + +	s->stream = substream; +	return 0; +} + +static int psc_i2s_pcm_close(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; +	struct psc_i2s_stream *s; + +	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream); + +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +		s = &psc_i2s->capture; +	else +		s = &psc_i2s->playback; + +	s->stream = NULL; +	return 0; +} + +static snd_pcm_uframes_t +psc_i2s_pcm_pointer(struct snd_pcm_substream *substream) +{ +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data; +	struct psc_i2s_stream *s; +	dma_addr_t count; + +	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) +		s = &psc_i2s->capture; +	else +		s = &psc_i2s->playback; + +	count = s->period_current_pt - s->period_start; + +	return bytes_to_frames(substream->runtime, count); +} + +static struct snd_pcm_ops psc_i2s_pcm_ops = { +	.open		= psc_i2s_pcm_open, +	.close		= psc_i2s_pcm_close, +	.ioctl		= snd_pcm_lib_ioctl, +	.pointer	= psc_i2s_pcm_pointer, +}; + +static u64 psc_i2s_pcm_dmamask = 0xffffffff; +static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, +			   struct snd_pcm *pcm) +{ +	struct snd_soc_pcm_runtime *rtd = pcm->private_data; +	size_t size = psc_i2s_pcm_hardware.buffer_bytes_max; +	int rc = 0; + +	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n", +		card, dai, pcm); + +	if (!card->dev->dma_mask) +		card->dev->dma_mask = &psc_i2s_pcm_dmamask; +	if (!card->dev->coherent_dma_mask) +		card->dev->coherent_dma_mask = 0xffffffff; + +	if (pcm->streams[0].substream) { +		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, +					&pcm->streams[0].substream->dma_buffer); +		if (rc) +			goto playback_alloc_err; +	} + +	if (pcm->streams[1].substream) { +		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size, +					&pcm->streams[1].substream->dma_buffer); +		if (rc) +			goto capture_alloc_err; +	} + +	return 0; + + capture_alloc_err: +	if (pcm->streams[0].substream) +		snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer); + playback_alloc_err: +	dev_err(card->dev, "Cannot allocate buffer(s)\n"); +	return -ENOMEM; +} + +static void psc_i2s_pcm_free(struct snd_pcm *pcm) +{ +	struct snd_soc_pcm_runtime *rtd = pcm->private_data; +	struct snd_pcm_substream *substream; +	int stream; + +	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm); + +	for (stream = 0; stream < 2; stream++) { +		substream = pcm->streams[stream].substream; +		if (substream) { +			snd_dma_free_pages(&substream->dma_buffer); +			substream->dma_buffer.area = NULL; +			substream->dma_buffer.addr = 0; +		} +	} +} + +struct snd_soc_platform psc_i2s_pcm_soc_platform = { +	.name		= "mpc5200-psc-audio", +	.pcm_ops	= &psc_i2s_pcm_ops, +	.pcm_new	= &psc_i2s_pcm_new, +	.pcm_free	= &psc_i2s_pcm_free, +}; + +/* --------------------------------------------------------------------- + * Sysfs attributes for debugging + */ + +static ssize_t psc_i2s_status_show(struct device *dev, +			   struct device_attribute *attr, char *buf) +{ +	struct psc_i2s *psc_i2s = dev_get_drvdata(dev); + +	return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x " +			"tfnum=%i tfstat=0x%.4x\n", +			in_be16(&psc_i2s->psc_regs->sr_csr.status), +			in_be32(&psc_i2s->psc_regs->sicr), +			in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff, +			in_be16(&psc_i2s->fifo_regs->rfstat), +			in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff, +			in_be16(&psc_i2s->fifo_regs->tfstat)); +} + +static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name) +{ +	if (strcmp(name, "playback_underrun") == 0) +		return &psc_i2s->stats.underrun_count; +	if (strcmp(name, "capture_overrun") == 0) +		return &psc_i2s->stats.overrun_count; + +	return NULL; +} + +static ssize_t psc_i2s_stat_show(struct device *dev, +				 struct device_attribute *attr, char *buf) +{ +	struct psc_i2s *psc_i2s = dev_get_drvdata(dev); +	int *attrib; + +	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); +	if (!attrib) +		return 0; + +	return sprintf(buf, "%i\n", *attrib); +} + +static ssize_t psc_i2s_stat_store(struct device *dev, +				  struct device_attribute *attr, +				  const char *buf, +				  size_t count) +{ +	struct psc_i2s *psc_i2s = dev_get_drvdata(dev); +	int *attrib; + +	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name); +	if (!attrib) +		return 0; + +	*attrib = simple_strtoul(buf, NULL, 0); +	return count; +} + +DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL); +DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store); +DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show, psc_i2s_stat_store); + +/* --------------------------------------------------------------------- + * OF platform bus binding code: + * - Probe/remove operations + * - OF device match table + */ +static int __devinit psc_i2s_of_probe(struct of_device *op, +				      const struct of_device_id *match) +{ +	phys_addr_t fifo; +	struct psc_i2s *psc_i2s; +	struct resource res; +	int size, psc_id, irq, rc; +	const __be32 *prop; +	void __iomem *regs; + +	dev_dbg(&op->dev, "probing psc i2s device\n"); + +	/* Get the PSC ID */ +	prop = of_get_property(op->node, "cell-index", &size); +	if (!prop || size < sizeof *prop) +		return -ENODEV; +	psc_id = be32_to_cpu(*prop); + +	/* Fetch the registers and IRQ of the PSC */ +	irq = irq_of_parse_and_map(op->node, 0); +	if (of_address_to_resource(op->node, 0, &res)) { +		dev_err(&op->dev, "Missing reg property\n"); +		return -ENODEV; +	} +	regs = ioremap(res.start, 1 + res.end - res.start); +	if (!regs) { +		dev_err(&op->dev, "Could not map registers\n"); +		return -ENODEV; +	} + +	/* Allocate and initialize the driver private data */ +	psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL); +	if (!psc_i2s) { +		iounmap(regs); +		return -ENOMEM; +	} +	spin_lock_init(&psc_i2s->lock); +	psc_i2s->irq = irq; +	psc_i2s->psc_regs = regs; +	psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs; +	psc_i2s->dev = &op->dev; +	psc_i2s->playback.psc_i2s = psc_i2s; +	psc_i2s->capture.psc_i2s = psc_i2s; +	snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1); + +	/* Fill out the CPU DAI structure */ +	memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai); +	psc_i2s->dai.private_data = psc_i2s; +	psc_i2s->dai.name = psc_i2s->name; +	psc_i2s->dai.id = psc_id; + +	/* Find the address of the fifo data registers and setup the +	 * DMA tasks */ +	fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32); +	psc_i2s->capture.bcom_task = +		bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512); +	psc_i2s->playback.bcom_task = +		bcom_psc_gen_bd_tx_init(psc_id, 10, fifo); +	if (!psc_i2s->capture.bcom_task || +	    !psc_i2s->playback.bcom_task) { +		dev_err(&op->dev, "Could not allocate bestcomm tasks\n"); +		iounmap(regs); +		kfree(psc_i2s); +		return -ENODEV; +	} + +	/* Disable all interrupts and reset the PSC */ +	out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0); +	out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */ +	out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */ +	out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */ +	out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */ + +	/* Configure the serial interface mode; defaulting to CODEC8 mode */ +	psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S | +			MPC52xx_PSC_SICR_CLKPOL; +	if (of_get_property(op->node, "fsl,cellslave", NULL)) +		psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE | +				 MPC52xx_PSC_SICR_GENCLK; +	out_be32(&psc_i2s->psc_regs->sicr, +		 psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8); + +	/* Check for the codec handle.  If it is not present then we +	 * are done */ +	if (!of_get_property(op->node, "codec-handle", NULL)) +		return 0; + +	/* Set up mode register; +	 * First write: RxRdy (FIFO Alarm) generates rx FIFO irq +	 * Second write: register Normal mode for non loopback +	 */ +	out_8(&psc_i2s->psc_regs->mode, 0); +	out_8(&psc_i2s->psc_regs->mode, 0); + +	/* Set the TX and RX fifo alarm thresholds */ +	out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100); +	out_8(&psc_i2s->fifo_regs->rfcntl, 0x4); +	out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100); +	out_8(&psc_i2s->fifo_regs->tfcntl, 0x7); + +	/* Lookup the IRQ numbers */ +	psc_i2s->playback.irq = +		bcom_get_task_irq(psc_i2s->playback.bcom_task); +	psc_i2s->capture.irq = +		bcom_get_task_irq(psc_i2s->capture.bcom_task); + +	/* Save what we've done so it can be found again later */ +	dev_set_drvdata(&op->dev, psc_i2s); + +	/* Register the SYSFS files */ +	rc = device_create_file(psc_i2s->dev, &dev_attr_status); +	rc = device_create_file(psc_i2s->dev, &dev_attr_capture_overrun); +	rc = device_create_file(psc_i2s->dev, &dev_attr_playback_underrun); +	if (rc) +		dev_info(psc_i2s->dev, "error creating sysfs files\n"); + +	/* Tell the ASoC OF helpers about it */ +	of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node, +				     &psc_i2s->dai); + +	return 0; +} + +static int __devexit psc_i2s_of_remove(struct of_device *op) +{ +	struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev); + +	dev_dbg(&op->dev, "psc_i2s_remove()\n"); + +	bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task); +	bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task); + +	iounmap(psc_i2s->psc_regs); +	iounmap(psc_i2s->fifo_regs); +	kfree(psc_i2s); +	dev_set_drvdata(&op->dev, NULL); + +	return 0; +} + +/* Match table for of_platform binding */ +static struct of_device_id psc_i2s_match[] __devinitdata = { +	{ .compatible = "fsl,mpc5200-psc-i2s", }, +	{} +}; +MODULE_DEVICE_TABLE(of, psc_i2s_match); + +static struct of_platform_driver psc_i2s_driver = { +	.match_table = psc_i2s_match, +	.probe = psc_i2s_of_probe, +	.remove = __devexit_p(psc_i2s_of_remove), +	.driver = { +		.name = "mpc5200-psc-i2s", +		.owner = THIS_MODULE, +	}, +}; + +/* --------------------------------------------------------------------- + * Module setup and teardown; simply register the of_platform driver + * for the PSC in I2S mode. + */ +static int __init psc_i2s_init(void) +{ +	return of_register_platform_driver(&psc_i2s_driver); +} +module_init(psc_i2s_init); + +static void __exit psc_i2s_exit(void) +{ +	of_unregister_platform_driver(&psc_i2s_driver); +} +module_exit(psc_i2s_exit); + + diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 4bdc9d8..94f89de 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -68,10 +68,6 @@ static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)  	guts_set_pmuxcr_dma(machine_data->guts, machine_data->dma_id,  		machine_data->dma_channel_id[1], 0); -	guts_set_pmuxcr_dma(machine_data->guts, 1, 0, 0); -	guts_set_pmuxcr_dma(machine_data->guts, 1, 3, 0); -	guts_set_pmuxcr_dma(machine_data->guts, 0, 3, 0); -  	switch (machine_data->ssi_id) {  	case 0:  		clrsetbits_be32(&machine_data->guts->pmuxcr, @@ -230,6 +226,8 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,  	struct fsl_ssi_info ssi_info;  	struct fsl_dma_info dma_info;  	int ret = -ENODEV; +	unsigned int playback_dma_channel; +	unsigned int capture_dma_channel;  	machine_data = kzalloc(sizeof(struct mpc8610_hpcd_data), GFP_KERNEL);  	if (!machine_data) @@ -381,8 +379,9 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,  		goto error;  	} -	/* Find the DMA channels to use.  For now, we always use the first DMA -	   controller. */ +	/* Find the DMA channels to use.  Both SSIs need to use the same DMA +	 * controller, so let's use DMA#1. +	 */  	for_each_compatible_node(dma_np, NULL, "fsl,mpc8610-dma") {  		iprop = of_get_property(dma_np, "cell-index", NULL);  		if (iprop && (*iprop == 0)) { @@ -397,14 +396,19 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,  	}  	machine_data->dma_id = *iprop; +	/* SSI1 needs to use DMA Channels 0 and 1, and SSI2 needs to use DMA +	 * channels 2 and 3.  This is just how the MPC8610 is wired +	 * internally. +	 */ +	playback_dma_channel = (machine_data->ssi_id == 0) ? 0 : 2; +	capture_dma_channel = (machine_data->ssi_id == 0) ? 1 : 3; +  	/* -	 * Find the DMA channels to use.  For now, we always use DMA channel 0 -	 * for playback, and DMA channel 1 for capture. +	 * Find the DMA channels to use.  	 */  	while ((dma_channel_np = of_get_next_child(dma_np, dma_channel_np))) {  		iprop = of_get_property(dma_channel_np, "cell-index", NULL); -		/* Is it DMA channel 0? */ -		if (iprop && (*iprop == 0)) { +		if (iprop && (*iprop == playback_dma_channel)) {  			/* dma_channel[0] and dma_irq[0] are for playback */  			dma_info.dma_channel[0] = of_iomap(dma_channel_np, 0);  			dma_info.dma_irq[0] = @@ -412,7 +416,7 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,  			machine_data->dma_channel_id[0] = *iprop;  			continue;  		} -		if (iprop && (*iprop == 1)) { +		if (iprop && (*iprop == capture_dma_channel)) {  			/* dma_channel[1] and dma_irq[1] are for capture */  			dma_info.dma_channel[1] = of_iomap(dma_channel_np, 0);  			dma_info.dma_irq[1] = diff --git a/sound/soc/fsl/soc-of-simple.c b/sound/soc/fsl/soc-of-simple.c new file mode 100644 index 0000000..0382fda --- /dev/null +++ b/sound/soc/fsl/soc-of-simple.c @@ -0,0 +1,171 @@ +/* + * OF helpers for ALSA SoC Layer + * + * Copyright (C) 2008, Secret Lab Technologies Ltd. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/bitops.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-of-simple.h> +#include <sound/initval.h> + +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ALSA SoC OpenFirmware bindings"); + +static DEFINE_MUTEX(of_snd_soc_mutex); +static LIST_HEAD(of_snd_soc_device_list); +static int of_snd_soc_next_index; + +struct of_snd_soc_device { +	int id; +	struct list_head list; +	struct snd_soc_device device; +	struct snd_soc_machine machine; +	struct snd_soc_dai_link dai_link; +	struct platform_device *pdev; +	struct device_node *platform_node; +	struct device_node *codec_node; +}; + +static struct snd_soc_ops of_snd_soc_ops = { +}; + +static struct of_snd_soc_device * +of_snd_soc_get_device(struct device_node *codec_node) +{ +	struct of_snd_soc_device *of_soc; + +	list_for_each_entry(of_soc, &of_snd_soc_device_list, list) { +		if (of_soc->codec_node == codec_node) +			return of_soc; +	} + +	of_soc = kzalloc(sizeof(struct of_snd_soc_device), GFP_KERNEL); +	if (!of_soc) +		return NULL; + +	/* Initialize the structure and add it to the global list */ +	of_soc->codec_node = codec_node; +	of_soc->id = of_snd_soc_next_index++; +	of_soc->machine.dai_link = &of_soc->dai_link; +	of_soc->machine.num_links = 1; +	of_soc->device.machine = &of_soc->machine; +	of_soc->dai_link.ops = &of_snd_soc_ops; +	list_add(&of_soc->list, &of_snd_soc_device_list); + +	return of_soc; +} + +static void of_snd_soc_register_device(struct of_snd_soc_device *of_soc) +{ +	struct platform_device *pdev; +	int rc; + +	/* Only register the device if both the codec and platform have +	 * been registered */ +	if ((!of_soc->device.codec_data) || (!of_soc->platform_node)) +		return; + +	pr_info("platform<-->codec match achieved; registering machine\n"); + +	pdev = platform_device_alloc("soc-audio", of_soc->id); +	if (!pdev) { +		pr_err("of_soc: platform_device_alloc() failed\n"); +		return; +	} + +	pdev->dev.platform_data = of_soc; +	platform_set_drvdata(pdev, &of_soc->device); +	of_soc->device.dev = &pdev->dev; + +	/* The ASoC device is complete; register it */ +	rc = platform_device_add(pdev); +	if (rc) { +		pr_err("of_soc: platform_device_add() failed\n"); +		return; +	} + +} + +int of_snd_soc_register_codec(struct snd_soc_codec_device *codec_dev, +			      void *codec_data, struct snd_soc_dai *dai, +			      struct device_node *node) +{ +	struct of_snd_soc_device *of_soc; +	int rc = 0; + +	pr_info("registering ASoC codec driver: %s\n", node->full_name); + +	mutex_lock(&of_snd_soc_mutex); +	of_soc = of_snd_soc_get_device(node); +	if (!of_soc) { +		rc = -ENOMEM; +		goto out; +	} + +	/* Store the codec data */ +	of_soc->device.codec_data = codec_data; +	of_soc->device.codec_dev = codec_dev; +	of_soc->dai_link.name = (char *)node->name; +	of_soc->dai_link.stream_name = (char *)node->name; +	of_soc->dai_link.codec_dai = dai; + +	/* Now try to register the SoC device */ +	of_snd_soc_register_device(of_soc); + + out: +	mutex_unlock(&of_snd_soc_mutex); +	return rc; +} +EXPORT_SYMBOL_GPL(of_snd_soc_register_codec); + +int of_snd_soc_register_platform(struct snd_soc_platform *platform, +				 struct device_node *node, +				 struct snd_soc_dai *cpu_dai) +{ +	struct of_snd_soc_device *of_soc; +	struct device_node *codec_node; +	const phandle *handle; +	int len, rc = 0; + +	pr_info("registering ASoC platform driver: %s\n", node->full_name); + +	handle = of_get_property(node, "codec-handle", &len); +	if (!handle || len < sizeof(handle)) +		return -ENODEV; +	codec_node = of_find_node_by_phandle(*handle); +	if (!codec_node) +		return -ENODEV; +	pr_info("looking for codec: %s\n", codec_node->full_name); + +	mutex_lock(&of_snd_soc_mutex); +	of_soc = of_snd_soc_get_device(codec_node); +	if (!of_soc) { +		rc = -ENOMEM; +		goto out; +	} + +	of_soc->platform_node = node; +	of_soc->dai_link.cpu_dai = cpu_dai; +	of_soc->device.platform = platform; +	of_soc->machine.name = of_soc->dai_link.cpu_dai->name; + +	/* Now try to register the SoC device */ +	of_snd_soc_register_device(of_soc); + + out: +	mutex_unlock(&of_snd_soc_mutex); +	return rc; +} +EXPORT_SYMBOL_GPL(of_snd_soc_register_platform); diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 87d0ed0..d166b6b 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -290,6 +290,7 @@ static struct snd_soc_machine snd_soc_machine_n810 = {  /* Audio private data */  static struct aic3x_setup_data n810_aic33_setup = { +	.i2c_bus = 2,  	.i2c_address = 0x18,  	.gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED,  	.gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 9212c37..f8c1cdd 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -1,6 +1,7 @@  config SND_PXA2XX_SOC  	tristate "SoC Audio for the Intel PXA2xx chip"  	depends on ARCH_PXA +	select SND_PXA2XX_LIB  	help  	  Say Y or M if you want to add support for codecs attached to  	  the PXA2xx AC97, I2S or SSP interface. You will also need @@ -13,6 +14,8 @@ config SND_PXA2XX_AC97  config SND_PXA2XX_SOC_AC97  	tristate  	select AC97_BUS +	select SND_ARM +	select SND_PXA2XX_LIB_AC97  	select SND_SOC_AC97_BUS  config SND_PXA2XX_SOC_I2S diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 0a53f72..1a8373d 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -18,13 +18,13 @@  #include <linux/timer.h>  #include <linux/interrupt.h>  #include <linux/platform_device.h> +#include <linux/gpio.h>  #include <sound/core.h>  #include <sound/pcm.h>  #include <sound/soc.h>  #include <sound/soc-dapm.h>  #include <asm/mach-types.h> -#include <asm/hardware/scoop.h>  #include <mach/pxa-regs.h>  #include <mach/hardware.h>  #include <mach/corgi.h> @@ -54,8 +54,8 @@ static void corgi_ext_control(struct snd_soc_codec *codec)  	switch (corgi_jack_func) {  	case CORGI_HP:  		/* set = unmute headphone */ -		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); -		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); +		gpio_set_value(CORGI_GPIO_MUTE_L, 1); +		gpio_set_value(CORGI_GPIO_MUTE_R, 1);  		snd_soc_dapm_disable_pin(codec, "Mic Jack");  		snd_soc_dapm_disable_pin(codec, "Line Jack");  		snd_soc_dapm_enable_pin(codec, "Headphone Jack"); @@ -63,24 +63,24 @@ static void corgi_ext_control(struct snd_soc_codec *codec)  		break;  	case CORGI_MIC:  		/* reset = mute headphone */ -		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); -		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); +		gpio_set_value(CORGI_GPIO_MUTE_L, 0); +		gpio_set_value(CORGI_GPIO_MUTE_R, 0);  		snd_soc_dapm_enable_pin(codec, "Mic Jack");  		snd_soc_dapm_disable_pin(codec, "Line Jack");  		snd_soc_dapm_disable_pin(codec, "Headphone Jack");  		snd_soc_dapm_disable_pin(codec, "Headset Jack");  		break;  	case CORGI_LINE: -		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); -		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); +		gpio_set_value(CORGI_GPIO_MUTE_L, 0); +		gpio_set_value(CORGI_GPIO_MUTE_R, 0);  		snd_soc_dapm_disable_pin(codec, "Mic Jack");  		snd_soc_dapm_enable_pin(codec, "Line Jack");  		snd_soc_dapm_disable_pin(codec, "Headphone Jack");  		snd_soc_dapm_disable_pin(codec, "Headset Jack");  		break;  	case CORGI_HEADSET: -		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); -		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); +		gpio_set_value(CORGI_GPIO_MUTE_L, 0); +		gpio_set_value(CORGI_GPIO_MUTE_R, 1);  		snd_soc_dapm_enable_pin(codec, "Mic Jack");  		snd_soc_dapm_disable_pin(codec, "Line Jack");  		snd_soc_dapm_disable_pin(codec, "Headphone Jack"); @@ -114,8 +114,8 @@ static int corgi_shutdown(struct snd_pcm_substream *substream)  	struct snd_soc_codec *codec = rtd->socdev->codec;  	/* set = unmute headphone */ -	set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); -	set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); +	gpio_set_value(CORGI_GPIO_MUTE_L, 1); +	gpio_set_value(CORGI_GPIO_MUTE_R, 1);  	return 0;  } @@ -218,22 +218,14 @@ static int corgi_set_spk(struct snd_kcontrol *kcontrol,  static int corgi_amp_event(struct snd_soc_dapm_widget *w,  	struct snd_kcontrol *k, int event)  { -	if (SND_SOC_DAPM_EVENT_ON(event)) -		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); -	else -		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_APM_ON); - +	gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));  	return 0;  }  static int corgi_mic_event(struct snd_soc_dapm_widget *w,  	struct snd_kcontrol *k, int event)  { -	if (SND_SOC_DAPM_EVENT_ON(event)) -		set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); -	else -		reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MIC_BIAS); - +	gpio_set_value(CORGI_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));  	return 0;  } @@ -330,6 +322,7 @@ static struct snd_soc_machine snd_soc_machine_corgi = {  /* corgi audio private data */  static struct wm8731_setup_data corgi_wm8731_setup = { +	.i2c_bus = 0,  	.i2c_address = 0x1b,  }; diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index a4697f7..f84f7d8 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -284,6 +284,7 @@ static struct snd_soc_machine snd_soc_machine_poodle = {  /* poodle audio private data */  static struct wm8731_setup_data poodle_wm8731_setup = { +	.i2c_bus = 0,  	.i2c_address = 0x1b,  }; diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index d94a495..a7a3a9c 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -13,225 +13,30 @@  #include <linux/init.h>  #include <linux/module.h>  #include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/wait.h> -#include <linux/clk.h> -#include <linux/delay.h>  #include <sound/core.h> -#include <sound/pcm.h>  #include <sound/ac97_codec.h> -#include <sound/initval.h>  #include <sound/soc.h> +#include <sound/pxa2xx-lib.h> -#include <asm/irq.h> -#include <linux/mutex.h>  #include <mach/hardware.h>  #include <mach/pxa-regs.h> -#include <mach/pxa2xx-gpio.h> -#include <mach/audio.h>  #include "pxa2xx-pcm.h"  #include "pxa2xx-ac97.h" -static DEFINE_MUTEX(car_mutex); -static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); -static volatile long gsr_bits; -static struct clk *ac97_clk; -#ifdef CONFIG_PXA27x -static struct clk *ac97conf_clk; -#endif - -/* - * Beware PXA27x bugs: - * - *   o Slot 12 read from modem space will hang controller. - *   o CDONE, SDONE interrupt fails after any slot 12 IO. - * - * We therefore have an hybrid approach for waiting on SDONE (interrupt or - * 1 jiffy timeout if interrupt never comes). - */ - -static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, -	unsigned short reg) -{ -	unsigned short val = -1; -	volatile u32 *reg_addr; - -	mutex_lock(&car_mutex); - -	/* set up primary or secondary codec/modem space */ -#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) -	reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; -#else -	if (reg == AC97_GPIO_STATUS) -		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; -	else -		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; -#endif -	reg_addr += (reg >> 1); - -#ifndef CONFIG_PXA27x -	if (reg == AC97_GPIO_STATUS) { -		/* read from controller cache */ -		val = *reg_addr; -		goto out; -	} -#endif - -	/* start read access across the ac97 link */ -	GSR = GSR_CDONE | GSR_SDONE; -	gsr_bits = 0; -	val = *reg_addr; - -	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); -	if (!((GSR | gsr_bits) & GSR_SDONE)) { -		printk(KERN_ERR "%s: read error (ac97_reg=%x GSR=%#lx)\n", -				__func__, reg, GSR | gsr_bits); -		val = -1; -		goto out; -	} - -	/* valid data now */ -	GSR = GSR_CDONE | GSR_SDONE; -	gsr_bits = 0; -	val = *reg_addr; -	/* but we've just started another cycle... */ -	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); - -out:	mutex_unlock(&car_mutex); -	return val; -} - -static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, -	unsigned short val) -{ -	volatile u32 *reg_addr; - -	mutex_lock(&car_mutex); - -	/* set up primary or secondary codec/modem space */ -#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) -	reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; -#else -	if (reg == AC97_GPIO_STATUS) -		reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; -	else -		reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; -#endif -	reg_addr += (reg >> 1); - -	GSR = GSR_CDONE | GSR_SDONE; -	gsr_bits = 0; -	*reg_addr = val; -	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1); -	if (!((GSR | gsr_bits) & GSR_CDONE)) -		printk(KERN_ERR "%s: write error (ac97_reg=%x GSR=%#lx)\n", -				__func__, reg, GSR | gsr_bits); - -	mutex_unlock(&car_mutex); -} -  static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97)  { -#ifdef CONFIG_PXA3xx -	int timeout = 100; -#endif -	gsr_bits = 0; - -#ifdef CONFIG_PXA27x -	/* warm reset broken on Bulverde, -	   so manually keep AC97 reset high */ -	pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); -	udelay(10); -	GCR |= GCR_WARM_RST; -	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); -	udelay(500); -#elif defined(CONFIG_PXA3xx) -	/* Can't use interrupts */ -	GCR |= GCR_WARM_RST; -	while (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)) && timeout--) -		mdelay(1); -#else -	GCR |= GCR_WARM_RST | GCR_PRIRDY_IEN | GCR_SECRDY_IEN; -	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); -#endif - -	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) -		printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n", -				 __func__, gsr_bits); +	pxa2xx_ac97_try_warm_reset(ac97); -	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); -	GCR |= GCR_SDONE_IE|GCR_CDONE_IE; +	pxa2xx_ac97_finish_reset(ac97);  }  static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97)  { -#ifdef CONFIG_PXA3xx -	int timeout = 1000; - -	/* Hold CLKBPB for 100us */ -	GCR = 0; -	GCR = GCR_CLKBPB; -	udelay(100); -	GCR = 0; -#endif +	pxa2xx_ac97_try_cold_reset(ac97); -	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */ -	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */ - -	gsr_bits = 0; -#ifdef CONFIG_PXA27x -	/* PXA27x Developers Manual section 13.5.2.2.1 */ -	clk_enable(ac97conf_clk); -	udelay(5); -	clk_disable(ac97conf_clk); -	GCR = GCR_COLD_RST; -	udelay(50); -#elif defined(CONFIG_PXA3xx) -	/* Can't use interrupts on PXA3xx */ -	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); - -	GCR = GCR_WARM_RST | GCR_COLD_RST; -	while (!(GSR & (GSR_PCR | GSR_SCR)) && timeout--) -		mdelay(10); -#else -	GCR = GCR_COLD_RST; -	GCR |= GCR_CDONE_IE|GCR_SDONE_IE; -	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1); -#endif - -	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) -		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n", -				 __func__, gsr_bits); - -	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); -	GCR |= GCR_SDONE_IE|GCR_CDONE_IE; -} - -static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id) -{ -	long status; - -	status = GSR; -	if (status) { -		GSR = status; -		gsr_bits |= status; -		wake_up(&gsr_wq); - -#ifdef CONFIG_PXA27x -		/* Although we don't use those we still need to clear them -		   since they tend to spuriously trigger when MMC is used -		   (hardware bug? go figure)... */ -		MISR = MISR_EOC; -		PISR = PISR_EOC; -		MCSR = MCSR_EOC; -#endif - -		return IRQ_HANDLED; -	} - -	return IRQ_NONE; +	pxa2xx_ac97_finish_reset(ac97);  }  struct snd_ac97_bus_ops soc_ac97_ops = { @@ -244,7 +49,7 @@ struct snd_ac97_bus_ops soc_ac97_ops = {  static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {  	.name			= "AC97 PCM Stereo out",  	.dev_addr		= __PREG(PCDR), -	.drcmr			= &DRCMRTXPCDR, +	.drcmr			= &DRCMR(12),  	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |  				  DCMD_BURST32 | DCMD_WIDTH4,  }; @@ -252,7 +57,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {  static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {  	.name			= "AC97 PCM Stereo in",  	.dev_addr		= __PREG(PCDR), -	.drcmr			= &DRCMRRXPCDR, +	.drcmr			= &DRCMR(11),  	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |  				  DCMD_BURST32 | DCMD_WIDTH4,  }; @@ -260,7 +65,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {  static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {  	.name			= "AC97 Aux PCM (Slot 5) Mono out",  	.dev_addr		= __PREG(MODR), -	.drcmr			= &DRCMRTXMODR, +	.drcmr			= &DRCMR(10),  	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |  				  DCMD_BURST16 | DCMD_WIDTH2,  }; @@ -268,7 +73,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {  static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {  	.name			= "AC97 Aux PCM (Slot 5) Mono in",  	.dev_addr		= __PREG(MODR), -	.drcmr			= &DRCMRRXMODR, +	.drcmr			= &DRCMR(9),  	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |  				  DCMD_BURST16 | DCMD_WIDTH2,  }; @@ -276,7 +81,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {  static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {  	.name			= "AC97 Mic PCM (Slot 6) Mono in",  	.dev_addr		= __PREG(MCDR), -	.drcmr			= &DRCMRRXMCDR, +	.drcmr			= &DRCMR(8),  	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |  				  DCMD_BURST16 | DCMD_WIDTH2,  }; @@ -285,24 +90,13 @@ static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {  static int pxa2xx_ac97_suspend(struct platform_device *pdev,  	struct snd_soc_dai *dai)  { -	GCR |= GCR_ACLINK_OFF; -	clk_disable(ac97_clk); -	return 0; +	return pxa2xx_ac97_hw_suspend();  }  static int pxa2xx_ac97_resume(struct platform_device *pdev,  	struct snd_soc_dai *dai)  { -	pxa_gpio_mode(GPIO31_SYNC_AC97_MD); -	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); -	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); -	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); -#ifdef CONFIG_PXA27x -	/* Use GPIO 113 as AC97 Reset on Bulverde */ -	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); -#endif -	clk_enable(ac97_clk); -	return 0; +	return pxa2xx_ac97_hw_resume();  }  #else @@ -313,61 +107,13 @@ static int pxa2xx_ac97_resume(struct platform_device *pdev,  static int pxa2xx_ac97_probe(struct platform_device *pdev,  			     struct snd_soc_dai *dai)  { -	int ret; - -	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); -	if (ret < 0) -		goto err; - -	pxa_gpio_mode(GPIO31_SYNC_AC97_MD); -	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); -	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); -	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); -#ifdef CONFIG_PXA27x -	/* Use GPIO 113 as AC97 Reset on Bulverde */ -	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); - -	ac97conf_clk = clk_get(&pdev->dev, "AC97CONFCLK"); -	if (IS_ERR(ac97conf_clk)) { -		ret = PTR_ERR(ac97conf_clk); -		ac97conf_clk = NULL; -		goto err_irq; -	} -#endif -	ac97_clk = clk_get(&pdev->dev, "AC97CLK"); -	if (IS_ERR(ac97_clk)) { -		ret = PTR_ERR(ac97_clk); -		ac97_clk = NULL; -		goto err_irq; -	} -	clk_enable(ac97_clk); -	return 0; - - err_irq: -	GCR |= GCR_ACLINK_OFF; -#ifdef CONFIG_PXA27x -	if (ac97conf_clk) { -		clk_put(ac97conf_clk); -		ac97conf_clk = NULL; -	} -#endif -	free_irq(IRQ_AC97, NULL); - err: -	return ret; +	return pxa2xx_ac97_hw_probe(pdev);  }  static void pxa2xx_ac97_remove(struct platform_device *pdev,  			       struct snd_soc_dai *dai)  { -	GCR |= GCR_ACLINK_OFF; -	free_irq(IRQ_AC97, NULL); -#ifdef CONFIG_PXA27x -	clk_put(ac97conf_clk); -	ac97conf_clk = NULL; -#endif -	clk_disable(ac97_clk); -	clk_put(ac97_clk); -	ac97_clk = NULL; +	pxa2xx_ac97_hw_remove(pdev);  }  static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream, diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index c796b18..2fb5829 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -21,6 +21,7 @@  #include <sound/pcm.h>  #include <sound/initval.h>  #include <sound/soc.h> +#include <sound/pxa2xx-lib.h>  #include <mach/hardware.h>  #include <mach/pxa-regs.h> @@ -30,6 +31,54 @@  #include "pxa2xx-pcm.h"  #include "pxa2xx-i2s.h" +struct pxa2xx_gpio { +	u32 sys; +	u32	rx; +	u32 tx; +	u32 clk; +	u32 frm; +}; + +/* + * I2S Controller Register and Bit Definitions + */ +#define SACR0		__REG(0x40400000)  /* Global Control Register */ +#define SACR1		__REG(0x40400004)  /* Serial Audio I 2 S/MSB-Justified Control Register */ +#define SASR0		__REG(0x4040000C)  /* Serial Audio I 2 S/MSB-Justified Interface and FIFO Status Register */ +#define SAIMR		__REG(0x40400014)  /* Serial Audio Interrupt Mask Register */ +#define SAICR		__REG(0x40400018)  /* Serial Audio Interrupt Clear Register */ +#define SADIV		__REG(0x40400060)  /* Audio Clock Divider Register. */ +#define SADR		__REG(0x40400080)  /* Serial Audio Data Register (TX and RX FIFO access Register). */ + +#define SACR0_RFTH(x)	((x) << 12)	/* Rx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_TFTH(x)	((x) << 8)	/* Tx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_STRF	(1 << 5)	/* FIFO Select for EFWR Special Function */ +#define SACR0_EFWR	(1 << 4)	/* Enable EFWR Function  */ +#define SACR0_RST	(1 << 3)	/* FIFO, i2s Register Reset */ +#define SACR0_BCKD	(1 << 2) 	/* Bit Clock Direction */ +#define SACR0_ENB	(1 << 0)	/* Enable I2S Link */ +#define SACR1_ENLBF	(1 << 5)	/* Enable Loopback */ +#define SACR1_DRPL	(1 << 4) 	/* Disable Replaying Function */ +#define SACR1_DREC	(1 << 3)	/* Disable Recording Function */ +#define SACR1_AMSL	(1 << 0)	/* Specify Alternate Mode */ + +#define SASR0_I2SOFF	(1 << 7)	/* Controller Status */ +#define SASR0_ROR	(1 << 6)	/* Rx FIFO Overrun */ +#define SASR0_TUR	(1 << 5)	/* Tx FIFO Underrun */ +#define SASR0_RFS	(1 << 4)	/* Rx FIFO Service Request */ +#define SASR0_TFS	(1 << 3)	/* Tx FIFO Service Request */ +#define SASR0_BSY	(1 << 2)	/* I2S Busy */ +#define SASR0_RNE	(1 << 1)	/* Rx FIFO Not Empty */ +#define SASR0_TNF	(1 << 0) 	/* Tx FIFO Not Empty */ + +#define SAICR_ROR	(1 << 6)	/* Clear Rx FIFO Overrun Interrupt */ +#define SAICR_TUR	(1 << 5)	/* Clear Tx FIFO Underrun Interrupt */ + +#define SAIMR_ROR	(1 << 6)	/* Enable Rx FIFO Overrun Condition Interrupt */ +#define SAIMR_TUR	(1 << 5)	/* Enable Tx FIFO Underrun Condition Interrupt */ +#define SAIMR_RFS	(1 << 4)	/* Enable Rx FIFO Service Interrupt */ +#define SAIMR_TFS	(1 << 3)	/* Enable Tx FIFO Service Interrupt */ +  struct pxa_i2s_port {  	u32 sadiv;  	u32 sacr0; @@ -44,7 +93,7 @@ static struct clk *clk_i2s;  static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {  	.name			= "I2S PCM Stereo out",  	.dev_addr		= __PREG(SADR), -	.drcmr			= &DRCMRTXSADR, +	.drcmr			= &DRCMR(3),  	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |  				  DCMD_BURST32 | DCMD_WIDTH4,  }; @@ -52,7 +101,7 @@ static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {  static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {  	.name			= "I2S PCM Stereo in",  	.dev_addr		= __PREG(SADR), -	.drcmr			= &DRCMRRXSADR, +	.drcmr			= &DRCMR(2),  	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |  				  DCMD_BURST32 | DCMD_WIDTH4,  }; @@ -65,11 +114,6 @@ static struct pxa2xx_gpio gpio_bus[] = {  		.frm = GPIO31_SYNC_I2S_MD,  	},  	{ /* I2S SoC Master */ -#ifdef CONFIG_PXA27x -		.sys = GPIO113_I2S_SYSCLK_MD, -#else -		.sys = GPIO32_SYSCLK_I2S_MD, -#endif  		.rx = GPIO29_SDATA_IN_I2S_MD,  		.tx = GPIO30_SDATA_OUT_I2S_MD,  		.clk = GPIO28_BITCLK_OUT_I2S_MD, @@ -343,6 +387,11 @@ static struct platform_driver pxa2xx_i2s_driver = {  static int __init pxa2xx_i2s_init(void)  { +	if (cpu_is_pxa27x()) +		gpio_bus[1].sys = GPIO113_I2S_SYSCLK_MD; +	else +		gpio_bus[1].sys = GPIO32_SYSCLK_I2S_MD; +  	clk_i2s = ERR_PTR(-ENOENT);  	return platform_driver_register(&pxa2xx_i2s_driver);  } diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 4345f38..afcd892 100644 --- a/sound/soc/pxa/pxa2xx-pcm.c +++ b/sound/soc/pxa/pxa2xx-pcm.c @@ -10,64 +10,14 @@   * published by the Free Software Foundation.   */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/slab.h>  #include <linux/dma-mapping.h>  #include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h>  #include <sound/soc.h> - -#include <asm/dma.h> -#include <mach/hardware.h> -#include <mach/pxa-regs.h> -#include <mach/audio.h> +#include <sound/pxa2xx-lib.h>  #include "pxa2xx-pcm.h" - -static const struct snd_pcm_hardware pxa2xx_pcm_hardware = { -	.info			= SNDRV_PCM_INFO_MMAP | -				  SNDRV_PCM_INFO_MMAP_VALID | -				  SNDRV_PCM_INFO_INTERLEAVED | -				  SNDRV_PCM_INFO_PAUSE | -				  SNDRV_PCM_INFO_RESUME, -	.formats		= SNDRV_PCM_FMTBIT_S16_LE | -					SNDRV_PCM_FMTBIT_S24_LE | -					SNDRV_PCM_FMTBIT_S32_LE, -	.period_bytes_min	= 32, -	.period_bytes_max	= 8192 - 32, -	.periods_min		= 1, -	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc), -	.buffer_bytes_max	= 128 * 1024, -	.fifo_size		= 32, -}; - -struct pxa2xx_runtime_data { -	int dma_ch; -	struct pxa2xx_pcm_dma_params *params; -	pxa_dma_desc *dma_desc_array; -	dma_addr_t dma_desc_array_phys; -}; - -static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) -{ -	struct snd_pcm_substream *substream = dev_id; -	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; -	int dcsr; - -	dcsr = DCSR(dma_ch); -	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; - -	if (dcsr & DCSR_ENDINTR) { -		snd_pcm_period_elapsed(substream); -	} else { -		printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", -			prtd->params->name, dma_ch, dcsr); -	} -} +#include "../../arm/pxa2xx-pcm.h"  static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,  	struct snd_pcm_hw_params *params) @@ -76,10 +26,6 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,  	struct pxa2xx_runtime_data *prtd = runtime->private_data;  	struct snd_soc_pcm_runtime *rtd = substream->private_data;  	struct pxa2xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data; -	size_t totsize = params_buffer_bytes(params); -	size_t period = params_period_bytes(params); -	pxa_dma_desc *dma_desc; -	dma_addr_t dma_buff_phys, next_desc_phys;  	int ret;  	/* return if this is a bufferless transfer e.g. @@ -106,42 +52,16 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,  		prtd->dma_ch = ret;  	} -	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); -	runtime->dma_bytes = totsize; - -	dma_desc = prtd->dma_desc_array; -	next_desc_phys = prtd->dma_desc_array_phys; -	dma_buff_phys = runtime->dma_addr; -	do { -		next_desc_phys += sizeof(pxa_dma_desc); -		dma_desc->ddadr = next_desc_phys; -		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -			dma_desc->dsadr = dma_buff_phys; -			dma_desc->dtadr = prtd->params->dev_addr; -		} else { -			dma_desc->dsadr = prtd->params->dev_addr; -			dma_desc->dtadr = dma_buff_phys; -		} -		if (period > totsize) -			period = totsize; -		dma_desc->dcmd = prtd->params->dcmd | period | DCMD_ENDIRQEN; -		dma_desc++; -		dma_buff_phys += period; -	} while (totsize -= period); -	dma_desc[-1].ddadr = prtd->dma_desc_array_phys; - -	return 0; +	return __pxa2xx_pcm_hw_params(substream, params);  }  static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)  {  	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; -	if (prtd && prtd->params) -		*prtd->params->drcmr = 0; +	__pxa2xx_pcm_hw_free(substream);  	if (prtd->dma_ch) { -		snd_pcm_set_runtime_buffer(substream, NULL);  		pxa_free_dma(prtd->dma_ch);  		prtd->dma_ch = 0;  	} @@ -149,188 +69,21 @@ static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)  	return 0;  } -static int pxa2xx_pcm_prepare(struct snd_pcm_substream *substream) -{ -	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; - -	DCSR(prtd->dma_ch) &= ~DCSR_RUN; -	DCSR(prtd->dma_ch) = 0; -	DCMD(prtd->dma_ch) = 0; -	*prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD; - -	return 0; -} - -static int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ -	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data; -	int ret = 0; - -	switch (cmd) { -	case SNDRV_PCM_TRIGGER_START: -		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; -		DCSR(prtd->dma_ch) = DCSR_RUN; -		break; - -	case SNDRV_PCM_TRIGGER_STOP: -	case SNDRV_PCM_TRIGGER_SUSPEND: -	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -		DCSR(prtd->dma_ch) &= ~DCSR_RUN; -		break; - -	case SNDRV_PCM_TRIGGER_RESUME: -		DCSR(prtd->dma_ch) |= DCSR_RUN; -		break; -	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -		DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys; -		DCSR(prtd->dma_ch) |= DCSR_RUN; -		break; - -	default: -		ret = -EINVAL; -	} - -	return ret; -} - -static snd_pcm_uframes_t -pxa2xx_pcm_pointer(struct snd_pcm_substream *substream) -{ -	struct snd_pcm_runtime *runtime = substream->runtime; -	struct pxa2xx_runtime_data *prtd = runtime->private_data; - -	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? -			 DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch); -	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr); - -	if (x == runtime->buffer_size) -		x = 0; -	return x; -} - -static int pxa2xx_pcm_open(struct snd_pcm_substream *substream) -{ -	struct snd_pcm_runtime *runtime = substream->runtime; -	struct pxa2xx_runtime_data *prtd; -	int ret; - -	snd_soc_set_runtime_hwparams(substream, &pxa2xx_pcm_hardware); - -	/* -	 * For mysterious reasons (and despite what the manual says) -	 * playback samples are lost if the DMA count is not a multiple -	 * of the DMA burst size.  Let's add a rule to enforce that. -	 */ -	ret = snd_pcm_hw_constraint_step(runtime, 0, -		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32); -	if (ret) -		goto out; - -	ret = snd_pcm_hw_constraint_step(runtime, 0, -		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32); -	if (ret) -		goto out; - -	ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); -	if (ret < 0) -		goto out; - -	prtd = kzalloc(sizeof(struct pxa2xx_runtime_data), GFP_KERNEL); -	if (prtd == NULL) { -		ret = -ENOMEM; -		goto out; -	} - -	prtd->dma_desc_array = -		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE, -				       &prtd->dma_desc_array_phys, GFP_KERNEL); -	if (!prtd->dma_desc_array) { -		ret = -ENOMEM; -		goto err1; -	} - -	runtime->private_data = prtd; -	return 0; - - err1: -	kfree(prtd); - out: -	return ret; -} - -static int pxa2xx_pcm_close(struct snd_pcm_substream *substream) -{ -	struct snd_pcm_runtime *runtime = substream->runtime; -	struct pxa2xx_runtime_data *prtd = runtime->private_data; - -	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE, -			      prtd->dma_desc_array, prtd->dma_desc_array_phys); -	kfree(prtd); -	return 0; -} - -static int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream, -	struct vm_area_struct *vma) -{ -	struct snd_pcm_runtime *runtime = substream->runtime; -	return dma_mmap_writecombine(substream->pcm->card->dev, vma, -				     runtime->dma_area, -				     runtime->dma_addr, -				     runtime->dma_bytes); -} -  struct snd_pcm_ops pxa2xx_pcm_ops = { -	.open		= pxa2xx_pcm_open, -	.close		= pxa2xx_pcm_close, +	.open		= __pxa2xx_pcm_open, +	.close		= __pxa2xx_pcm_close,  	.ioctl		= snd_pcm_lib_ioctl,  	.hw_params	= pxa2xx_pcm_hw_params,  	.hw_free	= pxa2xx_pcm_hw_free, -	.prepare	= pxa2xx_pcm_prepare, +	.prepare	= __pxa2xx_pcm_prepare,  	.trigger	= pxa2xx_pcm_trigger,  	.pointer	= pxa2xx_pcm_pointer,  	.mmap		= pxa2xx_pcm_mmap,  }; -static int pxa2xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ -	struct snd_pcm_substream *substream = pcm->streams[stream].substream; -	struct snd_dma_buffer *buf = &substream->dma_buffer; -	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max; -	buf->dev.type = SNDRV_DMA_TYPE_DEV; -	buf->dev.dev = pcm->card->dev; -	buf->private_data = NULL; -	buf->area = dma_alloc_writecombine(pcm->card->dev, size, -					   &buf->addr, GFP_KERNEL); -	if (!buf->area) -		return -ENOMEM; -	buf->bytes = size; -	return 0; -} - -static void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm) -{ -	struct snd_pcm_substream *substream; -	struct snd_dma_buffer *buf; -	int stream; - -	for (stream = 0; stream < 2; stream++) { -		substream = pcm->streams[stream].substream; -		if (!substream) -			continue; - -		buf = &substream->dma_buffer; -		if (!buf->area) -			continue; - -		dma_free_writecombine(pcm->card->dev, buf->bytes, -				      buf->area, buf->addr); -		buf->area = NULL; -	} -} -  static u64 pxa2xx_pcm_dmamask = DMA_32BIT_MASK; -int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, +static int pxa2xx_soc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,  	struct snd_pcm *pcm)  {  	int ret = 0; @@ -360,7 +113,7 @@ int pxa2xx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,  struct snd_soc_platform pxa2xx_soc_platform = {  	.name		= "pxa2xx-audio",  	.pcm_ops 	= &pxa2xx_pcm_ops, -	.pcm_new	= pxa2xx_pcm_new, +	.pcm_new	= pxa2xx_soc_pcm_new,  	.pcm_free	= pxa2xx_pcm_free_dma_buffers,  };  EXPORT_SYMBOL_GPL(pxa2xx_soc_platform); diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h index 54c9c75..60c3b20 100644 --- a/sound/soc/pxa/pxa2xx-pcm.h +++ b/sound/soc/pxa/pxa2xx-pcm.h @@ -13,21 +13,6 @@  #ifndef _PXA2XX_PCM_H  #define _PXA2XX_PCM_H -struct pxa2xx_pcm_dma_params { -	char *name;			/* stream identifier */ -	u32 dcmd;			/* DMA descriptor dcmd field */ -	volatile u32 *drcmr;		/* the DMA request channel to use */ -	u32 dev_addr;			/* device physical address for DMA */ -}; - -struct pxa2xx_gpio { -	u32 sys; -	u32	rx; -	u32 tx; -	u32 clk; -	u32 frm; -}; -  /* platform data */  extern struct snd_soc_platform pxa2xx_soc_platform; diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 37cb768..9a70b00 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -19,16 +19,15 @@  #include <linux/timer.h>  #include <linux/interrupt.h>  #include <linux/platform_device.h> +#include <linux/gpio.h>  #include <sound/core.h>  #include <sound/pcm.h>  #include <sound/soc.h>  #include <sound/soc-dapm.h>  #include <asm/mach-types.h> -#include <asm/hardware/scoop.h>  #include <mach/pxa-regs.h>  #include <mach/hardware.h> -#include <mach/akita.h>  #include <mach/spitz.h>  #include "../codecs/wm8750.h"  #include "pxa2xx-pcm.h" @@ -63,8 +62,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec)  		snd_soc_dapm_disable_pin(codec, "Mic Jack");  		snd_soc_dapm_disable_pin(codec, "Line Jack");  		snd_soc_dapm_enable_pin(codec, "Headphone Jack"); -		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); -		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); +		gpio_set_value(SPITZ_GPIO_MUTE_L, 1); +		gpio_set_value(SPITZ_GPIO_MUTE_R, 1);  		break;  	case SPITZ_MIC:  		/* enable mic jack and bias, mute hp */ @@ -72,8 +71,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec)  		snd_soc_dapm_disable_pin(codec, "Headset Jack");  		snd_soc_dapm_disable_pin(codec, "Line Jack");  		snd_soc_dapm_enable_pin(codec, "Mic Jack"); -		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); -		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); +		gpio_set_value(SPITZ_GPIO_MUTE_L, 0); +		gpio_set_value(SPITZ_GPIO_MUTE_R, 0);  		break;  	case SPITZ_LINE:  		/* enable line jack, disable mic bias and mute hp */ @@ -81,8 +80,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec)  		snd_soc_dapm_disable_pin(codec, "Headset Jack");  		snd_soc_dapm_disable_pin(codec, "Mic Jack");  		snd_soc_dapm_enable_pin(codec, "Line Jack"); -		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); -		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); +		gpio_set_value(SPITZ_GPIO_MUTE_L, 0); +		gpio_set_value(SPITZ_GPIO_MUTE_R, 0);  		break;  	case SPITZ_HEADSET:  		/* enable and unmute headset jack enable mic bias, mute L hp */ @@ -90,8 +89,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec)  		snd_soc_dapm_enable_pin(codec, "Mic Jack");  		snd_soc_dapm_disable_pin(codec, "Line Jack");  		snd_soc_dapm_enable_pin(codec, "Headset Jack"); -		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); -		set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); +		gpio_set_value(SPITZ_GPIO_MUTE_L, 0); +		gpio_set_value(SPITZ_GPIO_MUTE_R, 1);  		break;  	case SPITZ_HP_OFF: @@ -100,8 +99,8 @@ static void spitz_ext_control(struct snd_soc_codec *codec)  		snd_soc_dapm_disable_pin(codec, "Headset Jack");  		snd_soc_dapm_disable_pin(codec, "Mic Jack");  		snd_soc_dapm_disable_pin(codec, "Line Jack"); -		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); -		reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); +		gpio_set_value(SPITZ_GPIO_MUTE_L, 0); +		gpio_set_value(SPITZ_GPIO_MUTE_R, 0);  		break;  	}  	snd_soc_dapm_sync(codec); @@ -215,23 +214,14 @@ static int spitz_set_spk(struct snd_kcontrol *kcontrol,  static int spitz_mic_bias(struct snd_soc_dapm_widget *w,  	struct snd_kcontrol *k, int event)  { -	if (machine_is_borzoi() || machine_is_spitz()) { -		if (SND_SOC_DAPM_EVENT_ON(event)) -			set_scoop_gpio(&spitzscoop2_device.dev, -				SPITZ_SCP2_MIC_BIAS); -		else -			reset_scoop_gpio(&spitzscoop2_device.dev, -				SPITZ_SCP2_MIC_BIAS); -	} +	if (machine_is_borzoi() || machine_is_spitz()) +		gpio_set_value(SPITZ_GPIO_MIC_BIAS, +				SND_SOC_DAPM_EVENT_ON(event)); + +	if (machine_is_akita()) +		gpio_set_value(AKITA_GPIO_MIC_BIAS, +				SND_SOC_DAPM_EVENT_ON(event)); -	if (machine_is_akita()) { -		if (SND_SOC_DAPM_EVENT_ON(event)) -			akita_set_ioexp(&akitaioexp_device.dev, -				AKITA_IOEXP_MIC_BIAS); -		else -			akita_reset_ioexp(&akitaioexp_device.dev, -				AKITA_IOEXP_MIC_BIAS); -	}  	return 0;  } @@ -337,6 +327,7 @@ static struct snd_soc_machine snd_soc_machine_spitz = {  /* spitz audio private data */  static struct wm8750_setup_data spitz_wm8750_setup = { +	.i2c_bus = 0,  	.i2c_address = 0x1b,  }; diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 8089f8e..73a50e9 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -24,6 +24,7 @@  #include <sound/soc-dapm.h>  #include <sound/tlv.h> +#include <asm/mach-types.h>  #include <asm/hardware/scoop.h>  #include <mach/regs-clock.h>  #include <mach/regs-gpio.h> @@ -586,6 +587,7 @@ static struct snd_soc_machine neo1973 = {  };  static struct wm8753_setup_data neo1973_wm8753_setup = { +	.i2c_bus = 0,  	.i2c_address = 0x1a,  }; @@ -596,54 +598,20 @@ static struct snd_soc_device neo1973_snd_devdata = {  	.codec_data = &neo1973_wm8753_setup,  }; -static struct i2c_client client_template; - -static const unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END }; - -/* Magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind) +static int lm4857_i2c_probe(struct i2c_client *client, +			    const struct i2c_device_id *id)  { -	int ret; -  	DBG("Entered %s\n", __func__); -	client_template.adapter = adap; -	client_template.addr = addr; - -	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); -	if (i2c == NULL) -		return -ENOMEM; - -	ret = i2c_attach_client(i2c); -	if (ret < 0) { -		printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr); -		goto exit_err; -	} -  	lm4857_write_regs(); -	return ret; - -exit_err: -	kfree(i2c); -	return ret; -} - -static int lm4857_i2c_detach(struct i2c_client *client) -{ -	DBG("Entered %s\n", __func__); - -	i2c_detach_client(client); -	kfree(client);  	return 0;  } -static int lm4857_i2c_attach(struct i2c_adapter *adap) +static int lm4857_i2c_remove(struct i2c_client *client)  {  	DBG("Entered %s\n", __func__); -	return i2c_probe(adap, &addr_data, lm4857_amp_probe); +	return 0;  }  static u8 lm4857_state; @@ -681,27 +649,67 @@ static void lm4857_shutdown(struct i2c_client *dev)  	lm4857_write_regs();  } -/* corgi i2c codec control layer */ +static const struct i2c_device_id lm4857_i2c_id[] = { +	{ "neo1973_lm4857", 0 } +	{ } +}; +  static struct i2c_driver lm4857_i2c_driver = {  	.driver = {  		.name = "LM4857 I2C Amp",  		.owner = THIS_MODULE,  	}, -	.id =             I2C_DRIVERID_LM4857,  	.suspend =        lm4857_suspend,  	.resume	=         lm4857_resume,  	.shutdown =       lm4857_shutdown, -	.attach_adapter = lm4857_i2c_attach, -	.detach_client =  lm4857_i2c_detach, -	.command =        NULL, -}; - -static struct i2c_client client_template = { -	.name =   "LM4857", -	.driver = &lm4857_i2c_driver, +	.probe =          lm4857_i2c_probe, +	.remove =         lm4857_i2c_remove, +	.id_table =       lm4857_i2c_id,  };  static struct platform_device *neo1973_snd_device; +static struct i2c_client *lm4857_client; + +static int __init neo1973_add_lm4857_device(struct platform_device *pdev, +					    int i2c_bus, +					    unsigned short i2c_address) +{ +	struct i2c_board_info info; +	struct i2c_adapter *adapter; +	struct i2c_client *client; +	int ret; + +	ret = i2c_add_driver(&lm4857_i2c_driver); +	if (ret != 0) { +		dev_err(&pdev->dev, "can't add lm4857 driver\n"); +		return ret; +	} + +	memset(&info, 0, sizeof(struct i2c_board_info)); +	info.addr = i2c_address; +	strlcpy(info.type, "neo1973_lm4857", I2C_NAME_SIZE); + +	adapter = i2c_get_adapter(i2c_bus); +	if (!adapter) { +		dev_err(&pdev->dev, "can't get i2c adapter %d\n", i2c_bus); +		goto err_driver; +	} + +	client = i2c_new_device(adapter, &info); +	i2c_put_adapter(adapter); +	if (!client) { +		dev_err(&pdev->dev, "can't add lm4857 device at 0x%x\n", +			(unsigned int)info.addr); +		goto err_driver; +	} + +	lm4857_client = client; +	return 0; + +err_driver: +	i2c_del_driver(&lm4857_i2c_driver); +	return -ENODEV; +}  static int __init neo1973_init(void)  { @@ -709,6 +717,12 @@ static int __init neo1973_init(void)  	DBG("Entered %s\n", __func__); +	if (!machine_is_neo1973_gta01()) { +		printk(KERN_INFO +			"Only GTA01 hardware supported by ASoC driver\n"); +		return -ENODEV; +	} +  	neo1973_snd_device = platform_device_alloc("soc-audio", -1);  	if (!neo1973_snd_device)  		return -ENOMEM; @@ -717,12 +731,15 @@ static int __init neo1973_init(void)  	neo1973_snd_devdata.dev = &neo1973_snd_device->dev;  	ret = platform_device_add(neo1973_snd_device); -	if (ret) +	if (ret) {  		platform_device_put(neo1973_snd_device); +		return ret; +	} -	ret = i2c_add_driver(&lm4857_i2c_driver); +	ret = neo1973_add_lm4857_device(neo1973_snd_device, +					neo1973_wm8753_setup, 0x7C);  	if (ret != 0) -		printk(KERN_ERR "can't add i2c driver"); +		platform_device_unregister(neo1973_snd_device);  	return ret;  } @@ -731,6 +748,7 @@ static void __exit neo1973_exit(void)  {  	DBG("Entered %s\n", __func__); +	i2c_unregister_device(lm4857_client);  	i2c_del_driver(&lm4857_i2c_driver);  	platform_device_unregister(neo1973_snd_device);  } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 83f1190..ad38113 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -340,6 +340,12 @@ static int soc_codec_close(struct snd_pcm_substream *substream)  	}  	codec->active--; +	/* Muting the DAC suppresses artifacts caused during digital +	 * shutdown, for example from stopping clocks. +	 */ +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) +		snd_soc_dai_digital_mute(codec_dai, 1); +  	if (cpu_dai->ops.shutdown)  		cpu_dai->ops.shutdown(substream); @@ -970,9 +976,29 @@ static ssize_t codec_reg_show(struct device *dev,  		step = codec->reg_cache_step;  	count += sprintf(buf, "%s registers\n", codec->name); -	for (i = 0; i < codec->reg_cache_size; i += step) -		count += sprintf(buf + count, "%2x: %4x\n", i, -			codec->read(codec, i)); +	for (i = 0; i < codec->reg_cache_size; i += step) { +		count += sprintf(buf + count, "%2x: ", i); +		if (count >= PAGE_SIZE - 1) +			break; + +		if (codec->display_register) +			count += codec->display_register(codec, buf + count, +							 PAGE_SIZE - count, i); +		else +			count += snprintf(buf + count, PAGE_SIZE - count, +					  "%4x", codec->read(codec, i)); + +		if (count >= PAGE_SIZE - 1) +			break; + +		count += snprintf(buf + count, PAGE_SIZE - count, "\n"); +		if (count >= PAGE_SIZE - 1) +			break; +	} + +	/* Truncate count; min() would cause a warning */ +	if (count >= PAGE_SIZE) +		count = PAGE_SIZE - 1;  	return count;  } @@ -1296,10 +1322,10 @@ int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	uinfo->count = e->shift_l == e->shift_r ? 1 : 2; -	uinfo->value.enumerated.items = e->mask; +	uinfo->value.enumerated.items = e->max; -	if (uinfo->value.enumerated.item > e->mask - 1) -		uinfo->value.enumerated.item = e->mask - 1; +	if (uinfo->value.enumerated.item > e->max - 1) +		uinfo->value.enumerated.item = e->max - 1;  	strcpy(uinfo->value.enumerated.name,  		e->texts[uinfo->value.enumerated.item]);  	return 0; @@ -1322,7 +1348,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,  	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  	unsigned short val, bitmask; -	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) +	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)  		;  	val = snd_soc_read(codec, e->reg);  	ucontrol->value.enumerated.item[0] @@ -1352,14 +1378,14 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,  	unsigned short val;  	unsigned short mask, bitmask; -	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) +	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)  		; -	if (ucontrol->value.enumerated.item[0] > e->mask - 1) +	if (ucontrol->value.enumerated.item[0] > e->max - 1)  		return -EINVAL;  	val = ucontrol->value.enumerated.item[0] << e->shift_l;  	mask = (bitmask - 1) << e->shift_l;  	if (e->shift_l != e->shift_r) { -		if (ucontrol->value.enumerated.item[1] > e->mask - 1) +		if (ucontrol->value.enumerated.item[1] > e->max - 1)  			return -EINVAL;  		val |= ucontrol->value.enumerated.item[1] << e->shift_r;  		mask |= (bitmask - 1) << e->shift_r; @@ -1386,10 +1412,10 @@ int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	uinfo->count = 1; -	uinfo->value.enumerated.items = e->mask; +	uinfo->value.enumerated.items = e->max; -	if (uinfo->value.enumerated.item > e->mask - 1) -		uinfo->value.enumerated.item = e->mask - 1; +	if (uinfo->value.enumerated.item > e->max - 1) +		uinfo->value.enumerated.item = e->max - 1;  	strcpy(uinfo->value.enumerated.name,  		e->texts[uinfo->value.enumerated.item]);  	return 0; @@ -1434,9 +1460,11 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);  int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_info *uinfo)  { -	int max = (kcontrol->private_value >> 16) & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0x0f; -	int rshift = (kcontrol->private_value >> 12) & 0x0f; +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	int max = mc->max; +	unsigned int shift = mc->min; +	unsigned int rshift = mc->rshift;  	if (max == 1)  		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; @@ -1462,13 +1490,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);  int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  { +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value;  	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0x0f; -	int rshift = (kcontrol->private_value >> 12) & 0x0f; -	int max = (kcontrol->private_value >> 16) & 0xff; -	int mask = (1 << fls(max)) - 1; -	int invert = (kcontrol->private_value >> 24) & 0x01; +	unsigned int reg = mc->reg; +	unsigned int shift = mc->shift; +	unsigned int rshift = mc->rshift; +	int max = mc->max; +	unsigned int mask = (1 << fls(max)) - 1; +	unsigned int invert = mc->invert;  	ucontrol->value.integer.value[0] =  		(snd_soc_read(codec, reg) >> shift) & mask; @@ -1499,13 +1529,15 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw);  int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  { +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value;  	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0x0f; -	int rshift = (kcontrol->private_value >> 12) & 0x0f; -	int max = (kcontrol->private_value >> 16) & 0xff; -	int mask = (1 << fls(max)) - 1; -	int invert = (kcontrol->private_value >> 24) & 0x01; +	unsigned int reg = mc->reg; +	unsigned int shift = mc->shift; +	unsigned int rshift = mc->rshift; +	int max = mc->max; +	unsigned int mask = (1 << fls(max)) - 1; +	unsigned int invert = mc->invert;  	unsigned short val, val2, val_mask;  	val = (ucontrol->value.integer.value[0] & mask); @@ -1537,7 +1569,9 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw);  int snd_soc_info_volsw_2r(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_info *uinfo)  { -	int max = (kcontrol->private_value >> 12) & 0xff; +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	int max = mc->max;  	if (max == 1)  		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; @@ -1563,13 +1597,15 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_2r);  int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  { +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value;  	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int reg2 = (kcontrol->private_value >> 24) & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0x0f; -	int max = (kcontrol->private_value >> 12) & 0xff; -	int mask = (1<<fls(max))-1; -	int invert = (kcontrol->private_value >> 20) & 0x01; +	unsigned int reg = mc->reg; +	unsigned int reg2 = mc->rreg; +	unsigned int shift = mc->shift; +	int max = mc->max; +	unsigned int mask = (1<<fls(max))-1; +	unsigned int invert = mc->invert;  	ucontrol->value.integer.value[0] =  		(snd_soc_read(codec, reg) >> shift) & mask; @@ -1598,13 +1634,15 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_2r);  int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  { +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value;  	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int reg2 = (kcontrol->private_value >> 24) & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0x0f; -	int max = (kcontrol->private_value >> 12) & 0xff; -	int mask = (1 << fls(max)) - 1; -	int invert = (kcontrol->private_value >> 20) & 0x01; +	unsigned int reg = mc->reg; +	unsigned int reg2 = mc->rreg; +	unsigned int shift = mc->shift; +	int max = mc->max; +	unsigned int mask = (1 << fls(max)) - 1; +	unsigned int invert = mc->invert;  	int err;  	unsigned short val, val2, val_mask; @@ -1641,8 +1679,10 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);  int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_info *uinfo)  { -	int max = (signed char)((kcontrol->private_value >> 16) & 0xff); -	int min = (signed char)((kcontrol->private_value >> 24) & 0xff); +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	int max = mc->max; +	int min = mc->min;  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;  	uinfo->count = 2; @@ -1664,9 +1704,11 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);  int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  { +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value;  	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int min = (signed char)((kcontrol->private_value >> 24) & 0xff); +	unsigned int reg = mc->reg; +	int min = mc->min;  	int val = snd_soc_read(codec, reg);  	ucontrol->value.integer.value[0] = @@ -1689,9 +1731,11 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);  int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  { +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value;  	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int min = (signed char)((kcontrol->private_value >> 24) & 0xff); +	unsigned int reg = mc->reg; +	int min = mc->min;  	unsigned short val;  	val = (ucontrol->value.integer.value[0]+min) & 0xff; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f9d100b..9ca9c08 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -38,6 +38,7 @@  #include <linux/bitops.h>  #include <linux/platform_device.h>  #include <linux/jiffies.h> +#include <linux/debugfs.h>  #include <sound/core.h>  #include <sound/pcm.h>  #include <sound/pcm_params.h> @@ -67,7 +68,9 @@ static int dapm_status = 1;  module_param(dapm_status, int, 0);  MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries"); -static unsigned int pop_time; +static struct dentry *asoc_debugfs; + +static u32 pop_time;  static void pop_wait(void)  { @@ -104,10 +107,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,  	case snd_soc_dapm_switch:  	case snd_soc_dapm_mixer: {  		int val; -		int reg = w->kcontrols[i].private_value & 0xff; -		int shift = (w->kcontrols[i].private_value >> 8) & 0x0f; -		int mask = (w->kcontrols[i].private_value >> 16) & 0xff; -		int invert = (w->kcontrols[i].private_value >> 24) & 0x01; +		struct soc_mixer_control *mc = (struct soc_mixer_control *) +			w->kcontrols[i].private_value; +		unsigned int reg = mc->reg; +		unsigned int shift = mc->shift; +		int max = mc->max; +		unsigned int mask = (1 << fls(max)) - 1; +		unsigned int invert = mc->invert;  		val = snd_soc_read(w->codec, reg);  		val = (val >> shift) & mask; @@ -122,13 +128,13 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,  		struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;  		int val, item, bitmask; -		for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) +		for (bitmask = 1; bitmask < e->max; bitmask <<= 1)  		;  		val = snd_soc_read(w->codec, e->reg);  		item = (val >> e->shift_l) & (bitmask - 1);  		p->connect = 0; -		for (i = 0; i < e->mask; i++) { +		for (i = 0; i < e->max; i++) {  			if (!(strcmp(p->name, e->texts[i])) && item == i)  				p->connect = 1;  		} @@ -165,7 +171,7 @@ static int dapm_connect_mux(struct snd_soc_codec *codec,  	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  	int i; -	for (i = 0; i < e->mask; i++) { +	for (i = 0; i < e->max; i++) {  		if (!(strcmp(control_name, e->texts[i]))) {  			list_add(&path->list, &codec->dapm_paths);  			list_add(&path->list_sink, &dest->sources); @@ -247,16 +253,19 @@ static int dapm_set_pga(struct snd_soc_dapm_widget *widget, int power)  		return 0;  	if (widget->num_kcontrols && k) { -		int reg = k->private_value & 0xff; -		int shift = (k->private_value >> 8) & 0x0f; -		int mask = (k->private_value >> 16) & 0xff; -		int invert = (k->private_value >> 24) & 0x01; +		struct soc_mixer_control *mc = +			(struct soc_mixer_control *)k->private_value; +		unsigned int reg = mc->reg; +		unsigned int shift = mc->shift; +		int max = mc->max; +		unsigned int mask = (1 << fls(max)) - 1; +		unsigned int invert = mc->invert;  		if (power) {  			int i;  			/* power up has happended, increase volume to last level */  			if (invert) { -				for (i = mask; i > widget->saved_value; i--) +				for (i = max; i > widget->saved_value; i--)  					snd_soc_update_bits(widget->codec, reg, mask, i);  			} else {  				for (i = 0; i < widget->saved_value; i++) @@ -684,7 +693,7 @@ static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)  /* test and update the power status of a mux widget */  static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,  				 struct snd_kcontrol *kcontrol, int mask, -				 int val, struct soc_enum* e) +				 int mux, int val, struct soc_enum *e)  {  	struct snd_soc_dapm_path *path;  	int found = 0; @@ -700,12 +709,12 @@ static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,  		if (path->kcontrol != kcontrol)  			continue; -		if (!path->name || ! e->texts[val]) +		if (!path->name || !e->texts[mux])  			continue;  		found = 1;  		/* we now need to match the string in the enum to the path */ -		if (!(strcmp(path->name, e->texts[val]))) +		if (!(strcmp(path->name, e->texts[mux])))  			path->connect = 1; /* new connection */  		else  			path->connect = 0; /* old connection must be powered down */ @@ -811,51 +820,35 @@ static ssize_t dapm_widget_show(struct device *dev,  static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); -/* pop/click delay times */ -static ssize_t dapm_pop_time_show(struct device *dev, -	struct device_attribute *attr, char *buf) -{ -	return sprintf(buf, "%d\n", pop_time); -} - -static ssize_t dapm_pop_time_store(struct device *dev, -				   struct device_attribute *attr, -				   const char *buf, size_t count) - -{ -	unsigned long val; - -	if (strict_strtoul(buf, 10, &val) >= 0) -		pop_time = val; -	else -		printk(KERN_ERR "Unable to parse pop_time setting\n"); - -	return count; -} - -static DEVICE_ATTR(dapm_pop_time, 0744, dapm_pop_time_show, -		   dapm_pop_time_store); -  int snd_soc_dapm_sys_add(struct device *dev)  {  	int ret = 0; -	if (dapm_status) { -		ret = device_create_file(dev, &dev_attr_dapm_widget); +	if (!dapm_status) +		return 0; -		if (ret == 0) -			ret = device_create_file(dev, &dev_attr_dapm_pop_time); -	} +	ret = device_create_file(dev, &dev_attr_dapm_widget); +	if (ret != 0) +		return ret; -	return ret; +	asoc_debugfs = debugfs_create_dir("asoc", NULL); +	if (!IS_ERR(asoc_debugfs)) +		debugfs_create_u32("dapm_pop_time", 0744, asoc_debugfs, +				   &pop_time); +	else +		asoc_debugfs = NULL; + +	return 0;  }  static void snd_soc_dapm_sys_remove(struct device *dev)  {  	if (dapm_status) { -		device_remove_file(dev, &dev_attr_dapm_pop_time);  		device_remove_file(dev, &dev_attr_dapm_widget);  	} + +	if (asoc_debugfs) +		debugfs_remove_recursive(asoc_debugfs);  }  /* free all dapm widgets and resources */ @@ -1133,12 +1126,14 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  {  	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0x0f; -	int rshift = (kcontrol->private_value >> 12) & 0x0f; -	int max = (kcontrol->private_value >> 16) & 0xff; -	int invert = (kcontrol->private_value >> 24) & 0x01; -	int mask = (1 << fls(max)) - 1; +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	unsigned int reg = mc->reg; +	unsigned int shift = mc->shift; +	unsigned int rshift = mc->rshift; +	int max = mc->max; +	unsigned int invert = mc->invert; +	unsigned int mask = (1 << fls(max)) - 1;  	/* return the saved value if we are powered down */  	if (widget->id == snd_soc_dapm_pga && !widget->power) { @@ -1176,12 +1171,14 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,  	struct snd_ctl_elem_value *ucontrol)  {  	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol); -	int reg = kcontrol->private_value & 0xff; -	int shift = (kcontrol->private_value >> 8) & 0x0f; -	int rshift = (kcontrol->private_value >> 12) & 0x0f; -	int max = (kcontrol->private_value >> 16) & 0xff; -	int mask = (1 << fls(max)) - 1; -	int invert = (kcontrol->private_value >> 24) & 0x01; +	struct soc_mixer_control *mc = +		(struct soc_mixer_control *)kcontrol->private_value; +	unsigned int reg = mc->reg; +	unsigned int shift = mc->shift; +	unsigned int rshift = mc->rshift; +	int max = mc->max; +	unsigned int mask = (1 << fls(max)) - 1; +	unsigned int invert = mc->invert;  	unsigned short val, val2, val_mask;  	int ret; @@ -1248,7 +1245,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,  	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  	unsigned short val, bitmask; -	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) +	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)  		;  	val = snd_soc_read(widget->codec, e->reg);  	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); @@ -1278,15 +1275,15 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,  	unsigned short mask, bitmask;  	int ret = 0; -	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1) +	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)  		; -	if (ucontrol->value.enumerated.item[0] > e->mask - 1) +	if (ucontrol->value.enumerated.item[0] > e->max - 1)  		return -EINVAL;  	mux = ucontrol->value.enumerated.item[0];  	val = mux << e->shift_l;  	mask = (bitmask - 1) << e->shift_l;  	if (e->shift_l != e->shift_r) { -		if (ucontrol->value.enumerated.item[1] > e->mask - 1) +		if (ucontrol->value.enumerated.item[1] > e->max - 1)  			return -EINVAL;  		val |= ucontrol->value.enumerated.item[1] << e->shift_r;  		mask |= (bitmask - 1) << e->shift_r; @@ -1294,7 +1291,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,  	mutex_lock(&widget->codec->mutex);  	widget->value = val; -	dapm_mux_update_power(widget, kcontrol, mask, mux, e); +	dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);  	if (widget->event) {  		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {  			ret = widget->event(widget, diff --git a/sound/sound_core.c b/sound/sound_core.c index 1b04259..4ae07e2 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -1,5 +1,61 @@  /* - *	Sound core handling. Breaks out sound functions to submodules + *	Sound core.  This file is composed of two parts.  sound_class + *	which is common to both OSS and ALSA and OSS sound core which + *	is used OSS or emulation of it. + */ + +/* + * First, the common part. + */ +#include <linux/module.h> +#include <linux/device.h> +#include <linux/err.h> + +#ifdef CONFIG_SOUND_OSS_CORE +static int __init init_oss_soundcore(void); +static void cleanup_oss_soundcore(void); +#else +static inline int init_oss_soundcore(void)	{ return 0; } +static inline void cleanup_oss_soundcore(void)	{ } +#endif + +struct class *sound_class; +EXPORT_SYMBOL(sound_class); + +MODULE_DESCRIPTION("Core sound module"); +MODULE_AUTHOR("Alan Cox"); +MODULE_LICENSE("GPL"); + +static int __init init_soundcore(void) +{ +	int rc; + +	rc = init_oss_soundcore(); +	if (rc) +		return rc; + +	sound_class = class_create(THIS_MODULE, "sound"); +	if (IS_ERR(sound_class)) { +		cleanup_oss_soundcore(); +		return PTR_ERR(sound_class); +	} + +	return 0; +} + +static void __exit cleanup_soundcore(void) +{ +	cleanup_oss_soundcore(); +	class_destroy(sound_class); +} + +module_init(init_soundcore); +module_exit(cleanup_soundcore); + + +#ifdef CONFIG_SOUND_OSS_CORE +/* + *	OSS sound core handling. Breaks out sound functions to submodules   *	   *	Author:		Alan Cox <alan.cox@linux.org>   * @@ -34,21 +90,17 @@   *	locking at some point in 2.3.x.   */ -#include <linux/module.h>  #include <linux/init.h>  #include <linux/slab.h>  #include <linux/smp_lock.h>  #include <linux/types.h>  #include <linux/kernel.h> -#include <linux/fs.h>  #include <linux/sound.h>  #include <linux/major.h>  #include <linux/kmod.h> -#include <linux/device.h>  #define SOUND_STEP 16 -  struct sound_unit  {  	int unit_minor; @@ -64,9 +116,6 @@ extern int msnd_classic_init(void);  extern int msnd_pinnacle_init(void);  #endif -struct class *sound_class; -EXPORT_SYMBOL(sound_class); -  /*   *	Low level list operator. Scan the ordered list, find a hole and   *	join into it. Called with the lock asserted @@ -523,31 +572,23 @@ int soundcore_open(struct inode *inode, struct file *file)  	return -ENODEV;  } -MODULE_DESCRIPTION("Core sound module"); -MODULE_AUTHOR("Alan Cox"); -MODULE_LICENSE("GPL");  MODULE_ALIAS_CHARDEV_MAJOR(SOUND_MAJOR); -static void __exit cleanup_soundcore(void) +static void cleanup_oss_soundcore(void)  {  	/* We have nothing to really do here - we know the lists must be  	   empty */  	unregister_chrdev(SOUND_MAJOR, "sound"); -	class_destroy(sound_class);  } -static int __init init_soundcore(void) +static int __init init_oss_soundcore(void)  {  	if (register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops)==-1) {  		printk(KERN_ERR "soundcore: sound device already in use.\n");  		return -EBUSY;  	} -	sound_class = class_create(THIS_MODULE, "sound"); -	if (IS_ERR(sound_class)) -		return PTR_ERR(sound_class);  	return 0;  } -module_init(init_soundcore); -module_exit(cleanup_soundcore); +#endif /* CONFIG_SOUND_OSS_CORE */ diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index 5af5503..f87933e 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -766,7 +766,6 @@ static int __devinit snd_amd7930_pcm(struct snd_amd7930 *amd)  			       /* playback count */ 1,  			       /* capture count */  1, &pcm)) < 0)  		return err; -	snd_assert(pcm != NULL, return -EINVAL);  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_amd7930_playback_ops);  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_amd7930_capture_ops); @@ -789,13 +788,6 @@ static int __devinit snd_amd7930_pcm(struct snd_amd7930 *amd)  static int snd_amd7930_info_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo)  { -	int type = kctl->private_value; - -	snd_assert(type == VOLUME_MONITOR || -		   type == VOLUME_CAPTURE || -		   type == VOLUME_PLAYBACK, return -EINVAL); -	(void) type; -  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;  	uinfo->count = 1;  	uinfo->value.integer.min = 0; @@ -810,10 +802,6 @@ static int snd_amd7930_get_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem  	int type = kctl->private_value;  	int *swval; -	snd_assert(type == VOLUME_MONITOR || -		   type == VOLUME_CAPTURE || -		   type == VOLUME_PLAYBACK, return -EINVAL); -  	switch (type) {  	case VOLUME_MONITOR:  		swval = &amd->mgain; @@ -839,10 +827,6 @@ static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem  	int type = kctl->private_value;  	int *swval, change; -	snd_assert(type == VOLUME_MONITOR || -		   type == VOLUME_CAPTURE || -		   type == VOLUME_PLAYBACK, return -EINVAL); -  	switch (type) {  	case VOLUME_MONITOR:  		swval = &amd->mgain; @@ -905,7 +889,8 @@ static int __devinit snd_amd7930_mixer(struct snd_amd7930 *amd)  	struct snd_card *card;  	int idx, err; -	snd_assert(amd != NULL && amd->card != NULL, return -EINVAL); +	if (snd_BUG_ON(!amd || !amd->card)) +		return -EINVAL;  	card = amd->card;  	strcpy(card->mixername, card->shortname); diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 727438d..d44bf98 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -1543,7 +1543,8 @@ static int __init snd_cs4231_mixer(struct snd_card *card)  	struct snd_cs4231 *chip = card->private_data;  	int err, idx; -	snd_assert(chip != NULL && chip->pcm != NULL, return -EINVAL); +	if (snd_BUG_ON(!chip || !chip->pcm)) +		return -EINVAL;  	strcpy(card->mixername, chip->pcm->name); diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 2edb0ad..c257ad8 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -2225,7 +2225,6 @@ static int __devinit snd_dbri_pcm(struct snd_card *card)  			       /* playback count */ 1,  			       /* capture count */  1, &pcm)) < 0)  		return err; -	snd_assert(pcm != NULL, return -EINVAL);  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dbri_ops);  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_dbri_ops); @@ -2265,9 +2264,10 @@ static int snd_cs4215_get_volume(struct snd_kcontrol *kcontrol,  {  	struct snd_dbri *dbri = snd_kcontrol_chip(kcontrol);  	struct dbri_streaminfo *info; -	snd_assert(dbri != NULL, return -EINVAL); + +	if (snd_BUG_ON(!dbri)) +		return -EINVAL;  	info = &dbri->stream_info[kcontrol->private_value]; -	snd_assert(info != NULL, return -EINVAL);  	ucontrol->value.integer.value[0] = info->left_gain;  	ucontrol->value.integer.value[1] = info->right_gain; @@ -2333,7 +2333,9 @@ static int snd_cs4215_get_single(struct snd_kcontrol *kcontrol,  	int shift = (kcontrol->private_value >> 8) & 0xff;  	int mask = (kcontrol->private_value >> 16) & 0xff;  	int invert = (kcontrol->private_value >> 24) & 1; -	snd_assert(dbri != NULL, return -EINVAL); + +	if (snd_BUG_ON(!dbri)) +		return -EINVAL;  	if (elem < 4)  		ucontrol->value.integer.value[0] = @@ -2358,7 +2360,9 @@ static int snd_cs4215_put_single(struct snd_kcontrol *kcontrol,  	int invert = (kcontrol->private_value >> 24) & 1;  	int changed = 0;  	unsigned short val; -	snd_assert(dbri != NULL, return -EINVAL); + +	if (snd_BUG_ON(!dbri)) +		return -EINVAL;  	val = (ucontrol->value.integer.value[0] & mask);  	if (invert == 1) @@ -2434,7 +2438,8 @@ static int __devinit snd_dbri_mixer(struct snd_card *card)  	int idx, err;  	struct snd_dbri *dbri; -	snd_assert(card != NULL && card->private_data != NULL, return -EINVAL); +	if (snd_BUG_ON(!card || !card->private_data)) +		return -EINVAL;  	dbri = card->private_data;  	strcpy(card->mixername, card->shortname); diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index c89d2ea..f16a3fc 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -93,10 +93,10 @@ int snd_emux_register(struct snd_emux *emu, struct snd_card *card, int index, ch  	int err;  	struct snd_sf_callback sf_cb; -	snd_assert(emu->hw != NULL, return -EINVAL); -	snd_assert(emu->max_voices > 0, return -EINVAL); -	snd_assert(card != NULL, return -EINVAL); -	snd_assert(name != NULL, return -EINVAL); +	if (snd_BUG_ON(!emu->hw || emu->max_voices <= 0)) +		return -EINVAL; +	if (snd_BUG_ON(!card || !name)) +		return -EINVAL;  	emu->card = card;  	emu->name = kstrdup(name, GFP_KERNEL); diff --git a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c index c6917ba..00fc005 100644 --- a/sound/synth/emux/emux_nrpn.c +++ b/sound/synth/emux/emux_nrpn.c @@ -289,8 +289,8 @@ snd_emux_nrpn(void *p, struct snd_midi_channel *chan,  	struct snd_emux_port *port;  	port = p; -	snd_assert(port != NULL, return); -	snd_assert(chan != NULL, return); +	if (snd_BUG_ON(!port || !chan)) +		return;  	if (chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB] == 127 &&  	    chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB] <= 26) { @@ -379,8 +379,8 @@ snd_emux_sysex(void *p, unsigned char *buf, int len, int parsed,  	struct snd_emux *emu;  	port = p; -	snd_assert(port != NULL, return); -	snd_assert(chset != NULL, return); +	if (snd_BUG_ON(!port || !chset)) +		return;  	emu = port->emu;  	switch (parsed) { diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index f60a98e..5c47b6c 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c @@ -114,7 +114,8 @@ snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)  	char tmpname[64];  	emu = closure; -	snd_assert(arg != NULL && emu != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg || !emu)) +		return -ENXIO;  	mutex_lock(&emu->register_mutex); @@ -183,12 +184,15 @@ snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg)  	struct snd_emux *emu;  	struct snd_emux_port *p; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	p = arg->private_data; -	snd_assert(p != NULL, return -ENXIO); +	if (snd_BUG_ON(!p)) +		return -ENXIO;  	emu = p->emu; -	snd_assert(emu != NULL, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	mutex_lock(&emu->register_mutex);  	snd_emux_sounds_off_all(p); @@ -212,12 +216,15 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,  	struct snd_emux_port *p;  	int rc; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	p = arg->private_data; -	snd_assert(p != NULL, return -ENXIO); +	if (snd_BUG_ON(!p)) +		return -ENXIO;  	emu = p->emu; -	snd_assert(emu != NULL, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	if (format == GUS_PATCH)  		rc = snd_soundfont_load_guspatch(emu->sflist, buf, count, @@ -252,12 +259,15 @@ snd_emux_ioctl_seq_oss(struct snd_seq_oss_arg *arg, unsigned int cmd, unsigned l  	struct snd_emux_port *p;  	struct snd_emux *emu; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	p = arg->private_data; -	snd_assert(p != NULL, return -ENXIO); +	if (snd_BUG_ON(!p)) +		return -ENXIO;  	emu = p->emu; -	snd_assert(emu != NULL, return -ENXIO); +	if (snd_BUG_ON(!emu)) +		return -ENXIO;  	switch (cmd) {  	case SNDCTL_SEQ_RESETSAMPLES: @@ -282,9 +292,11 @@ snd_emux_reset_seq_oss(struct snd_seq_oss_arg *arg)  {  	struct snd_emux_port *p; -	snd_assert(arg != NULL, return -ENXIO); +	if (snd_BUG_ON(!arg)) +		return -ENXIO;  	p = arg->private_data; -	snd_assert(p != NULL, return -ENXIO); +	if (snd_BUG_ON(!p)) +		return -ENXIO;  	snd_emux_reset_port(p);  	return 0;  } @@ -302,9 +314,11 @@ snd_emux_event_oss_input(struct snd_seq_event *ev, int direct, void *private_dat  	unsigned char cmd, *data;  	p = private_data; -	snd_assert(p != NULL, return -EINVAL); +	if (snd_BUG_ON(!p)) +		return -EINVAL;  	emu = p->emu; -	snd_assert(emu != NULL, return -EINVAL); +	if (snd_BUG_ON(!emu)) +		return -EINVAL;  	if (ev->type != SNDRV_SEQ_EVENT_OSS)  		return snd_emux_event_input(ev, direct, private_data, atomic, hop); diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c index d176cc0..335aa2c 100644 --- a/sound/synth/emux/emux_seq.c +++ b/sound/synth/emux/emux_seq.c @@ -257,7 +257,8 @@ snd_emux_event_input(struct snd_seq_event *ev, int direct, void *private_data,  	struct snd_emux_port *port;  	port = private_data; -	snd_assert(port != NULL && ev != NULL, return -EINVAL); +	if (snd_BUG_ON(!port || !ev)) +		return -EINVAL;  	snd_midi_process_event(&emux_ops, ev, &port->chset); @@ -308,9 +309,11 @@ snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info)  	struct snd_emux *emu;  	p = private_data; -	snd_assert(p != NULL, return -EINVAL); +	if (snd_BUG_ON(!p)) +		return -EINVAL;  	emu = p->emu; -	snd_assert(emu != NULL, return -EINVAL); +	if (snd_BUG_ON(!emu)) +		return -EINVAL;  	mutex_lock(&emu->register_mutex);  	snd_emux_init_port(p); @@ -329,9 +332,11 @@ snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info)  	struct snd_emux *emu;  	p = private_data; -	snd_assert(p != NULL, return -EINVAL); +	if (snd_BUG_ON(!p)) +		return -EINVAL;  	emu = p->emu; -	snd_assert(emu != NULL, return -EINVAL); +	if (snd_BUG_ON(!emu)) +		return -EINVAL;  	mutex_lock(&emu->register_mutex);  	snd_emux_sounds_off_all(p); diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c index b343818..2cc6f6f 100644 --- a/sound/synth/emux/emux_synth.c +++ b/sound/synth/emux/emux_synth.c @@ -66,12 +66,12 @@ snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)  	struct snd_emux_port *port;  	port = p; -	snd_assert(port != NULL && chan != NULL, return); +	if (snd_BUG_ON(!port || !chan)) +		return;  	emu = port->emu; -	snd_assert(emu != NULL, return); -	snd_assert(emu->ops.get_voice != NULL, return); -	snd_assert(emu->ops.trigger != NULL, return); +	if (snd_BUG_ON(!emu || !emu->ops.get_voice || !emu->ops.trigger)) +		return;  	key = note; /* remember the original note */  	nvoices = get_zone(emu, port, ¬e, vel, chan, table); @@ -164,11 +164,12 @@ snd_emux_note_off(void *p, int note, int vel, struct snd_midi_channel *chan)  	struct snd_emux_port *port;  	port = p; -	snd_assert(port != NULL && chan != NULL, return); +	if (snd_BUG_ON(!port || !chan)) +		return;  	emu = port->emu; -	snd_assert(emu != NULL, return); -	snd_assert(emu->ops.release != NULL, return); +	if (snd_BUG_ON(!emu || !emu->ops.release)) +		return;  	spin_lock_irqsave(&emu->voice_lock, flags);  	for (ch = 0; ch < emu->max_voices; ch++) { @@ -242,11 +243,12 @@ snd_emux_key_press(void *p, int note, int vel, struct snd_midi_channel *chan)  	struct snd_emux_port *port;  	port = p; -	snd_assert(port != NULL && chan != NULL, return); +	if (snd_BUG_ON(!port || !chan)) +		return;  	emu = port->emu; -	snd_assert(emu != NULL, return); -	snd_assert(emu->ops.update != NULL, return); +	if (snd_BUG_ON(!emu || !emu->ops.update)) +		return;  	spin_lock_irqsave(&emu->voice_lock, flags);  	for (ch = 0; ch < emu->max_voices; ch++) { @@ -276,8 +278,8 @@ snd_emux_update_channel(struct snd_emux_port *port, struct snd_midi_channel *cha  		return;  	emu = port->emu; -	snd_assert(emu != NULL, return); -	snd_assert(emu->ops.update != NULL, return); +	if (snd_BUG_ON(!emu || !emu->ops.update)) +		return;  	spin_lock_irqsave(&emu->voice_lock, flags);  	for (i = 0; i < emu->max_voices; i++) { @@ -303,8 +305,8 @@ snd_emux_update_port(struct snd_emux_port *port, int update)  		return;  	emu = port->emu; -	snd_assert(emu != NULL, return); -	snd_assert(emu->ops.update != NULL, return); +	if (snd_BUG_ON(!emu || !emu->ops.update)) +		return;  	spin_lock_irqsave(&emu->voice_lock, flags);  	for (i = 0; i < emu->max_voices; i++) { @@ -326,7 +328,8 @@ snd_emux_control(void *p, int type, struct snd_midi_channel *chan)  	struct snd_emux_port *port;  	port = p; -	snd_assert(port != NULL && chan != NULL, return); +	if (snd_BUG_ON(!port || !chan)) +		return;  	switch (type) {  	case MIDI_CTL_MSB_MAIN_VOLUME: @@ -400,11 +403,12 @@ snd_emux_terminate_note(void *p, int note, struct snd_midi_channel *chan)  	struct snd_emux_port *port;  	port = p; -	snd_assert(port != NULL && chan != NULL, return); +	if (snd_BUG_ON(!port || !chan)) +		return;  	emu = port->emu; -	snd_assert(emu != NULL, return); -	snd_assert(emu->ops.terminate != NULL, return); +	if (snd_BUG_ON(!emu || !emu->ops.terminate)) +		return;  	terminate_note1(emu, note, chan, 1);  } @@ -451,10 +455,11 @@ snd_emux_sounds_off_all(struct snd_emux_port *port)  	struct snd_emux_voice *vp;  	unsigned long flags; -	snd_assert(port != NULL, return); +	if (snd_BUG_ON(!port)) +		return;  	emu = port->emu; -	snd_assert(emu != NULL, return); -	snd_assert(emu->ops.terminate != NULL, return); +	if (snd_BUG_ON(!emu || !emu->ops.terminate)) +		return;  	spin_lock_irqsave(&emu->voice_lock, flags);  	for (i = 0; i < emu->max_voices; i++) { diff --git a/sound/synth/util_mem.c b/sound/synth/util_mem.c index deabe5f8..c85522e 100644 --- a/sound/synth/util_mem.c +++ b/sound/synth/util_mem.c @@ -55,7 +55,8 @@ void snd_util_memhdr_free(struct snd_util_memhdr *hdr)  {  	struct list_head *p; -	snd_assert(hdr != NULL, return); +	if (!hdr) +		return;  	/* release all blocks */  	while ((p = hdr->block.next) != &hdr->block) {  		list_del(p); @@ -74,8 +75,8 @@ __snd_util_mem_alloc(struct snd_util_memhdr *hdr, int size)  	unsigned int units, prev_offset;  	struct list_head *p; -	snd_assert(hdr != NULL, return NULL); -	snd_assert(size > 0, return NULL); +	if (snd_BUG_ON(!hdr || size <= 0)) +		return NULL;  	/* word alignment */  	units = size; @@ -161,7 +162,8 @@ __snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)   */  int snd_util_mem_free(struct snd_util_memhdr *hdr, struct snd_util_memblk *blk)  { -	snd_assert(hdr && blk, return -EINVAL); +	if (snd_BUG_ON(!hdr || !blk)) +		return -EINVAL;  	mutex_lock(&hdr->block_mutex);  	__snd_util_mem_free(hdr, blk); diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index ffcdc8f..4f0eac9 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -67,5 +67,17 @@ config SND_USB_CAIAQ_INPUT  	   * Native Instruments Kore Controller 2  	   * Native Instruments Audio Kontrol 1 +config SND_USB_US122L +	tristate "Tascam US-122L USB driver" +	depends on X86 && EXPERIMENTAL +	select SND_HWDEP +	select SND_RAWMIDI +	help +	  Say Y here to include support for Tascam US-122L USB Audio/MIDI +	  interfaces. + +	  To compile this driver as a module, choose M here: the module +	  will be called snd-usb-us122l. +  endif	# SND_USB diff --git a/sound/usb/Makefile b/sound/usb/Makefile index aa252ef..abb288b 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -8,5 +8,6 @@ snd-usb-lib-objs := usbmidi.o  # Toplevel Module Dependency  obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o  obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o +obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o  obj-$(CONFIG_SND) += usx2y/ caiaq/ diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index b8cfb7c..bbd70d5 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -71,6 +71,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };  static int nrpacks = 8;		/* max. number of packets per urb */  static int async_unlink = 1;  static int device_setup[SNDRV_CARDS]; /* device parameter for this card*/ +static int ignore_ctl_error;  module_param_array(index, int, NULL, 0444);  MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); @@ -88,7 +89,9 @@ module_param(async_unlink, bool, 0444);  MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");  module_param_array(device_setup, int, NULL, 0444);  MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); - +module_param(ignore_ctl_error, bool, 0444); +MODULE_PARM_DESC(ignore_ctl_error, +		 "Ignore errors from USB controller for mixer interfaces.");  /*   * debug the h/w constraints @@ -481,7 +484,7 @@ static int retire_playback_sync_urb_hs(struct snd_usb_substream *subs,  }  /* - * process after E-Mu 0202/0404 high speed playback sync complete + * process after E-Mu 0202/0404/Tracker Pre high speed playback sync complete   *   * These devices return the number of samples per packet instead of the number   * of samples per microframe. @@ -841,7 +844,8 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru  		return -EBADFD;  	for (i = 0; i < subs->nurbs; i++) { -		snd_assert(subs->dataurb[i].urb, return -EINVAL); +		if (snd_BUG_ON(!subs->dataurb[i].urb)) +			return -EINVAL;  		if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {  			snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);  			goto __error; @@ -849,7 +853,8 @@ static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *ru  	}  	if (subs->syncpipe) {  		for (i = 0; i < SYNC_URBS; i++) { -			snd_assert(subs->syncurb[i].urb, return -EINVAL); +			if (snd_BUG_ON(!subs->syncurb[i].urb)) +				return -EINVAL;  			if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {  				snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);  				goto __error; @@ -1321,10 +1326,12 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)  	int err;  	iface = usb_ifnum_to_if(dev, fmt->iface); -	snd_assert(iface, return -EINVAL); +	if (WARN_ON(!iface)) +		return -EINVAL;  	alts = &iface->altsetting[fmt->altset_idx];  	altsd = get_iface_desc(alts); -	snd_assert(altsd->bAlternateSetting == fmt->altsetting, return -EINVAL); +	if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) +		return -EINVAL;  	if (fmt == subs->cur_audiofmt)  		return 0; @@ -2257,6 +2264,7 @@ static void init_substream(struct snd_usb_stream *as, int stream, struct audiofo  		switch (as->chip->usb_id) {  		case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */  		case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */ +		case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */  			subs->ops.retire_sync = retire_playback_sync_urb_hs_emu;  			break;  		} @@ -2989,12 +2997,12 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,  }  /* - * Create a stream for an Edirol UA-700/UA-25 interface.  The only way - * to detect the sample rate is by looking at wMaxPacketSize. + * Create a stream for an Edirol UA-700/UA-25/UA-4FX interface.   + * The only way to detect the sample rate is by looking at wMaxPacketSize.   */ -static int create_ua700_ua25_quirk(struct snd_usb_audio *chip, -				   struct usb_interface *iface, -				   const struct snd_usb_audio_quirk *quirk) +static int create_uaxx_quirk(struct snd_usb_audio *chip, +			      struct usb_interface *iface, +			      const struct snd_usb_audio_quirk *quirk)  {  	static const struct audioformat ua_format = {  		.format = SNDRV_PCM_FORMAT_S24_3LE, @@ -3009,8 +3017,8 @@ static int create_ua700_ua25_quirk(struct snd_usb_audio *chip,  	struct audioformat *fp;  	int stream, err; -	/* both PCM and MIDI interfaces have 2 altsettings */ -	if (iface->num_altsetting != 2) +	/* both PCM and MIDI interfaces have 2 or more altsettings */ +	if (iface->num_altsetting < 2)  		return -ENXIO;  	alts = &iface->altsetting[1];  	altsd = get_iface_desc(alts); @@ -3024,20 +3032,20 @@ static int create_ua700_ua25_quirk(struct snd_usb_audio *chip,  			.type = QUIRK_MIDI_FIXED_ENDPOINT,  			.data = &ua700_ep  		}; -		static const struct snd_usb_midi_endpoint_info ua25_ep = { +		static const struct snd_usb_midi_endpoint_info uaxx_ep = {  			.out_cables = 0x0001,  			.in_cables  = 0x0001  		}; -		static const struct snd_usb_audio_quirk ua25_quirk = { +		static const struct snd_usb_audio_quirk uaxx_quirk = {  			.type = QUIRK_MIDI_FIXED_ENDPOINT, -			.data = &ua25_ep +			.data = &uaxx_ep  		};  		if (chip->usb_id == USB_ID(0x0582, 0x002b))  			return snd_usb_create_midi_interface(chip, iface,  							     &ua700_quirk);  		else  			return snd_usb_create_midi_interface(chip, iface, -							     &ua25_quirk); +							     &uaxx_quirk);  	}  	if (altsd->bNumEndpoints != 1) @@ -3369,9 +3377,9 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip,  		[QUIRK_MIDI_CME] = snd_usb_create_midi_interface,  		[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,  		[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, -		[QUIRK_AUDIO_EDIROL_UA700_UA25] = create_ua700_ua25_quirk,  		[QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,  		[QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk, +		[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk  	};  	if (quirk->type < QUIRK_TYPE_COUNT) { @@ -3629,7 +3637,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,  	if (err > 0) {  		/* create normal USB audio interfaces */  		if (snd_usb_create_streams(chip, ifnum) < 0 || -		    snd_usb_create_mixer(chip, ifnum) < 0) { +		    snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) {  			goto __error;  		}  	} diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 7cf18c3..36e4f7a 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -156,11 +156,12 @@ enum quirk_type {  	QUIRK_MIDI_RAW,  	QUIRK_MIDI_EMAGIC,  	QUIRK_MIDI_CME, +	QUIRK_MIDI_US122L,  	QUIRK_AUDIO_STANDARD_INTERFACE,  	QUIRK_AUDIO_FIXED_ENDPOINT, -	QUIRK_AUDIO_EDIROL_UA700_UA25,  	QUIRK_AUDIO_EDIROL_UA1000,  	QUIRK_AUDIO_EDIROL_UA101, +	QUIRK_AUDIO_EDIROL_UAXX,  	QUIRK_TYPE_COUNT  }; @@ -222,7 +223,8 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,  		    __u8 request, __u8 requesttype, __u16 value, __u16 index,  		    void *data, __u16 size, int timeout); -int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif); +int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, +			 int ignore_error);  void snd_usb_mixer_disconnect(struct list_head *p);  int snd_usb_create_midi_interface(struct snd_usb_audio *chip, struct usb_interface *iface, diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 6676a177..5962e4b 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -669,6 +669,42 @@ static struct usb_protocol_ops snd_usbmidi_raw_ops = {  	.output = snd_usbmidi_raw_output,  }; +static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep, +				     uint8_t *buffer, int buffer_length) +{ +	if (buffer_length != 9) +		return; +	buffer_length = 8; +	while (buffer_length && buffer[buffer_length - 1] == 0xFD) +		buffer_length--; +	if (buffer_length) +		snd_usbmidi_input_data(ep, 0, buffer, buffer_length); +} + +static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep) +{ +	int count; + +	if (!ep->ports[0].active) +		return; +	count = ep->urb->dev->speed == USB_SPEED_HIGH ? 1 : 2; +	count = snd_rawmidi_transmit(ep->ports[0].substream, +				     ep->urb->transfer_buffer, +				     count); +	if (count < 1) { +		ep->ports[0].active = 0; +		return; +	} + +	memset(ep->urb->transfer_buffer + count, 0xFD, 9 - count); +	ep->urb->transfer_buffer_length = count; +} + +static struct usb_protocol_ops snd_usbmidi_122l_ops = { +	.input = snd_usbmidi_us122l_input, +	.output = snd_usbmidi_us122l_output, +}; +  /*   * Emagic USB MIDI protocol: raw MIDI with "F5 xx" port switching.   */ @@ -1076,6 +1112,15 @@ void snd_usbmidi_disconnect(struct list_head* p)  		}  		if (ep->in)  			usb_kill_urb(ep->in->urb); +		/* free endpoints here; later call can result in Oops */ +		if (ep->out) { +			snd_usbmidi_out_endpoint_delete(ep->out); +			ep->out = NULL; +		} +		if (ep->in) { +			snd_usbmidi_in_endpoint_delete(ep->in); +			ep->in = NULL; +		}  	}  	del_timer_sync(&umidi->error_timer);  } @@ -1714,6 +1759,9 @@ int snd_usb_create_midi_interface(struct snd_usb_audio* chip,  			umidi->usb_protocol_ops =  				&snd_usbmidi_maudio_broken_running_status_ops;  		break; +	case QUIRK_MIDI_US122L: +		umidi->usb_protocol_ops = &snd_usbmidi_122l_ops; +		/* fall through */  	case QUIRK_MIDI_FIXED_ENDPOINT:  		memcpy(&endpoints[0], quirk->data,  		       sizeof(struct snd_usb_midi_endpoint_info)); diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index 89c63d0..a492461 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -59,12 +59,13 @@ static const struct rc_config {  	u8  offset;  	u8  length;  	u8  packet_length; +	u8  min_packet_length; /* minimum accepted length of the URB result */  	u8  mute_mixer_id;  	u32 mute_code;  } rc_configs[] = { -	{ USB_ID(0x041e, 0x3000), 0, 1, 2,  18, 0x0013 }, /* Extigy       */ -	{ USB_ID(0x041e, 0x3020), 2, 1, 6,  18, 0x0013 }, /* Audigy 2 NX  */ -	{ USB_ID(0x041e, 0x3040), 2, 2, 6,  2,  0x6e91 }, /* Live! 24-bit */ +	{ USB_ID(0x041e, 0x3000), 0, 1, 2, 1,  18, 0x0013 }, /* Extigy       */ +	{ USB_ID(0x041e, 0x3020), 2, 1, 6, 6,  18, 0x0013 }, /* Audigy 2 NX  */ +	{ USB_ID(0x041e, 0x3040), 2, 2, 6, 6,  2,  0x6e91 }, /* Live! 24-bit */  };  struct usb_mixer_interface { @@ -1388,7 +1389,8 @@ static int mixer_ctl_selector_info(struct snd_kcontrol *kcontrol, struct snd_ctl  	struct usb_mixer_elem_info *cval = kcontrol->private_data;  	char **itemlist = (char **)kcontrol->private_value; -	snd_assert(itemlist, return -EINVAL); +	if (snd_BUG_ON(!itemlist)) +		return -EINVAL;  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  	uinfo->count = 1;  	uinfo->value.enumerated.items = cval->max; @@ -1781,7 +1783,7 @@ static void snd_usb_soundblaster_remote_complete(struct urb *urb)  	const struct rc_config *rc = mixer->rc_cfg;  	u32 code; -	if (urb->status < 0 || urb->actual_length < rc->packet_length) +	if (urb->status < 0 || urb->actual_length < rc->min_packet_length)  		return;  	code = mixer->rc_buffer[rc->offset]; @@ -2012,7 +2014,8 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,  	}  } -int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif) +int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, +			 int ignore_error)  {  	static struct snd_device_ops dev_ops = {  		.dev_free = snd_usb_mixer_dev_free @@ -2027,9 +2030,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif)  		return -ENOMEM;  	mixer->chip = chip;  	mixer->ctrlif = ctrlif; -#ifdef IGNORE_CTL_ERROR -	mixer->ignore_ctl_error = 1; -#endif +	mixer->ignore_ctl_error = ignore_error;  	mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL);  	if (!mixer->id_elems) {  		kfree(mixer); diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index 9ea726c..69689e7 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -62,6 +62,13 @@  	.idProduct = 0x3f04,  	.bInterfaceClass = USB_CLASS_AUDIO,  }, +{ +	/* E-Mu Tracker Pre */ +	.match_flags = USB_DEVICE_ID_MATCH_DEVICE, +	.idVendor = 0x041e, +	.idProduct = 0x3f0a, +	.bInterfaceClass = USB_CLASS_AUDIO, +},  /*   * Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface @@ -855,15 +862,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),  		.data = (const struct snd_usb_audio_quirk[]) {  			{  				.ifnum = 1, -				.type = QUIRK_AUDIO_EDIROL_UA700_UA25 +				.type = QUIRK_AUDIO_EDIROL_UAXX  			},  			{  				.ifnum = 2, -				.type = QUIRK_AUDIO_EDIROL_UA700_UA25 +				.type = QUIRK_AUDIO_EDIROL_UAXX  			},  			{  				.ifnum = 3, -				.type = QUIRK_AUDIO_EDIROL_UA700_UA25 +				.type = QUIRK_AUDIO_EDIROL_UAXX  			},  			{  				.ifnum = -1 @@ -1197,15 +1204,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),  		.data = (const struct snd_usb_audio_quirk[]) {  			{  				.ifnum = 0, -				.type = QUIRK_AUDIO_EDIROL_UA700_UA25 +				.type = QUIRK_AUDIO_EDIROL_UAXX  			},  			{  				.ifnum = 1, -				.type = QUIRK_AUDIO_EDIROL_UA700_UA25 +				.type = QUIRK_AUDIO_EDIROL_UAXX  			},  			{  				.ifnum = 2, -				.type = QUIRK_AUDIO_EDIROL_UA700_UA25 +				.type = QUIRK_AUDIO_EDIROL_UAXX  			},  			{  				.ifnum = -1 @@ -1338,6 +1345,36 @@ YAMAHA_DEVICE(0x7010, "UB99"),  		}  	}  }, +{ +	/* +	 * This quirk is for the "Advanced Driver" mode. If off, the UA-4FX +	 * is standard compliant, but has only 16-bit PCM and no MIDI. +	 */ +	USB_DEVICE(0x0582, 0x00a3), +	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { +		.vendor_name = "EDIROL", +		.product_name = "UA-4FX", +		.ifnum = QUIRK_ANY_INTERFACE, +		.type = QUIRK_COMPOSITE, +		.data = (const struct snd_usb_audio_quirk[]) { +			{ +				.ifnum = 0, +				.type = QUIRK_AUDIO_EDIROL_UAXX +			}, +			{ +				.ifnum = 1, +				.type = QUIRK_AUDIO_EDIROL_UAXX +			}, +			{ +				.ifnum = 2, +				.type = QUIRK_AUDIO_EDIROL_UAXX +			}, +			{ +				.ifnum = -1 +			} +		} +	} +},  	/* TODO: add Edirol MD-P1 support */  {  	USB_DEVICE(0x582, 0x00a6), @@ -1383,7 +1420,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),  		}  	}  }, -  {  	/* Roland SonicCell */  	USB_DEVICE(0x0582, 0x00c2), @@ -1415,7 +1451,35 @@ YAMAHA_DEVICE(0x7010, "UB99"),  		}  	}  }, - +{ +	/* BOSS GT-10 */ +	USB_DEVICE(0x0582, 0x00da), +	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { +		.ifnum = QUIRK_ANY_INTERFACE, +		.type = QUIRK_COMPOSITE, +		.data = (const struct snd_usb_audio_quirk[]) { +			{ +				.ifnum = 0, +				.type = QUIRK_AUDIO_STANDARD_INTERFACE +			}, +			{ +				.ifnum = 1, +				.type = QUIRK_AUDIO_STANDARD_INTERFACE +			}, +			{ +				.ifnum = 2, +				.type = QUIRK_MIDI_FIXED_ENDPOINT, +				.data = & (const struct snd_usb_midi_endpoint_info) { +					.out_cables = 0x0001, +					.in_cables  = 0x0001 +				} +			}, +			{ +				.ifnum = -1 +			} +		} +	} +},  /* Guillemot devices */  { diff --git a/sound/usb/usx2y/Makefile b/sound/usb/usx2y/Makefile index 9ac22bc..7489330 100644 --- a/sound/usb/usx2y/Makefile +++ b/sound/usb/usx2y/Makefile @@ -1,3 +1,5 @@  snd-usb-usx2y-objs := usbusx2y.o usX2Yhwdep.o usx2yhwdeppcm.o +snd-usb-us122l-objs := us122l.o  obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-usx2y.o +obj-$(CONFIG_SND_USB_US122L) += snd-usb-us122l.o diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c new file mode 100644 index 0000000..b441fe2 --- /dev/null +++ b/sound/usb/usx2y/us122l.c @@ -0,0 +1,692 @@ +/* + * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sound/core.h> +#include <sound/hwdep.h> +#include <sound/pcm.h> +#include <sound/initval.h> +#define MODNAME "US122L" +#include "usb_stream.c" +#include "../usbaudio.h" +#include "us122l.h" + +MODULE_AUTHOR("Karsten Wiese <fzu@wemgehoertderstaat.de>"); +MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.5"); +MODULE_LICENSE("GPL"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-max */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* Id for this card */ +							/* Enable this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for "NAME_ALLCAPS"."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for "NAME_ALLCAPS"."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable "NAME_ALLCAPS"."); + +static int snd_us122l_card_used[SNDRV_CARDS]; + + +static int us122l_create_usbmidi(struct snd_card *card) +{ +	static struct snd_usb_midi_endpoint_info quirk_data = { +		.out_ep = 4, +		.in_ep = 3, +		.out_cables =	0x001, +		.in_cables =	0x001 +	}; +	static struct snd_usb_audio_quirk quirk = { +		.vendor_name =	"US122L", +		.product_name =	NAME_ALLCAPS, +		.ifnum = 	1, +		.type = QUIRK_MIDI_US122L, +		.data = &quirk_data +	}; +	struct usb_device *dev = US122L(card)->chip.dev; +	struct usb_interface *iface = usb_ifnum_to_if(dev, 1); + +	return snd_usb_create_midi_interface(&US122L(card)->chip, +					     iface, &quirk); +} + +/* + * Wrapper for usb_control_msg(). + * Allocates a temp buffer to prevent dmaing from/to the stack. + */ +static int us122l_ctl_msg(struct usb_device *dev, unsigned int pipe, +			  __u8 request, __u8 requesttype, +			  __u16 value, __u16 index, void *data, +			  __u16 size, int timeout) +{ +	int err; +	void *buf = NULL; + +	if (size > 0) { +		buf = kmemdup(data, size, GFP_KERNEL); +		if (!buf) +			return -ENOMEM; +	} +	err = usb_control_msg(dev, pipe, request, requesttype, +			      value, index, buf, size, timeout); +	if (size > 0) { +		memcpy(data, buf, size); +		kfree(buf); +	} +	return err; +} + +static void pt_info_set(struct usb_device *dev, u8 v) +{ +	int ret; + +	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), +			      'I', +			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, +			      v, 0, NULL, 0, 1000); +	snd_printdd(KERN_DEBUG "%i\n", ret); +} + +static void usb_stream_hwdep_vm_open(struct vm_area_struct *area) +{ +	struct us122l *us122l = area->vm_private_data; +	atomic_inc(&us122l->mmap_count); +	snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count)); +} + +static int usb_stream_hwdep_vm_fault(struct vm_area_struct *area, +				     struct vm_fault *vmf) +{ +	unsigned long offset; +	struct page *page; +	void *vaddr; +	struct us122l *us122l = area->vm_private_data; +	struct usb_stream *s; +	int vm_f = VM_FAULT_SIGBUS; + +	mutex_lock(&us122l->mutex); +	s = us122l->sk.s; +	if (!s) +		goto out; + +	offset = vmf->pgoff << PAGE_SHIFT; +	if (offset < PAGE_ALIGN(s->read_size)) +		vaddr = (char *)s + offset; +	else { +		offset -= PAGE_ALIGN(s->read_size); +		if (offset >= PAGE_ALIGN(s->write_size)) +			goto out; + +		vaddr = us122l->sk.write_page + offset; +	} +	page = virt_to_page(vaddr); + +	get_page(page); +	mutex_unlock(&us122l->mutex); + +	vmf->page = page; +	vm_f = 0; +out: +	return vm_f; +} + +static void usb_stream_hwdep_vm_close(struct vm_area_struct *area) +{ +	struct us122l *us122l = area->vm_private_data; +	atomic_dec(&us122l->mmap_count); +	snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count)); +} + +static struct vm_operations_struct usb_stream_hwdep_vm_ops = { +	.open = usb_stream_hwdep_vm_open, +	.fault = usb_stream_hwdep_vm_fault, +	.close = usb_stream_hwdep_vm_close, +}; + + +static int usb_stream_hwdep_open(struct snd_hwdep *hw, struct file *file) +{ +	struct us122l	*us122l = hw->private_data; +	struct usb_interface *iface; +	snd_printdd(KERN_DEBUG "%p %p\n", hw, file); +	if (hw->used >= 2) +		return -EBUSY; + +	if (!us122l->first) +		us122l->first = file; +	iface = usb_ifnum_to_if(us122l->chip.dev, 1); +	usb_autopm_get_interface(iface); +	return 0; +} + +static int usb_stream_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ +	struct us122l	*us122l = hw->private_data; +	struct usb_interface *iface = usb_ifnum_to_if(us122l->chip.dev, 1); +	snd_printdd(KERN_DEBUG "%p %p\n", hw, file); +	usb_autopm_put_interface(iface); +	if (us122l->first == file) +		us122l->first = NULL; +	mutex_lock(&us122l->mutex); +	if (us122l->master == file) +		us122l->master = us122l->slave; + +	us122l->slave = NULL; +	mutex_unlock(&us122l->mutex); +	return 0; +} + +static int usb_stream_hwdep_mmap(struct snd_hwdep *hw, +				 struct file *filp, struct vm_area_struct *area) +{ +	unsigned long	size = area->vm_end - area->vm_start; +	struct us122l	*us122l = hw->private_data; +	unsigned long offset; +	struct usb_stream *s; +	int err = 0; +	bool read; + +	offset = area->vm_pgoff << PAGE_SHIFT; +	mutex_lock(&us122l->mutex); +	s = us122l->sk.s; +	read = offset < s->read_size; +	if (read && area->vm_flags & VM_WRITE) { +		err = -EPERM; +		goto out; +	} +	snd_printdd(KERN_DEBUG "%lu %u\n", size, +		    read ? s->read_size : s->write_size); +	/* if userspace tries to mmap beyond end of our buffer, fail */ +	if (size > PAGE_ALIGN(read ? s->read_size : s->write_size)) { +		snd_printk(KERN_WARNING "%lu > %u\n", size, +			   read ? s->read_size : s->write_size); +		err = -EINVAL; +		goto out; +	} + +	area->vm_ops = &usb_stream_hwdep_vm_ops; +	area->vm_flags |= VM_RESERVED; +	area->vm_private_data = us122l; +	atomic_inc(&us122l->mmap_count); +out: +	mutex_unlock(&us122l->mutex); +	return err; +} + +static unsigned int usb_stream_hwdep_poll(struct snd_hwdep *hw, +					  struct file *file, poll_table *wait) +{ +	struct us122l	*us122l = hw->private_data; +	struct usb_stream *s = us122l->sk.s; +	unsigned	*polled; +	unsigned int	mask; + +	poll_wait(file, &us122l->sk.sleep, wait); + +	switch (s->state) { +	case usb_stream_ready: +		if (us122l->first == file) +			polled = &s->periods_polled; +		else +			polled = &us122l->second_periods_polled; +		if (*polled != s->periods_done) { +			*polled = s->periods_done; +			mask = POLLIN | POLLOUT | POLLWRNORM; +			break; +		} +		/* Fall through */ +		mask = 0; +		break; +	default: +		mask = POLLIN | POLLOUT | POLLWRNORM | POLLERR; +		break; +	} +	return mask; +} + +static void us122l_stop(struct us122l *us122l) +{ +	struct list_head *p; +	list_for_each(p, &us122l->chip.midi_list) +		snd_usbmidi_input_stop(p); + +	usb_stream_stop(&us122l->sk); +	usb_stream_free(&us122l->sk); +} + +static int us122l_set_sample_rate(struct usb_device *dev, int rate) +{ +	unsigned int ep = 0x81; +	unsigned char data[3]; +	int err; + +	data[0] = rate; +	data[1] = rate >> 8; +	data[2] = rate >> 16; +	err = us122l_ctl_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, +			     USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, +			     SAMPLING_FREQ_CONTROL << 8, ep, data, 3, 1000); +	if (err < 0) +		snd_printk(KERN_ERR "%d: cannot set freq %d to ep 0x%x\n", +			   dev->devnum, rate, ep); +	return err; +} + +static bool us122l_start(struct us122l *us122l, +			 unsigned rate, unsigned period_frames) +{ +	struct list_head *p; +	int err; +	unsigned use_packsize = 0; +	bool success = false; + +	if (us122l->chip.dev->speed == USB_SPEED_HIGH) { +		/* The us-122l's descriptor defaults to iso max_packsize 78, +		   which isn't needed for samplerates <= 48000. +		   Lets save some memory: +		*/ +		switch (rate) { +		case 44100: +			use_packsize = 36; +			break; +		case 48000: +			use_packsize = 42; +			break; +		case 88200: +			use_packsize = 72; +			break; +		} +	} +	if (!usb_stream_new(&us122l->sk, us122l->chip.dev, 1, 2, +			    rate, use_packsize, period_frames, 6)) +		goto out; + +	err = us122l_set_sample_rate(us122l->chip.dev, rate); +	if (err < 0) { +		us122l_stop(us122l); +		snd_printk(KERN_ERR "us122l_set_sample_rate error \n"); +		goto out; +	} +	err = usb_stream_start(&us122l->sk); +	if (err < 0) { +		us122l_stop(us122l); +		snd_printk(KERN_ERR "us122l_start error %i \n", err); +		goto out; +	} +	list_for_each(p, &us122l->chip.midi_list) +		snd_usbmidi_input_start(p); +	success = true; +out: +	return success; +} + +static int usb_stream_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, +				  unsigned cmd, unsigned long arg) +{ +	struct usb_stream_config *cfg; +	struct us122l *us122l = hw->private_data; +	unsigned min_period_frames; +	int err = 0; +	bool high_speed; + +	if (cmd != SNDRV_USB_STREAM_IOCTL_SET_PARAMS) +		return -ENOTTY; + +	cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); +	if (!cfg) +		return -ENOMEM; + +	if (copy_from_user(cfg, (void *)arg, sizeof(*cfg))) { +		err = -EFAULT; +		goto free; +	} +	if (cfg->version != USB_STREAM_INTERFACE_VERSION) { +		err = -ENXIO; +		goto free; +	} +	high_speed = us122l->chip.dev->speed == USB_SPEED_HIGH; +	if ((cfg->sample_rate != 44100 && cfg->sample_rate != 48000  && +	     (!high_speed || +	      (cfg->sample_rate != 88200 && cfg->sample_rate != 96000))) || +	    cfg->frame_size != 6 || +	    cfg->period_frames > 0x3000) { +		err = -EINVAL; +		goto free; +	} +	switch (cfg->sample_rate) { +	case 44100: +		min_period_frames = 48; +		break; +	case 48000: +		min_period_frames = 52; +		break; +	default: +		min_period_frames = 104; +		break; +	} +	if (!high_speed) +		min_period_frames <<= 1; +	if (cfg->period_frames < min_period_frames) { +		err = -EINVAL; +		goto free; +	} + +	snd_power_wait(hw->card, SNDRV_CTL_POWER_D0); + +	mutex_lock(&us122l->mutex); +	if (!us122l->master) +		us122l->master = file; +	else if (us122l->master != file) { +		if (memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg))) { +			err = -EIO; +			goto unlock; +		} +		us122l->slave = file; +	} +	if (!us122l->sk.s || +	    memcmp(cfg, &us122l->sk.s->cfg, sizeof(*cfg)) || +	    us122l->sk.s->state == usb_stream_xrun) { +		us122l_stop(us122l); +		if (!us122l_start(us122l, cfg->sample_rate, cfg->period_frames)) +			err = -EIO; +		else +			err = 1; +	} +unlock: +	mutex_unlock(&us122l->mutex); +free: +	kfree(cfg); +	return err; +} + +#define SND_USB_STREAM_ID "USB STREAM" +static int usb_stream_hwdep_new(struct snd_card *card) +{ +	int err; +	struct snd_hwdep *hw; +	struct usb_device *dev = US122L(card)->chip.dev; + +	err = snd_hwdep_new(card, SND_USB_STREAM_ID, 0, &hw); +	if (err < 0) +		return err; + +	hw->iface = SNDRV_HWDEP_IFACE_USB_STREAM; +	hw->private_data = US122L(card); +	hw->ops.open = usb_stream_hwdep_open; +	hw->ops.release = usb_stream_hwdep_release; +	hw->ops.ioctl = usb_stream_hwdep_ioctl; +	hw->ops.ioctl_compat = usb_stream_hwdep_ioctl; +	hw->ops.mmap = usb_stream_hwdep_mmap; +	hw->ops.poll = usb_stream_hwdep_poll; + +	sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", +		dev->bus->busnum, dev->devnum); +	return 0; +} + + +static bool us122l_create_card(struct snd_card *card) +{ +	int err; +	struct us122l *us122l = US122L(card); + +	err = usb_set_interface(us122l->chip.dev, 1, 1); +	if (err) { +		snd_printk(KERN_ERR "usb_set_interface error \n"); +		return false; +	} + +	pt_info_set(us122l->chip.dev, 0x11); +	pt_info_set(us122l->chip.dev, 0x10); + +	if (!us122l_start(us122l, 44100, 256)) +		return false; + +	err = us122l_create_usbmidi(card); +	if (err < 0) { +		snd_printk(KERN_ERR "us122l_create_usbmidi error %i \n", err); +		us122l_stop(us122l); +		return false; +	} +	err = usb_stream_hwdep_new(card); +	if (err < 0) { +/* release the midi resources */ +		struct list_head *p; +		list_for_each(p, &us122l->chip.midi_list) +			snd_usbmidi_disconnect(p); + +		us122l_stop(us122l); +		return false; +	} +	return true; +} + +static struct snd_card *usx2y_create_card(struct usb_device *device) +{ +	int		dev; +	struct snd_card *card; +	for (dev = 0; dev < SNDRV_CARDS; ++dev) +		if (enable[dev] && !snd_us122l_card_used[dev]) +			break; +	if (dev >= SNDRV_CARDS) +		return NULL; +	card = snd_card_new(index[dev], id[dev], THIS_MODULE, +			    sizeof(struct us122l)); +	if (!card) +		return NULL; +	snd_us122l_card_used[US122L(card)->chip.index = dev] = 1; + +	US122L(card)->chip.dev = device; +	US122L(card)->chip.card = card; +	mutex_init(&US122L(card)->mutex); +	init_waitqueue_head(&US122L(card)->sk.sleep); +	INIT_LIST_HEAD(&US122L(card)->chip.midi_list); +	strcpy(card->driver, "USB "NAME_ALLCAPS""); +	sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); +	sprintf(card->longname, "%s (%x:%x if %d at %03d/%03d)", +		card->shortname, +		le16_to_cpu(device->descriptor.idVendor), +		le16_to_cpu(device->descriptor.idProduct), +		0, +		US122L(card)->chip.dev->bus->busnum, +		US122L(card)->chip.dev->devnum +		); +	snd_card_set_dev(card, &device->dev); +	return card; +} + +static void *us122l_usb_probe(struct usb_interface *intf, +			      const struct usb_device_id *device_id) +{ +	struct usb_device *device = interface_to_usbdev(intf); +	struct snd_card *card = usx2y_create_card(device); + +	if (!card) +		return NULL; + +	if (!us122l_create_card(card) || +	    snd_card_register(card) < 0) { +		snd_card_free(card); +		return NULL; +	} + +	usb_get_dev(device); +	return card; +} + +static int snd_us122l_probe(struct usb_interface *intf, +			    const struct usb_device_id *id) +{ +	struct snd_card *card; +	snd_printdd(KERN_DEBUG"%p:%i\n", +		    intf, intf->cur_altsetting->desc.bInterfaceNumber); +	if (intf->cur_altsetting->desc.bInterfaceNumber != 1) +		return 0; + +	card = us122l_usb_probe(usb_get_intf(intf), id); + +	if (card) { +		usb_set_intfdata(intf, card); +		return 0; +	} + +	usb_put_intf(intf); +	return -EIO; +} + +static void snd_us122l_disconnect(struct usb_interface *intf) +{ +	struct snd_card *card; +	struct us122l *us122l; +	struct list_head *p; + +	card = usb_get_intfdata(intf); +	if (!card) +		return; + +	snd_card_disconnect(card); + +	us122l = US122L(card); +	mutex_lock(&us122l->mutex); +	us122l_stop(us122l); +	mutex_unlock(&us122l->mutex); +	us122l->chip.shutdown = 1; + +/* release the midi resources */ +	list_for_each(p, &us122l->chip.midi_list) { +		snd_usbmidi_disconnect(p); +	} + +	usb_put_intf(intf); +	usb_put_dev(US122L(card)->chip.dev); + +	while (atomic_read(&us122l->mmap_count)) +		msleep(500); + +	snd_card_free(card); +} + +static int snd_us122l_suspend(struct usb_interface *intf, pm_message_t message) +{ +	struct snd_card *card; +	struct us122l *us122l; +	struct list_head *p; + +	card = dev_get_drvdata(&intf->dev); +	if (!card) +		return 0; +	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + +	us122l = US122L(card); +	if (!us122l) +		return 0; + +	list_for_each(p, &us122l->chip.midi_list) +		snd_usbmidi_input_stop(p); + +	mutex_lock(&us122l->mutex); +	usb_stream_stop(&us122l->sk); +	mutex_unlock(&us122l->mutex); + +	return 0; +} + +static int snd_us122l_resume(struct usb_interface *intf) +{ +	struct snd_card *card; +	struct us122l *us122l; +	struct list_head *p; +	int err; + +	card = dev_get_drvdata(&intf->dev); +	if (!card) +		return 0; + +	us122l = US122L(card); +	if (!us122l) +		return 0; + +	mutex_lock(&us122l->mutex); +	/* needed, doesn't restart without: */ +	err = usb_set_interface(us122l->chip.dev, 1, 1); +	if (err) { +		snd_printk(KERN_ERR "usb_set_interface error \n"); +		goto unlock; +	} + +	pt_info_set(us122l->chip.dev, 0x11); +	pt_info_set(us122l->chip.dev, 0x10); + +	err = us122l_set_sample_rate(us122l->chip.dev, +				     us122l->sk.s->cfg.sample_rate); +	if (err < 0) { +		snd_printk(KERN_ERR "us122l_set_sample_rate error \n"); +		goto unlock; +	} +	err = usb_stream_start(&us122l->sk); +	if (err) +		goto unlock; + +	list_for_each(p, &us122l->chip.midi_list) +		snd_usbmidi_input_start(p); +unlock: +	mutex_unlock(&us122l->mutex); +	snd_power_change_state(card, SNDRV_CTL_POWER_D0); +	return err; +} + +static struct usb_device_id snd_us122l_usb_id_table[] = { +	{ +		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE, +		.idVendor =	0x0644, +		.idProduct =	USB_ID_US122L +	}, +/*  	{ */		/* US-144 maybe works when @USB1.1. Untested. */ +/* 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE, */ +/* 		.idVendor =	0x0644, */ +/* 		.idProduct =	USB_ID_US144 */ +/* 	}, */ +	{ /* terminator */ } +}; + +MODULE_DEVICE_TABLE(usb, snd_us122l_usb_id_table); +static struct usb_driver snd_us122l_usb_driver = { +	.name =		"snd-usb-us122l", +	.probe =	snd_us122l_probe, +	.disconnect =	snd_us122l_disconnect, +	.suspend =	snd_us122l_suspend, +	.resume =	snd_us122l_resume, +	.reset_resume =	snd_us122l_resume, +	.id_table =	snd_us122l_usb_id_table, +	.supports_autosuspend = 1 +}; + + +static int __init snd_us122l_module_init(void) +{ +	return usb_register(&snd_us122l_usb_driver); +} + +static void __exit snd_us122l_module_exit(void) +{ +	usb_deregister(&snd_us122l_usb_driver); +} + +module_init(snd_us122l_module_init) +module_exit(snd_us122l_module_exit) diff --git a/sound/usb/usx2y/us122l.h b/sound/usb/usx2y/us122l.h new file mode 100644 index 0000000..3d10c4b --- /dev/null +++ b/sound/usb/usx2y/us122l.h @@ -0,0 +1,27 @@ +#ifndef US122L_H +#define US122L_H + + +struct us122l { +	struct snd_usb_audio 	chip; +	int			stride; +	struct usb_stream_kernel sk; + +	struct mutex		mutex; +	struct file		*first; +	unsigned		second_periods_polled; +	struct file		*master; +	struct file		*slave; + +	atomic_t		mmap_count; +}; + + +#define US122L(c) ((struct us122l *)(c)->private_data) + +#define NAME_ALLCAPS "US-122L" + +#define USB_ID_US122L 0x800E +#define USB_ID_US144 0x800F + +#endif diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c new file mode 100644 index 0000000..ff23cc1 --- /dev/null +++ b/sound/usb/usx2y/usb_stream.c @@ -0,0 +1,761 @@ +/* + * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/usb.h> + +#include "usb_stream.h" + + +/*                             setup                                  */ + +static unsigned usb_stream_next_packet_size(struct usb_stream_kernel *sk) +{ +	struct usb_stream *s = sk->s; +	sk->out_phase_peeked = (sk->out_phase & 0xffff) + sk->freqn; +	return (sk->out_phase_peeked >> 16) * s->cfg.frame_size; +} + +static void playback_prep_freqn(struct usb_stream_kernel *sk, struct urb *urb) +{ +	struct usb_stream *s = sk->s; +	unsigned l = 0; +	int pack; + +	urb->iso_frame_desc[0].offset = 0; +	urb->iso_frame_desc[0].length =	usb_stream_next_packet_size(sk); +	sk->out_phase = sk->out_phase_peeked; +	urb->transfer_buffer_length = urb->iso_frame_desc[0].length; + +	for (pack = 1; pack < sk->n_o_ps; pack++) { +		l = usb_stream_next_packet_size(sk); +		if (s->idle_outsize + urb->transfer_buffer_length + l > +		    s->period_size) +			goto check; + +		sk->out_phase = sk->out_phase_peeked; +		urb->iso_frame_desc[pack].offset = urb->transfer_buffer_length; +		urb->iso_frame_desc[pack].length = l; +		urb->transfer_buffer_length += l; +	} +	snd_printdd(KERN_DEBUG "%i\n", urb->transfer_buffer_length); + +check: +	urb->number_of_packets = pack; +	s->idle_outsize += urb->transfer_buffer_length - s->period_size; +	snd_printdd(KERN_DEBUG "idle=%i ul=%i ps=%i\n", s->idle_outsize, +		    urb->transfer_buffer_length, s->period_size); +} + +static void init_pipe_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, +			   struct urb **urbs, char *transfer, +			   struct usb_device *dev, int pipe) +{ +	int u, p; +	int maxpacket = use_packsize ? +		use_packsize : usb_maxpacket(dev, pipe, usb_pipeout(pipe)); +	int transfer_length = maxpacket * sk->n_o_ps; + +	for (u = 0; u < USB_STREAM_NURBS; +	     ++u, transfer += transfer_length) { +		struct urb *urb = urbs[u]; +		struct usb_iso_packet_descriptor *desc; +		urb->transfer_flags = URB_ISO_ASAP; +		urb->transfer_buffer = transfer; +		urb->dev = dev; +		urb->pipe = pipe; +		urb->number_of_packets = sk->n_o_ps; +		urb->context = sk; +		urb->interval = 1; +		if (usb_pipeout(pipe)) +			continue; + +		urb->transfer_buffer_length = transfer_length; +		desc = urb->iso_frame_desc; +		desc->offset = 0; +		desc->length = maxpacket; +		for (p = 1; p < sk->n_o_ps; ++p) { +			desc[p].offset = desc[p - 1].offset + maxpacket; +			desc[p].length = maxpacket; +		} +	} +} + +static void init_urbs(struct usb_stream_kernel *sk, unsigned use_packsize, +		      struct usb_device *dev, int in_pipe, int out_pipe) +{ +	struct usb_stream	*s = sk->s; +	char			*indata = (char *)s + sizeof(*s) + +					sizeof(struct usb_stream_packet) * +					s->inpackets; +	int			u; + +	for (u = 0; u < USB_STREAM_NURBS; ++u) { +		sk->inurb[u] = usb_alloc_urb(sk->n_o_ps, GFP_KERNEL); +		sk->outurb[u] = usb_alloc_urb(sk->n_o_ps, GFP_KERNEL); +	} + +	init_pipe_urbs(sk, use_packsize, sk->inurb, indata, dev, in_pipe); +	init_pipe_urbs(sk, use_packsize, sk->outurb, sk->write_page, dev, +		       out_pipe); +} + + +/* + * convert a sampling rate into our full speed format (fs/1000 in Q16.16) + * this will overflow at approx 524 kHz + */ +static inline unsigned get_usb_full_speed_rate(unsigned rate) +{ +	return ((rate << 13) + 62) / 125; +} + +/* + * convert a sampling rate into USB high speed format (fs/8000 in Q16.16) + * this will overflow at approx 4 MHz + */ +static inline unsigned get_usb_high_speed_rate(unsigned rate) +{ +	return ((rate << 10) + 62) / 125; +} + +void usb_stream_free(struct usb_stream_kernel *sk) +{ +	struct usb_stream *s; +	unsigned u; + +	for (u = 0; u < USB_STREAM_NURBS; ++u) { +		usb_free_urb(sk->inurb[u]); +		sk->inurb[u] = NULL; +		usb_free_urb(sk->outurb[u]); +		sk->outurb[u] = NULL; +	} + +	s = sk->s; +	if (!s) +		return; + +	free_pages((unsigned long)sk->write_page, get_order(s->write_size)); +	sk->write_page = NULL; +	free_pages((unsigned long)s, get_order(s->read_size)); +	sk->s = NULL; +} + +struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk, +				  struct usb_device *dev, +				  unsigned in_endpoint, unsigned out_endpoint, +				  unsigned sample_rate, unsigned use_packsize, +				  unsigned period_frames, unsigned frame_size) +{ +	int packets, max_packsize; +	int in_pipe, out_pipe; +	int read_size = sizeof(struct usb_stream); +	int write_size; +	int usb_frames = dev->speed == USB_SPEED_HIGH ? 8000 : 1000; +	int pg; + +	in_pipe = usb_rcvisocpipe(dev, in_endpoint); +	out_pipe = usb_sndisocpipe(dev, out_endpoint); + +	max_packsize = use_packsize ? +		use_packsize : usb_maxpacket(dev, in_pipe, 0); + +	/* +		t_period = period_frames / sample_rate +		iso_packs = t_period / t_iso_frame +			= (period_frames / sample_rate) * (1 / t_iso_frame) +	*/ + +	packets = period_frames * usb_frames / sample_rate + 1; + +	if (dev->speed == USB_SPEED_HIGH) +		packets = (packets + 7) & ~7; + +	read_size += packets * USB_STREAM_URBDEPTH * +		(max_packsize + sizeof(struct usb_stream_packet)); + +	max_packsize = usb_maxpacket(dev, out_pipe, 1); +	write_size = max_packsize * packets * USB_STREAM_URBDEPTH; + +	if (read_size >= 256*PAGE_SIZE || write_size >= 256*PAGE_SIZE) { +		snd_printk(KERN_WARNING "a size exceeds 128*PAGE_SIZE\n"); +		goto out; +	} + +	pg = get_order(read_size); +	sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg); +	if (!sk->s) { +		snd_printk(KERN_WARNING "couldn't __get_free_pages()\n"); +		goto out; +	} +	sk->s->cfg.version = USB_STREAM_INTERFACE_VERSION; + +	sk->s->read_size = read_size; + +	sk->s->cfg.sample_rate = sample_rate; +	sk->s->cfg.frame_size = frame_size; +	sk->n_o_ps = packets; +	sk->s->inpackets = packets * USB_STREAM_URBDEPTH; +	sk->s->cfg.period_frames = period_frames; +	sk->s->period_size = frame_size * period_frames; + +	sk->s->write_size = write_size; +	pg = get_order(write_size); + +	sk->write_page = +		(void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg); +	if (!sk->write_page) { +		snd_printk(KERN_WARNING "couldn't __get_free_pages()\n"); +		usb_stream_free(sk); +		return NULL; +	} + +	/* calculate the frequency in 16.16 format */ +	if (dev->speed == USB_SPEED_FULL) +		sk->freqn = get_usb_full_speed_rate(sample_rate); +	else +		sk->freqn = get_usb_high_speed_rate(sample_rate); + +	init_urbs(sk, use_packsize, dev, in_pipe, out_pipe); +	sk->s->state = usb_stream_stopped; +out: +	return sk->s; +} + + +/*                             start                                  */ + +static bool balance_check(struct usb_stream_kernel *sk, struct urb *urb) +{ +	bool r; +	if (unlikely(urb->status)) { +		if (urb->status != -ESHUTDOWN && urb->status != -ENOENT) +			snd_printk(KERN_WARNING "status=%i\n", urb->status); +		sk->iso_frame_balance = 0x7FFFFFFF; +		return false; +	} +	r = sk->iso_frame_balance == 0; +	if (!r) +		sk->i_urb = urb; +	return r; +} + +static bool balance_playback(struct usb_stream_kernel *sk, struct urb *urb) +{ +	sk->iso_frame_balance += urb->number_of_packets; +	return balance_check(sk, urb); +} + +static bool balance_capture(struct usb_stream_kernel *sk, struct urb *urb) +{ +	sk->iso_frame_balance -= urb->number_of_packets; +	return balance_check(sk, urb); +} + +static void subs_set_complete(struct urb **urbs, void (*complete)(struct urb *)) +{ +	int u; + +	for (u = 0; u < USB_STREAM_NURBS; u++) { +		struct urb *urb = urbs[u]; +		urb->complete = complete; +	} +} + +int usb_stream_prepare_playback(struct usb_stream_kernel *sk, struct urb *inurb) +{ +	struct usb_stream *s = sk->s; +	struct urb *io; +	struct usb_iso_packet_descriptor *id, *od; +	int p, l = 0; + +	io = sk->idle_outurb; +	od = io->iso_frame_desc; +	io->transfer_buffer_length = 0; + +	for (p = 0; s->sync_packet < 0; ++p, ++s->sync_packet) { +		struct urb *ii = sk->completed_inurb; +		id = ii->iso_frame_desc + +			ii->number_of_packets + s->sync_packet; +		l = id->actual_length; + +		od[p].length = l; +		od[p].offset = io->transfer_buffer_length; +		io->transfer_buffer_length += l; +	} + +	for (; +	     s->sync_packet < inurb->number_of_packets && p < sk->n_o_ps; +	     ++p, ++s->sync_packet) { +		l = inurb->iso_frame_desc[s->sync_packet].actual_length; + +		if (s->idle_outsize + io->transfer_buffer_length + l > +		    s->period_size) +			goto check_ok; + +		od[p].length = l; +		od[p].offset = io->transfer_buffer_length; +		io->transfer_buffer_length += l; +	} + +check_ok: +	s->sync_packet -= inurb->number_of_packets; +	if (s->sync_packet < -2 || s->sync_packet > 0) { +		snd_printk(KERN_WARNING "invalid sync_packet = %i;" +			   " p=%i nop=%i %i %x %x %x > %x\n", +			   s->sync_packet, p, inurb->number_of_packets, +			   s->idle_outsize + io->transfer_buffer_length + l, +			   s->idle_outsize, io->transfer_buffer_length,  l, +			   s->period_size); +		return -1; +	} +	if (io->transfer_buffer_length % s->cfg.frame_size) { +		snd_printk(KERN_WARNING"invalid outsize = %i\n", +			   io->transfer_buffer_length); +		return -1; +	} +	s->idle_outsize += io->transfer_buffer_length - s->period_size; +	io->number_of_packets = p; +	if (s->idle_outsize > 0) { +		snd_printk(KERN_WARNING "idle=%i\n", s->idle_outsize); +		return -1; +	} +	return 0; +} + +static void prepare_inurb(int number_of_packets, struct urb *iu) +{ +	struct usb_iso_packet_descriptor *id; +	int p; + +	iu->number_of_packets = number_of_packets; +	id = iu->iso_frame_desc; +	id->offset = 0; +	for (p = 0; p < iu->number_of_packets - 1; ++p) +		id[p + 1].offset = id[p].offset + id[p].length; + +	iu->transfer_buffer_length = +		id[0].length * iu->number_of_packets; +} + +static int submit_urbs(struct usb_stream_kernel *sk, +		       struct urb *inurb, struct urb *outurb) +{ +	int err; +	prepare_inurb(sk->idle_outurb->number_of_packets, sk->idle_inurb); +	err = usb_submit_urb(sk->idle_inurb, GFP_ATOMIC); +	if (err < 0) { +		snd_printk(KERN_ERR "%i\n", err); +		return err; +	} +	sk->idle_inurb = sk->completed_inurb; +	sk->completed_inurb = inurb; +	err = usb_submit_urb(sk->idle_outurb, GFP_ATOMIC); +	if (err < 0) { +		snd_printk(KERN_ERR "%i\n", err); +		return err; +	} +	sk->idle_outurb = sk->completed_outurb; +	sk->completed_outurb = outurb; +	return 0; +} + +#ifdef DEBUG_LOOP_BACK +/* +  This loop_back() shows how to read/write the period data. + */ +static void loop_back(struct usb_stream *s) +{ +	char *i, *o; +	int il, ol, l, p; +	struct urb *iu; +	struct usb_iso_packet_descriptor *id; + +	o = s->playback1st_to; +	ol = s->playback1st_size; +	l = 0; + +	if (s->insplit_pack >= 0) { +		iu = sk->idle_inurb; +		id = iu->iso_frame_desc; +		p = s->insplit_pack; +	} else +		goto second; +loop: +	for (; p < iu->number_of_packets && l < s->period_size; ++p) { +		i = iu->transfer_buffer + id[p].offset; +		il = id[p].actual_length; +		if (l + il > s->period_size) +			il = s->period_size - l; +		if (il <= ol) { +			memcpy(o, i, il); +			o += il; +			ol -= il; +		} else { +			memcpy(o, i, ol); +			singen_6pack(o, ol); +			o = s->playback_to; +			memcpy(o, i + ol, il - ol); +			o += il - ol; +			ol = s->period_size - s->playback1st_size; +		} +		l += il; +	} +	if (iu == sk->completed_inurb) { +		if (l != s->period_size) +			printk(KERN_DEBUG"%s:%i %i\n", __func__, __LINE__, +			       l/(int)s->cfg.frame_size); + +		return; +	} +second: +	iu = sk->completed_inurb; +	id = iu->iso_frame_desc; +	p = 0; +	goto loop; + +} +#else +static void loop_back(struct usb_stream *s) +{ +} +#endif + +static void stream_idle(struct usb_stream_kernel *sk, +			struct urb *inurb, struct urb *outurb) +{ +	struct usb_stream *s = sk->s; +	int l, p; +	int insize = s->idle_insize; +	int urb_size = 0; + +	s->inpacket_split = s->next_inpacket_split; +	s->inpacket_split_at = s->next_inpacket_split_at; +	s->next_inpacket_split = -1; +	s->next_inpacket_split_at = 0; + +	for (p = 0; p < inurb->number_of_packets; ++p) { +		struct usb_iso_packet_descriptor *id = inurb->iso_frame_desc; +		l = id[p].actual_length; +		if (unlikely(l == 0 || id[p].status)) { +			snd_printk(KERN_WARNING "underrun, status=%u\n", +				   id[p].status); +			goto err_out; +		} +		s->inpacket_head++; +		s->inpacket_head %= s->inpackets; +		if (s->inpacket_split == -1) +			s->inpacket_split = s->inpacket_head; + +		s->inpacket[s->inpacket_head].offset = +			id[p].offset + (inurb->transfer_buffer - (void *)s); +		s->inpacket[s->inpacket_head].length = l; +		if (insize + l > s->period_size && +		    s->next_inpacket_split == -1) { +			s->next_inpacket_split = s->inpacket_head; +			s->next_inpacket_split_at = s->period_size - insize; +		} +		insize += l; +		urb_size += l; +	} +	s->idle_insize += urb_size - s->period_size; +	if (s->idle_insize < 0) { +		snd_printk(KERN_WARNING "%i\n", +			   (s->idle_insize)/(int)s->cfg.frame_size); +		goto err_out; +	} +	s->insize_done += urb_size; + +	l = s->idle_outsize; +	s->outpacket[0].offset = (sk->idle_outurb->transfer_buffer - +				  sk->write_page) - l; + +	if (usb_stream_prepare_playback(sk, inurb) < 0) +		goto err_out; + +	s->outpacket[0].length = sk->idle_outurb->transfer_buffer_length + l; +	s->outpacket[1].offset = sk->completed_outurb->transfer_buffer - +		sk->write_page; + +	if (submit_urbs(sk, inurb, outurb) < 0) +		goto err_out; + +	loop_back(s); +	s->periods_done++; +	wake_up_all(&sk->sleep); +	return; +err_out: +	s->state = usb_stream_xrun; +	wake_up_all(&sk->sleep); +} + +static void i_capture_idle(struct urb *urb) +{ +	struct usb_stream_kernel *sk = urb->context; +	if (balance_capture(sk, urb)) +		stream_idle(sk, urb, sk->i_urb); +} + +static void i_playback_idle(struct urb *urb) +{ +	struct usb_stream_kernel *sk = urb->context; +	if (balance_playback(sk, urb)) +		stream_idle(sk, sk->i_urb, urb); +} + +static void stream_start(struct usb_stream_kernel *sk, +			 struct urb *inurb, struct urb *outurb) +{ +	struct usb_stream *s = sk->s; +	if (s->state >= usb_stream_sync1) { +		int l, p, max_diff, max_diff_0; +		int urb_size = 0; +		unsigned frames_per_packet, min_frames = 0; +		frames_per_packet = (s->period_size - s->idle_insize); +		frames_per_packet <<= 8; +		frames_per_packet /= +			s->cfg.frame_size * inurb->number_of_packets; +		frames_per_packet++; + +		max_diff_0 = s->cfg.frame_size; +		if (s->cfg.period_frames >= 256) +			max_diff_0 <<= 1; +		if (s->cfg.period_frames >= 1024) +			max_diff_0 <<= 1; +		max_diff = max_diff_0; +		for (p = 0; p < inurb->number_of_packets; ++p) { +			int diff; +			l = inurb->iso_frame_desc[p].actual_length; +			urb_size += l; + +			min_frames += frames_per_packet; +			diff = urb_size - +				(min_frames >> 8) * s->cfg.frame_size; +			if (diff < max_diff) { +				snd_printdd(KERN_DEBUG "%i %i %i %i\n", +					    s->insize_done, +					    urb_size / (int)s->cfg.frame_size, +					    inurb->number_of_packets, diff); +				max_diff = diff; +			} +		} +		s->idle_insize -= max_diff - max_diff_0; +		s->idle_insize += urb_size - s->period_size; +		if (s->idle_insize < 0) { +			snd_printk("%i %i %i\n", +				   s->idle_insize, urb_size, s->period_size); +			return; +		} else if (s->idle_insize == 0) { +			s->next_inpacket_split = +				(s->inpacket_head + 1) % s->inpackets; +			s->next_inpacket_split_at = 0; +		} else { +			unsigned split = s->inpacket_head; +			l = s->idle_insize; +			while (l > s->inpacket[split].length) { +				l -= s->inpacket[split].length; +				if (split == 0) +					split = s->inpackets - 1; +				else +					split--; +			} +			s->next_inpacket_split = split; +			s->next_inpacket_split_at = +				s->inpacket[split].length - l; +		} + +		s->insize_done += urb_size; + +		if (usb_stream_prepare_playback(sk, inurb) < 0) +			return; + +	} else +		playback_prep_freqn(sk, sk->idle_outurb); + +	if (submit_urbs(sk, inurb, outurb) < 0) +		return; + +	if (s->state == usb_stream_sync1 && s->insize_done > 360000) { +		/* just guesswork                            ^^^^^^ */ +		s->state = usb_stream_ready; +		subs_set_complete(sk->inurb, i_capture_idle); +		subs_set_complete(sk->outurb, i_playback_idle); +	} +} + +static void i_capture_start(struct urb *urb) +{ +	struct usb_iso_packet_descriptor *id = urb->iso_frame_desc; +	struct usb_stream_kernel *sk = urb->context; +	struct usb_stream *s = sk->s; +	int p; +	int empty = 0; + +	if (urb->status) { +		snd_printk(KERN_WARNING "status=%i\n", urb->status); +		return; +	} + +	for (p = 0; p < urb->number_of_packets; ++p) { +		int l = id[p].actual_length; +		if (l < s->cfg.frame_size) { +			++empty; +			if (s->state >= usb_stream_sync0) { +				snd_printk(KERN_WARNING "%i\n", l); +				return; +			} +		} +		s->inpacket_head++; +		s->inpacket_head %= s->inpackets; +		s->inpacket[s->inpacket_head].offset = +			id[p].offset + (urb->transfer_buffer - (void *)s); +		s->inpacket[s->inpacket_head].length = l; +	} +#ifdef SHOW_EMPTY +	if (empty) { +		printk(KERN_DEBUG"%s:%i: %i", __func__, __LINE__, +		       urb->iso_frame_desc[0].actual_length); +		for (pack = 1; pack < urb->number_of_packets; ++pack) { +			int l = urb->iso_frame_desc[pack].actual_length; +			printk(" %i", l); +		} +		printk("\n"); +	} +#endif +	if (!empty && s->state < usb_stream_sync1) +		++s->state; + +	if (balance_capture(sk, urb)) +		stream_start(sk, urb, sk->i_urb); +} + +static void i_playback_start(struct urb *urb) +{ +	struct usb_stream_kernel *sk = urb->context; +	if (balance_playback(sk, urb)) +		stream_start(sk, sk->i_urb, urb); +} + +int usb_stream_start(struct usb_stream_kernel *sk) +{ +	struct usb_stream *s = sk->s; +	int frame = 0, iters = 0; +	int u, err; +	int try = 0; + +	if (s->state != usb_stream_stopped) +		return -EAGAIN; + +	subs_set_complete(sk->inurb, i_capture_start); +	subs_set_complete(sk->outurb, i_playback_start); +	memset(sk->write_page, 0, s->write_size); +dotry: +	s->insize_done = 0; +	s->idle_insize = 0; +	s->idle_outsize = 0; +	s->sync_packet = -1; +	s->inpacket_head = -1; +	sk->iso_frame_balance = 0; +	++try; +	for (u = 0; u < 2; u++) { +		struct urb *inurb = sk->inurb[u]; +		struct urb *outurb = sk->outurb[u]; +		playback_prep_freqn(sk, outurb); +		inurb->number_of_packets = outurb->number_of_packets; +		inurb->transfer_buffer_length = +			inurb->number_of_packets * +			inurb->iso_frame_desc[0].length; +		preempt_disable(); +		if (u == 0) { +			int now; +			struct usb_device *dev = inurb->dev; +			frame = usb_get_current_frame_number(dev); +			do { +				now = usb_get_current_frame_number(dev); +				++iters; +			} while (now > -1 && now == frame); +		} +		err = usb_submit_urb(inurb, GFP_ATOMIC); +		if (err < 0) { +			preempt_enable(); +			snd_printk(KERN_ERR"usb_submit_urb(sk->inurb[%i])" +				   " returned %i\n", u, err); +			return err; +		} +		err = usb_submit_urb(outurb, GFP_ATOMIC); +		if (err < 0) { +			preempt_enable(); +			snd_printk(KERN_ERR"usb_submit_urb(sk->outurb[%i])" +				   " returned %i\n", u, err); +			return err; +		} +		preempt_enable(); +		if (inurb->start_frame != outurb->start_frame) { +			snd_printd(KERN_DEBUG +				   "u[%i] start_frames differ in:%u out:%u\n", +				   u, inurb->start_frame, outurb->start_frame); +			goto check_retry; +		} +	} +	snd_printdd(KERN_DEBUG "%i %i\n", frame, iters); +	try = 0; +check_retry: +	if (try) { +		usb_stream_stop(sk); +		if (try < 5) { +			msleep(1500); +			snd_printd(KERN_DEBUG "goto dotry;\n"); +			goto dotry; +		} +		snd_printk(KERN_WARNING"couldn't start" +			   " all urbs on the same start_frame.\n"); +		return -EFAULT; +	} + +	sk->idle_inurb = sk->inurb[USB_STREAM_NURBS - 2]; +	sk->idle_outurb = sk->outurb[USB_STREAM_NURBS - 2]; +	sk->completed_inurb = sk->inurb[USB_STREAM_NURBS - 1]; +	sk->completed_outurb = sk->outurb[USB_STREAM_NURBS - 1]; + +/* wait, check */ +	{ +		int wait_ms = 3000; +		while (s->state != usb_stream_ready && wait_ms > 0) { +			snd_printdd(KERN_DEBUG "%i\n", s->state); +			msleep(200); +			wait_ms -= 200; +		} +	} + +	return s->state == usb_stream_ready ? 0 : -EFAULT; +} + + +/*                             stop                                   */ + +void usb_stream_stop(struct usb_stream_kernel *sk) +{ +	int u; +	if (!sk->s) +		return; +	for (u = 0; u < USB_STREAM_NURBS; ++u) { +		usb_kill_urb(sk->inurb[u]); +		usb_kill_urb(sk->outurb[u]); +	} +	sk->s->state = usb_stream_stopped; +	msleep(400); +} diff --git a/sound/usb/usx2y/usb_stream.h b/sound/usb/usx2y/usb_stream.h new file mode 100644 index 0000000..4dd74ab --- /dev/null +++ b/sound/usb/usx2y/usb_stream.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2007, 2008 Karsten Wiese <fzu@wemgehoertderstaat.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define USB_STREAM_INTERFACE_VERSION 2 + +#define SNDRV_USB_STREAM_IOCTL_SET_PARAMS \ +	_IOW('H', 0x90, struct usb_stream_config) + +struct usb_stream_packet { +	unsigned offset; +	unsigned length; +}; + + +struct usb_stream_config { +	unsigned version; +	unsigned sample_rate; +	unsigned period_frames; +	unsigned frame_size; +}; + +struct usb_stream { +	struct usb_stream_config cfg; +	unsigned read_size; +	unsigned write_size; + +	int period_size; + +	unsigned state; + +	int idle_insize; +	int idle_outsize; +	int sync_packet; +	unsigned insize_done; +	unsigned periods_done; +	unsigned periods_polled; + +	struct usb_stream_packet outpacket[2]; +	unsigned		 inpackets; +	unsigned		 inpacket_head; +	unsigned		 inpacket_split; +	unsigned		 inpacket_split_at; +	unsigned		 next_inpacket_split; +	unsigned		 next_inpacket_split_at; +	struct usb_stream_packet inpacket[0]; +}; + +enum usb_stream_state { +	usb_stream_invalid, +	usb_stream_stopped, +	usb_stream_sync0, +	usb_stream_sync1, +	usb_stream_ready, +	usb_stream_running, +	usb_stream_xrun, +}; + +#if __KERNEL__ + +#define USB_STREAM_NURBS 4 +#define USB_STREAM_URBDEPTH 4 + +struct usb_stream_kernel { +	struct usb_stream *s; + +	void *write_page; + +	unsigned n_o_ps; + +	struct urb *inurb[USB_STREAM_NURBS]; +	struct urb *idle_inurb; +	struct urb *completed_inurb; +	struct urb *outurb[USB_STREAM_NURBS]; +	struct urb *idle_outurb; +	struct urb *completed_outurb; +	struct urb *i_urb; + +	int iso_frame_balance; + +	wait_queue_head_t sleep; + +	unsigned out_phase; +	unsigned out_phase_peeked; +	unsigned freqn; +}; + +struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk, +				  struct usb_device *dev, +				  unsigned in_endpoint, unsigned out_endpoint, +				  unsigned sample_rate, unsigned use_packsize, +				  unsigned period_frames, unsigned frame_size); +void usb_stream_free(struct usb_stream_kernel *); +int usb_stream_start(struct usb_stream_kernel *); +void usb_stream_stop(struct usb_stream_kernel *); + + +#endif  | 
