diff options
author | David Vrabel <david.vrabel@csr.com> | 2008-10-20 16:07:19 +0100 |
---|---|---|
committer | David Vrabel <david.vrabel@csr.com> | 2008-10-20 16:07:19 +0100 |
commit | 61e0e79ee3c609eb34edf2fe023708cba6a79b1f (patch) | |
tree | 663deacffd4071120dc9badb70428fe5f124c7b9 /sound | |
parent | c15895ef30c2c03e99802951787183039a349d32 (diff) | |
parent | 0cfd81031a26717fe14380d18275f8e217571615 (diff) |
Merge branch 'master' into for-upstream
Conflicts:
Documentation/ABI/testing/sysfs-bus-usb
drivers/Makefile
Diffstat (limited to 'sound')
353 files changed, 30514 insertions, 11051 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index 8ebf512ced6..200aca1faa7 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 7a16a3331f7..6c515b2b8bb 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/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h index 622cd37a011..a0f223c13f6 100644 --- a/sound/aoa/soundbus/soundbus.h +++ b/sound/aoa/soundbus/soundbus.h @@ -8,7 +8,7 @@ #ifndef __SOUNDBUS_H #define __SOUNDBUS_H -#include <asm/of_device.h> +#include <linux/of_device.h> #include <sound/pcm.h> #include <linux/list.h> diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 351e19ea378..f8e6de48d81 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 4ef6dd00c6e..2054de11de8 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 b0a47449496..89096e811a4 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 00000000000..34c1d94f921 --- /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_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_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_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_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_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_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 199cca3366d..c2635beb4c8 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 00000000000..1c93eb77cb9 --- /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 381094aab23..535704f7749 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 b79f1e80378..5c4a4d38a08 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 b9c51bf8cd7..1dcd51d81d1 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 335d45ecde6..66348c92f88 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 da8e685eef9..d57125a5687 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 281b2e2ef0e..6d71f9a7ccb 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 6101259ad86..368dc9c4aef 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 202dac0e4d8..c58d8227254 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 6d6589f9389..195cafc5a55 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 c67773ad929..527b207462b 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 e35789a9275..e4af138d651 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 df46bbc25dc..ef2352c2e45 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -545,12 +545,13 @@ 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, - MKDEV(0, 0), NULL, - "card%i", card->number); + card->card_dev = device_create(sound_class, card->dev, + MKDEV(0, 0), NULL, + "card%i", card->number); if (IS_ERR(card->card_dev)) card->card_dev = NULL; } diff --git a/sound/core/jack.c b/sound/core/jack.c new file mode 100644 index 00000000000..bd2d9e6b55e --- /dev/null +++ b/sound/core/jack.c @@ -0,0 +1,166 @@ +/* + * 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) + return; + + 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 f5d6d8d1297..1b3534d6768 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -33,9 +33,6 @@ #include <linux/moduleparam.h> #include <linux/mutex.h> #include <sound/memalloc.h> -#ifdef CONFIG_SBUS -#include <asm/sbus.h> -#endif MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>"); @@ -46,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); @@ -67,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 @@ -111,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) @@ -152,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 */ @@ -180,39 +159,6 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, } #endif /* CONFIG_HAS_DMA */ -#ifdef CONFIG_SBUS - -static void *snd_malloc_sbus_pages(struct device *dev, size_t size, - dma_addr_t *dma_addr) -{ - struct sbus_dev *sdev = (struct sbus_dev *)dev; - int pg; - void *res; - - snd_assert(size > 0, return NULL); - snd_assert(dma_addr != NULL, return NULL); - pg = get_order(size); - res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) - inc_snd_pages(pg); - return res; -} - -static void snd_free_sbus_pages(struct device *dev, size_t size, - void *ptr, dma_addr_t dma_addr) -{ - struct sbus_dev *sdev = (struct sbus_dev *)dev; - int pg; - - if (ptr == NULL) - return; - pg = get_order(size); - dec_snd_pages(pg); - sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); -} - -#endif /* CONFIG_SBUS */ - /* * * ALSA generic memory management @@ -236,8 +182,10 @@ static void snd_free_sbus_pages(struct device *dev, size_t size, 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; @@ -247,11 +195,6 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->area = snd_malloc_pages(size, (unsigned long)device); dmab->addr = 0; break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr); - break; -#endif #ifdef CONFIG_HAS_DMA case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); @@ -292,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; @@ -320,11 +265,6 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) case SNDRV_DMA_TYPE_CONTINUOUS: snd_free_pages(dmab->area, dmab->bytes); break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); - break; -#endif #ifdef CONFIG_HAS_DMA case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); @@ -353,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) { @@ -387,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; @@ -431,7 +373,7 @@ static int snd_mem_proc_read(struct seq_file *seq, void *offset) long pages = snd_allocated_pages >> (PAGE_SHIFT-12); struct snd_mem_list *mem; int devno; - static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" }; + static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG" }; mutex_lock(&list_mutex); seq_printf(seq, "pages : %li bytes (%li pages per %likB)\n", diff --git a/sound/core/oss/copy.c b/sound/core/oss/copy.c index 9ded30d0e97..05b58d4fc2b 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 f874f6ca365..6faa1d71920 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 da3dbd41669..4c1d1682719 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 581aa2c60e6..4690b8b5681 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 77f96194a0e..f7649d4d950 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 4c601b192dd..1af62b8b86c 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 bec94138205..6751daa3bb5 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 14dfb3175d8..a466443c4a2 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 da7ab7a3e82..0dcc2870d53 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 ece25c718e9..192a433a240 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,9 +798,9 @@ 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; @@ -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 49aa693fba8..36d7a599823 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 1533f0379e9..921691080f3 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; @@ -903,12 +908,12 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, EXPORT_SYMBOL(snd_pcm_hw_rule_add); /** - * snd_pcm_hw_constraint_mask + * snd_pcm_hw_constraint_mask - apply the given bitmap mask constraint * @runtime: PCM runtime instance * @var: hw_params variable to apply the mask * @mask: the bitmap mask * - * Apply the constraint of the given bitmap mask to a mask parameter. + * Apply the constraint of the given bitmap mask to a 32-bit mask parameter. */ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int32_t mask) @@ -923,12 +928,12 @@ int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, snd_pcm_hw_param } /** - * snd_pcm_hw_constraint_mask64 + * snd_pcm_hw_constraint_mask64 - apply the given bitmap mask constraint * @runtime: PCM runtime instance * @var: hw_params variable to apply the mask * @mask: the 64bit bitmap mask * - * Apply the constraint of the given bitmap mask to a mask parameter. + * Apply the constraint of the given bitmap mask to a 64-bit mask parameter. */ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var, u_int64_t mask) @@ -944,7 +949,7 @@ int snd_pcm_hw_constraint_mask64(struct snd_pcm_runtime *runtime, snd_pcm_hw_par } /** - * snd_pcm_hw_constraint_integer + * snd_pcm_hw_constraint_integer - apply an integer constraint to an interval * @runtime: PCM runtime instance * @var: hw_params variable to apply the integer constraint * @@ -959,7 +964,7 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); /** - * snd_pcm_hw_constraint_minmax + * snd_pcm_hw_constraint_minmax - apply a min/max range constraint to an interval * @runtime: PCM runtime instance * @var: hw_params variable to apply the range * @min: the minimal value @@ -990,7 +995,7 @@ static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, /** - * snd_pcm_hw_constraint_list + * snd_pcm_hw_constraint_list - apply a list of constraints to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the list constraint @@ -1026,7 +1031,7 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_ratnums + * snd_pcm_hw_constraint_ratnums - apply ratnums constraint to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the ratnums constraint @@ -1059,7 +1064,7 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_ratdens + * snd_pcm_hw_constraint_ratdens - apply ratdens constraint to a parameter * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the ratdens constraint @@ -1090,7 +1095,7 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_msbits + * snd_pcm_hw_constraint_msbits - add a hw constraint msbits rule * @runtime: PCM runtime instance * @cond: condition bits * @width: sample bits width @@ -1118,7 +1123,7 @@ static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, } /** - * snd_pcm_hw_constraint_step + * snd_pcm_hw_constraint_step - add a hw constraint step rule * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the step constraint @@ -1149,7 +1154,7 @@ static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm } /** - * snd_pcm_hw_constraint_pow2 + * snd_pcm_hw_constraint_pow2 - add a hw constraint power-of-2 rule * @runtime: PCM runtime instance * @cond: condition bits * @var: hw_params variable to apply the power-of-2 constraint @@ -1197,13 +1202,13 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) EXPORT_SYMBOL(_snd_pcm_hw_params_any); /** - * snd_pcm_hw_param_value + * snd_pcm_hw_param_value - return @params field @var value * @params: the hw_params instance * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL + * @dir: pointer to the direction (-1,0,1) or %NULL * - * Return the value for field PAR if it's fixed in configuration space - * defined by PARAMS. Return -EINVAL otherwise + * Return the value for field @var if it's fixed in configuration space + * defined by @params. Return -%EINVAL otherwise. */ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var, int *dir) @@ -1266,13 +1271,13 @@ static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, /** - * snd_pcm_hw_param_first + * snd_pcm_hw_param_first - refine config space and return minimum value * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL + * @dir: pointer to the direction (-1,0,1) or %NULL * - * Inside configuration space defined by PARAMS remove from PAR all + * Inside configuration space defined by @params remove from @var all * values > minimum. Reduce configuration space accordingly. * Return the minimum. */ @@ -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); } @@ -1311,13 +1317,13 @@ static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, /** - * snd_pcm_hw_param_last + * snd_pcm_hw_param_last - refine config space and return maximum value * @pcm: PCM instance * @params: the hw_params instance * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL + * @dir: pointer to the direction (-1,0,1) or %NULL * - * Inside configuration space defined by PARAMS remove from PAR all + * Inside configuration space defined by @params remove from @var all * values < maximum. Reduce configuration space accordingly. * Return the maximum. */ @@ -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); } @@ -1338,11 +1345,11 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, EXPORT_SYMBOL(snd_pcm_hw_param_last); /** - * snd_pcm_hw_param_choose + * snd_pcm_hw_param_choose - choose a configuration defined by @params * @pcm: PCM instance * @params: the hw_params instance * - * Choose one configuration from configuration space defined by PARAMS + * Choose one configuration from configuration space defined by @params. * The configuration chosen is that obtained fixing in this order: * first access, first format, first subformat, min channels, * min rate, min period time, max buffer size, min tick time @@ -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 ff07b4a9992..a6d42808828 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 c487025d345..aef18682c03 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); @@ -880,10 +875,8 @@ static struct action_ops snd_pcm_action_start = { }; /** - * snd_pcm_start + * snd_pcm_start - start all linked streams * @substream: the PCM substream instance - * - * Start all linked streams. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -931,12 +924,11 @@ static struct action_ops snd_pcm_action_stop = { }; /** - * snd_pcm_stop + * snd_pcm_stop - try to stop all running streams in the substream group * @substream: the PCM substream instance * @state: PCM state after stopping the stream * - * Try to stop all running streams in the substream group. - * The state of each stream is changed to the given value after that unconditionally. + * The state of each stream is then changed to the given state unconditionally. */ int snd_pcm_stop(struct snd_pcm_substream *substream, int state) { @@ -946,11 +938,10 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, int state) EXPORT_SYMBOL(snd_pcm_stop); /** - * snd_pcm_drain_done + * snd_pcm_drain_done - stop the DMA only when the given stream is playback * @substream: the PCM substream * - * Stop the DMA only when the given stream is playback. - * The state is changed to SETUP. + * After stopping, the state is changed to SETUP. * Unlike snd_pcm_stop(), this affects only the given stream. */ int snd_pcm_drain_done(struct snd_pcm_substream *substream) @@ -1070,10 +1061,9 @@ static struct action_ops snd_pcm_action_suspend = { }; /** - * snd_pcm_suspend + * snd_pcm_suspend - trigger SUSPEND to all linked streams * @substream: the PCM substream * - * Trigger SUSPEND to all linked streams. * After this call, all streams are changed to SUSPENDED state. */ int snd_pcm_suspend(struct snd_pcm_substream *substream) @@ -1093,10 +1083,9 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream) EXPORT_SYMBOL(snd_pcm_suspend); /** - * snd_pcm_suspend_all + * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm * @pcm: the PCM instance * - * Trigger SUSPEND to all substreams in the given pcm. * After this call, all streams are changed to SUSPENDED state. */ int snd_pcm_suspend_all(struct snd_pcm *pcm) @@ -1250,7 +1239,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; @@ -1319,11 +1307,9 @@ static struct action_ops snd_pcm_action_prepare = { }; /** - * snd_pcm_prepare + * snd_pcm_prepare - prepare the PCM substream to be triggerable * @substream: the PCM substream instance * @file: file to refer f_flags - * - * Prepare the PCM substream to be triggerable. */ static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) @@ -1421,7 +1407,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,7 +1526,8 @@ 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; @@ -1934,33 +1920,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, @@ -1971,7 +1965,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))) { @@ -2067,8 +2062,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) @@ -2086,7 +2081,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; } @@ -2170,7 +2166,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); @@ -2493,8 +2490,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; @@ -2563,8 +2558,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: { @@ -2643,8 +2640,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: { @@ -2783,7 +2782,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; @@ -2806,21 +2806,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; } @@ -2838,7 +2834,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; @@ -2872,17 +2869,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) @@ -2893,7 +2887,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; } @@ -2908,7 +2901,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); @@ -2946,7 +2940,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); @@ -3016,7 +3011,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; @@ -3056,7 +3050,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; @@ -3188,7 +3181,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)) @@ -3220,7 +3212,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) { @@ -3248,9 +3241,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(); @@ -3384,6 +3377,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 */ @@ -3400,6 +3404,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, @@ -3412,5 +3417,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 033a024d153..2c89c04f291 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 b917a9f981c..c4995c9f573 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -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 97b30fb4c36..51e64e30dd3 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 777796e9449..f25e3cc7ddf 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 e024e4588b8..945a27c34a9 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 7a1545d2d95..8ca2be339f3 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 9628c06e4ea..38693f47c26 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 05410e536a4..1f997675c89 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 3a94ed021bd..0d75afa786b 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 0cf6ac47731..7fb55436287 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 99b35360c50..4d26146a62c 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 1c32a53d6bd..3bf7d73ac52 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 85969db576c..0101a8b99b7 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 4a48c6ee8ee..e7a8e9e4edb 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 d8fcd62e400..f745c317d6a 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 cefd228cd2a..d4564edd61d 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 1003ae375d4..44a69bb8d4f 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; @@ -272,9 +274,8 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, return minor; } snd_minors[minor] = preg; - preg->dev = device_create_drvdata(sound_class, device, - MKDEV(major, minor), - private_data, "%s", name); + preg->dev = device_create(sound_class, device, MKDEV(major, minor), + private_data, "%s", name); if (IS_ERR(preg->dev)) { snd_minors[minor] = NULL; mutex_unlock(&sound_mutex); diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 7be51546eb9..7fe12264ff8 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 0af337efc64..e582face89d 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 5512f5373c5..e05802ae6e1 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 4e4c69e6cb4..e5e749f3e0e 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 b5e1a71bb64..5b89c0883d6 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 ebe4359047c..780582340fe 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 cebcb8b78ac..16feafa2c51 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 239347f2615..9a2271dc046 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 fb64c890109..962bb9c8b9c 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 74f6e53eae0..49b9e240915 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 9529e3bf286..23f4857f02c 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 585af2eb143..473b07f6ae8 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 efd22e92bce..8d6362e2d4c 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 5a347321f8c..c71b8d148d7 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 fdbf86571b1..27de574c08f 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 fb8932af888..0e1ba9b4790 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 9c3d361accf..020a5d51247 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 b1e74e40cba..5c0c77dd01c 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 1f4942ea141..9840eb43648 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 d20d893b3b6..0341451f814 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 f350835ade9..2cad2d61251 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 288926d2e20..ee47abab764 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/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index 83e90057270..c13a178383b 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -87,8 +87,7 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea) static int snd_tea575x_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long data) { - struct video_device *dev = video_devdata(file); - struct snd_tea575x *tea = video_get_drvdata(dev); + struct snd_tea575x *tea = video_drvdata(file); void __user *arg = (void __user *)data; switch(cmd) { @@ -175,6 +174,21 @@ static void snd_tea575x_release(struct video_device *vfd) { } +static int snd_tea575x_exclusive_open(struct inode *inode, struct file *file) +{ + struct snd_tea575x *tea = video_drvdata(file); + + return test_and_set_bit(0, &tea->in_use) ? -EBUSY : 0; +} + +static int snd_tea575x_exclusive_release(struct inode *inode, struct file *file) +{ + struct snd_tea575x *tea = video_drvdata(file); + + clear_bit(0, &tea->in_use); + return 0; +} + /* * initialize all the tea575x chips */ @@ -193,9 +207,10 @@ void snd_tea575x_init(struct snd_tea575x *tea) tea->vd.release = snd_tea575x_release; video_set_drvdata(&tea->vd, tea); tea->vd.fops = &tea->fops; + tea->in_use = 0; tea->fops.owner = tea->card->module; - tea->fops.open = video_exclusive_open; - tea->fops.release = video_exclusive_release; + tea->fops.open = snd_tea575x_exclusive_open; + tea->fops.release = snd_tea575x_exclusive_release; tea->fops.ioctl = snd_tea575x_ioctl; if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) { snd_printk(KERN_ERR "unable to register tea575x tuner\n"); diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 5769a13c1d9..660beb41f76 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 c0ce7db2a1b..63af13d901a 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 68f1260b560..77524244a84 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 4b8dfe2e3dc..3bfca7c59ba 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 ae23331e920..3d6dea3ff92 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 5f5271efdc5..b68d20edc20 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 630c90f9ee5..00000000000 --- 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 154e728f592..3e74d1a3928 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 4d198ec71e9..e49aec700a5 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 5067ee00193..5870ca21ab5 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 e9462b9944b..ddd289120aa 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 521db705d17..00000000000 --- 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 4d4b8ddc26b..91f9c15d3e3 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 de71910401e..6a85fdc53b6 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 1e1e575b1db..4fbb508a817 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 cccc16c8113..12eb98f2f93 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 ebdb3346930..0dd43414016 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 99731dc9732..38510aeb21c 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 f87c6236661..f94c1976e63 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 ca0d7ace0c7..5faecfb602d 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 854a9f74b46..58c972b2af0 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 2a1e2f5d12c..440755cc001 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 0797ca441a3..19706b0d849 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 b35be7d9a9f..96678d5d383 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 1be16c9700f..c99c6078be3 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 35f3d7b1653..49037d074c7 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 f7e8192270a..2a6cc1cfe94 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 fe03bb82053..658d55769c9 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 b432d9ae874..27a65150225 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 73d4572d136..406a431af91 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 da3d152bcad..ca35924dc3b 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 a07274ecb14..2c7503bf127 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 06ad7863dff..48a16d86583 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 3a6c6fe1ec4..4c095bc7c72 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 2efaa7f205a..dfc449a2194 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 a33384a55b0..f14a7c0b699 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 0bb9b925660..4c410820a99 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 00000000000..454fee769a3 --- /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 00000000000..3d6c5f2838a --- /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 fbef38a9604..1881cec11e7 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 d4fafb6eec6..1ca7427c4b6 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 c611514f7ff..e0ae4d4d6a5 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/ac97_codec.c b/sound/oss/ac97_codec.c index b63839e8f9b..456a1b4d783 100644 --- a/sound/oss/ac97_codec.c +++ b/sound/oss/ac97_codec.c @@ -30,7 +30,7 @@ ************************************************************************** * * History - * May 02, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk> * Removed non existant WM9700 * Added support for WM9705, WM9708, WM9709, WM9710, WM9711 * WM9712 and WM9717 diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c index 51e1fde62e8..a0274f3dac0 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 3eb782720e5..f456574a964 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 a94b9df489d..00000000000 --- 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 2bd3b52d8a3..00000000000 --- 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 a690ca57adb..6c0a770ed05 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/oss/soundcard.c b/sound/oss/soundcard.c index 7d89c081a08..61aaedae6b7 100644 --- a/sound/oss/soundcard.c +++ b/sound/oss/soundcard.c @@ -560,19 +560,18 @@ static int __init oss_init(void) sound_dmap_flag = (dmabuf > 0 ? 1 : 0); for (i = 0; i < ARRAY_SIZE(dev_list); i++) { - device_create_drvdata(sound_class, NULL, - MKDEV(SOUND_MAJOR, dev_list[i].minor), - NULL, "%s", dev_list[i].name); + device_create(sound_class, NULL, + MKDEV(SOUND_MAJOR, dev_list[i].minor), NULL, + "%s", dev_list[i].name); if (!dev_list[i].num) continue; for (j = 1; j < *dev_list[i].num; j++) - device_create_drvdata(sound_class, NULL, - MKDEV(SOUND_MAJOR, - dev_list[i].minor + (j*0x10)), - NULL, - "%s%d", dev_list[i].name, j); + device_create(sound_class, NULL, + MKDEV(SOUND_MAJOR, + dev_list[i].minor + (j*0x10)), + NULL, "%s%d", dev_list[i].name, j); } if (sound_nblocks >= 1024) diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index 99f5483abf2..41f870f8a11 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 31f52d3fc21..7003711f4fc 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 8c49a00a5e3..6704acbca8c 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 f4fbc795ee8..6e831aff1bd 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -476,7 +476,7 @@ static int patch_yamaha_ymf753(struct snd_ac97 * ac97) } /* - * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * May 2, 2003 Liam Girdwood <lrg@slimlogic.co.uk> * removed broken wolfson00 patch. * added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717. */ @@ -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 39ec55b57b1..92f3a976ef2 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 33d37b1c42f..0f819ddb3eb 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 27ce6136ab0..ba570053d4d 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 457228fb22a..085a52b8c80 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 d457a32a793..2f106306c7f 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 4aad35bba11..cf46bba563c 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 333c62de862..b070e571451 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 f9a58b4a30e..b9d2f202cf9 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 22f18f3cfbc..333007c523a 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 4ecdd635ed1..3aa8d973540 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 6abe8a3bd36..a7d89662acf 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 893ee4f1ea7..c7885117da3 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 9971b5b7735..1a74ca62c31 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 7556fd90d0e..ef9308f7c45 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 e214e567dec..fb6dc398025 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 ccc8bedb5b1..f4f0c8f5dad 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 2873cfe48c3..dd7c41b037b 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 4159e3bc186..29043301ebb 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 79938eed7e9..60228731841 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 48eb7c59911..417e25add82 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 e16dc92e82f..8dbc5c4ba42 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 52a93318957..c3736bbd819 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 e6c10077039..be0e18192de 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 3aa37e76eba..afa273330e8 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 2757c896084..db6c952e9d7 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 144fc567bec..2fef37a2a5b 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 d6ac7734609..f05e39f7aad 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 500e150b49f..90730a5ecb4 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 f3ad13d06be..a7e09ec2107 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 990c9a60a0a..ede75c6ca0f 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 97e42e11514..d61b5cbccca 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 891c7051909..227386602f9 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 91f5bff66d3..77bf2a83d99 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 c0b4bf0be7d..eaa619bd2a0 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 45088ebcce5..0e649dcdbf6 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 42bae6f7e9a..e10f027bde0 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 491a4a50f86..5ff4dbb62da 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 71dc4c8865b..7dba08f0ab8 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 c4d76d16661..8578c70c61f 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 7d379f5131f..6a47672f930 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 958cb2a65a4..d7300a1aa26 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 84fac1fbf10..4cd9a1faaec 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 1bf298d214b..20ee7599600 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 ab0c726d648..1980c6d207e 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 00000000000..9b77b3e0fa9 --- /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 00000000000..de4036e6e71 --- /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 d2e1093f8e9..6447754ae56 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 efc682888b3..60468f56240 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 59e4389c94a..0ca30894f7c 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 1c53e337ecb..9f316c1b279 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 5c9e578f7f2..7957fefda73 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 2fdf2358dbc..dfbcfa88da4 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 1e5aff5c48d..743d77922bc 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 e8003d99f0b..2b00c4afdf9 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 12272508b11..ba61575983f 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 00000000000..2eed2c8b98d --- /dev/null +++ b/sound/pci/hda/patch_nvhdmi.c @@ -0,0 +1,165 @@ +/* + * 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->pcm_type = HDA_PCM_TYPE_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 66025161bd6..e72707cb60a 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; @@ -806,6 +822,27 @@ static void alc_sku_automute(struct hda_codec *codec) spec->jack_present ? 0 : PIN_OUT); } +static void alc_mic_automute(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC]; + unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]; + unsigned int mix_nid = spec->capsrc_nids[0]; + unsigned int capsrc_idx_mic, capsrc_idx_fmic; + + capsrc_idx_mic = mic_nid - 0x18; + capsrc_idx_fmic = fmic_nid - 0x18; + present = snd_hda_codec_read(codec, mic_nid, 0, + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + 0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80)); + snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + 0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0)); + snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); +} + /* unsolicited event for HP jack sensing */ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) { @@ -813,10 +850,17 @@ static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) res >>= 28; else res >>= 26; - if (res != ALC880_HP_EVENT) - return; + if (res == ALC880_HP_EVENT) + alc_sku_automute(codec); + if (res == ALC880_MIC_EVENT) + alc_mic_automute(codec); +} + +static void alc_inithook(struct hda_codec *codec) +{ alc_sku_automute(codec); + alc_mic_automute(codec); } /* additional initialization for ALC888 variants */ @@ -855,7 +899,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 +990,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 +1005,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 +1014,7 @@ do_sku: default: break; } - + /* is laptop or Desktop and enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ @@ -1002,10 +1046,18 @@ do_sku: else return; } + if (spec->autocfg.hp_pins[0]) + snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_HP_EVENT); + + if (spec->autocfg.input_pins[AUTO_PIN_MIC] && + spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC]) + snd_hda_codec_write(codec, + spec->autocfg.input_pins[AUTO_PIN_MIC], 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + AC_USRSP_EN | ALC880_MIC_EVENT); - 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 +1348,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 +1876,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 +1921,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 +2020,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 +2102,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 +2684,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 +2721,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 +3739,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]; @@ -3787,7 +3843,7 @@ static void alc880_auto_init(struct hda_codec *codec) alc880_auto_init_extra_out(codec); alc880_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } /* @@ -4124,6 +4180,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 +4230,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 +4584,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 +4609,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 +4670,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 +4781,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 +4806,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 +4889,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 +4925,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 +5017,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 +5123,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 +5165,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 +5194,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)}, - + { } }; @@ -5134,7 +5254,7 @@ static void alc260_auto_init(struct hda_codec *codec) alc260_auto_init_multi_out(codec); alc260_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -5155,6 +5275,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 +5293,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 +5339,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 +6070,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 +6086,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 +6112,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 +6130,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 +6456,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 +6469,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 }; @@ -6527,7 +6664,7 @@ static void alc882_auto_init(struct hda_codec *codec) alc882_auto_init_analog_input(codec); alc882_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */ @@ -6554,16 +6691,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 +6858,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 +7189,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 +7411,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 +7438,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 +7532,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 +7597,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 +7704,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 +7754,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 +7803,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 +7816,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 +7846,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 +8001,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 +8038,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 +8062,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 +8190,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 +8318,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,18 +8334,21 @@ 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), SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP), SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG), + SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V), SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG), + 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 +8384,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 +8524,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 +8641,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, + }, }; @@ -8354,7 +8793,7 @@ static void alc883_auto_init(struct hda_codec *codec) alc883_auto_init_analog_input(codec); alc883_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc883(struct hda_codec *codec) @@ -8398,8 +8837,13 @@ static int patch_alc883(struct hda_codec *codec) switch (codec->vendor_id) { case 0x10ec0888: - spec->stream_name_analog = "ALC888 Analog"; - spec->stream_name_digital = "ALC888 Digital"; + if (codec->revision_id == 0x100101) { + spec->stream_name_analog = "ALC1200 Analog"; + spec->stream_name_digital = "ALC1200 Digital"; + } else { + spec->stream_name_analog = "ALC888 Analog"; + spec->stream_name_digital = "ALC888 Digital"; + } break; case 0x10ec0889: spec->stream_name_analog = "ALC889 Analog"; @@ -8452,6 +8896,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 +9284,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 +9309,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 +9341,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 +9490,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 +9756,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 +10023,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 +10078,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 +10239,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 @@ -9711,7 +10325,7 @@ static void alc262_auto_init(struct hda_codec *codec) alc262_auto_init_analog_input(codec); alc262_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } /* @@ -9729,13 +10343,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 +10382,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, 0xff7b, "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 +10537,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 +10565,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 +10660,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 +10696,7 @@ static int patch_alc262(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc262_loopbacks; #endif - + return 0; } @@ -10049,7 +10705,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 +10765,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 +10825,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 +10857,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 +10874,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 +10900,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 +11089,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 +11288,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 +11310,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 +11331,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 +11343,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 +11373,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; @@ -10728,7 +11457,7 @@ static void alc268_auto_init(struct hda_codec *codec) alc268_auto_init_mono_speaker_out(codec); alc268_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } /* @@ -10739,6 +11468,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 +11483,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 +11563,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 +11724,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 +11740,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 +11774,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 +11785,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 +11850,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 +11942,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 +11994,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 +12009,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 +12179,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 +12202,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) @@ -11380,21 +12240,27 @@ static void alc269_auto_init(struct hda_codec *codec) alc269_auto_init_hp_out(codec); alc269_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(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 +12275,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 +12366,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 +12568,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 +12711,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 +12780,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 +12842,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 +12913,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 +12950,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)}, @@ -12442,7 +13321,7 @@ static void alc861_auto_init(struct hda_codec *codec) alc861_auto_init_hp_out(codec); alc861_auto_init_analog_input(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } #ifdef CONFIG_SND_HDA_POWER_SAVE @@ -12659,7 +13538,7 @@ static int patch_alc861(struct hda_codec *codec) if (!spec->loopback.amplist) spec->loopback.amplist = alc861_loopbacks; #endif - + return 0; } @@ -12913,7 +13792,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 +13937,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 +13999,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 +14024,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 +14183,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, - }, + }, }; /* @@ -13554,7 +14433,7 @@ static void alc861vd_auto_init(struct hda_codec *codec) alc861vd_auto_init_analog_input(codec); alc861vd_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc861vd(struct hda_codec *codec) @@ -13883,13 +14762,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 +15060,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 +15163,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 +15190,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 +15276,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 +15304,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 +15413,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 +15444,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 +15629,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 +15691,68 @@ 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, 0x19a3, "ASUS G50V", 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, 0x1993, "ASUS N20", 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 +15843,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 +15902,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 +16023,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 +16043,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 +16240,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++] = @@ -14801,7 +16264,7 @@ static void alc662_auto_init(struct hda_codec *codec) alc662_auto_init_analog_input(codec); alc662_auto_init_input_src(codec); if (spec->unsol_event) - alc_sku_automute(codec); + alc_inithook(codec); } static int patch_alc662(struct hda_codec *codec) @@ -14846,6 +16309,9 @@ static int patch_alc662(struct hda_codec *codec) if (codec->vendor_id == 0x10ec0663) { spec->stream_name_analog = "ALC663 Analog"; spec->stream_name_digital = "ALC663 Digital"; + } else if (codec->vendor_id == 0x10ec0272) { + spec->stream_name_analog = "ALC272 Analog"; + spec->stream_name_digital = "ALC272 Digital"; } else { spec->stream_name_analog = "ALC662 Analog"; spec->stream_name_digital = "ALC662 Digital"; @@ -14883,6 +16349,7 @@ struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 }, { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 }, + { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, @@ -14896,10 +16363,15 @@ 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 }, + { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 }, { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200", + .patch = patch_alc883 }, { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index f3da621f25c..a2ac7205d45 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 }; @@ -249,8 +322,12 @@ static hda_nid_t stac92hd71bxx_mux_nids[2] = { 0x1a, 0x1b }; -static hda_nid_t stac92hd71bxx_dmux_nids[1] = { - 0x1c, +static hda_nid_t stac92hd71bxx_dmux_nids[2] = { + 0x1c, 0x1d, +}; + +static hda_nid_t stac92hd71bxx_smux_nids[2] = { + 0x24, 0x25, }; static hda_nid_t stac92hd71bxx_dac_nids[1] = { @@ -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,25 +843,36 @@ 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}, /* connect headphone jack to dac1 */ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, }; -#define HD_DISABLE_PORTF 3 +#define HD_DISABLE_PORTF 2 static struct hda_verb stac92hd71bxx_analog_core_init[] = { /* start of config #1 */ /* connect port 0f to audio mixer */ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ /* unmute right and left channels for node 0x0f */ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* start of config #2 */ @@ -655,10 +881,6 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = { { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, /* connect headphone jack to dac1 */ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* connect port 0d to audio mixer */ - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2}, - /* unmute dac0 input in audio mixer */ - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, /* unmute right and left channels for nodes 0x0a, 0xd */ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -690,12 +912,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 +935,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 +987,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,22 +1072,59 @@ 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), + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 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("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT), + + HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT), + + HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT), { } /* end */ }; @@ -843,11 +1134,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 +1144,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 +1153,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 +1164,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 +1177,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 +1195,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 +1255,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 +1282,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 +1630,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, + 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, + 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 +1807,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 +1830,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 +1854,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 +1941,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 */ }; @@ -1867,6 +2231,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 +2296,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 +2312,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 +2568,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 +2579,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 +2608,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 +2617,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) { @@ -2427,7 +2816,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; - hda_nid_t nid; + hda_nid_t nid = 0; int i, err; struct sigmatel_spec *spec = codec->spec; @@ -2467,6 +2856,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 +2972,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 +3002,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 +3159,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 +3295,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 +3318,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 +3355,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 +3405,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 +3431,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 +3621,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 +3648,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 +3716,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 +3838,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 +3859,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 +4051,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 +4068,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 +4101,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 +4117,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 +4154,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 +4201,146 @@ 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 struct hda_input_mux stac92hd71bxx_dmux = { + .num_items = 4, + .items = { + { "Analog Inputs", 0x00 }, + { "Mixer", 0x01 }, + { "Digital Mic 1", 0x02 }, + { "Digital Mic 2", 0x03 }, + } +}; + static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -3624,9 +4351,12 @@ 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; + memcpy(&spec->private_dimux, &stac92hd71bxx_dmux, + sizeof(stac92hd71bxx_dmux)); spec->board_config = snd_hda_check_board_config(codec, STAC_92HD71BXX_MODELS, stac92hd71bxx_models, @@ -3653,47 +4383,101 @@ 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; + spec->dinput_mux = &spec->private_dimux; /* disable VSW */ spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF]; 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->dinput_mux = &spec->private_dimux; 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_mask = 0x50; 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; spec->multiout.dac_nids = stac92hd71bxx_dac_nids; + if (spec->dinput_mux) + spec->private_dimux.num_items += + spec->num_dmics - + (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1); err = stac92xx_parse_auto_config(codec, 0x21, 0x23); if (!err) { @@ -3711,8 +4495,6 @@ again: return err; } - codec->patch_ops = stac92xx_patch_ops; - return 0; }; @@ -3854,10 +4636,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 +4686,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 +4762,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 +4804,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 +5126,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 e7e43524f8c..63e4871e5d8 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 dab31b2756a..03391da8c8c 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 868ae291b96..110d16e5273 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 0ed96c17805..d216362626d 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 ea7116c304c..f7f14df81f2 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 013fc4f0482..6fe35b81204 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 29d449d73c9..5b442383fcd 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 762fbd7a750..fdae6deba16 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 e596d777d9d..1b3f1170271 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 b4e0c16852a..c51659b9caf 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 5a158b73dca..de29be8c965 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 13e841b5548..7fc22d9d442 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 203cdc1bf8d..6bc3f91b728 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 4d2631434dc..b508bb360b9 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 a08d17c7e65..5af9e84456d 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 03a394e442f..423c1a204c0 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 048d99e25ab..c88d1eace1c 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 faf674e671a..93449e46456 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 4a44c0f20f7..5f8006b4275 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 0037be74fde..9ff3f9e3440 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 3dd0c796327..2d0dce649a6 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 785085e4835..b9a06c27939 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 f9860314613..3782b52bc0e 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 6fdda1f70b2..3ba6174c3df 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 06d13e71711..50c9f8a0508 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 dad393ae040..1ab833f843e 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 c5829d30ef8..b60f6212745 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 74a64488007..19107c6307e 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 83f135f80df..3126c4b403d 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 22f37851045..84f481d41ef 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 05eb8994c14..304da169bfd 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 c4ad65a3406..c262049961e 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 01d7b75f918..98c6a8c65d8 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 2c7e2533679..0e06c6c9fcc 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 000e6fed6e3..7143259cfe3 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 d2f043278cf..96640d9c227 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 6a359624734..e9f0706ed3e 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 4d6fbb36ab8..d723543bead 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 ab423bc8234..98762f909d6 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 a123f0e6ba2..2570907134d 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 0d3d305b0a0..cd408b86c83 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 a69b4206c69..c612b435ca2 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 3fd7f1b29b0..f9779e23fe5 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 6781be9e307..1aafe956ee2 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 31f64ee3988..5bd79d2a5a1 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 631f3a63999..7e87f398ff0 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 92d49aadf57..90d0d62bd0b 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 99bf2a65a6f..989e04abb52 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 106c48225bb..7bd33e6552a 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; diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c index baa2a723737..89f5c328acf 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 009df8dd37a..f746e15b848 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 54df8baf916..7c920f3e7fe 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 f743530add8..4dfda6674be 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 933a66d3080..d849349f2c6 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/playpaq_wm8510.c b/sound/soc/at32/playpaq_wm8510.c index 3f326219f1e..b1966e4dfcd 100644 --- a/sound/soc/at32/playpaq_wm8510.c +++ b/sound/soc/at32/playpaq_wm8510.c @@ -304,7 +304,7 @@ static const struct snd_soc_dapm_widget playpaq_dapm_widgets[] = { -static const char *intercon[][3] = { +static const struct snd_soc_dapm_route intercon[] = { /* speaker connected to SPKOUT */ {"Ext Spk", NULL, "SPKOUTP"}, {"Ext Spk", NULL, "SPKOUTN"}, @@ -312,9 +312,6 @@ static const char *intercon[][3] = { {"Mic Bias", NULL, "Int Mic"}, {"MICN", NULL, "Mic Bias"}, {"MICP", NULL, "Mic Bias"}, - - /* Terminator */ - {NULL, NULL, NULL}, }; @@ -334,11 +331,8 @@ static int playpaq_wm8510_init(struct snd_soc_codec *codec) /* * Setup audio path interconnects */ - for (i = 0; intercon[i][0] != NULL; i++) { - snd_soc_dapm_connect_input(codec, - intercon[i][0], - intercon[i][1], intercon[i][2]); - } + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + /* always connected pins */ @@ -377,6 +371,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 +400,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 +470,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/Kconfig b/sound/soc/at91/Kconfig index 905186502e0..85a883299c2 100644 --- a/sound/soc/at91/Kconfig +++ b/sound/soc/at91/Kconfig @@ -8,20 +8,3 @@ config SND_AT91_SOC config SND_AT91_SOC_SSC tristate - -config SND_AT91_SOC_ETI_B1_WM8731 - tristate "SoC Audio support for WM8731-based Endrelia ETI-B1 boards" - depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1) - select SND_AT91_SOC_SSC - select SND_SOC_WM8731 - help - Say Y if you want to add support for SoC audio on WM8731-based - Endrelia Technologies Inc ETI-B1 or ETI-C1 boards. - -config SND_AT91_SOC_ETI_SLAVE - bool "Run codec in slave Mode on Endrelia boards" - depends on SND_AT91_SOC_ETI_B1_WM8731 - default n - help - Say Y if you want to run with the AT91 SSC generating the BCLK - and LRC signals on Endrelia boards. diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile index f23da17cc32..b817f11df28 100644 --- a/sound/soc/at91/Makefile +++ b/sound/soc/at91/Makefile @@ -4,8 +4,3 @@ snd-soc-at91-ssc-objs := at91-ssc.o obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o - -# AT91 Machine Support -snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o - -obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c index 5d44515e62e..1b61cc46126 100644 --- a/sound/soc/at91/at91-ssc.c +++ b/sound/soc/at91/at91-ssc.c @@ -5,7 +5,7 @@ * Endrelia Technologies Inc. * * Based on pxa2xx Platform drivers by - * Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Liam Girdwood <lrg@slimlogic.co.uk> * * 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 @@ -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 deleted file mode 100644 index b81d6b2cfa1..00000000000 --- a/sound/soc/at91/eti_b1_wm8731.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - * eti_b1_wm8731 -- SoC audio for AT91RM9200-based Endrelia ETI_B1 board. - * - * Author: Frank Mandarino <fmandarino@endrelia.com> - * Endrelia Technologies Inc. - * Created: Mar 29, 2006 - * - * Based on corgi.c by: - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> - * Richard Purdie <richard@openedhand.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/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/clk.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> - -#include <mach/hardware.h> -#include <mach/gpio.h> - -#include "../codecs/wm8731.h" -#include "at91-pcm.h" -#include "at91-ssc.h" - -#if 0 -#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x) -#else -#define DBG(x...) -#endif - -static struct clk *pck1_clk; -static struct clk *pllb_clk; - - -static int eti_b1_startup(struct snd_pcm_substream *substream) -{ - 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; - int ret; - - /* cpu clock is the AT91 master clock sent to the SSC */ - ret = snd_soc_dai_set_sysclk(cpu_dai, AT91_SYSCLK_MCK, - 60000000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* codec system clock is supplied by PCK1, set to 12MHz */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, - 12000000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* Start PCK1 clock. */ - clk_enable(pck1_clk); - DBG("pck1 started\n"); - - return 0; -} - -static void eti_b1_shutdown(struct snd_pcm_substream *substream) -{ - /* Stop PCK1 clock. */ - clk_disable(pck1_clk); - DBG("pck1 stopped\n"); -} - -static int eti_b1_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; - int ret; - -#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE - unsigned int rate; - int cmr_div, period; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* - * The SSC clock dividers depend on the sample rate. The CMR.DIV - * field divides the system master clock MCK to drive the SSC TK - * signal which provides the codec BCLK. The TCMR.PERIOD and - * RCMR.PERIOD fields further divide the BCLK signal to drive - * the SSC TF and RF signals which provide the codec DACLRC and - * ADCLRC clocks. - * - * The dividers were determined through trial and error, where a - * CMR.DIV value is chosen such that the resulting BCLK value is - * divisible, or almost divisible, by (2 * sample rate), and then - * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. - */ - rate = params_rate(params); - - switch (rate) { - case 8000: - cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */ - period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */ - break; - case 32000: - cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */ - period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */ - break; - case 48000: - cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */ - period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */ - break; - default: - printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate); - return -EINVAL; - } - - /* set the MCK divider for BCLK */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div); - if (ret < 0) - return ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* set the BCLK divider for DACLRC */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - AT91SSC_TCMR_PERIOD, period); - } else { - /* set the BCLK divider for ADCLRC */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - AT91SSC_RCMR_PERIOD, period); - } - if (ret < 0) - return ret; - -#else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ - /* - * Codec in Master Mode. - */ - - /* set codec DAI configuration */ - ret = snd_soc_dai_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 = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - -#endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ - - return 0; -} - -static struct snd_soc_ops eti_b1_ops = { - .startup = eti_b1_startup, - .hw_params = eti_b1_hw_params, - .shutdown = eti_b1_shutdown, -}; - - -static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = { - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), -}; - -static const struct snd_soc_dapm_route intercon[] = { - - /* speaker connected to LHPOUT */ - {"Ext Spk", NULL, "LHPOUT"}, - - /* mic is connected to Mic Jack, with WM8731 Mic Bias */ - {"MICIN", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Int Mic"}, -}; - -/* - * Logic for a wm8731 as connected on a Endrelia ETI-B1 board. - */ -static int eti_b1_wm8731_init(struct snd_soc_codec *codec) -{ - DBG("eti_b1_wm8731_init() called\n"); - - /* Add specific widgets */ - snd_soc_dapm_new_controls(codec, eti_b1_dapm_widgets, - ARRAY_SIZE(eti_b1_dapm_widgets)); - - /* Set up specific audio path interconnects */ - snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon)); - - /* not connected */ - snd_soc_dapm_disable_pin(codec, "RLINEIN"); - snd_soc_dapm_disable_pin(codec, "LLINEIN"); - - /* always connected */ - snd_soc_dapm_enable_pin(codec, "Int Mic"); - snd_soc_dapm_enable_pin(codec, "Ext Spk"); - - snd_soc_dapm_sync(codec); - - return 0; -} - -static struct snd_soc_dai_link eti_b1_dai = { - .name = "WM8731", - .stream_name = "WM8731 PCM", - .cpu_dai = &at91_ssc_dai[1], - .codec_dai = &wm8731_dai, - .init = eti_b1_wm8731_init, - .ops = &eti_b1_ops, -}; - -static struct snd_soc_machine snd_soc_machine_eti_b1 = { - .name = "ETI_B1_WM8731", - .dai_link = &eti_b1_dai, - .num_links = 1, -}; - -static struct wm8731_setup_data eti_b1_wm8731_setup = { - .i2c_address = 0x1a, -}; - -static struct snd_soc_device eti_b1_snd_devdata = { - .machine = &snd_soc_machine_eti_b1, - .platform = &at91_soc_platform, - .codec_dev = &soc_codec_dev_wm8731, - .codec_data = &eti_b1_wm8731_setup, -}; - -static struct platform_device *eti_b1_snd_device; - -static int __init eti_b1_init(void) -{ - int ret; - struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; - - if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) { - DBG("SSC1 memory region is busy\n"); - return -EBUSY; - } - - ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K); - if (!ssc->base) { - DBG("SSC1 memory ioremap failed\n"); - ret = -ENOMEM; - goto fail_release_mem; - } - - ssc->pid = AT91RM9200_ID_SSC1; - - eti_b1_snd_device = platform_device_alloc("soc-audio", -1); - if (!eti_b1_snd_device) { - DBG("platform device allocation failed\n"); - ret = -ENOMEM; - goto fail_io_unmap; - } - - platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata); - eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev; - - ret = platform_device_add(eti_b1_snd_device); - if (ret) { - DBG("platform device add failed\n"); - platform_device_put(eti_b1_snd_device); - goto fail_io_unmap; - } - - at91_set_A_periph(AT91_PIN_PB6, 0); /* TF1 */ - at91_set_A_periph(AT91_PIN_PB7, 0); /* TK1 */ - at91_set_A_periph(AT91_PIN_PB8, 0); /* TD1 */ - at91_set_A_periph(AT91_PIN_PB9, 0); /* RD1 */ -/* at91_set_A_periph(AT91_PIN_PB10, 0);*/ /* RK1 */ - at91_set_A_periph(AT91_PIN_PB11, 0); /* RF1 */ - - /* - * Set PCK1 parent to PLLB and its rate to 12 Mhz. - */ - pllb_clk = clk_get(NULL, "pllb"); - pck1_clk = clk_get(NULL, "pck1"); - - clk_set_parent(pck1_clk, pllb_clk); - clk_set_rate(pck1_clk, 12000000); - - DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk)); - - /* assign the GPIO pin to PCK1 */ - at91_set_B_periph(AT91_PIN_PA24, 0); - -#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE - printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n"); -#else - printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n"); -#endif - return ret; - -fail_io_unmap: - iounmap(ssc->base); -fail_release_mem: - release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); - return ret; -} - -static void __exit eti_b1_exit(void) -{ - struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; - - clk_put(pck1_clk); - clk_put(pllb_clk); - - platform_device_unregister(eti_b1_snd_device); - - iounmap(ssc->base); - release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); -} - -module_init(eti_b1_init); -module_exit(eti_b1_exit); - -/* Module information */ -MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>"); -MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig new file mode 100644 index 00000000000..dc006206f62 --- /dev/null +++ b/sound/soc/blackfin/Kconfig @@ -0,0 +1,101 @@ +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_SOC_AD73311 + tristate "SoC AD73311 Audio support for Blackfin" + depends on SND_BF5XX_I2S + select SND_BF5XX_SOC_I2S + select SND_SOC_AD73311 + help + Say Y if you want to add support for AD73311 codec on Blackfin. + +config SND_BFIN_AD73311_SE + int "PF pin for AD73311L Chip Select" + depends on SND_BF5XX_SOC_AD73311 + default 4 + help + Enter the GPIO used to control AD73311's SE pin. Acceptable + values are 0 to 7 + +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 00000000000..97bb37a6359 --- /dev/null +++ b/sound/soc/blackfin/Makefile @@ -0,0 +1,21 @@ +# 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 +snd-ad73311-objs := bf5xx-ad73311.o + +obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o +obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o +obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c new file mode 100644 index 00000000000..25e50d2ea1e --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -0,0 +1,457 @@ +/* + * 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; + sport->tx_delay_pos = sport->tx_pos; + } 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; + struct sport_device *sport = runtime->private_data; + bf5xx_mmap_copy(pcm, runtime->period_size); + if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (sport->once == 0) { + snd_pcm_period_elapsed(pcm); + bf5xx_mmap_copy(pcm, runtime->period_size); + sport->once = 1; + } + } +#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) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + memset(runtime->dma_area, 0, runtime->buffer_size); + 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) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + 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 { + 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) { + bf5xx_mmap_copy(substream, runtime->period_size); + snd_pcm_period_elapsed(substream); + sport->tx_delay_pos = 0; + 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_delay_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; +} + +static int bf5xx_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + + pr_debug("%s enter\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sport->once = 0; + memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); + } else + memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); + + return 0; +} + +#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, + .close = bf5xx_pcm_close, + .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 00000000000..350125a0ae2 --- /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 00000000000..5e5aafb6485 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -0,0 +1,406 @@ +/* + * 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); + + 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 00000000000..3f77cc558dc --- /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 00000000000..124425d2232 --- /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-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c new file mode 100644 index 00000000000..622c9b90953 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad73311.c @@ -0,0 +1,240 @@ +/* + * File: sound/soc/blackfin/bf5xx-ad73311.c + * Author: Cliff Cai <Cliff.Cai@analog.com> + * + * Created: Thur Sep 25 2008 + * Description: Board driver for ad73311 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 <linux/delay.h> +#include <linux/gpio.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/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "../codecs/ad73311.h" +#include "bf5xx-sport.h" +#include "bf5xx-i2s-pcm.h" +#include "bf5xx-i2s.h" + +#if CONFIG_SND_BF5XX_SPORT_NUM == 0 +#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1 +#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1 +#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2 +#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16 +#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT +#else +#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1 +#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1 +#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2 +#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16 +#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT +#endif + +#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE + +static struct snd_soc_machine bf5xx_ad73311; + +static int snd_ad73311_startup(void) +{ + pr_debug("%s enter\n", __func__); + + /* Pull up SE pin on AD73311L */ + gpio_set_value(GPIO_SE, 1); + return 0; +} + +static int snd_ad73311_configure(void) +{ + unsigned short ctrl_regs[6]; + unsigned short status = 0; + int count = 0; + + /* DMCLK = MCLK = 16.384 MHz + * SCLK = DMCLK/8 = 2.048 MHz + * Sample Rate = DMCLK/2048 = 8 KHz + */ + ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \ + REGB_SCDIV(0) | REGB_DIRATE(0); + ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \ + REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ; + ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \ + REGD_IGS(2); + ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f); + ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ; + ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA; + + local_irq_disable(); + snd_ad73311_startup(); + udelay(1); + + bfin_write_SPORT_TCR1(TFSR); + bfin_write_SPORT_TCR2(0xF); + SSYNC(); + + /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to + * FIFO before enable SPORT to transfer the data + */ + for (count = 0; count < 6; count++) + bfin_write_SPORT_TX16(ctrl_regs[count]); + SSYNC(); + bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN); + SSYNC(); + + /* When TUVF is set, the data is already send out */ + while (!(status & TUVF) && count++ < 10000) { + udelay(1); + status = bfin_read_SPORT_STAT(); + SSYNC(); + } + bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN); + SSYNC(); + local_irq_enable(); + + if (count == 10000) { + printk(KERN_ERR "ad73311: failed to configure codec\n"); + return -1; + } + return 0; +} + +static int bf5xx_probe(struct platform_device *pdev) +{ + int err; + if (gpio_request(GPIO_SE, "AD73311_SE")) { + printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE); + return -EBUSY; + } + + gpio_direction_output(GPIO_SE, 0); + + err = snd_ad73311_configure(); + if (err < 0) + return -EFAULT; + + return 0; +} + +static int bf5xx_ad73311_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_ad73311_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 *cpu_dai = rtd->dai->cpu_dai; + int ret = 0; + + pr_debug("%s rate %d format %x\n", __func__, params_rate(params), + params_format(params)); + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + return 0; +} + + +static struct snd_soc_ops bf5xx_ad73311_ops = { + .startup = bf5xx_ad73311_startup, + .hw_params = bf5xx_ad73311_hw_params, +}; + +static struct snd_soc_dai_link bf5xx_ad73311_dai = { + .name = "ad73311", + .stream_name = "AD73311", + .cpu_dai = &bf5xx_i2s_dai, + .codec_dai = &ad73311_dai, + .ops = &bf5xx_ad73311_ops, +}; + +static struct snd_soc_machine bf5xx_ad73311 = { + .name = "bf5xx_ad73311", + .probe = bf5xx_probe, + .dai_link = &bf5xx_ad73311_dai, + .num_links = 1, +}; + +static struct snd_soc_device bf5xx_ad73311_snd_devdata = { + .machine = &bf5xx_ad73311, + .platform = &bf5xx_i2s_soc_platform, + .codec_dev = &soc_codec_dev_ad73311, +}; + +static struct platform_device *bf52x_ad73311_snd_device; + +static int __init bf5xx_ad73311_init(void) +{ + int ret; + + pr_debug("%s enter\n", __func__); + bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1); + if (!bf52x_ad73311_snd_device) + return -ENOMEM; + + platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); + bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev; + ret = platform_device_add(bf52x_ad73311_snd_device); + + if (ret) + platform_device_put(bf52x_ad73311_snd_device); + + return ret; +} + +static void __exit bf5xx_ad73311_exit(void) +{ + pr_debug("%s enter\n", __func__); + platform_device_unregister(bf52x_ad73311_snd_device); +} + +module_init(bf5xx_ad73311_init); +module_exit(bf5xx_ad73311_exit); + +/* Module information */ +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin"); +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 00000000000..61fccf92519 --- /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 00000000000..4d4609a97c5 --- /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 00000000000..827587f0818 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -0,0 +1,311 @@ +/* + * 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 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}, +}; + +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: + bf5xx_i2s.tcr1 |= TFSR | TCKFE; + bf5xx_i2s.rcr1 |= RFSR | RCKFE; + bf5xx_i2s.tcr2 |= TSFSE; + bf5xx_i2s.rcr2 |= RSFSE; + break; + case SND_SOC_DAIFMT_DSP_A: + bf5xx_i2s.tcr1 |= TFSR; + bf5xx_i2s.rcr1 |= RFSR; + 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; + sport_handle->wdsize = 2; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bf5xx_i2s.tcr2 |= 23; + bf5xx_i2s.rcr2 |= 23; + sport_handle->wdsize = 3; + break; + case SNDRV_PCM_FORMAT_S32_LE: + bf5xx_i2s.tcr2 |= 31; + bf5xx_i2s.rcr2 |= 31; + sport_handle->wdsize = 4; + 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:slave mode. + */ + ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, + bf5xx_i2s.rcr2, 0, 0); + if (ret) { + pr_err("SPORT is busy!\n"); + return -EBUSY; + } + + ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, + 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) +{ + 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; +} + +static void bf5xx_i2s_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + pr_debug("%s enter\n", __func__); + peripheral_free_list(&sport_req[sport_num][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, + .remove = bf5xx_i2s_remove, + .suspend = bf5xx_i2s_suspend, + .resume = bf5xx_i2s_resume, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = BF5XX_I2S_RATES, + .formats = BF5XX_I2S_FORMATS,}, + .capture = { + .channels_min = 1, + .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 00000000000..7107d1a0b06 --- /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 00000000000..3b99e484d55 --- /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 00000000000..fcadcc081f7 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-sport.h @@ -0,0 +1,194 @@ +/* + * 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; + int tx_delay_pos; + int once; +#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 00000000000..e15f67fd776 --- /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 1db04a28a53..38a0e3b620a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1,32 +1,45 @@ -config SND_SOC_AC97_CODEC - tristate - select SND_AC97_CODEC - -config SND_SOC_AK4535 - tristate - -config SND_SOC_UDA1380 - tristate +config SND_SOC_ALL_CODECS + tristate "Build all ASoC CODEC drivers" + depends on I2C + select SPI + select SPI_MASTER + select SND_SOC_AD73311 + select SND_SOC_AK4535 + select SND_SOC_CS4270 + select SND_SOC_SSM2602 + select SND_SOC_TLV320AIC23 + 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. -config SND_SOC_WM8510 - tristate + If unsure select "N". -config SND_SOC_WM8731 - tristate -config SND_SOC_WM8750 - tristate - -config SND_SOC_WM8753 +config SND_SOC_AC97_CODEC tristate + select SND_AC97_CODEC -config SND_SOC_WM8990 +config SND_SOC_AD1980 tristate -config SND_SOC_WM9712 +config SND_SOC_AD73311 tristate -config SND_SOC_WM9713 +config SND_SOC_AK4535 tristate # Cirrus Logic CS4270 Codec @@ -47,6 +60,53 @@ config SND_SOC_CS4270_VD33_ERRATA bool depends on SND_SOC_CS4270 +config SND_SOC_SSM2602 + tristate + +config SND_SOC_TLV320AIC23 + tristate + depends on I2C + +config SND_SOC_TLV320AIC26 + tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE + depends on 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 + +config SND_SOC_WM8750 + tristate + +config SND_SOC_WM8753 + tristate + +config SND_SOC_WM8900 + tristate + +config SND_SOC_WM8903 + tristate + +config SND_SOC_WM8971 + tristate + +config SND_SOC_WM8990 + tristate + +config SND_SOC_WM9712 + tristate + +config SND_SOC_WM9713 + tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index d7b97abcf72..90f0a585fc7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,25 +1,43 @@ snd-soc-ac97-objs := ac97.o +snd-soc-ad1980-objs := ad1980.o +snd-soc-ad73311-objs := ad73311.o snd-soc-ak4535-objs := ak4535.o +snd-soc-cs4270-objs := cs4270.o +snd-soc-ssm2602-objs := ssm2602.o +snd-soc-tlv320aic23-objs := tlv320aic23.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_AD73311) += snd-soc-ad73311.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_TLV320AIC23) += snd-soc-tlv320aic23.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/ac97.c b/sound/soc/codecs/ac97.c index 61fd96ca7bc..bd1ebdc6c86 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -2,8 +2,7 @@ * ac97.c -- ALSA Soc AC97 codec support * * Copyright 2005 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * 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 diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c new file mode 100644 index 00000000000..1397b8e06c0 --- /dev/null +++ b/sound/soc/codecs/ad1980.c @@ -0,0 +1,308 @@ +/* + * 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/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 00000000000..db6c8500d66 --- /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/ad73311.c b/sound/soc/codecs/ad73311.c new file mode 100644 index 00000000000..37af8607b00 --- /dev/null +++ b/sound/soc/codecs/ad73311.c @@ -0,0 +1,107 @@ +/* + * ad73311.c -- ALSA Soc AD73311 codec support + * + * Copyright: Analog Device Inc. + * Author: 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. + * + * Revision history + * 25th Sep 2008 Initial 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 "ad73311.h" + +struct snd_soc_dai ad73311_dai = { + .name = "AD73311", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}; +EXPORT_SYMBOL_GPL(ad73311_dai); + +static int ad73311_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + mutex_init(&codec->mutex); + codec->name = "AD73311"; + codec->owner = THIS_MODULE; + codec->dai = &ad73311_dai; + codec->num_dai = 1; + socdev->codec = codec; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "ad73311: failed to create pcms\n"); + goto pcm_err; + } + + ret = snd_soc_register_card(socdev); + if (ret < 0) { + printk(KERN_ERR "ad73311: failed to register card\n"); + goto register_err; + } + + return ret; + +register_err: + snd_soc_free_pcms(socdev); +pcm_err: + kfree(socdev->codec); + socdev->codec = NULL; + return ret; +} + +static int ad73311_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_free_pcms(socdev); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ad73311 = { + .probe = ad73311_soc_probe, + .remove = ad73311_soc_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311); + +MODULE_DESCRIPTION("ASoC ad73311 driver"); +MODULE_AUTHOR("Cliff Cai "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h new file mode 100644 index 00000000000..507ce0c30ed --- /dev/null +++ b/sound/soc/codecs/ad73311.h @@ -0,0 +1,90 @@ +/* + * File: sound/soc/codec/ad73311.h + * Based on: + * Author: Cliff Cai <cliff.cai@analog.com> + * + * Created: Thur Sep 25, 2008 + * Description: definitions for AD73311 registers + * + * + * Modified: + * Copyright 2006 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 __AD73311_H__ +#define __AD73311_H__ + +#define AD_CONTROL 0x8000 +#define AD_DATA 0x0000 +#define AD_READ 0x4000 +#define AD_WRITE 0x0000 + +/* Control register A */ +#define CTRL_REG_A (0 << 8) + +#define REGA_MODE_PRO 0x00 +#define REGA_MODE_DATA 0x01 +#define REGA_MODE_MIXED 0x03 +#define REGA_DLB 0x04 +#define REGA_SLB 0x08 +#define REGA_DEVC(x) ((x & 0x7) << 4) +#define REGA_RESET 0x80 + +/* Control register B */ +#define CTRL_REG_B (1 << 8) + +#define REGB_DIRATE(x) (x & 0x3) +#define REGB_SCDIV(x) ((x & 0x3) << 2) +#define REGB_MCDIV(x) ((x & 0x7) << 4) +#define REGB_CEE (1 << 7) + +/* Control register C */ +#define CTRL_REG_C (2 << 8) + +#define REGC_PUDEV (1 << 0) +#define REGC_PUADC (1 << 3) +#define REGC_PUDAC (1 << 4) +#define REGC_PUREF (1 << 5) +#define REGC_REFUSE (1 << 6) + +/* Control register D */ +#define CTRL_REG_D (3 << 8) + +#define REGD_IGS(x) (x & 0x7) +#define REGD_RMOD (1 << 3) +#define REGD_OGS(x) ((x & 0x7) << 4) +#define REGD_MUTE (x << 7) + +/* Control register E */ +#define CTRL_REG_E (4 << 8) + +#define REGE_DA(x) (x & 0x1f) +#define REGE_IBYP (1 << 5) + +/* Control register F */ +#define CTRL_REG_F (5 << 8) + +#define REGF_SEEN (1 << 5) +#define REGF_INV (1 << 6) +#define REGF_ALB (1 << 7) + +extern struct snd_soc_dai ad73311_dai; +extern struct snd_soc_codec_device soc_codec_dev_ad73311; +#endif diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 7da9f467b7b..2a89b5888e1 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -28,7 +28,6 @@ #include "ak4535.h" -#define AUDIO_NAME "ak4535" #define AK4535_VERSION "0.3" struct snd_soc_codec_device soc_codec_dev_ak4535; @@ -535,87 +534,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 +621,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 +643,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 +672,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 e9fe30e2c05..c7a58703ea3 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/ssm2602.c b/sound/soc/codecs/ssm2602.c new file mode 100644 index 00000000000..44ef0dacd56 --- /dev/null +++ b/sound/soc/codecs/ssm2602.c @@ -0,0 +1,775 @@ +/* + * 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 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 00000000000..f344e6d76e3 --- /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/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c new file mode 100644 index 00000000000..44308dac9e1 --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.c @@ -0,0 +1,714 @@ +/* + * ALSA SoC TLV320AIC23 codec driver + * + * Author: Arun KS, <arunks@mistralsolutions.com> + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + * 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. + * + * Notes: + * The AIC23 is a driver for a low power stereo audio + * codec tlv320aic23 + * + * The machine layer should disable unsupported inputs/outputs by + * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc. + */ + +#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/tlv.h> +#include <sound/initval.h> + +#include "tlv320aic23.h" + +#define AIC23_VERSION "0.1" + +struct tlv320aic23_srate_reg_info { + u32 sample_rate; + u8 control; /* SR3, SR2, SR1, SR0 and BOSR */ + u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */ +}; + +/* + * AIC23 register cache + */ +static const u16 tlv320aic23_reg[] = { + 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */ + 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */ + 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */ +}; + +/* + * read tlv320aic23 register cache + */ +static inline unsigned int tlv320aic23_read_reg_cache(struct snd_soc_codec + *codec, unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg >= ARRAY_SIZE(tlv320aic23_reg)) + return -1; + return cache[reg]; +} + +/* + * write tlv320aic23 register cache + */ +static inline void tlv320aic23_write_reg_cache(struct snd_soc_codec *codec, + u8 reg, u16 value) +{ + u16 *cache = codec->reg_cache; + if (reg >= ARRAY_SIZE(tlv320aic23_reg)) + return; + cache[reg] = value; +} + +/* + * write to the tlv320aic23 register space + */ +static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + + u8 data[2]; + + /* TLV320AIC23 has 7 bit address and 9 bits of data + * so we need to switch one data bit into reg and rest + * of data into val + */ + + if ((reg < 0 || reg > 9) && (reg != 15)) { + printk(KERN_WARNING "%s Invalid register R%d\n", __func__, reg); + return -1; + } + + data[0] = (reg << 1) | (value >> 8 & 0x01); + data[1] = value & 0xff; + + tlv320aic23_write_reg_cache(codec, reg, value); + + if (codec->hw_write(codec->control_data, data, 2) == 2) + return 0; + + printk(KERN_ERR "%s cannot write %03x to register R%d\n", __func__, + value, reg); + + return -EIO; +} + +static const char *rec_src_text[] = { "Line", "Mic" }; +static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"}; + +static const struct soc_enum rec_src_enum = + SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); + +static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls = +SOC_DAPM_ENUM("Input Select", rec_src_enum); + +static const struct soc_enum tlv320aic23_rec_src = + SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); +static const struct soc_enum tlv320aic23_deemph = + SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text); + +static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0); +static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0); + +static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u16 val, reg; + + val = (ucontrol->value.integer.value[0] & 0x07); + + /* linear conversion to userspace + * 000 = -6db + * 001 = -9db + * 010 = -12db + * 011 = -18db (Min) + * 100 = 0db (Max) + */ + val = (val >= 4) ? 4 : (3 - val); + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (~0x1C0); + tlv320aic23_write(codec, TLV320AIC23_ANLG, reg | (val << 6)); + + return 0; +} + +static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u16 val; + + val = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (0x1C0); + val = val >> 6; + val = (val >= 4) ? 4 : (3 - val); + ucontrol->value.integer.value[0] = val; + return 0; + +} + +#define SOC_TLV320AIC23_SINGLE_TLV(xname, reg, 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, .get = snd_soc_tlv320aic23_get_volsw,\ + .put = snd_soc_tlv320aic23_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + +static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL, + TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv), + SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1), + SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL, + TLV320AIC23_RINVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL, + TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv), + SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1), + SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0), + SOC_TLV320AIC23_SINGLE_TLV("Sidetone Volume", TLV320AIC23_ANLG, + 6, 4, 0, sidetone_vol_tlv), + SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph), +}; + +/* add non dapm controls */ +static int tlv320aic23_add_controls(struct snd_soc_codec *codec) +{ + + int err, i; + + for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&tlv320aic23_snd_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + + return 0; + +} + +/* PGA Mixer controls for Line and Mic switch */ +static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0), + SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1), + SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, + &tlv320aic23_rec_src_mux_controls), + SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1, + &tlv320aic23_output_mixer_controls[0], + ARRAY_SIZE(tlv320aic23_output_mixer_controls)), + SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_INPUT("MICIN"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* Output Mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "Playback Switch", "DAC"}, + {"Output Mixer", "Mic Sidetone Switch", "Mic Input"}, + + /* Outputs */ + {"RHPOUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, + + /* Inputs */ + {"Line Input", "NULL", "LLINEIN"}, + {"Line Input", "NULL", "RLINEIN"}, + + {"Mic Input", "NULL", "MICIN"}, + + /* input mux */ + {"Capture Source", "Line", "Line Input"}, + {"Capture Source", "Mic", "Mic Input"}, + {"ADC", NULL, "Capture Source"}, + +}; + +/* tlv320aic23 related */ +static const struct tlv320aic23_srate_reg_info srate_reg_info[] = { + {4000, 0x06, 1}, /* 4000 */ + {8000, 0x06, 0}, /* 8000 */ + {16000, 0x0C, 1}, /* 16000 */ + {22050, 0x11, 1}, /* 22050 */ + {24000, 0x00, 1}, /* 24000 */ + {32000, 0x0C, 0}, /* 32000 */ + {44100, 0x11, 0}, /* 44100 */ + {48000, 0x00, 0}, /* 48000 */ + {88200, 0x1F, 0}, /* 88200 */ + {96000, 0x0E, 0}, /* 96000 */ +}; + +static int tlv320aic23_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + ARRAY_SIZE(tlv320aic23_dapm_widgets)); + + /* set up audio path interconnects */ + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static int tlv320aic23_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 iface_reg, data; + u8 count = 0; + + iface_reg = + tlv320aic23_read_reg_cache(codec, + TLV320AIC23_DIGT_FMT) & ~(0x03 << 2); + + /* Search for the right sample rate */ + /* Verify what happens if the rate is not supported + * now it goes to 96Khz */ + while ((srate_reg_info[count].sample_rate != params_rate(params)) && + (count < ARRAY_SIZE(srate_reg_info))) { + count++; + } + + data = (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) | + (srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) | + TLV320AIC23_USB_CLK_ON; + + tlv320aic23_write(codec, TLV320AIC23_SRATE, data); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface_reg |= (0x01 << 2); + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface_reg |= (0x02 << 2); + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface_reg |= (0x03 << 2); + break; + } + tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg); + + return 0; +} + +static int tlv320aic23_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 */ + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0001); + + return 0; +} + +static void tlv320aic23_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) { + udelay(50); + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); + } +} + +static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 reg; + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT); + if (mute) + reg |= TLV320AIC23_DACM_MUTE; + + else + reg &= ~TLV320AIC23_DACM_MUTE; + + tlv320aic23_write(codec, TLV320AIC23_DIGT, reg); + + return 0; +} + +static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface_reg; + + iface_reg = + tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT_FMT) & (~0x03); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface_reg |= TLV320AIC23_MS_MASTER; + 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_reg |= TLV320AIC23_FOR_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg |= TLV320AIC23_FOR_DSP; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg |= TLV320AIC23_FOR_LJUST; + break; + default: + return -EINVAL; + + } + + tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg); + + return 0; +} + +static int tlv320aic23_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; + + switch (freq) { + case 12000000: + return 0; + } + return -EINVAL; +} + +static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_PWR) & 0xff7f; + + switch (level) { + case SND_SOC_BIAS_ON: + /* vref/mid, osc on, dac unmute */ + tlv320aic23_write(codec, TLV320AIC23_PWR, reg); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* everything off except vref/vmid, */ + tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040); + break; + case SND_SOC_BIAS_OFF: + /* everything off, dac mute, inactive */ + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); + tlv320aic23_write(codec, TLV320AIC23_PWR, 0xffff); + break; + } + codec->bias_level = level; + return 0; +} + +#define AIC23_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_dai tlv320aic23_dai = { + .name = "tlv320aic23", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC23_RATES, + .formats = AIC23_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AIC23_RATES, + .formats = AIC23_FORMATS,}, + .ops = { + .prepare = tlv320aic23_pcm_prepare, + .hw_params = tlv320aic23_hw_params, + .shutdown = tlv320aic23_shutdown, + }, + .dai_ops = { + .digital_mute = tlv320aic23_mute, + .set_fmt = tlv320aic23_set_dai_fmt, + .set_sysclk = tlv320aic23_set_dai_sysclk, + } +}; +EXPORT_SYMBOL_GPL(tlv320aic23_dai); + +static int tlv320aic23_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; + + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); + tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int tlv320aic23_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + int i; + u16 reg; + + /* Sync reg_cache with the hardware */ + for (reg = 0; reg < ARRAY_SIZE(tlv320aic23_reg); i++) { + u16 val = tlv320aic23_read_reg_cache(codec, reg); + tlv320aic23_write(codec, reg, val); + } + + tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + tlv320aic23_set_bias_level(codec, codec->suspend_bias_level); + + return 0; +} + +/* + * initialise the AIC23 driver + * register the mixer and dsp interfaces with the kernel + */ +static int tlv320aic23_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + int ret = 0; + u16 reg; + + codec->name = "tlv320aic23"; + codec->owner = THIS_MODULE; + codec->read = tlv320aic23_read_reg_cache; + codec->write = tlv320aic23_write; + codec->set_bias_level = tlv320aic23_set_bias_level; + codec->dai = &tlv320aic23_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(tlv320aic23_reg); + codec->reg_cache = + kmemdup(tlv320aic23_reg, sizeof(tlv320aic23_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) + return -ENOMEM; + + /* Reset codec */ + tlv320aic23_write(codec, TLV320AIC23_RESET, 0); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "tlv320aic23: failed to create pcms\n"); + goto pcm_err; + } + + /* power on device */ + tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + tlv320aic23_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K); + + /* Unmute input */ + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_LINVOL); + tlv320aic23_write(codec, TLV320AIC23_LINVOL, + (reg & (~TLV320AIC23_LIM_MUTED)) | + (TLV320AIC23_LRS_ENABLED)); + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_RINVOL); + tlv320aic23_write(codec, TLV320AIC23_RINVOL, + (reg & (~TLV320AIC23_LIM_MUTED)) | + TLV320AIC23_LRS_ENABLED); + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG); + tlv320aic23_write(codec, TLV320AIC23_ANLG, + (reg) & (~TLV320AIC23_BYPASS_ON) & + (~TLV320AIC23_MICM_MUTED)); + + /* Default output volume */ + tlv320aic23_write(codec, TLV320AIC23_LCHNVOL, + TLV320AIC23_DEFAULT_OUT_VOL & + TLV320AIC23_OUT_VOL_MASK); + tlv320aic23_write(codec, TLV320AIC23_RCHNVOL, + TLV320AIC23_DEFAULT_OUT_VOL & + TLV320AIC23_OUT_VOL_MASK); + + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1); + + tlv320aic23_add_controls(codec); + tlv320aic23_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) { + printk(KERN_ERR "tlv320aic23: 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 *tlv320aic23_socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +/* + * If the i2c layer weren't so broken, we could pass this kind of data + * around + */ +static int tlv320aic23_codec_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct snd_soc_device *socdev = tlv320aic23_socdev; + struct snd_soc_codec *codec = socdev->codec; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EINVAL; + + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = tlv320aic23_init(socdev); + if (ret < 0) { + printk(KERN_ERR "tlv320aic23: failed to initialise AIC23\n"); + goto err; + } + return ret; + +err: + kfree(codec); + kfree(i2c); + return ret; +} +static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c) +{ + put_device(&i2c->dev); + return 0; +} + +static const struct i2c_device_id tlv320aic23_id[] = { + {"tlv320aic23", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); + +static struct i2c_driver tlv320aic23_i2c_driver = { + .driver = { + .name = "tlv320aic23", + }, + .probe = tlv320aic23_codec_probe, + .remove = __exit_p(tlv320aic23_i2c_remove), + .id_table = tlv320aic23_id, +}; + +#endif + +static int tlv320aic23_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION); + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + socdev->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + tlv320aic23_socdev = socdev; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + codec->hw_write = (hw_write_t) i2c_master_send; + codec->hw_read = NULL; + ret = i2c_add_driver(&tlv320aic23_i2c_driver); + if (ret != 0) + printk(KERN_ERR "can't add i2c driver"); +#endif + return ret; +} + +static int tlv320aic23_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) + tlv320aic23_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(&tlv320aic23_i2c_driver); +#endif + kfree(codec->reg_cache); + kfree(codec); + + return 0; +} +struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = { + .probe = tlv320aic23_probe, + .remove = tlv320aic23_remove, + .suspend = tlv320aic23_suspend, + .resume = tlv320aic23_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23); + +MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver"); +MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h new file mode 100644 index 00000000000..79d1faf8e57 --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.h @@ -0,0 +1,122 @@ +/* + * ALSA SoC TLV320AIC23 codec driver + * + * Author: Arun KS, <arunks@mistralsolutions.com> + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd + * + * 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 _TLV320AIC23_H +#define _TLV320AIC23_H + +/* Codec TLV320AIC23 */ +#define TLV320AIC23_LINVOL 0x00 +#define TLV320AIC23_RINVOL 0x01 +#define TLV320AIC23_LCHNVOL 0x02 +#define TLV320AIC23_RCHNVOL 0x03 +#define TLV320AIC23_ANLG 0x04 +#define TLV320AIC23_DIGT 0x05 +#define TLV320AIC23_PWR 0x06 +#define TLV320AIC23_DIGT_FMT 0x07 +#define TLV320AIC23_SRATE 0x08 +#define TLV320AIC23_ACTIVE 0x09 +#define TLV320AIC23_RESET 0x0F + +/* Left (right) line input volume control register */ +#define TLV320AIC23_LRS_ENABLED 0x0100 +#define TLV320AIC23_LIM_MUTED 0x0080 +#define TLV320AIC23_LIV_DEFAULT 0x0017 +#define TLV320AIC23_LIV_MAX 0x001f +#define TLV320AIC23_LIV_MIN 0x0000 + +/* Left (right) channel headphone volume control register */ +#define TLV320AIC23_LZC_ON 0x0080 +#define TLV320AIC23_LHV_DEFAULT 0x0079 +#define TLV320AIC23_LHV_MAX 0x007f +#define TLV320AIC23_LHV_MIN 0x0000 + +/* Analog audio path control register */ +#define TLV320AIC23_STA_REG(x) ((x)<<6) +#define TLV320AIC23_STE_ENABLED 0x0020 +#define TLV320AIC23_DAC_SELECTED 0x0010 +#define TLV320AIC23_BYPASS_ON 0x0008 +#define TLV320AIC23_INSEL_MIC 0x0004 +#define TLV320AIC23_MICM_MUTED 0x0002 +#define TLV320AIC23_MICB_20DB 0x0001 + +/* Digital audio path control register */ +#define TLV320AIC23_DACM_MUTE 0x0008 +#define TLV320AIC23_DEEMP_32K 0x0002 +#define TLV320AIC23_DEEMP_44K 0x0004 +#define TLV320AIC23_DEEMP_48K 0x0006 +#define TLV320AIC23_ADCHP_ON 0x0001 + +/* Power control down register */ +#define TLV320AIC23_DEVICE_PWR_OFF 0x0080 +#define TLV320AIC23_CLK_OFF 0x0040 +#define TLV320AIC23_OSC_OFF 0x0020 +#define TLV320AIC23_OUT_OFF 0x0010 +#define TLV320AIC23_DAC_OFF 0x0008 +#define TLV320AIC23_ADC_OFF 0x0004 +#define TLV320AIC23_MIC_OFF 0x0002 +#define TLV320AIC23_LINE_OFF 0x0001 + +/* Digital audio interface register */ +#define TLV320AIC23_MS_MASTER 0x0040 +#define TLV320AIC23_LRSWAP_ON 0x0020 +#define TLV320AIC23_LRP_ON 0x0010 +#define TLV320AIC23_IWL_16 0x0000 +#define TLV320AIC23_IWL_20 0x0004 +#define TLV320AIC23_IWL_24 0x0008 +#define TLV320AIC23_IWL_32 0x000C +#define TLV320AIC23_FOR_I2S 0x0002 +#define TLV320AIC23_FOR_DSP 0x0003 +#define TLV320AIC23_FOR_LJUST 0x0001 + +/* Sample rate control register */ +#define TLV320AIC23_CLKOUT_HALF 0x0080 +#define TLV320AIC23_CLKIN_HALF 0x0040 +#define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */ +#define TLV320AIC23_USB_CLK_ON 0x0001 +#define TLV320AIC23_SR_MASK 0xf +#define TLV320AIC23_CLKOUT_SHIFT 7 +#define TLV320AIC23_CLKIN_SHIFT 6 +#define TLV320AIC23_SR_SHIFT 2 +#define TLV320AIC23_BOSR_SHIFT 1 + +/* Digital interface register */ +#define TLV320AIC23_ACT_ON 0x0001 + +/* + * AUDIO related MACROS + */ + +#define TLV320AIC23_DEFAULT_OUT_VOL 0x70 +#define TLV320AIC23_DEFAULT_IN_VOLUME 0x10 + +#define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN +#define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX +#define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \ + TLV320AIC23_OUT_VOL_MIN) +#define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX + +#define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN +#define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX +#define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \ + TLV320AIC23_IN_VOL_MIN) +#define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX + +#define TLV320AIC23_SIDETONE_MASK 0x1c0 +#define TLV320AIC23_SIDETONE_0 0x100 +#define TLV320AIC23_SIDETONE_6 0x000 +#define TLV320AIC23_SIDETONE_9 0x040 +#define TLV320AIC23_SIDETONE_12 0x080 +#define TLV320AIC23_SIDETONE_18 0x0c0 + +extern struct snd_soc_dai tlv320aic23_dai; +extern struct snd_soc_codec_device soc_codec_dev_tlv320aic23; + +#endif /* _TLV320AIC23_H */ diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c new file mode 100644 index 00000000000..bed8a9e63dd --- /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 00000000000..786ba16c945 --- /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 5f9abb19943..05336ed7e49 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 @@ -48,7 +48,6 @@ #include "tlv320aic3x.h" -#define AUDIO_NAME "aic3x" #define AIC3X_VERSION "0.2" /* codec private data */ @@ -991,7 +990,7 @@ EXPORT_SYMBOL_GPL(aic3x_headset_detected); SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) struct snd_soc_dai aic3x_dai = { - .name = "aic3x", + .name = "tlv320aic3x", .playback = { .stream_name = "Playback", .channels_min = 1, @@ -1055,7 +1054,7 @@ static int aic3x_init(struct snd_soc_device *socdev) struct aic3x_setup_data *setup = socdev->codec_data; int reg, ret = 0; - codec->name = "aic3x"; + codec->name = "tlv320aic3x"; codec->owner = THIS_MODULE; codec->read = aic3x_read_reg_cache; codec->write = aic3x_write; @@ -1172,71 +1171,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 +1211,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 +1221,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 +1293,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 +1320,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 d76c079b86e..00a195aa02e 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 807318fbdc8..a69ee72a7af 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -36,7 +36,6 @@ #include "uda1380.h" #define UDA1380_VERSION "0.6" -#define AUDIO_NAME "uda1380" /* * uda1380 register cache @@ -701,87 +700,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 +787,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 +802,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 +828,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 50c603e2c9f..c55c17a52a1 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 3d998e6a997..d8ca2da8d63 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -3,7 +3,7 @@ * * Copyright 2006 Wolfson Microelectronics PLC. * - * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * 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 @@ -18,6 +18,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> @@ -27,7 +28,6 @@ #include "wm8510.h" -#define AUDIO_NAME "wm8510" #define WM8510_VERSION "0.6" struct snd_soc_codec_device soc_codec_dev_wm8510; @@ -55,6 +55,9 @@ static const u16 wm8510_reg[WM8510_CACHEREGNUM] = { 0x0001, }; +#define WM8510_POWER1_BIASEN 0x08 +#define WM8510_POWER1_BUFIOEN 0x10 + /* * read wm8510 register cache */ @@ -199,7 +202,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), }; @@ -224,9 +227,9 @@ SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0), SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0), -SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0, - &wm8510_micpga_controls[0], - ARRAY_SIZE(wm8510_micpga_controls)), +SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0, + &wm8510_micpga_controls[0], + ARRAY_SIZE(wm8510_micpga_controls)), SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, &wm8510_boost_controls[0], ARRAY_SIZE(wm8510_boost_controls)), @@ -526,23 +529,35 @@ static int wm8510_mute(struct snd_soc_dai *dai, int mute) static int wm8510_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3; switch (level) { case SND_SOC_BIAS_ON: - wm8510_write(codec, WM8510_POWER1, 0x1ff); - wm8510_write(codec, WM8510_POWER2, 0x1ff); - wm8510_write(codec, WM8510_POWER3, 0x1ff); - break; case SND_SOC_BIAS_PREPARE: + power1 |= 0x1; /* VMID 50k */ + wm8510_write(codec, WM8510_POWER1, power1); + break; + case SND_SOC_BIAS_STANDBY: + power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; + + if (codec->bias_level == SND_SOC_BIAS_OFF) { + /* Initial cap charge at VMID 5k */ + wm8510_write(codec, WM8510_POWER1, power1 | 0x3); + mdelay(100); + } + + power1 |= 0x2; /* VMID 500k */ + wm8510_write(codec, WM8510_POWER1, power1); break; + case SND_SOC_BIAS_OFF: - /* everything off, dac mute, inactive */ - wm8510_write(codec, WM8510_POWER1, 0x0); - wm8510_write(codec, WM8510_POWER2, 0x0); - wm8510_write(codec, WM8510_POWER3, 0x0); + wm8510_write(codec, WM8510_POWER1, 0); + wm8510_write(codec, WM8510_POWER2, 0); + wm8510_write(codec, WM8510_POWER3, 0); break; } + codec->bias_level = level; return 0; } @@ -640,6 +655,7 @@ static int wm8510_init(struct snd_soc_device *socdev) } /* power on device */ + codec->bias_level = SND_SOC_BIAS_OFF; wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm8510_add_controls(codec); wm8510_add_widgets(codec); @@ -665,90 +681,144 @@ 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 +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8510_spi_probe(struct spi_device *spi) +{ + struct snd_soc_device *socdev = wm8510_socdev; + struct snd_soc_codec *codec = socdev->codec; + int ret; + + codec->control_data = spi; + + ret = wm8510_init(socdev); + if (ret < 0) + dev_err(&spi->dev, "failed to initialise WM8510\n"); + + return ret; +} + +static int __devexit wm8510_spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver wm8510_spi_driver = { + .driver = { + .name = "wm8510", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm8510_spi_probe, + .remove = __devexit_p(wm8510_spi_remove), +}; + +static int wm8510_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 wm8510_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -771,14 +841,17 @@ 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); + ret = wm8510_add_i2c_device(pdev, setup); + } +#endif +#if defined(CONFIG_SPI_MASTER) + if (setup->spi) { + codec->hw_write = (hw_write_t)wm8510_spi_write; + ret = spi_register_driver(&wm8510_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) @@ -798,8 +871,12 @@ 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 +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8510_spi_driver); +#endif kfree(codec); return 0; diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h index f5d2e42eb3f..bdefcf5c69f 100644 --- a/sound/soc/codecs/wm8510.h +++ b/sound/soc/codecs/wm8510.h @@ -94,6 +94,8 @@ #define WM8510_MCLKDIV_12 (7 << 5) struct wm8510_setup_data { + int spi; + 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 00000000000..627ebfb4209 --- /dev/null +++ b/sound/soc/codecs/wm8580.c @@ -0,0 +1,1053 @@ +/* + * 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/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 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 00000000000..589ddaba21d --- /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 9402fcaf04f..7f8a7e36b33 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> @@ -28,7 +29,6 @@ #include "wm8731.h" -#define AUDIO_NAME "wm8731" #define WM8731_VERSION "0.13" struct snd_soc_codec_device soc_codec_dev_wm8731; @@ -570,88 +570,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 +736,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 +772,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 99f2e3c60e3..95190e9c0c1 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 dd1f55404b2..9b7296ee5b0 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> @@ -28,7 +29,6 @@ #include "wm8750.h" -#define AUDIO_NAME "WM8750" #define WM8750_VERSION "0.12" /* codec private data */ @@ -841,88 +841,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 +990,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 +1011,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 +1066,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 8ef30e628b2..1dc100e19cf 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 e873414840c..d426eaa2218 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -2,8 +2,7 @@ * wm8753.c -- WM8753 ALSA Soc Audio driver * * Copyright 2003 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * 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 @@ -40,6 +39,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> @@ -51,7 +51,6 @@ #include "wm8753.h" -#define AUDIO_NAME "wm8753" #define WM8753_VERSION "0.16" static int caps_charge = 2000; @@ -1637,86 +1636,145 @@ 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 + +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8753_spi_probe(struct spi_device *spi) +{ + struct snd_soc_device *socdev = wm8753_socdev; + struct snd_soc_codec *codec = socdev->codec; + int ret; + + codec->control_data = spi; + + ret = wm8753_init(socdev); + if (ret < 0) + dev_err(&spi->dev, "failed to initialise WM8753\n"); + + return ret; +} + +static int __devexit wm8753_spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver wm8753_spi_driver = { + .driver = { + .name = "wm8753", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm8753_spi_probe, + .remove = __devexit_p(wm8753_spi_remove), }; + +static int wm8753_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 wm8753_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -1748,14 +1806,17 @@ 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); + ret = wm8753_add_i2c_device(pdev, setup); + } +#endif +#if defined(CONFIG_SPI_MASTER) + if (setup->spi) { + codec->hw_write = (hw_write_t)wm8753_spi_write; + ret = spi_register_driver(&wm8753_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) { @@ -1796,8 +1857,12 @@ 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 +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8753_spi_driver); +#endif kfree(codec->private_data); kfree(codec); diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h index 44f5f1ff0cc..f55704ce931 100644 --- a/sound/soc/codecs/wm8753.h +++ b/sound/soc/codecs/wm8753.h @@ -2,8 +2,7 @@ * wm8753.h -- audio driver for WM8753 * * Copyright 2003 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * 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 @@ -79,6 +78,8 @@ #define WM8753_ADCTL2 0x3f struct wm8753_setup_data { + int spi; + 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 00000000000..3b326c9b558 --- /dev/null +++ b/sound/soc/codecs/wm8900.c @@ -0,0 +1,1541 @@ +/* + * 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/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 00000000000..ba450d99e90 --- /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 00000000000..ce40d787760 --- /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, 1), +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, 1), +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 00000000000..cec622f2f66 --- /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 00000000000..f41a578ddd4 --- /dev/null +++ b/sound/soc/codecs/wm8971.c @@ -0,0 +1,941 @@ +/* + * 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 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 00000000000..ef4f08f9f34 --- /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 dd995ef448b..572d22b0880 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -30,7 +30,6 @@ #include "wm8990.h" -#define AUDIO_NAME "wm8990" #define WM8990_VERSION "0.2" /* codec private data */ @@ -1477,81 +1476,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 +1564,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 +1586,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 +1613,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 0a08325d544..0e192f3b078 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/wm9712.c b/sound/soc/codecs/wm9712.c index 2f1c91b1d55..ffb471e420e 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -2,8 +2,7 @@ * wm9712.c -- ALSA Soc WM9712 codec support * * Copyright 2006 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * 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 diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 38d1fe0971f..aba402b3c99 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -2,8 +2,7 @@ * wm9713.c -- ALSA Soc WM9713 codec support * * Copyright 2006 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * 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 @@ -419,8 +418,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 +586,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 +956,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 65fdbd81a37..9e6062cd6b5 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 5ebf1ff71c4..abb5fedb0b1 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 c5b091807ee..241648ce887 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 6a5e56a782b..76feaa65737 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 8d6a45e75a6..62cb4eb07e3 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 3368ace6097..bba9546ba5f 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 62f680a4a77..035da4afec3 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 00000000000..86923299bc1 --- /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 4bdc9d8fc90..94f89debde1 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 00000000000..0382fdac51c --- /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/Kconfig b/sound/soc/omap/Kconfig index aea27e70043..8b7766b998d 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -13,3 +13,11 @@ config SND_OMAP_SOC_N810 select SND_SOC_TLV320AIC3X help Say Y if you want to add support for SoC audio on Nokia N810. + +config SND_OMAP_SOC_OSK5912 + tristate "SoC Audio support for omap osk5912" + depends on SND_OMAP_SOC && MACH_OMAP_OSK + select SND_OMAP_SOC_MCBSP + select SND_SOC_TLV320AIC23 + help + Say Y if you want to add support for SoC audio on osk5912. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index d8d8d58075e..e09d1f297f6 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -7,5 +7,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o # OMAP Machine Support snd-soc-n810-objs := n810.o +snd-soc-osk5912-objs := osk5912.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o +obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 87d0ed01f65..fae3ad36e0b 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -247,9 +247,9 @@ static int n810_aic33_init(struct snd_soc_codec *codec) int i, err; /* Not connected */ - snd_soc_dapm_disable_pin(codec, "MONO_LOUT"); - snd_soc_dapm_disable_pin(codec, "HPLCOM"); - snd_soc_dapm_disable_pin(codec, "HPRCOM"); + snd_soc_dapm_nc_pin(codec, "MONO_LOUT"); + snd_soc_dapm_nc_pin(codec, "HPLCOM"); + snd_soc_dapm_nc_pin(codec, "HPRCOM"); /* Add N810 specific controls */ for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) { @@ -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/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 35310e16d7f..0a063a98a66 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -59,12 +59,7 @@ static struct omap_mcbsp_data mcbsp_data[NUM_LINKS]; * Stream DMA parameters. DMA request line and port address are set runtime * since they are different between OMAP1 and later OMAPs */ -static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2] = { -{ - { .name = "I2S PCM Stereo out", }, - { .name = "I2S PCM Stereo in", }, -}, -}; +static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2]; #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) static const int omap1_dma_reqs[][2] = { @@ -84,11 +79,22 @@ static const unsigned long omap1_mcbsp_port[][2] = { static const int omap1_dma_reqs[][2] = {}; static const unsigned long omap1_mcbsp_port[][2] = {}; #endif -#if defined(CONFIG_ARCH_OMAP2420) -static const int omap2420_dma_reqs[][2] = { + +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) +static const int omap24xx_dma_reqs[][2] = { { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) + { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, + { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, + { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, +#endif }; +#else +static const int omap24xx_dma_reqs[][2] = {}; +#endif + +#if defined(CONFIG_ARCH_OMAP2420) static const unsigned long omap2420_mcbsp_port[][2] = { { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, @@ -96,10 +102,43 @@ static const unsigned long omap2420_mcbsp_port[][2] = { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, }; #else -static const int omap2420_dma_reqs[][2] = {}; static const unsigned long omap2420_mcbsp_port[][2] = {}; #endif +#if defined(CONFIG_ARCH_OMAP2430) +static const unsigned long omap2430_mcbsp_port[][2] = { + { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, + OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, + OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, + OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, + OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, + OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, +}; +#else +static const unsigned long omap2430_mcbsp_port[][2] = {}; +#endif + +#if defined(CONFIG_ARCH_OMAP34XX) +static const unsigned long omap34xx_mcbsp_port[][2] = { + { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, +}; +#else +static const unsigned long omap34xx_mcbsp_port[][2] = {}; +#endif + static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -167,14 +206,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, dma = omap1_dma_reqs[bus_id][substream->stream]; port = omap1_mcbsp_port[bus_id][substream->stream]; } else if (cpu_is_omap2420()) { - dma = omap2420_dma_reqs[bus_id][substream->stream]; + dma = omap24xx_dma_reqs[bus_id][substream->stream]; port = omap2420_mcbsp_port[bus_id][substream->stream]; + } else if (cpu_is_omap2430()) { + dma = omap24xx_dma_reqs[bus_id][substream->stream]; + port = omap2430_mcbsp_port[bus_id][substream->stream]; + } else if (cpu_is_omap343x()) { + dma = omap24xx_dma_reqs[bus_id][substream->stream]; + port = omap34xx_mcbsp_port[bus_id][substream->stream]; } else { - /* - * TODO: Add support for 2430 and 3430 - */ return -ENODEV; } + omap_mcbsp_dai_dma_params[id][substream->stream].name = + substream->stream ? "Audio Capture" : "Audio Playback"; omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; @@ -245,6 +289,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, regs->rcr2 |= RDATDLY(1); regs->xcr2 |= XDATDLY(1); break; + case SND_SOC_DAIFMT_DSP_A: + /* 0-bit data delay */ + regs->rcr2 |= RDATDLY(0); + regs->xcr2 |= XDATDLY(0); + break; default: /* Unsupported data format */ return -EINVAL; @@ -310,7 +359,7 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, int clk_id) { int sel_bit; - u16 reg; + u16 reg, reg_devconf1 = OMAP243X_CONTROL_DEVCONF1; if (cpu_class_is_omap1()) { /* OMAP1's can use only external source clock */ @@ -320,6 +369,12 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, return 0; } + if (cpu_is_omap2420() && mcbsp_data->bus_id > 1) + return -EINVAL; + + if (cpu_is_omap343x()) + reg_devconf1 = OMAP343X_CONTROL_DEVCONF1; + switch (mcbsp_data->bus_id) { case 0: reg = OMAP2_CONTROL_DEVCONF0; @@ -329,20 +384,26 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, reg = OMAP2_CONTROL_DEVCONF0; sel_bit = 6; break; - /* TODO: Support for ports 3 - 5 in OMAP2430 and OMAP34xx */ + case 2: + reg = reg_devconf1; + sel_bit = 0; + break; + case 3: + reg = reg_devconf1; + sel_bit = 2; + break; + case 4: + reg = reg_devconf1; + sel_bit = 4; + break; default: return -EINVAL; } - if (cpu_class_is_omap2()) { - if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) { - omap_ctrl_writel(omap_ctrl_readl(reg) & - ~(1 << sel_bit), reg); - } else { - omap_ctrl_writel(omap_ctrl_readl(reg) | - (1 << sel_bit), reg); - } - } + if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) + omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg); + else + omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg); return 0; } @@ -376,37 +437,49 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, return err; } -struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS] = { -{ - .name = "omap-mcbsp-dai", - .id = 0, - .type = SND_SOC_DAI_I2S, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = OMAP_MCBSP_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = OMAP_MCBSP_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = { - .startup = omap_mcbsp_dai_startup, - .shutdown = omap_mcbsp_dai_shutdown, - .trigger = omap_mcbsp_dai_trigger, - .hw_params = omap_mcbsp_dai_hw_params, - }, - .dai_ops = { - .set_fmt = omap_mcbsp_dai_set_dai_fmt, - .set_clkdiv = omap_mcbsp_dai_set_clkdiv, - .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, - }, - .private_data = &mcbsp_data[0].bus_id, -}, +#define OMAP_MCBSP_DAI_BUILDER(link_id) \ +{ \ + .name = "omap-mcbsp-dai-(link_id)", \ + .id = (link_id), \ + .type = SND_SOC_DAI_I2S, \ + .playback = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = OMAP_MCBSP_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .capture = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = OMAP_MCBSP_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = { \ + .startup = omap_mcbsp_dai_startup, \ + .shutdown = omap_mcbsp_dai_shutdown, \ + .trigger = omap_mcbsp_dai_trigger, \ + .hw_params = omap_mcbsp_dai_hw_params, \ + }, \ + .dai_ops = { \ + .set_fmt = omap_mcbsp_dai_set_dai_fmt, \ + .set_clkdiv = omap_mcbsp_dai_set_clkdiv, \ + .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \ + }, \ + .private_data = &mcbsp_data[(link_id)].bus_id, \ +} + +struct snd_soc_dai omap_mcbsp_dai[] = { + OMAP_MCBSP_DAI_BUILDER(0), + OMAP_MCBSP_DAI_BUILDER(1), +#if NUM_LINKS >= 3 + OMAP_MCBSP_DAI_BUILDER(2), +#endif +#if NUM_LINKS == 5 + OMAP_MCBSP_DAI_BUILDER(3), + OMAP_MCBSP_DAI_BUILDER(4), +#endif }; + EXPORT_SYMBOL_GPL(omap_mcbsp_dai); MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>"); diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index ed8afb55067..df7ad13ba73 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h @@ -38,11 +38,17 @@ enum omap_mcbsp_div { OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ }; -/* - * REVISIT: Preparation for the ASoC v2. Let the number of available links to - * be same than number of McBSP ports found in OMAP(s) we are compiling for. - */ -#define NUM_LINKS 1 +#if defined(CONFIG_ARCH_OMAP2420) +#define NUM_LINKS 2 +#endif +#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) +#undef NUM_LINKS +#define NUM_LINKS 3 +#endif +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +#undef NUM_LINKS +#define NUM_LINKS 5 +#endif extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 690bfeaec4a..e9084fdd208 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -97,7 +97,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, prtd->dma_data = dma_data; err = omap_request_dma(dma_data->dma_req, dma_data->name, omap_pcm_dma_irq, substream, &prtd->dma_ch); - if (!cpu_is_omap1510()) { + if (!err & !cpu_is_omap1510()) { /* * Link channel with itself so DMA doesn't need any * reprogramming while looping the buffer @@ -147,12 +147,14 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC; dma_params.src_start = runtime->dma_addr; dma_params.dst_start = dma_data->port_addr; + dma_params.dst_port = OMAP_DMA_PORT_MPUI; } else { dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC; dma_params.src_start = dma_data->port_addr; dma_params.dst_start = runtime->dma_addr; + dma_params.src_port = OMAP_DMA_PORT_MPUI; } /* * Set DMA transfer frame size equal to ALSA period size and frame diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c new file mode 100644 index 00000000000..0fe73379689 --- /dev/null +++ b/sound/soc/omap/osk5912.c @@ -0,0 +1,232 @@ +/* + * osk5912.c -- SoC audio for OSK 5912 + * + * Copyright (C) 2008 Mistral Solutions + * + * Contact: Arun KS <arunks@mistralsolutions.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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <linux/gpio.h> +#include <mach/mcbsp.h> + +#include "omap-mcbsp.h" +#include "omap-pcm.h" +#include "../codecs/tlv320aic23.h" + +#define CODEC_CLOCK 12000000 + +static struct clk *tlv320aic23_mclk; + +static int osk_startup(struct snd_pcm_substream *substream) +{ + return clk_enable(tlv320aic23_mclk); +} + +static void osk_shutdown(struct snd_pcm_substream *substream) +{ + clk_disable(tlv320aic23_mclk); +} + +static int osk_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; + int err; + + /* Set codec DAI configuration */ + err = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBM_CFM); + if (err < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return err; + } + + /* Set cpu DAI configuration */ + err = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBM_CFM); + if (err < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return err; + } + + /* Set the codec system clock for DAC and ADC */ + err = + snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); + + if (err < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return err; + } + + return err; +} + +static struct snd_soc_ops osk_ops = { + .startup = osk_startup, + .hw_params = osk_hw_params, + .shutdown = osk_shutdown, +}; + +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Headphone Jack", NULL, "LHPOUT"}, + {"Headphone Jack", NULL, "RHPOUT"}, + + {"LLINEIN", NULL, "Line In"}, + {"RLINEIN", NULL, "Line In"}, + + {"MICIN", NULL, "Mic Jack"}, +}; + +static int osk_tlv320aic23_init(struct snd_soc_codec *codec) +{ + + /* Add osk5912 specific widgets */ + snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + ARRAY_SIZE(tlv320aic23_dapm_widgets)); + + /* Set up osk5912 specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(codec, "Line In"); + snd_soc_dapm_enable_pin(codec, "Mic Jack"); + + snd_soc_dapm_sync(codec); + + return 0; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link osk_dai = { + .name = "TLV320AIC23", + .stream_name = "AIC23", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &tlv320aic23_dai, + .init = osk_tlv320aic23_init, + .ops = &osk_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_machine snd_soc_machine_osk = { + .name = "OSK5912", + .dai_link = &osk_dai, + .num_links = 1, +}; + +/* Audio subsystem */ +static struct snd_soc_device osk_snd_devdata = { + .machine = &snd_soc_machine_osk, + .platform = &omap_soc_platform, + .codec_dev = &soc_codec_dev_tlv320aic23, +}; + +static struct platform_device *osk_snd_device; + +static int __init osk_soc_init(void) +{ + int err; + u32 curRate; + struct device *dev; + + if (!(machine_is_omap_osk())) + return -ENODEV; + + osk_snd_device = platform_device_alloc("soc-audio", -1); + if (!osk_snd_device) + return -ENOMEM; + + platform_set_drvdata(osk_snd_device, &osk_snd_devdata); + osk_snd_devdata.dev = &osk_snd_device->dev; + *(unsigned int *)osk_dai.cpu_dai->private_data = 0; /* McBSP1 */ + err = platform_device_add(osk_snd_device); + if (err) + goto err1; + + dev = &osk_snd_device->dev; + + tlv320aic23_mclk = clk_get(dev, "mclk"); + if (IS_ERR(tlv320aic23_mclk)) { + printk(KERN_ERR "Could not get mclk clock\n"); + return -ENODEV; + } + + if (clk_get_usecount(tlv320aic23_mclk) > 0) { + /* MCLK is already in use */ + printk(KERN_WARNING + "MCLK in use at %d Hz. We change it to %d Hz\n", + (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK); + } + + /* + * Configure 12 MHz output on MCLK. + */ + curRate = (uint) clk_get_rate(tlv320aic23_mclk); + if (curRate != CODEC_CLOCK) { + if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) { + printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n"); + err = -ECANCELED; + goto err1; + } + } + + printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n", + (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK, + clk_get_usecount(tlv320aic23_mclk)); + + return 0; +err1: + clk_put(tlv320aic23_mclk); + platform_device_del(osk_snd_device); + platform_device_put(osk_snd_device); + + return err; + +} + +static void __exit osk_soc_exit(void) +{ + platform_device_unregister(osk_snd_device); +} + +module_init(osk_soc_init); +module_exit(osk_soc_exit); + +MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); +MODULE_DESCRIPTION("ALSA SoC OSK 5912"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 9212c37a33b..f8c1cdd940a 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 0a53f72077f..2718eaf7895 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -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; } @@ -289,8 +281,8 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_disable_pin(codec, "LLINEIN"); - snd_soc_dapm_disable_pin(codec, "RLINEIN"); + snd_soc_dapm_nc_pin(codec, "LLINEIN"); + snd_soc_dapm_nc_pin(codec, "RLINEIN"); /* Add corgi specific controls */ for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { @@ -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/em-x270.c b/sound/soc/pxa/em-x270.c index d9c3f7b28be..e6ff6929ab4 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -9,7 +9,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index a4697f7e292..4d9930c5278 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -242,8 +242,8 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_disable_pin(codec, "LLINEIN"); - snd_soc_dapm_disable_pin(codec, "RLINEIN"); + snd_soc_dapm_nc_pin(codec, "LLINEIN"); + snd_soc_dapm_nc_pin(codec, "RLINEIN"); snd_soc_dapm_enable_pin(codec, "MICIN"); /* Add poodle specific controls */ @@ -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 d94a495bd6b..a7a3a9c5c6f 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 c796b188277..e758034db5c 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -3,7 +3,7 @@ * * Copyright 2005 Wolfson Microelectronics PLC. * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * lrg@slimlogic.co.uk * * 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 @@ -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); } @@ -356,6 +405,6 @@ module_init(pxa2xx_i2s_init); module_exit(pxa2xx_i2s_exit); /* Module information */ -MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c index 4345f387fe4..afcd892cd2f 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 54c9c755e50..60c3b20aeeb 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 37cb768fc93..d307b6757e9 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -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; } @@ -291,13 +281,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) int i, err; /* NC codec pins */ - snd_soc_dapm_disable_pin(codec, "RINPUT1"); - snd_soc_dapm_disable_pin(codec, "LINPUT2"); - snd_soc_dapm_disable_pin(codec, "RINPUT2"); - snd_soc_dapm_disable_pin(codec, "LINPUT3"); - snd_soc_dapm_disable_pin(codec, "RINPUT3"); - snd_soc_dapm_disable_pin(codec, "OUT3"); - snd_soc_dapm_disable_pin(codec, "MONO1"); + snd_soc_dapm_nc_pin(codec, "RINPUT1"); + snd_soc_dapm_nc_pin(codec, "LINPUT2"); + snd_soc_dapm_nc_pin(codec, "RINPUT2"); + snd_soc_dapm_nc_pin(codec, "LINPUT3"); + snd_soc_dapm_nc_pin(codec, "RINPUT3"); + snd_soc_dapm_nc_pin(codec, "OUT3"); + snd_soc_dapm_nc_pin(codec, "MONO1"); /* Add spitz specific controls */ for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { @@ -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/pxa/tosa.c b/sound/soc/pxa/tosa.c index 2baaa750f12..afefe41b8c4 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -190,8 +190,8 @@ static int tosa_ac97_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_disable_pin(codec, "OUT3"); - snd_soc_dapm_disable_pin(codec, "MONOOUT"); + snd_soc_dapm_nc_pin(codec, "OUT3"); + snd_soc_dapm_nc_pin(codec, "MONOOUT"); /* add tosa specific controls */ for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 8089f8ee05c..87ddfefcc2f 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> @@ -510,21 +511,20 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec) DBG("Entered %s\n", __func__); /* set up NC codec pins */ - snd_soc_dapm_disable_pin(codec, "LOUT2"); - snd_soc_dapm_disable_pin(codec, "ROUT2"); - snd_soc_dapm_disable_pin(codec, "OUT3"); - snd_soc_dapm_disable_pin(codec, "OUT4"); - snd_soc_dapm_disable_pin(codec, "LINE1"); - snd_soc_dapm_disable_pin(codec, "LINE2"); - - - /* set endpoints to default mode */ - set_scenario_endpoints(codec, NEO_AUDIO_OFF); + snd_soc_dapm_nc_pin(codec, "LOUT2"); + snd_soc_dapm_nc_pin(codec, "ROUT2"); + snd_soc_dapm_nc_pin(codec, "OUT3"); + snd_soc_dapm_nc_pin(codec, "OUT4"); + snd_soc_dapm_nc_pin(codec, "LINE1"); + snd_soc_dapm_nc_pin(codec, "LINE2"); /* Add neo1973 specific widgets */ snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, ARRAY_SIZE(wm8753_dapm_widgets)); + /* set endpoints to default mode */ + set_scenario_endpoints(codec, NEO_AUDIO_OFF); + /* add neo1973 specific controls */ for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) { err = snd_ctl_add(codec->card, @@ -586,6 +586,7 @@ static struct snd_soc_machine neo1973 = { }; static struct wm8753_setup_data neo1973_wm8753_setup = { + .i2c_bus = 0, .i2c_address = 0x1a, }; @@ -596,54 +597,24 @@ 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; - } + i2c = client; 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); + i2c = NULL; + + return 0; } static u8 lm4857_state; @@ -681,24 +652,22 @@ 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; @@ -709,6 +678,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 +692,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); + if (ret != 0) - printk(KERN_ERR "can't add i2c driver"); + platform_device_unregister(neo1973_snd_device); return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 83f1190293a..462e635dfc7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4,8 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * with code, comments and ideas from :- * Richard Purdie <richard@openedhand.com> * @@ -340,6 +339,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 +975,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 +1321,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 +1347,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 +1377,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 +1411,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 +1459,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 +1489,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 +1528,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 +1568,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 +1596,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 +1633,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 +1678,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 +1703,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 +1730,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; @@ -1842,7 +1885,7 @@ module_init(snd_soc_init); module_exit(snd_soc_exit); /* Module information */ -MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_DESCRIPTION("ALSA SoC Core"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:soc-audio"); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f9d100bc847..7351db9606e 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2,8 +2,7 @@ * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management * * Copyright 2005 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * 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 @@ -38,6 +37,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 +67,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 +106,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 +127,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 +170,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 +252,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 +692,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 +708,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 +819,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) && 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 +1125,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 +1170,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 +1244,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 +1274,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 +1290,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, @@ -1487,6 +1483,26 @@ int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin) EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); /** + * snd_soc_dapm_nc_pin - permanently disable pin. + * @codec: SoC codec + * @pin: pin name + * + * Marks the specified pin as being not connected, disabling it along + * any parent or child widgets. At present this is identical to + * snd_soc_dapm_disable_pin() but in future it will be extended to do + * additional things such as disabling controls which only affect + * paths through the pin. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin) +{ + return snd_soc_dapm_set_pin(codec, pin, 0); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); + +/** * snd_soc_dapm_get_pin_status - get audio pin status * @codec: audio codec * @pin: audio signal pin endpoint (or start point) @@ -1524,6 +1540,6 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev) EXPORT_SYMBOL_GPL(snd_soc_dapm_free); /* Module information */ -MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); MODULE_LICENSE("GPL"); diff --git a/sound/sound_core.c b/sound/sound_core.c index 1b04259a432..faef87a9bc3 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 @@ -171,9 +220,8 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati else sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP); - device_create_drvdata(sound_class, dev, - MKDEV(SOUND_MAJOR, s->unit_minor), - NULL, s->name+6); + device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor), + NULL, s->name+6); return r; fail: @@ -523,31 +571,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 0c63e0585b1..f87933e4881 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -1,6 +1,6 @@ /* * Driver for AMD7930 sound chips found on Sparcs. - * Copyright (C) 2002 David S. Miller <davem@redhat.com> + * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net> * * Based entirely upon drivers/sbus/audio/amd7930.c which is: * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu) @@ -35,6 +35,8 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> @@ -44,7 +46,6 @@ #include <asm/io.h> #include <asm/irq.h> -#include <asm/sbus.h> #include <asm/prom.h> static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -335,8 +336,8 @@ struct snd_amd7930 { int pgain; int mgain; + struct of_device *op; unsigned int irq; - unsigned int regs_size; struct snd_amd7930 *next; }; @@ -765,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); @@ -788,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; @@ -809,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; @@ -838,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; @@ -904,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); @@ -920,13 +906,16 @@ static int __devinit snd_amd7930_mixer(struct snd_amd7930 *amd) static int snd_amd7930_free(struct snd_amd7930 *amd) { + struct of_device *op = amd->op; + amd7930_idle(amd); if (amd->irq) free_irq(amd->irq, amd); if (amd->regs) - sbus_iounmap(amd->regs, amd->regs_size); + of_iounmap(&op->resource[0], amd->regs, + resource_size(&op->resource[0])); kfree(amd); @@ -945,13 +934,12 @@ static struct snd_device_ops snd_amd7930_dev_ops = { }; static int __devinit snd_amd7930_create(struct snd_card *card, - struct resource *rp, - unsigned int reg_size, + struct of_device *op, int irq, int dev, struct snd_amd7930 **ramd) { - unsigned long flags; struct snd_amd7930 *amd; + unsigned long flags; int err; *ramd = NULL; @@ -961,9 +949,10 @@ static int __devinit snd_amd7930_create(struct snd_card *card, spin_lock_init(&amd->lock); amd->card = card; - amd->regs_size = reg_size; + amd->op = op; - amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930"); + amd->regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), "amd7930"); if (!amd->regs) { snd_printk("amd7930-%d: Unable to map chip registers.\n", dev); return -EIO; @@ -1012,12 +1001,15 @@ static int __devinit snd_amd7930_create(struct snd_card *card, return 0; } -static int __devinit amd7930_attach_common(struct resource *rp, int irq) +static int __devinit amd7930_sbus_probe(struct of_device *op, const struct of_device_id *match) { + struct resource *rp = &op->resource[0]; static int dev_num; struct snd_card *card; struct snd_amd7930 *amd; - int err; + int err, irq; + + irq = op->irqs[0]; if (dev_num >= SNDRV_CARDS) return -ENODEV; @@ -1038,8 +1030,7 @@ static int __devinit amd7930_attach_common(struct resource *rp, int irq) (unsigned long long)rp->start, irq); - if ((err = snd_amd7930_create(card, rp, - (rp->end - rp->start) + 1, + if ((err = snd_amd7930_create(card, op, irq, dev_num, &amd)) < 0) goto out_err; @@ -1064,43 +1055,7 @@ out_err: return err; } -static int __devinit amd7930_obio_attach(struct device_node *dp) -{ - const struct linux_prom_registers *regs; - const struct linux_prom_irqs *irqp; - struct resource res, *rp; - int len; - - irqp = of_get_property(dp, "intr", &len); - if (!irqp) { - snd_printk("%s: Firmware node lacks IRQ property.\n", - dp->full_name); - return -ENODEV; - } - - regs = of_get_property(dp, "reg", &len); - if (!regs) { - snd_printk("%s: Firmware node lacks register property.\n", - dp->full_name); - return -ENODEV; - } - - rp = &res; - rp->start = regs->phys_addr; - rp->end = rp->start + regs->reg_size - 1; - rp->flags = IORESOURCE_IO | (regs->which_io & 0xff); - - return amd7930_attach_common(rp, irqp->pri); -} - -static int __devinit amd7930_sbus_probe(struct of_device *dev, const struct of_device_id *match) -{ - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - - return amd7930_attach_common(&sdev->resource[0], sdev->irqs[0]); -} - -static struct of_device_id amd7930_match[] = { +static const struct of_device_id amd7930_match[] = { { .name = "audio", }, @@ -1115,20 +1070,7 @@ static struct of_platform_driver amd7930_sbus_driver = { static int __init amd7930_init(void) { - struct device_node *dp; - - /* Try to find the sun4c "audio" node first. */ - dp = of_find_node_by_path("/"); - dp = dp->child; - while (dp) { - if (!strcmp(dp->name, "audio")) - amd7930_obio_attach(dp); - - dp = dp->sibling; - } - - /* Probe each SBUS for amd7930 chips. */ - return of_register_driver(&amd7930_sbus_driver, &sbus_bus_type); + return of_register_driver(&amd7930_sbus_driver, &of_bus_type); } static void __exit amd7930_exit(void) diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 1c4797be72e..d44bf98e965 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -1,6 +1,6 @@ /* * Driver for CS4231 sound chips found on Sparcs. - * Copyright (C) 2002 David S. Miller <davem@redhat.com> + * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net> * * Based entirely upon drivers/sbus/audio/cs4231.c which is: * Copyright (C) 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu) @@ -17,7 +17,8 @@ #include <linux/moduleparam.h> #include <linux/irq.h> #include <linux/io.h> - +#include <linux/of.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> @@ -29,13 +30,12 @@ #ifdef CONFIG_SBUS #define SBUS_SUPPORT -#include <asm/sbus.h> #endif #if defined(CONFIG_PCI) && defined(CONFIG_SPARC64) #define EBUS_SUPPORT #include <linux/pci.h> -#include <asm/ebus.h> +#include <asm/ebus_dma.h> #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -70,8 +70,6 @@ struct cs4231_dma_control { int (*request)(struct cs4231_dma_control *dma_cont, dma_addr_t bus_addr, size_t len); unsigned int (*address)(struct cs4231_dma_control *dma_cont); - void (*preallocate)(struct snd_cs4231 *chip, - struct snd_pcm *pcm); #ifdef EBUS_SUPPORT struct ebus_dma_info ebus_info; #endif @@ -114,21 +112,12 @@ struct snd_cs4231 { struct mutex mce_mutex; /* mutex for mce register */ struct mutex open_mutex; /* mutex for ALSA open/close */ - union { -#ifdef SBUS_SUPPORT - struct sbus_dev *sdev; -#endif -#ifdef EBUS_SUPPORT - struct pci_dev *pdev; -#endif - } dev_u; + struct of_device *op; unsigned int irq[2]; unsigned int regs_size; struct snd_cs4231 *next; }; -static struct snd_cs4231 *cs4231_list; - /* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for * now.... -DaveM */ @@ -267,27 +256,19 @@ static unsigned char snd_cs4231_original_image[32] = static u8 __cs4231_readb(struct snd_cs4231 *cp, void __iomem *reg_addr) { -#ifdef EBUS_SUPPORT if (cp->flags & CS4231_FLAG_EBUS) return readb(reg_addr); else -#endif -#ifdef SBUS_SUPPORT return sbus_readb(reg_addr); -#endif } static void __cs4231_writeb(struct snd_cs4231 *cp, u8 val, void __iomem *reg_addr) { -#ifdef EBUS_SUPPORT if (cp->flags & CS4231_FLAG_EBUS) return writeb(val, reg_addr); else -#endif -#ifdef SBUS_SUPPORT return sbus_writeb(val, reg_addr); -#endif } /* @@ -1258,7 +1239,9 @@ static int __init snd_cs4231_pcm(struct snd_card *card) pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; strcpy(pcm->name, "CS4231"); - chip->p_dma.preallocate(chip, pcm); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + &chip->op->dev, + 64 * 1024, 128 * 1024); chip->pcm = pcm; @@ -1560,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); @@ -1626,8 +1610,7 @@ static int __init cs4231_attach_finish(struct snd_card *card) if (err < 0) goto out_err; - chip->next = cs4231_list; - cs4231_list = chip; + dev_set_drvdata(&chip->op->dev, chip); dev++; return 0; @@ -1782,24 +1765,19 @@ static unsigned int sbus_dma_addr(struct cs4231_dma_control *dma_cont) return sbus_readl(base->regs + base->dir + APCVA); } -static void sbus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS, - snd_dma_sbus_data(chip->dev_u.sdev), - 64 * 1024, 128 * 1024); -} - /* * Init and exit routines */ static int snd_cs4231_sbus_free(struct snd_cs4231 *chip) { + struct of_device *op = chip->op; + if (chip->irq[0]) free_irq(chip->irq[0], chip); if (chip->port) - sbus_iounmap(chip->port, chip->regs_size); + of_iounmap(&op->resource[0], chip->port, chip->regs_size); return 0; } @@ -1816,7 +1794,7 @@ static struct snd_device_ops snd_cs4231_sbus_dev_ops = { }; static int __init snd_cs4231_sbus_create(struct snd_card *card, - struct sbus_dev *sdev, + struct of_device *op, int dev) { struct snd_cs4231 *chip = card->private_data; @@ -1827,13 +1805,13 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card, spin_lock_init(&chip->p_dma.sbus_info.lock); mutex_init(&chip->mce_mutex); mutex_init(&chip->open_mutex); - chip->dev_u.sdev = sdev; - chip->regs_size = sdev->reg_addrs[0].reg_size; + chip->op = op; + chip->regs_size = resource_size(&op->resource[0]); memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); - chip->port = sbus_ioremap(&sdev->resource[0], 0, - chip->regs_size, "cs4231"); + chip->port = of_ioremap(&op->resource[0], 0, + chip->regs_size, "cs4231"); if (!chip->port) { snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev); return -EIO; @@ -1848,22 +1826,20 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card, chip->p_dma.enable = sbus_dma_enable; chip->p_dma.request = sbus_dma_request; chip->p_dma.address = sbus_dma_addr; - chip->p_dma.preallocate = sbus_dma_preallocate; chip->c_dma.prepare = sbus_dma_prepare; chip->c_dma.enable = sbus_dma_enable; chip->c_dma.request = sbus_dma_request; chip->c_dma.address = sbus_dma_addr; - chip->c_dma.preallocate = sbus_dma_preallocate; - if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt, + if (request_irq(op->irqs[0], snd_cs4231_sbus_interrupt, IRQF_SHARED, "cs4231", chip)) { snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n", - dev, sdev->irqs[0]); + dev, op->irqs[0]); snd_cs4231_sbus_free(chip); return -EBUSY; } - chip->irq[0] = sdev->irqs[0]; + chip->irq[0] = op->irqs[0]; if (snd_cs4231_probe(chip) < 0) { snd_cs4231_sbus_free(chip); @@ -1880,9 +1856,9 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card, return 0; } -static int __init cs4231_sbus_attach(struct sbus_dev *sdev) +static int __devinit cs4231_sbus_probe(struct of_device *op, const struct of_device_id *match) { - struct resource *rp = &sdev->resource[0]; + struct resource *rp = &op->resource[0]; struct snd_card *card; int err; @@ -1894,9 +1870,9 @@ static int __init cs4231_sbus_attach(struct sbus_dev *sdev) card->shortname, rp->flags & 0xffL, (unsigned long long)rp->start, - sdev->irqs[0]); + op->irqs[0]); - err = snd_cs4231_sbus_create(card, sdev, dev); + err = snd_cs4231_sbus_create(card, op, dev); if (err < 0) { snd_card_free(card); return err; @@ -1949,30 +1925,25 @@ static unsigned int _ebus_dma_addr(struct cs4231_dma_control *dma_cont) return ebus_dma_addr(&dma_cont->ebus_info); } -static void _ebus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->dev_u.pdev), - 64*1024, 128*1024); -} - /* * Init and exit routines */ static int snd_cs4231_ebus_free(struct snd_cs4231 *chip) { + struct of_device *op = chip->op; + if (chip->c_dma.ebus_info.regs) { ebus_dma_unregister(&chip->c_dma.ebus_info); - iounmap(chip->c_dma.ebus_info.regs); + of_iounmap(&op->resource[2], chip->c_dma.ebus_info.regs, 0x10); } if (chip->p_dma.ebus_info.regs) { ebus_dma_unregister(&chip->p_dma.ebus_info); - iounmap(chip->p_dma.ebus_info.regs); + of_iounmap(&op->resource[1], chip->p_dma.ebus_info.regs, 0x10); } if (chip->port) - iounmap(chip->port); + of_iounmap(&op->resource[0], chip->port, 0x10); return 0; } @@ -1989,7 +1960,7 @@ static struct snd_device_ops snd_cs4231_ebus_dev_ops = { }; static int __init snd_cs4231_ebus_create(struct snd_card *card, - struct linux_ebus_device *edev, + struct of_device *op, int dev) { struct snd_cs4231 *chip = card->private_data; @@ -2001,35 +1972,35 @@ static int __init snd_cs4231_ebus_create(struct snd_card *card, mutex_init(&chip->mce_mutex); mutex_init(&chip->open_mutex); chip->flags |= CS4231_FLAG_EBUS; - chip->dev_u.pdev = edev->bus->self; + chip->op = op; memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)"); chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback; chip->c_dma.ebus_info.client_cookie = chip; - chip->c_dma.ebus_info.irq = edev->irqs[0]; + chip->c_dma.ebus_info.irq = op->irqs[0]; strcpy(chip->p_dma.ebus_info.name, "cs4231(play)"); chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback; chip->p_dma.ebus_info.client_cookie = chip; - chip->p_dma.ebus_info.irq = edev->irqs[1]; + chip->p_dma.ebus_info.irq = op->irqs[1]; chip->p_dma.prepare = _ebus_dma_prepare; chip->p_dma.enable = _ebus_dma_enable; chip->p_dma.request = _ebus_dma_request; chip->p_dma.address = _ebus_dma_addr; - chip->p_dma.preallocate = _ebus_dma_preallocate; chip->c_dma.prepare = _ebus_dma_prepare; chip->c_dma.enable = _ebus_dma_enable; chip->c_dma.request = _ebus_dma_request; chip->c_dma.address = _ebus_dma_addr; - chip->c_dma.preallocate = _ebus_dma_preallocate; - chip->port = ioremap(edev->resource[0].start, 0x10); - chip->p_dma.ebus_info.regs = ioremap(edev->resource[1].start, 0x10); - chip->c_dma.ebus_info.regs = ioremap(edev->resource[2].start, 0x10); + chip->port = of_ioremap(&op->resource[0], 0, 0x10, "cs4231"); + chip->p_dma.ebus_info.regs = + of_ioremap(&op->resource[1], 0, 0x10, "cs4231_pdma"); + chip->c_dma.ebus_info.regs = + of_ioremap(&op->resource[2], 0, 0x10, "cs4231_cdma"); if (!chip->port || !chip->p_dma.ebus_info.regs || !chip->c_dma.ebus_info.regs) { snd_cs4231_ebus_free(chip); @@ -2077,7 +2048,7 @@ static int __init snd_cs4231_ebus_create(struct snd_card *card, return 0; } -static int __init cs4231_ebus_attach(struct linux_ebus_device *edev) +static int __devinit cs4231_ebus_probe(struct of_device *op, const struct of_device_id *match) { struct snd_card *card; int err; @@ -2088,10 +2059,10 @@ static int __init cs4231_ebus_attach(struct linux_ebus_device *edev) sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, - edev->resource[0].start, - edev->irqs[0]); + op->resource[0].start, + op->irqs[0]); - err = snd_cs4231_ebus_create(card, edev, dev); + err = snd_cs4231_ebus_create(card, op, dev); if (err < 0) { snd_card_free(card); return err; @@ -2101,68 +2072,57 @@ static int __init cs4231_ebus_attach(struct linux_ebus_device *edev) } #endif -static int __init cs4231_init(void) +static int __devinit cs4231_probe(struct of_device *op, const struct of_device_id *match) { -#ifdef SBUS_SUPPORT - struct sbus_bus *sbus; - struct sbus_dev *sdev; -#endif #ifdef EBUS_SUPPORT - struct linux_ebus *ebus; - struct linux_ebus_device *edev; + if (!strcmp(op->node->parent->name, "ebus")) + return cs4231_ebus_probe(op, match); #endif - int found; - - found = 0; - #ifdef SBUS_SUPPORT - for_all_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, "SUNW,CS4231")) { - if (cs4231_sbus_attach(sdev) == 0) - found++; - } - } + if (!strcmp(op->node->parent->name, "sbus") || + !strcmp(op->node->parent->name, "sbi")) + return cs4231_sbus_probe(op, match); #endif -#ifdef EBUS_SUPPORT - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - int match = 0; - - if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) { - match = 1; - } else if (!strcmp(edev->prom_node->name, "audio")) { - const char *compat; - - compat = of_get_property(edev->prom_node, - "compatible", NULL); - if (compat && !strcmp(compat, "SUNW,CS4231")) - match = 1; - } + return -ENODEV; +} - if (match && - cs4231_ebus_attach(edev) == 0) - found++; - } - } -#endif +static int __devexit cs4231_remove(struct of_device *op) +{ + struct snd_cs4231 *chip = dev_get_drvdata(&op->dev); + snd_card_free(chip->card); - return (found > 0) ? 0 : -EIO; + return 0; } -static void __exit cs4231_exit(void) -{ - struct snd_cs4231 *p = cs4231_list; +static const struct of_device_id cs4231_match[] = { + { + .name = "SUNW,CS4231", + }, + { + .name = "audio", + .compatible = "SUNW,CS4231", + }, + {}, +}; - while (p != NULL) { - struct snd_cs4231 *next = p->next; +MODULE_DEVICE_TABLE(of, cs4231_match); - snd_card_free(p->card); +static struct of_platform_driver cs4231_driver = { + .name = "audio", + .match_table = cs4231_match, + .probe = cs4231_probe, + .remove = __devexit_p(cs4231_remove), +}; - p = next; - } +static int __init cs4231_init(void) +{ + return of_register_driver(&cs4231_driver, &of_bus_type); +} - cs4231_list = NULL; +static void __exit cs4231_exit(void) +{ + of_unregister_driver(&cs4231_driver); } module_init(cs4231_init); diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index ee2e1b4f355..c257ad8bdfb 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -57,6 +57,7 @@ #include <linux/delay.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/dma-mapping.h> #include <sound/core.h> #include <sound/pcm.h> @@ -66,7 +67,7 @@ #include <sound/initval.h> #include <linux/of.h> -#include <asm/sbus.h> +#include <linux/of_device.h> #include <asm/atomic.h> MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets"); @@ -297,7 +298,7 @@ struct dbri_streaminfo { /* This structure holds the information for both chips (DBRI & CS4215) */ struct snd_dbri { int regs_size, irq; /* Needed for unload */ - struct sbus_dev *sdev; /* SBUS device info */ + struct of_device *op; /* OF device info */ spinlock_t lock; struct dbri_dma *dma; /* Pointer to our DMA block */ @@ -2093,14 +2094,15 @@ static int snd_dbri_hw_params(struct snd_pcm_substream *substream, */ if (info->dvma_buffer == 0) { if (DBRI_STREAMNO(substream) == DBRI_PLAY) - direction = SBUS_DMA_TODEVICE; + direction = DMA_TO_DEVICE; else - direction = SBUS_DMA_FROMDEVICE; + direction = DMA_FROM_DEVICE; - info->dvma_buffer = sbus_map_single(dbri->sdev, - runtime->dma_area, - params_buffer_bytes(hw_params), - direction); + info->dvma_buffer = + dma_map_single(&dbri->op->dev, + runtime->dma_area, + params_buffer_bytes(hw_params), + direction); } direction = params_buffer_bytes(hw_params); @@ -2121,12 +2123,12 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *substream) */ if (info->dvma_buffer) { if (DBRI_STREAMNO(substream) == DBRI_PLAY) - direction = SBUS_DMA_TODEVICE; + direction = DMA_TO_DEVICE; else - direction = SBUS_DMA_FROMDEVICE; + direction = DMA_FROM_DEVICE; - sbus_unmap_single(dbri->sdev, info->dvma_buffer, - substream->runtime->buffer_size, direction); + dma_unmap_single(&dbri->op->dev, info->dvma_buffer, + substream->runtime->buffer_size, direction); info->dvma_buffer = 0; } if (info->pipe != -1) { @@ -2223,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); @@ -2263,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; @@ -2331,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] = @@ -2356,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) @@ -2432,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); @@ -2514,31 +2521,32 @@ static void __devinit snd_dbri_proc(struct snd_card *card) static void snd_dbri_free(struct snd_dbri *dbri); static int __devinit snd_dbri_create(struct snd_card *card, - struct sbus_dev *sdev, - int irq, int dev) + struct of_device *op, + int irq, int dev) { struct snd_dbri *dbri = card->private_data; int err; spin_lock_init(&dbri->lock); - dbri->sdev = sdev; + dbri->op = op; dbri->irq = irq; - dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma), - &dbri->dma_dvma); + dbri->dma = dma_alloc_coherent(&op->dev, + sizeof(struct dbri_dma), + &dbri->dma_dvma, GFP_ATOMIC); memset((void *)dbri->dma, 0, sizeof(struct dbri_dma)); dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n", dbri->dma, dbri->dma_dvma); /* Map the registers into memory. */ - dbri->regs_size = sdev->reg_addrs[0].reg_size; - dbri->regs = sbus_ioremap(&sdev->resource[0], 0, - dbri->regs_size, "DBRI Registers"); + dbri->regs_size = resource_size(&op->resource[0]); + dbri->regs = of_ioremap(&op->resource[0], 0, + dbri->regs_size, "DBRI Registers"); if (!dbri->regs) { printk(KERN_ERR "DBRI: could not allocate registers\n"); - sbus_free_consistent(sdev, sizeof(struct dbri_dma), - (void *)dbri->dma, dbri->dma_dvma); + dma_free_coherent(&op->dev, sizeof(struct dbri_dma), + (void *)dbri->dma, dbri->dma_dvma); return -EIO; } @@ -2546,9 +2554,9 @@ static int __devinit snd_dbri_create(struct snd_card *card, "DBRI audio", dbri); if (err) { printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq); - sbus_iounmap(dbri->regs, dbri->regs_size); - sbus_free_consistent(sdev, sizeof(struct dbri_dma), - (void *)dbri->dma, dbri->dma_dvma); + of_iounmap(&op->resource[0], dbri->regs, dbri->regs_size); + dma_free_coherent(&op->dev, sizeof(struct dbri_dma), + (void *)dbri->dma, dbri->dma_dvma); return err; } @@ -2572,27 +2580,23 @@ static void snd_dbri_free(struct snd_dbri *dbri) free_irq(dbri->irq, dbri); if (dbri->regs) - sbus_iounmap(dbri->regs, dbri->regs_size); + of_iounmap(&dbri->op->resource[0], dbri->regs, dbri->regs_size); if (dbri->dma) - sbus_free_consistent(dbri->sdev, sizeof(struct dbri_dma), - (void *)dbri->dma, dbri->dma_dvma); + dma_free_coherent(&dbri->op->dev, + sizeof(struct dbri_dma), + (void *)dbri->dma, dbri->dma_dvma); } -static int __devinit dbri_probe(struct of_device *of_dev, - const struct of_device_id *match) +static int __devinit dbri_probe(struct of_device *op, const struct of_device_id *match) { - struct sbus_dev *sdev = to_sbus_device(&of_dev->dev); struct snd_dbri *dbri; - int irq; struct resource *rp; struct snd_card *card; static int dev = 0; + int irq; int err; - dprintk(D_GEN, "DBRI: Found %s in SBUS slot %d\n", - sdev->prom_name, sdev->slot); - if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { @@ -2600,7 +2604,7 @@ static int __devinit dbri_probe(struct of_device *of_dev, return -ENOENT; } - irq = sdev->irqs[0]; + irq = op->irqs[0]; if (irq <= 0) { printk(KERN_ERR "DBRI-%d: No IRQ.\n", dev); return -ENODEV; @@ -2613,12 +2617,12 @@ static int __devinit dbri_probe(struct of_device *of_dev, strcpy(card->driver, "DBRI"); strcpy(card->shortname, "Sun DBRI"); - rp = &sdev->resource[0]; + rp = &op->resource[0]; sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d", card->shortname, rp->flags & 0xffL, (unsigned long long)rp->start, irq); - err = snd_dbri_create(card, sdev, irq, dev); + err = snd_dbri_create(card, op, irq, dev); if (err < 0) { snd_card_free(card); return err; @@ -2635,7 +2639,7 @@ static int __devinit dbri_probe(struct of_device *of_dev, /* /proc file handling */ snd_dbri_proc(card); - dev_set_drvdata(&of_dev->dev, card); + dev_set_drvdata(&op->dev, card); err = snd_card_register(card); if (err < 0) @@ -2643,7 +2647,7 @@ static int __devinit dbri_probe(struct of_device *of_dev, printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n", dev, dbri->regs, - dbri->irq, sdev->prom_name[9], dbri->mm.version); + dbri->irq, op->node->name[9], dbri->mm.version); dev++; return 0; @@ -2654,19 +2658,19 @@ _err: return err; } -static int __devexit dbri_remove(struct of_device *dev) +static int __devexit dbri_remove(struct of_device *op) { - struct snd_card *card = dev_get_drvdata(&dev->dev); + struct snd_card *card = dev_get_drvdata(&op->dev); snd_dbri_free(card->private_data); snd_card_free(card); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id dbri_match[] = { +static const struct of_device_id dbri_match[] = { { .name = "SUNW,DBRIe", }, @@ -2688,7 +2692,7 @@ static struct of_platform_driver dbri_sbus_driver = { /* Probe for the dbri chip and then attach the driver. */ static int __init dbri_init(void) { - return of_register_driver(&dbri_sbus_driver, &sbus_bus_type); + return of_register_driver(&dbri_sbus_driver, &of_bus_type); } static void __exit dbri_exit(void) diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index c89d2ea594b..f16a3fce459 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 c6917ba2c93..00fc005ecf6 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 f60a98ef7de..5c47b6c0926 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 d176cc01742..335aa2ce257 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 b343818dbb9..2cc6f6f7906 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 deabe5f899c..c85522e3808 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 ffcdc8f4ef6..4f0eac9bff1 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 aa252ef2ebf..abb288bfe35 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 b8cfb7c2276..bbd70d5814a 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 7cf18c38dc4..36e4f7a29ad 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 6676a177c99..5962e4b8442 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 89c63d073cc..a49246113e7 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 9ea726c049c..69689e79bf7 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 9ac22bce112..748933054b6 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 00000000000..c2515b680f9 --- /dev/null +++ b/sound/usb/usx2y/us122l.c @@ -0,0 +1,693 @@ +/* + * 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; + + mutex_lock(&us122l->mutex); + s = us122l->sk.s; + if (!s) + goto unlock; + + 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 unlock; + + vaddr = us122l->sk.write_page + offset; + } + page = virt_to_page(vaddr); + + get_page(page); + mutex_unlock(&us122l->mutex); + + vmf->page = page; + + return 0; +unlock: + mutex_unlock(&us122l->mutex); + return VM_FAULT_SIGBUS; +} + +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 00000000000..3d10c4b2a0f --- /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 00000000000..ff23cc1ce3b --- /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 00000000000..4dd74ab1e9c --- /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 |