diff options
Diffstat (limited to 'sound')
131 files changed, 7077 insertions, 1832 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index ee794ae06040..b65ee4701f98 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -77,7 +77,7 @@ source "sound/parisc/Kconfig" endmenu menu "Open Sound System" - depends on SOUND!=n && (BROKEN || (!SPARC32 && !SPARC64)) + depends on SOUND!=n config SOUND_PRIME tristate "Open Sound System (DEPRECATED)" diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 34c1740aa6e9..2e4a5e0d16db 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -20,5 +20,17 @@ config SND_ARMAACI select SND_PCM select SND_AC97_CODEC -endmenu +config SND_PXA2XX_PCM + tristate + select SND_PCM + +config SND_PXA2XX_AC97 + tristate "AC97 driver for the Intel PXA2xx chip" + depends on ARCH_PXA && SND + select SND_PXA2XX_PCM + select SND_AC97_CODEC + help + Say Y or M if you want to support any AC97 codec attached to + the PXA2xx AC97 interface. +endmenu diff --git a/sound/arm/Makefile b/sound/arm/Makefile index f74ec28e1068..103f136926d9 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -3,9 +3,11 @@ # snd-sa11xx-uda1341-objs := sa11xx-uda1341.o +snd-aaci-objs := aaci.o devdma.o +snd-pxa2xx-pcm-objs := pxa2xx-pcm.o +snd-pxa2xx-ac97-objs := pxa2xx-ac97.o -# Toplevel Module Dependency obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o - obj-$(CONFIG_SND_ARMAACI) += snd-aaci.o -snd-aaci-objs := aaci.o devdma.o +obj-$(CONFIG_SND_PXA2XX_PCM) += snd-pxa2xx-pcm.o +obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c new file mode 100644 index 000000000000..29450befb5da --- /dev/null +++ b/sound/arm/pxa2xx-ac97.c @@ -0,0 +1,410 @@ +/* + * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip. + * + * 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/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/delay.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> + +#include <asm/irq.h> +#include <asm/semaphore.h> +#include <asm/hardware.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/audio.h> + +#include "pxa2xx-pcm.h" + + +static DECLARE_MUTEX(car_mutex); +static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); +static volatile long gsr_bits; + +static unsigned short pxa2xx_ac97_read(ac97_t *ac97, unsigned short reg) +{ + unsigned short val = -1; + volatile u32 *reg_addr; + + down(&car_mutex); + if (CAR & CAR_CAIP) { + printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__); + goto out; + } + + /* 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_bits = 0; + val = *reg_addr; + if (reg == AC97_GPIO_STATUS) + goto out; + wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1); + if (!gsr_bits & GSR_SDONE) { + printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", + __FUNCTION__, reg, gsr_bits); + val = -1; + goto out; + } + + /* valid data now */ + gsr_bits = 0; + val = *reg_addr; + /* but we've just started another cycle... */ + wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1); + +out: up(&car_mutex); + return val; +} + +static void pxa2xx_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) +{ + volatile u32 *reg_addr; + + down(&car_mutex); + + if (CAR & CAR_CAIP) { + printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__); + goto out; + } + + /* set up primary or secondary codec space */ + reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE; + reg_addr += (reg >> 1); + gsr_bits = 0; + *reg_addr = val; + wait_event_timeout(gsr_wq, gsr_bits & GSR_CDONE, 1); + if (!gsr_bits & GSR_SDONE) + printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", + __FUNCTION__, reg, gsr_bits); + +out: up(&car_mutex); +} + +static void pxa2xx_ac97_reset(ac97_t *ac97) +{ + /* First, try cold reset */ + 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 */ + pxa_set_cken(1 << 31, 1); + udelay(5); + pxa_set_cken(1 << 31, 0); + GCR = GCR_COLD_RST; + udelay(50); +#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", + __FUNCTION__, 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); +#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", + __FUNCTION__, 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, struct pt_regs *regs) +{ + 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; +} + +static ac97_bus_ops_t pxa2xx_ac97_ops = { + .read = pxa2xx_ac97_read, + .write = pxa2xx_ac97_write, + .reset = pxa2xx_ac97_reset, +}; + +static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_out = { + .name = "AC97 PCM out", + .dev_addr = __PREG(PCDR), + .drcmr = &DRCMRTXPCDR, + .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_in = { + .name = "AC97 PCM in", + .dev_addr = __PREG(PCDR), + .drcmr = &DRCMRRXPCDR, + .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | + DCMD_BURST32 | DCMD_WIDTH4, +}; + +static snd_pcm_t *pxa2xx_ac97_pcm; +static ac97_t *pxa2xx_ac97_ac97; + +static int pxa2xx_ac97_pcm_startup(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + pxa2xx_audio_ops_t *platform_ops; + int r; + + runtime->hw.channels_min = 2; + runtime->hw.channels_max = 2; + + r = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_RATES_FRONT_DAC : AC97_RATES_ADC; + runtime->hw.rates = pxa2xx_ac97_ac97->rates[r]; + snd_pcm_limit_hw_rates(runtime); + + platform_ops = substream->pcm->card->dev->platform_data; + if (platform_ops && platform_ops->startup) + return platform_ops->startup(substream, platform_ops->priv); + else + return 0; +} + +static void pxa2xx_ac97_pcm_shutdown(snd_pcm_substream_t *substream) +{ + pxa2xx_audio_ops_t *platform_ops; + + platform_ops = substream->pcm->card->dev->platform_data; + if (platform_ops && platform_ops->shutdown) + platform_ops->shutdown(substream, platform_ops->priv); +} + +static int pxa2xx_ac97_pcm_prepare(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE; + return snd_ac97_set_rate(pxa2xx_ac97_ac97, reg, runtime->rate); +} + +static pxa2xx_pcm_client_t pxa2xx_ac97_pcm_client = { + .playback_params = &pxa2xx_ac97_pcm_out, + .capture_params = &pxa2xx_ac97_pcm_in, + .startup = pxa2xx_ac97_pcm_startup, + .shutdown = pxa2xx_ac97_pcm_shutdown, + .prepare = pxa2xx_ac97_pcm_prepare, +}; + +#ifdef CONFIG_PM + +static int pxa2xx_ac97_do_suspend(snd_card_t *card, unsigned int state) +{ + if (card->power_state != SNDRV_CTL_POWER_D3cold) { + pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; + snd_pcm_suspend_all(pxa2xx_ac97_pcm); + snd_ac97_suspend(pxa2xx_ac97_ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + if (platform_ops && platform_ops->suspend) + platform_ops->suspend(platform_ops->priv); + GCR |= GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + } + + return 0; +} + +static int pxa2xx_ac97_do_resume(snd_card_t *card) +{ + if (card->power_state != SNDRV_CTL_POWER_D0) { + pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data; + pxa_set_cken(CKEN2_AC97, 1); + if (platform_ops && platform_ops->resume) + platform_ops->resume(platform_ops->priv); + snd_ac97_resume(pxa2xx_ac97_ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + } + + return 0; +} + +static int pxa2xx_ac97_suspend(struct device *_dev, pm_message_t state, u32 level) +{ + snd_card_t *card = dev_get_drvdata(_dev); + int ret = 0; + + if (card && level == SUSPEND_DISABLE) + ret = pxa2xx_ac97_do_suspend(card, PMSG_SUSPEND); + + return ret; +} + +static int pxa2xx_ac97_resume(struct device *_dev, u32 level) +{ + snd_card_t *card = dev_get_drvdata(_dev); + int ret = 0; + + if (card && level == RESUME_ENABLE) + ret = pxa2xx_ac97_do_resume(card); + + return ret; +} + +#else +#define pxa2xx_ac97_suspend NULL +#define pxa2xx_ac97_resume NULL +#endif + +static int pxa2xx_ac97_probe(struct device *dev) +{ + snd_card_t *card; + ac97_bus_t *ac97_bus; + ac97_template_t ac97_template; + int ret; + + ret = -ENOMEM; + card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0); + if (!card) + goto err; + + card->dev = dev; + strncpy(card->driver, dev->driver->name, sizeof(card->driver)); + + ret = pxa2xx_pcm_new(card, &pxa2xx_ac97_pcm_client, &pxa2xx_ac97_pcm); + 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); +#endif + pxa_set_cken(CKEN2_AC97, 1); + + ret = snd_ac97_bus(card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus); + if (ret) + goto err; + memset(&ac97_template, 0, sizeof(ac97_template)); + ret = snd_ac97_mixer(ac97_bus, &ac97_template, &pxa2xx_ac97_ac97); + if (ret) + goto err; + + snprintf(card->shortname, sizeof(card->shortname), + "%s", snd_ac97_get_short_name(pxa2xx_ac97_ac97)); + snprintf(card->longname, sizeof(card->longname), + "%s (%s)", dev->driver->name, card->mixername); + + snd_card_set_pm_callback(card, pxa2xx_ac97_do_suspend, + pxa2xx_ac97_do_resume, NULL); + ret = snd_card_register(card); + if (ret == 0) { + dev_set_drvdata(dev, card); + return 0; + } + + err: + if (card) + snd_card_free(card); + if (CKEN & CKEN2_AC97) { + GCR |= GCR_ACLINK_OFF; + free_irq(IRQ_AC97, NULL); + pxa_set_cken(CKEN2_AC97, 0); + } + return ret; +} + +static int pxa2xx_ac97_remove(struct device *dev) +{ + snd_card_t *card = dev_get_drvdata(dev); + + if (card) { + snd_card_free(card); + dev_set_drvdata(dev, NULL); + GCR |= GCR_ACLINK_OFF; + free_irq(IRQ_AC97, NULL); + pxa_set_cken(CKEN2_AC97, 0); + } + + return 0; +} + +static struct device_driver pxa2xx_ac97_driver = { + .name = "pxa2xx-ac97", + .bus = &platform_bus_type, + .probe = pxa2xx_ac97_probe, + .remove = pxa2xx_ac97_remove, + .suspend = pxa2xx_ac97_suspend, + .resume = pxa2xx_ac97_resume, +}; + +static int __init pxa2xx_ac97_init(void) +{ + return driver_register(&pxa2xx_ac97_driver); +} + +static void __exit pxa2xx_ac97_exit(void) +{ + driver_unregister(&pxa2xx_ac97_driver); +} + +module_init(pxa2xx_ac97_init); +module_exit(pxa2xx_ac97_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip"); +MODULE_LICENSE("GPL"); diff --git a/sound/arm/pxa2xx-pcm.c b/sound/arm/pxa2xx-pcm.c new file mode 100644 index 000000000000..b1eb53b02eae --- /dev/null +++ b/sound/arm/pxa2xx-pcm.c @@ -0,0 +1,367 @@ +/* + * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip + * + * Author: Nicolas Pitre + * Created: Nov 30, 2004 + * Copyright: (C) 2004 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/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include <asm/dma.h> +#include <asm/hardware.h> +#include <asm/arch/pxa-regs.h> + +#include "pxa2xx-pcm.h" + + +static const snd_pcm_hardware_t 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; + pxa2xx_pcm_dma_params_t *params; + pxa_dma_desc *dma_desc_array; + dma_addr_t dma_desc_array_phys; +}; + +static int pxa2xx_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *params) +{ + snd_pcm_runtime_t *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(snd_pcm_substream_t *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(snd_pcm_substream_t *substream) +{ + pxa2xx_pcm_client_t *client = substream->private_data; + snd_pcm_runtime_t *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; + + return client->prepare(substream); +} + +static int pxa2xx_pcm_trigger(snd_pcm_substream_t *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 pt_regs *regs) +{ + snd_pcm_substream_t *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(snd_pcm_substream_t *substream) +{ + snd_pcm_runtime_t *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(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule) +{ + snd_interval_t *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(snd_pcm_substream_t *substream) +{ + pxa2xx_pcm_client_t *client = substream->private_data; + snd_pcm_runtime_t *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_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); + 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->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + client->playback_params : client->capture_params; + ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW, + pxa2xx_pcm_dma_irq, substream); + if (ret < 0) + 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); + out: + return ret; +} + +static int pxa2xx_pcm_close(snd_pcm_substream_t *substream) +{ + pxa2xx_pcm_client_t *client = substream->private_data; + struct pxa2xx_runtime_data *rtd = substream->runtime->private_data; + + 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(snd_pcm_substream_t *substream, struct vm_area_struct *vma) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + return dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); +} + +static snd_pcm_ops_t 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, + .prepare = pxa2xx_pcm_prepare, + .trigger = pxa2xx_pcm_trigger, + .pointer = pxa2xx_pcm_pointer, + .mmap = pxa2xx_pcm_mmap, +}; + +static int pxa2xx_pcm_preallocate_dma_buffer(snd_pcm_t *pcm, int stream) +{ + snd_pcm_substream_t *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(snd_pcm_t *pcm) +{ + snd_pcm_substream_t *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(snd_card_t *card, pxa2xx_pcm_client_t *client, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int play = client->playback_params ? 1 : 0; + int capt = client->capture_params ? 1 : 0; + int ret; + + ret = snd_pcm_new(card, "PXA2xx-PCM", 0, play, capt, &pcm); + if (ret) + goto out; + + pcm->private_data = client; + pcm->private_free = pxa2xx_pcm_free_dma_buffers; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &pxa2xx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + if (play) { + int stream = SNDRV_PCM_STREAM_PLAYBACK; + snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops); + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); + if (ret) + goto out; + } + if (capt) { + int stream = SNDRV_PCM_STREAM_CAPTURE; + snd_pcm_set_ops(pcm, stream, &pxa2xx_pcm_ops); + ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, stream); + if (ret) + goto out; + } + + if (rpcm) + *rpcm = pcm; + ret = 0; + + out: + return ret; +} + +EXPORT_SYMBOL(pxa2xx_pcm_new); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module"); +MODULE_LICENSE("GPL"); diff --git a/sound/arm/pxa2xx-pcm.h b/sound/arm/pxa2xx-pcm.h new file mode 100644 index 000000000000..43517597cab9 --- /dev/null +++ b/sound/arm/pxa2xx-pcm.h @@ -0,0 +1,29 @@ +/* + * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip + * + * Author: Nicolas Pitre + * Created: Nov 30, 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. + */ + +typedef struct { + 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 */ +} pxa2xx_pcm_dma_params_t; + +typedef struct { + pxa2xx_pcm_dma_params_t *playback_params; + pxa2xx_pcm_dma_params_t *capture_params; + int (*startup)(snd_pcm_substream_t *); + void (*shutdown)(snd_pcm_substream_t *); + int (*prepare)(snd_pcm_substream_t *); +} pxa2xx_pcm_client_t; + +extern int pxa2xx_pcm_new(snd_card_t *, pxa2xx_pcm_client_t *, snd_pcm_t **); + diff --git a/sound/core/Makefile b/sound/core/Makefile index 764ac184b223..969d75528bde 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -5,7 +5,7 @@ snd-objs := sound.o init.o memory.o info.o control.o misc.o \ device.o wrappers.o -ifeq ($(CONFIG_ISA),y) +ifeq ($(CONFIG_ISA_DMA_API),y) snd-objs += isadma.o endif ifeq ($(CONFIG_SND_OSSEMUL),y) diff --git a/sound/core/device.c b/sound/core/device.c index 18c71f913d2a..ca00ad7740c9 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -28,7 +28,7 @@ /** * snd_device_new - create an ALSA device component * @card: the card instance - * @type: the device type, SNDRV_DEV_TYPE_XXX + * @type: the device type, SNDRV_DEV_XXX * @device_data: the data pointer of this device * @ops: the operator table * @@ -46,7 +46,9 @@ int snd_device_new(snd_card_t *card, snd_device_type_t type, { snd_device_t *dev; - snd_assert(card != NULL && device_data != NULL && ops != NULL, return -ENXIO); + snd_assert(card != NULL, return -ENXIO); + snd_assert(device_data != NULL, return -ENXIO); + snd_assert(ops != NULL, return -ENXIO); dev = kcalloc(1, sizeof(*dev), GFP_KERNEL); if (dev == NULL) return -ENOMEM; @@ -102,7 +104,7 @@ int snd_device_free(snd_card_t *card, void *device_data) } /** - * snd_device_free - disconnect the device + * snd_device_disconnect - disconnect the device * @card: the card instance * @device_data: the data pointer to disconnect * @@ -118,7 +120,7 @@ int snd_device_disconnect(snd_card_t *card, void *device_data) { struct list_head *list; snd_device_t *dev; - + snd_assert(card != NULL, return -ENXIO); snd_assert(device_data != NULL, return -ENXIO); list_for_each(list, &card->devices) { @@ -154,8 +156,9 @@ int snd_device_register(snd_card_t *card, void *device_data) struct list_head *list; snd_device_t *dev; int err; - - snd_assert(card != NULL && device_data != NULL, return -ENXIO); + + snd_assert(card != NULL, return -ENXIO); + snd_assert(device_data != NULL, return -ENXIO); list_for_each(list, &card->devices) { dev = snd_device(list); if (dev->device_data != device_data) diff --git a/sound/core/info.c b/sound/core/info.c index 5e122bbe7c92..7f8bdf7b0058 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -702,7 +702,7 @@ int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len) } /** - * snd_info_get_line - parse a string token + * snd_info_get_str - parse a string token * @dest: the buffer to store the string token * @src: the original string * @len: the max. length of token - 1 @@ -939,7 +939,8 @@ int snd_info_unregister(snd_info_entry_t * entry) { struct proc_dir_entry *root; - snd_assert(entry != NULL && entry->p != NULL, return -ENXIO); + snd_assert(entry != NULL, return -ENXIO); + snd_assert(entry->p != NULL, return -ENXIO); root = entry->parent == NULL ? snd_proc_root : entry->parent->p; snd_assert(root, return -ENXIO); down(&info_mutex); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index dbc23e35fa06..39a54a415528 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -105,7 +105,8 @@ struct snd_mem_list { */ static void *snd_dma_hack_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, int flags) + dma_addr_t *dma_handle, + unsigned int __nocast flags) { void *ret; u64 dma_mask, coherent_dma_mask; @@ -511,7 +512,7 @@ static void free_all_reserved_pages(void) * proc file interface */ #define SND_MEM_PROC_FILE "driver/snd-page-alloc" -struct proc_dir_entry *snd_mem_proc; +static struct proc_dir_entry *snd_mem_proc; static int snd_mem_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) @@ -654,8 +655,7 @@ static int __init snd_mem_init(void) static void __exit snd_mem_exit(void) { - if (snd_mem_proc) - remove_proc_entry(SND_MEM_PROC_FILE, NULL); + remove_proc_entry(SND_MEM_PROC_FILE, NULL); free_all_reserved_pages(); if (snd_allocated_pages > 0) printk(KERN_ERR "snd-malloc: Memory leak? pages not freed = %li\n", snd_allocated_pages); diff --git a/sound/core/memory.c b/sound/core/memory.c index c1fb28e84330..1622893d00a2 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -56,7 +56,7 @@ static DEFINE_SPINLOCK(snd_alloc_vmalloc_lock); #define VMALLOC_MAGIC 0x87654320 static snd_info_entry_t *snd_memory_info_entry; -void snd_memory_init(void) +void __init snd_memory_init(void) { snd_alloc_kmalloc = 0; snd_alloc_vmalloc = 0; @@ -89,7 +89,7 @@ void snd_memory_done(void) } } -static void *__snd_kmalloc(size_t size, int flags, void *caller) +static void *__snd_kmalloc(size_t size, unsigned int __nocast flags, void *caller) { unsigned long cpu_flags; struct snd_alloc_track *t; @@ -111,12 +111,12 @@ static void *__snd_kmalloc(size_t size, int flags, void *caller) } #define _snd_kmalloc(size, flags) __snd_kmalloc((size), (flags), __builtin_return_address(0)); -void *snd_hidden_kmalloc(size_t size, int flags) +void *snd_hidden_kmalloc(size_t size, unsigned int __nocast flags) { return _snd_kmalloc(size, flags); } -void *snd_hidden_kcalloc(size_t n, size_t size, int flags) +void *snd_hidden_kcalloc(size_t n, size_t size, unsigned int __nocast flags) { void *ret = NULL; if (n != 0 && size > INT_MAX / n) @@ -184,7 +184,7 @@ void snd_hidden_vfree(void *obj) snd_wrapper_vfree(obj); } -char *snd_hidden_kstrdup(const char *s, int flags) +char *snd_hidden_kstrdup(const char *s, unsigned int __nocast flags) { int len; char *buf; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index de7444c586f9..a13bd7bb4c9f 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1705,13 +1705,12 @@ static int snd_pcm_oss_release_file(snd_pcm_oss_file_t *pcm_oss_file) if (snd_pcm_running(substream)) snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); snd_pcm_stream_unlock_irq(substream); - if (substream->open_flag) { + if (substream->ffile != NULL) { if (substream->ops->hw_free != NULL) substream->ops->hw_free(substream); substream->ops->close(substream); - substream->open_flag = 0; + substream->ffile = NULL; } - substream->ffile = NULL; snd_pcm_oss_release_substream(substream); snd_pcm_release_substream(substream); } @@ -1778,14 +1777,13 @@ static int snd_pcm_oss_open_file(struct file *file, snd_pcm_oss_release_file(pcm_oss_file); return err; } - psubstream->open_flag = 1; + psubstream->ffile = file; err = snd_pcm_hw_constraints_complete(psubstream); if (err < 0) { snd_printd("snd_pcm_hw_constraint_complete failed\n"); snd_pcm_oss_release_file(pcm_oss_file); return err; } - psubstream->ffile = file; snd_pcm_oss_init_substream(psubstream, psetup, minor); } if (csubstream != NULL) { @@ -1800,14 +1798,13 @@ static int snd_pcm_oss_open_file(struct file *file, snd_pcm_oss_release_file(pcm_oss_file); return err; } - csubstream->open_flag = 1; + csubstream->ffile = file; err = snd_pcm_hw_constraints_complete(csubstream); if (err < 0) { snd_printd("snd_pcm_hw_constraint_complete failed\n"); snd_pcm_oss_release_file(pcm_oss_file); return err; } - csubstream->ffile = file; snd_pcm_oss_init_substream(csubstream, csetup, minor); } diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 3920bf0eebbf..4b6307df846d 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -103,10 +103,24 @@ struct sndrv_pcm_sw_params32 { unsigned char reserved[64]; }; +/* recalcuate the boundary within 32bit */ +static snd_pcm_uframes_t recalculate_boundary(snd_pcm_runtime_t *runtime) +{ + snd_pcm_uframes_t boundary; + + if (! runtime->buffer_size) + return 0; + boundary = runtime->buffer_size; + while (boundary * 2 <= 0x7fffffffUL - runtime->buffer_size) + boundary *= 2; + return boundary; +} + static int snd_pcm_ioctl_sw_params_compat(snd_pcm_substream_t *substream, struct sndrv_pcm_sw_params32 __user *src) { snd_pcm_sw_params_t params; + snd_pcm_uframes_t boundary; int err; memset(¶ms, 0, sizeof(params)); @@ -120,10 +134,17 @@ static int snd_pcm_ioctl_sw_params_compat(snd_pcm_substream_t *substream, get_user(params.silence_threshold, &src->silence_threshold) || get_user(params.silence_size, &src->silence_size)) return -EFAULT; + /* + * Check silent_size parameter. Since we have 64bit boundary, + * silence_size must be compared with the 32bit boundary. + */ + boundary = recalculate_boundary(substream->runtime); + if (boundary && params.silence_size >= boundary) + params.silence_size = substream->runtime->boundary; err = snd_pcm_sw_params(substream, ¶ms); if (err < 0) return err; - if (put_user(params.boundary, &src->boundary)) + if (boundary && put_user(boundary, &src->boundary)) return -EFAULT; return err; } @@ -199,16 +220,6 @@ static int snd_pcm_status_user_compat(snd_pcm_substream_t *substream, return err; } -/* recalcuate the boundary within 32bit */ -static void recalculate_boundary(snd_pcm_runtime_t *runtime) -{ - if (! runtime->buffer_size) - return; - runtime->boundary = runtime->buffer_size; - while (runtime->boundary * 2 <= 0x7fffffffUL - runtime->buffer_size) - runtime->boundary *= 2; -} - /* both for HW_PARAMS and HW_REFINE */ static int snd_pcm_ioctl_hw_params_compat(snd_pcm_substream_t *substream, int refine, @@ -241,8 +252,11 @@ static int snd_pcm_ioctl_hw_params_compat(snd_pcm_substream_t *substream, goto error; } - if (! refine) - recalculate_boundary(runtime); + if (! refine) { + unsigned int new_boundary = recalculate_boundary(runtime); + if (new_boundary) + runtime->boundary = new_boundary; + } error: kfree(data); return err; @@ -380,6 +394,7 @@ static int snd_pcm_ioctl_sync_ptr_compat(snd_pcm_substream_t *substream, u32 sflags; struct sndrv_pcm_mmap_control scontrol; struct sndrv_pcm_mmap_status sstatus; + snd_pcm_uframes_t boundary; int err; snd_assert(runtime, return -EINVAL); @@ -395,17 +410,21 @@ static int snd_pcm_ioctl_sync_ptr_compat(snd_pcm_substream_t *substream, } status = runtime->status; control = runtime->control; + boundary = recalculate_boundary(runtime); + if (! boundary) + boundary = 0x7fffffff; snd_pcm_stream_lock_irq(substream); + /* FIXME: we should consider the boundary for the sync from app */ if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) control->appl_ptr = scontrol.appl_ptr; else - scontrol.appl_ptr = control->appl_ptr; + scontrol.appl_ptr = control->appl_ptr % boundary; if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) control->avail_min = scontrol.avail_min; else scontrol.avail_min = control->avail_min; sstatus.state = status->state; - sstatus.hw_ptr = status->hw_ptr; + sstatus.hw_ptr = status->hw_ptr % boundary; sstatus.tstamp = status->tstamp; sstatus.suspended_state = status->suspended_state; snd_pcm_stream_unlock_irq(substream); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index c5bfd0918cff..0082914a7e33 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1584,8 +1584,8 @@ int snd_pcm_hw_param_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, return snd_pcm_hw_param_value(params, var, NULL); } -int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, - snd_pcm_hw_param_t var, const snd_mask_t *val) +static int _snd_pcm_hw_param_mask(snd_pcm_hw_params_t *params, + snd_pcm_hw_param_t var, const snd_mask_t *val) { int changed; assert(hw_is_mask(var)); @@ -2063,7 +2063,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, if (((avail < runtime->control->avail_min && size > avail) || (size >= runtime->xfer_align && avail < runtime->xfer_align))) { wait_queue_t wait; - enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state; long tout; if (nonblock) { @@ -2097,6 +2097,9 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, case SNDRV_PCM_STATE_SUSPENDED: state = SUSPENDED; goto _end_loop; + case SNDRV_PCM_STATE_SETUP: + state = DROPPED; + goto _end_loop; default: break; } @@ -2123,6 +2126,9 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(snd_pcm_substream_t *substream, snd_printd("playback write error (DMA or IRQ trouble?)\n"); err = -EIO; goto _end_unlock; + case DROPPED: + err = -EBADFD; + goto _end_unlock; default: break; } @@ -2359,7 +2365,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, } else if ((avail < runtime->control->avail_min && size > avail) || (size >= runtime->xfer_align && avail < runtime->xfer_align)) { wait_queue_t wait; - enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED } state; + enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state; long tout; if (nonblock) { @@ -2394,6 +2400,9 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, goto _end_loop; case SNDRV_PCM_STATE_DRAINING: goto __draining; + case SNDRV_PCM_STATE_SETUP: + state = DROPPED; + goto _end_loop; default: break; } @@ -2420,6 +2429,9 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(snd_pcm_substream_t *substream, snd_printd("capture read error (DMA or IRQ trouble?)\n"); err = -EIO; goto _end_unlock; + case DROPPED: + err = -EBADFD; + goto _end_unlock; default: break; } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 10c2c9832649..03c17159dd8e 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1025,7 +1025,7 @@ static void snd_pcm_post_suspend(snd_pcm_substream_t *substream, int state) snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); if (substream->timer) - snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MPAUSE, &runtime->trigger_tstamp); + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND, &runtime->trigger_tstamp); runtime->status->suspended_state = runtime->status->state; runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; snd_pcm_tick_set(substream, 0); @@ -1115,7 +1115,7 @@ static void snd_pcm_post_resume(snd_pcm_substream_t *substream, int state) snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); if (substream->timer) - snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MCONTINUE, &runtime->trigger_tstamp); + snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, &runtime->trigger_tstamp); runtime->status->state = runtime->status->suspended_state; if (runtime->sleep_min) snd_pcm_tick_prepare(substream); @@ -1967,13 +1967,12 @@ static int snd_pcm_release_file(snd_pcm_file_t * pcm_file) runtime = substream->runtime; str = substream->pstr; snd_pcm_unlink(substream); - if (substream->open_flag) { + if (substream->ffile != NULL) { if (substream->ops->hw_free != NULL) substream->ops->hw_free(substream); substream->ops->close(substream); - substream->open_flag = 0; + substream->ffile = NULL; } - substream->ffile = NULL; snd_pcm_remove_file(str, pcm_file); snd_pcm_release_substream(substream); kfree(pcm_file); @@ -2022,18 +2021,15 @@ static int snd_pcm_open_file(struct file *file, snd_pcm_release_file(pcm_file); return err; } - substream->open_flag = 1; + substream->ffile = file; err = snd_pcm_hw_constraints_complete(substream); if (err < 0) { snd_printd("snd_pcm_hw_constraints_complete failed\n"); - substream->ops->close(substream); snd_pcm_release_file(pcm_file); return err; } - substream->ffile = file; - file->private_data = pcm_file; *rpcm_file = pcm_file; return 0; diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index 64cb50d7b589..402e2b4a34c6 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -38,7 +38,7 @@ obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-instr.o -obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-instr.o +obj-$(call sequencer,$(CONFIG_SND_GUS_SYNTH)) += snd-seq-midi-emul.o snd-seq-instr.o obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-seq-midi-emul.o snd-seq-instr.o diff --git a/sound/core/seq/instr/ainstr_gf1.c b/sound/core/seq/instr/ainstr_gf1.c index 0779c41ca037..32e91c6b25fe 100644 --- a/sound/core/seq/instr/ainstr_gf1.c +++ b/sound/core/seq/instr/ainstr_gf1.c @@ -50,7 +50,8 @@ static int snd_seq_gf1_copy_wave_from_stream(snd_gf1_ops_t *ops, { gf1_wave_t *wp, *prev; gf1_xwave_t xp; - int err, gfp_mask; + int err; + unsigned int gfp_mask; unsigned int real_size; gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; diff --git a/sound/core/seq/instr/ainstr_iw.c b/sound/core/seq/instr/ainstr_iw.c index 39ff72b2aab3..2622b8679ca7 100644 --- a/sound/core/seq/instr/ainstr_iw.c +++ b/sound/core/seq/instr/ainstr_iw.c @@ -58,7 +58,7 @@ static int snd_seq_iwffff_copy_env_from_stream(__u32 req_stype, iwffff_xenv_t *ex, char __user **data, long *len, - int gfp_mask) + unsigned int __nocast gfp_mask) { __u32 stype; iwffff_env_record_t *rp, *rp_last; @@ -128,7 +128,8 @@ static int snd_seq_iwffff_copy_wave_from_stream(snd_iwffff_ops_t *ops, { iwffff_wave_t *wp, *prev; iwffff_xwave_t xp; - int err, gfp_mask; + int err; + unsigned int gfp_mask; unsigned int real_size; gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; @@ -234,7 +235,8 @@ static int snd_seq_iwffff_put(void *private_data, snd_seq_kinstr_t *instr, iwffff_xinstrument_t ix; iwffff_layer_t *lp, *prev_lp; iwffff_xlayer_t lx; - int err, gfp_mask; + int err; + unsigned int gfp_mask; if (cmd != SNDRV_SEQ_INSTR_PUT_CMD_CREATE) return -EINVAL; diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h index da23c4db8dd5..973786758c55 100644 --- a/sound/core/seq/oss/seq_oss_device.h +++ b/sound/core/seq/oss/seq_oss_device.h @@ -158,21 +158,21 @@ void snd_seq_oss_readq_info_read(seq_oss_readq_t *q, snd_info_buffer_t *buf); #define is_nonblock_mode(mode) ((mode) & SNDRV_SEQ_OSS_FILE_NONBLOCK) /* dispatch event */ -inline static int +static inline int snd_seq_oss_dispatch(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int atomic, int hop) { return snd_seq_kernel_client_dispatch(dp->cseq, ev, atomic, hop); } /* ioctl */ -inline static int +static inline int snd_seq_oss_control(seq_oss_devinfo_t *dp, unsigned int type, void *arg) { return snd_seq_kernel_client_ctl(dp->cseq, type, arg); } /* fill the addresses in header */ -inline static void +static inline void snd_seq_oss_fill_addr(seq_oss_devinfo_t *dp, snd_seq_event_t *ev, int dest_client, int dest_port) { diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 00d841e82fbc..03acb2d519ba 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -36,12 +36,12 @@ #define semaphore_of(fp) ((fp)->f_dentry->d_inode->i_sem) -inline static int snd_seq_pool_available(pool_t *pool) +static inline int snd_seq_pool_available(pool_t *pool) { return pool->total_elements - atomic_read(&pool->counter); } -inline static int snd_seq_output_ok(pool_t *pool) +static inline int snd_seq_output_ok(pool_t *pool) { return snd_seq_pool_available(pool) >= pool->room; } diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 57be9155eb62..4374829ea770 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -134,7 +134,7 @@ static int event_process_midi(snd_seq_event_t * ev, int direct, seq_midisynth_t *msynth = (seq_midisynth_t *) private_data; unsigned char msg[10]; /* buffer for constructing midi messages */ snd_rawmidi_substream_t *substream; - int res; + int len; snd_assert(msynth != NULL, return -EINVAL); substream = msynth->output_rfile.output; @@ -146,20 +146,16 @@ static int event_process_midi(snd_seq_event_t * ev, int direct, snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags); return 0; } - res = snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); + snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream); snd_midi_event_reset_decode(msynth->parser); - if (res < 0) - return res; } else { if (msynth->parser == NULL) return -EIO; - res = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); - if (res < 0) - return res; - if ((res = dump_midi(substream, msg, res)) < 0) { + len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev); + if (len < 0) + return 0; + if (dump_midi(substream, msg, len) < 0) snd_midi_event_reset_decode(msynth->parser); - return res; - } } return 0; } diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c index df1e2bb39745..603b63716db6 100644 --- a/sound/core/seq/seq_midi_event.c +++ b/sound/core/seq/seq_midi_event.c @@ -146,7 +146,7 @@ void snd_midi_event_free(snd_midi_event_t *dev) /* * initialize record */ -inline static void reset_encode(snd_midi_event_t *dev) +static inline void reset_encode(snd_midi_event_t *dev) { dev->read = 0; dev->qlen = 0; diff --git a/sound/core/sound.c b/sound/core/sound.c index 7612884f530b..3271e9245490 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -432,7 +432,7 @@ EXPORT_SYMBOL(snd_device_new); EXPORT_SYMBOL(snd_device_register); EXPORT_SYMBOL(snd_device_free); /* isadma.c */ -#ifdef CONFIG_ISA +#ifdef CONFIG_ISA_DMA_API EXPORT_SYMBOL(snd_dma_program); EXPORT_SYMBOL(snd_dma_disable); EXPORT_SYMBOL(snd_dma_pointer); diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index de39d212bc15..e401c6703297 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -98,6 +98,7 @@ int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * int cidx = SNDRV_MINOR_OSS_CARD(minor); int track2 = -1; int register1 = -1, register2 = -1; + struct device *carddev = NULL; if (minor < 0) return minor; @@ -121,11 +122,13 @@ int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t * track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1); break; } - register1 = register_sound_special(reg->f_ops, minor); + if (card) + carddev = card->dev; + register1 = register_sound_special_device(reg->f_ops, minor, carddev); if (register1 != minor) goto __end; if (track2 >= 0) { - register2 = register_sound_special(reg->f_ops, track2); + register2 = register_sound_special_device(reg->f_ops, track2, carddev); if (register2 != track2) goto __end; } diff --git a/sound/core/timer.c b/sound/core/timer.c index cfaccd415b3b..4104f6e292e9 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -799,13 +799,13 @@ static int snd_timer_free(snd_timer_t *timer) return 0; } -int snd_timer_dev_free(snd_device_t *device) +static int snd_timer_dev_free(snd_device_t *device) { snd_timer_t *timer = device->device_data; return snd_timer_free(timer); } -int snd_timer_dev_register(snd_device_t *dev) +static int snd_timer_dev_register(snd_device_t *dev) { snd_timer_t *timer = dev->device_data; snd_timer_t *timer1; @@ -880,9 +880,11 @@ void snd_timer_notify(snd_timer_t *timer, enum sndrv_timer_event event, struct t struct list_head *p, *n; snd_runtime_check(timer->hw.flags & SNDRV_TIMER_HW_SLAVE, return); - snd_assert(event >= SNDRV_TIMER_EVENT_MSTART && event <= SNDRV_TIMER_EVENT_MPAUSE, return); + snd_assert(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) { + if (event == SNDRV_TIMER_EVENT_MSTART || + event == SNDRV_TIMER_EVENT_MCONTINUE || + event == SNDRV_TIMER_EVENT_MRESUME) { if (timer->hw.c_resolution) resolution = timer->hw.c_resolution(timer); else @@ -1555,10 +1557,14 @@ static int snd_timer_user_params(struct file *file, snd_timer_params_t __user *_ (1<<SNDRV_TIMER_EVENT_STOP)| (1<<SNDRV_TIMER_EVENT_CONTINUE)| (1<<SNDRV_TIMER_EVENT_PAUSE)| + (1<<SNDRV_TIMER_EVENT_SUSPEND)| + (1<<SNDRV_TIMER_EVENT_RESUME)| (1<<SNDRV_TIMER_EVENT_MSTART)| (1<<SNDRV_TIMER_EVENT_MSTOP)| (1<<SNDRV_TIMER_EVENT_MCONTINUE)| - (1<<SNDRV_TIMER_EVENT_MPAUSE))) { + (1<<SNDRV_TIMER_EVENT_MPAUSE)| + (1<<SNDRV_TIMER_EVENT_MSUSPEND)| + (1<<SNDRV_TIMER_EVENT_MRESUME))) { err = -EINVAL; goto _end; } diff --git a/sound/core/wrappers.c b/sound/core/wrappers.c index 9f393023c327..508e6d67ee19 100644 --- a/sound/core/wrappers.c +++ b/sound/core/wrappers.c @@ -27,7 +27,7 @@ #include <linux/fs.h> #ifdef CONFIG_SND_DEBUG_MEMORY -void *snd_wrapper_kmalloc(size_t size, int flags) +void *snd_wrapper_kmalloc(size_t size, unsigned int __nocast flags) { return kmalloc(size, flags); } diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 964b97e70c84..986df35fb829 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -168,7 +168,7 @@ typedef struct _snd_uart16550 { static snd_card_t *snd_serial_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; -inline static void snd_uart16550_add_timer(snd_uart16550_t *uart) +static inline void snd_uart16550_add_timer(snd_uart16550_t *uart) { if (! uart->timer_running) { /* timer 38600bps * 10bit * 16byte */ @@ -178,7 +178,7 @@ inline static void snd_uart16550_add_timer(snd_uart16550_t *uart) } } -inline static void snd_uart16550_del_timer(snd_uart16550_t *uart) +static inline void snd_uart16550_del_timer(snd_uart16550_t *uart) { if (uart->timer_running) { del_timer(&uart->buffer_timer); @@ -187,7 +187,7 @@ inline static void snd_uart16550_del_timer(snd_uart16550_t *uart) } /* This macro is only used in snd_uart16550_io_loop */ -inline static void snd_uart16550_buffer_output(snd_uart16550_t *uart) +static inline void snd_uart16550_buffer_output(snd_uart16550_t *uart) { unsigned short buff_out = uart->buff_out; if( uart->buff_in_count > 0 ) { @@ -579,7 +579,7 @@ static int snd_uart16550_output_close(snd_rawmidi_substream_t * substream) return 0; }; -inline static int snd_uart16550_buffer_can_write( snd_uart16550_t *uart, int Num ) +static inline int snd_uart16550_buffer_can_write( snd_uart16550_t *uart, int Num ) { if( uart->buff_in_count + Num < TX_BUFF_SIZE ) return 1; @@ -587,7 +587,7 @@ inline static int snd_uart16550_buffer_can_write( snd_uart16550_t *uart, int Num return 0; } -inline static int snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte) +static inline int snd_uart16550_write_buffer(snd_uart16550_t *uart, unsigned char byte) { unsigned short buff_in = uart->buff_in; if( uart->buff_in_count < TX_BUFF_SIZE ) { diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index f00c88886460..19fc68c23378 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -796,14 +796,14 @@ static int vx_iec958_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontro static snd_kcontrol_new_t vx_control_iec958_mask = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), .info = vx_iec958_info, /* shared */ .get = vx_iec958_mask_get, }; static snd_kcontrol_new_t vx_control_iec958 = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .info = vx_iec958_info, .get = vx_iec958_get, diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index af381b15fe5c..d4becf44e247 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -549,8 +549,8 @@ static int vx_stop_stream(vx_core_t *chip, vx_pipe_t *pipe) static snd_pcm_hardware_t vx_pcm_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID /*|*/ + /*SNDRV_PCM_INFO_RESUME*/), .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5000, @@ -949,8 +949,8 @@ static snd_pcm_ops_t vx_pcm_playback_ops = { static snd_pcm_hardware_t vx_pcm_capture_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_MMAP_VALID /*|*/ + /*SNDRV_PCM_INFO_RESUME*/), .formats = /*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5000, diff --git a/sound/drivers/vx/vx_uer.c b/sound/drivers/vx/vx_uer.c index 18114713c3b3..4fc38bde34f4 100644 --- a/sound/drivers/vx/vx_uer.c +++ b/sound/drivers/vx/vx_uer.c @@ -162,34 +162,24 @@ static int vx_read_uer_status(vx_core_t *chip, int *mode) static int vx_calc_clock_from_freq(vx_core_t *chip, int freq) { -#define XX_FECH48000 0x0000004B -#define XX_FECH32000 0x00000171 -#define XX_FECH24000 0x0000024B -#define XX_FECH16000 0x00000371 -#define XX_FECH12000 0x0000044B -#define XX_FECH8000 0x00000571 -#define XX_FECH44100 0x0000007F -#define XX_FECH29400 0x0000016F -#define XX_FECH22050 0x0000027F -#define XX_FECH14000 0x000003EF -#define XX_FECH11025 0x0000047F -#define XX_FECH7350 0x000005BF - - switch (freq) { - case 48000: return XX_FECH48000; - case 44100: return XX_FECH44100; - case 32000: return XX_FECH32000; - case 29400: return XX_FECH29400; - case 24000: return XX_FECH24000; - case 22050: return XX_FECH22050; - case 16000: return XX_FECH16000; - case 14000: return XX_FECH14000; - case 12000: return XX_FECH12000; - case 11025: return XX_FECH11025; - case 8000: return XX_FECH8000; - case 7350: return XX_FECH7350; - default: return freq; /* The value is already correct */ - } + int hexfreq; + + snd_assert(freq > 0, return 0); + + hexfreq = (28224000 * 10) / freq; + hexfreq = (hexfreq + 5) / 10; + + /* max freq = 55125 Hz */ + snd_assert(hexfreq > 0x00000200, return 0); + + if (hexfreq <= 0x03ff) + return hexfreq - 0x00000201; + if (hexfreq <= 0x07ff) + return (hexfreq / 2) - 1; + if (hexfreq <= 0x0fff) + return (hexfreq / 4) + 0x000001ff; + + return 0x5fe; /* min freq = 6893 Hz */ } diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c index f5e6018ea3f4..5adde308a00f 100644 --- a/sound/i2c/other/ak4114.c +++ b/sound/i2c/other/ak4114.c @@ -554,7 +554,6 @@ int snd_ak4114_check_rate_and_errors(ak4114_t *ak4114, unsigned int flags) if (snd_pcm_running(ak4114->capture_substream)) { // printk("rate changed (%i <- %i)\n", runtime->rate, res); snd_pcm_stop(ak4114->capture_substream, SNDRV_PCM_STATE_DRAINING); - wake_up(&runtime->sleep); res = 1; } snd_pcm_stream_unlock_irqrestore(ak4114->capture_substream, _flags); diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 148a856a43ad..be4ea60a3679 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -1,7 +1,7 @@ # ALSA ISA drivers menu "ISA devices" - depends on SND!=n && ISA + depends on SND!=n && ISA && ISA_DMA_API config SND_AD1848_LIB tristate diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index 563296d02894..0eb442ca23d6 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -53,6 +53,7 @@ static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* Pnp setup */ static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* PnP setup */ +static int clockfreq[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for ad1816a based soundcard."); @@ -74,6 +75,8 @@ module_param_array(dma1, int, NULL, 0444); MODULE_PARM_DESC(dma1, "1st DMA # for ad1816a driver."); module_param_array(dma2, int, NULL, 0444); MODULE_PARM_DESC(dma2, "2nd DMA # for ad1816a driver."); +module_param_array(clockfreq, int, NULL, 0444); +MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0)."); struct snd_card_ad1816a { struct pnp_dev *dev; @@ -209,6 +212,8 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard snd_card_free(card); return error; } + if (clockfreq[dev] >= 5000 && clockfreq[dev] <= 100000) + chip->clock_freq = clockfreq[dev]; strcpy(card->driver, "AD1816A"); strcpy(card->shortname, "ADI SoundPort AD1816A"); diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index 625b2eff14a1..ae860360ecf9 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -234,7 +234,7 @@ static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) ad1816a_t *chip = snd_pcm_substream_chip(substream); unsigned long flags; snd_pcm_runtime_t *runtime = substream->runtime; - unsigned int size; + unsigned int size, rate; spin_lock_irqsave(&chip->lock, flags); @@ -245,7 +245,10 @@ static int snd_ad1816a_playback_prepare(snd_pcm_substream_t *substream) snd_dma_program(chip->dma1, runtime->dma_addr, size, DMA_MODE_WRITE | DMA_AUTOINIT); - snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, runtime->rate); + rate = runtime->rate; + if (chip->clock_freq) + rate = (rate * 33000) / chip->clock_freq; + snd_ad1816a_write(chip, AD1816A_PLAYBACK_SAMPLE_RATE, rate); snd_ad1816a_out_mask(chip, AD1816A_PLAYBACK_CONFIG, AD1816A_FMT_ALL | AD1816A_FMT_STEREO, snd_ad1816a_get_format(chip, runtime->format, @@ -263,7 +266,7 @@ static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) ad1816a_t *chip = snd_pcm_substream_chip(substream); unsigned long flags; snd_pcm_runtime_t *runtime = substream->runtime; - unsigned int size; + unsigned int size, rate; spin_lock_irqsave(&chip->lock, flags); @@ -274,7 +277,10 @@ static int snd_ad1816a_capture_prepare(snd_pcm_substream_t *substream) snd_dma_program(chip->dma2, runtime->dma_addr, size, DMA_MODE_READ | DMA_AUTOINIT); - snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, runtime->rate); + rate = runtime->rate; + if (chip->clock_freq) + rate = (rate * 33000) / chip->clock_freq; + snd_ad1816a_write(chip, AD1816A_CAPTURE_SAMPLE_RATE, rate); snd_ad1816a_out_mask(chip, AD1816A_CAPTURE_CONFIG, AD1816A_FMT_ALL | AD1816A_FMT_STEREO, snd_ad1816a_get_format(chip, runtime->format, diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index 8fb3db103e48..bc642dc94547 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -1196,6 +1196,7 @@ int snd_ad1848_add_ctl(ad1848_t *chip, const char *name, int index, int type, un .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, diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c index 46776cc0c157..1fce8b9f37cf 100644 --- a/sound/isa/cmi8330.c +++ b/sound/isa/cmi8330.c @@ -194,8 +194,8 @@ AD1848_DOUBLE("Wavetable Capture Volume", 0, CMI8330_WAVGAIN, CMI8330_WAVGAIN, 4 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("IEC958 Input Capture Switch", 0, CMI8330_RMUX3D, 7, 1, 1), -AD1848_SINGLE("IEC958 Input Playback Switch", 0, CMI8330_MUTEMUX, 7, 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), }; #ifdef ENABLE_SB_MIXER diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c index 3e7a2a33a5ca..3199941edd9b 100644 --- a/sound/isa/cs423x/cs4231_lib.c +++ b/sound/isa/cs423x/cs4231_lib.c @@ -1346,6 +1346,8 @@ static void snd_cs4231_suspend(cs4231_t *chip) int reg; unsigned long flags; + if (chip->pcm) + 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); diff --git a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c index 337b0e2a8a36..23e1b5f19e1a 100644 --- a/sound/isa/gus/gus_io.c +++ b/sound/isa/gus/gus_io.c @@ -269,8 +269,9 @@ void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, #endif /* 0 */ -unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, - unsigned char reg, short w_16bit) +#ifdef CONFIG_SND_DEBUG +static unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, + unsigned char reg, short w_16bit) { unsigned int res; unsigned long flags; @@ -280,6 +281,7 @@ unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, spin_unlock_irqrestore(&gus->reg_lock, flags); return res; } +#endif /* diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index 94bbd344be5e..a636d9ce3502 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -417,11 +417,13 @@ static int snd_gus_check_version(snd_gus_card_t * gus) return 0; } +#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE)) static void snd_gus_seq_dev_free(snd_seq_device_t *seq_dev) { snd_gus_card_t *gus = seq_dev->private_data; gus->seq_dev = NULL; } +#endif int snd_gus_initialize(snd_gus_card_t *gus) { diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 95c7b3e53407..75bd6eca63e7 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -145,6 +145,14 @@ static snd_card_t *snd_opl3sa2_legacy[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; #ifdef CONFIG_PNP +static struct pnp_device_id snd_opl3sa2_pnpbiosids[] = { + { .id = "YMH0021" }, + { .id = "NMX2210" }, /* Gateway Solo 2500 */ + { .id = "" } /* end */ +}; + +MODULE_DEVICE_TABLE(pnp, snd_opl3sa2_pnpbiosids); + static struct pnp_card_device_id snd_opl3sa2_pnpids[] = { /* Yamaha YMF719E-S (Genius Sound Maker 3DX) */ { .id = "YMH0020", .devs = { { "YMH0021" } } }, @@ -568,20 +576,18 @@ static int snd_opl3sa2_resume(snd_card_t *card) #ifdef CONFIG_PNP static int __init snd_opl3sa2_pnp(int dev, opl3sa2_t *chip, - struct pnp_card_link *card, - const struct pnp_card_device_id *id) + struct pnp_dev *pdev, + int isapnp) { - struct pnp_dev *pdev; - struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); + struct pnp_resource_table * cfg; int err; + if (!isapnp && pnp_device_is_isapnp(pdev)) + return -ENOENT; /* we have another procedure - card */ + + cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); if (!cfg) return -ENOMEM; - pdev = chip->dev = pnp_request_card_device(card, id->devs[0].id, NULL); - if (chip->dev == NULL) { - kfree(cfg); - return -EBUSY; - } /* PnP initialization */ pnp_init_resource_table(cfg); if (sb_port[dev] != SNDRV_AUTO_PORT) @@ -601,7 +607,7 @@ static int __init snd_opl3sa2_pnp(int dev, opl3sa2_t *chip, if (irq[dev] != SNDRV_AUTO_IRQ) pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); err = pnp_manual_config_dev(pdev, cfg, 0); - if (err < 0) + if (err < 0 && isapnp) snd_printk(KERN_ERR "PnP manual resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { @@ -617,13 +623,31 @@ static int __init snd_opl3sa2_pnp(int dev, opl3sa2_t *chip, dma1[dev] = pnp_dma(pdev, 0); dma2[dev] = pnp_dma(pdev, 1); irq[dev] = pnp_irq(pdev, 0); - snd_printdd("PnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n", - sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]); - snd_printdd("PnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", - port[dev], dma1[dev], dma2[dev], irq[dev]); + snd_printdd("%sPnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n", + pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]); + snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", + pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]); kfree(cfg); + chip->dev = pdev; return 0; } + +static int __init snd_opl3sa2_cpnp(int dev, opl3sa2_t *chip, + struct pnp_card_link *card, + const struct pnp_card_device_id *id) +{ + struct pnp_dev *pdev; + struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); + + if (!cfg) + return -ENOMEM; + pdev = pnp_request_card_device(card, id->devs[0].id, NULL); + if (pdev == NULL) { + kfree(cfg); + return -EBUSY; + } + return snd_opl3sa2_pnp(dev, chip, pdev, 1); +} #endif /* CONFIG_PNP */ static int snd_opl3sa2_free(opl3sa2_t *chip) @@ -645,6 +669,7 @@ static int snd_opl3sa2_dev_free(snd_device_t *device) } static int __devinit snd_opl3sa2_probe(int dev, + struct pnp_dev *pdev, struct pnp_card_link *pcard, const struct pnp_card_device_id *pid) { @@ -695,8 +720,13 @@ static int __devinit snd_opl3sa2_probe(int dev, if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) goto __error; #ifdef CONFIG_PNP - if (isapnp[dev]) { - if ((err = snd_opl3sa2_pnp(dev, chip, pcard, pid)) < 0) + if (pdev) { + if ((err = snd_opl3sa2_pnp(dev, chip, pdev, 0)) < 0) + goto __error; + snd_card_set_dev(card, &pdev->dev); + } + if (pcard) { + if ((err = snd_opl3sa2_cpnp(dev, chip, pcard, pid)) < 0) goto __error; snd_card_set_dev(card, &pcard->card->dev); } @@ -768,7 +798,9 @@ static int __devinit snd_opl3sa2_probe(int dev, if ((err = snd_card_register(card)) < 0) goto __error; - if (pcard) + if (pdev) + pnp_set_drvdata(pdev, card); + else if (pcard) pnp_set_card_drvdata(pcard, card); else snd_opl3sa2_legacy[dev] = card; @@ -780,8 +812,8 @@ static int __devinit snd_opl3sa2_probe(int dev, } #ifdef CONFIG_PNP -static int __devinit snd_opl3sa2_pnp_detect(struct pnp_card_link *card, - const struct pnp_card_device_id *id) +static int __devinit snd_opl3sa2_pnp_detect(struct pnp_dev *pdev, + const struct pnp_device_id *id) { static int dev; int res; @@ -789,7 +821,7 @@ static int __devinit snd_opl3sa2_pnp_detect(struct pnp_card_link *card, for ( ; dev < SNDRV_CARDS; dev++) { if (!enable[dev] || !isapnp[dev]) continue; - res = snd_opl3sa2_probe(dev, card, id); + res = snd_opl3sa2_probe(dev, pdev, NULL, NULL); if (res < 0) return res; dev++; @@ -798,7 +830,40 @@ static int __devinit snd_opl3sa2_pnp_detect(struct pnp_card_link *card, return -ENODEV; } -static void __devexit snd_opl3sa2_pnp_remove(struct pnp_card_link * pcard) +static void __devexit snd_opl3sa2_pnp_remove(struct pnp_dev * pdev) +{ + snd_card_t *card = (snd_card_t *) pnp_get_drvdata(pdev); + + snd_card_disconnect(card); + snd_card_free_in_thread(card); +} + +static struct pnp_driver opl3sa2_pnp_driver = { + .name = "opl3sa2-pnpbios", + .id_table = snd_opl3sa2_pnpbiosids, + .probe = snd_opl3sa2_pnp_detect, + .remove = __devexit_p(snd_opl3sa2_pnp_remove), +}; + +static int __devinit snd_opl3sa2_pnp_cdetect(struct pnp_card_link *card, + const struct pnp_card_device_id *id) +{ + static int dev; + int res; + + for ( ; dev < SNDRV_CARDS; dev++) { + if (!enable[dev] || !isapnp[dev]) + continue; + res = snd_opl3sa2_probe(dev, NULL, card, id); + if (res < 0) + return res; + dev++; + return 0; + } + return -ENODEV; +} + +static void __devexit snd_opl3sa2_pnp_cremove(struct pnp_card_link * pcard) { snd_card_t *card = (snd_card_t *) pnp_get_card_drvdata(pcard); @@ -810,8 +875,8 @@ static struct pnp_card_driver opl3sa2_pnpc_driver = { .flags = PNP_DRIVER_RES_DISABLE, .name = "opl3sa2", .id_table = snd_opl3sa2_pnpids, - .probe = snd_opl3sa2_pnp_detect, - .remove = __devexit_p(snd_opl3sa2_pnp_remove), + .probe = snd_opl3sa2_pnp_cdetect, + .remove = __devexit_p(snd_opl3sa2_pnp_cremove), }; #endif /* CONFIG_PNP */ @@ -826,10 +891,11 @@ static int __init alsa_card_opl3sa2_init(void) if (isapnp[dev]) continue; #endif - if (snd_opl3sa2_probe(dev, NULL, NULL) >= 0) + if (snd_opl3sa2_probe(dev, NULL, NULL, NULL) >= 0) cards++; } #ifdef CONFIG_PNP + cards += pnp_register_driver(&opl3sa2_pnp_driver); cards += pnp_register_card_driver(&opl3sa2_pnpc_driver); #endif if (!cards) { diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c index 4afc4a1bc140..26e693078cb3 100644 --- a/sound/isa/sb/emu8000_patch.c +++ b/sound/isa/sb/emu8000_patch.c @@ -128,7 +128,7 @@ snd_emu8000_write_wait(emu8000_t *emu) * This is therefore much slower than need be, but is at least * working. */ -inline static void +static inline void write_word(emu8000_t *emu, int *offset, unsigned short data) { if (emu8000_reset_addr) { diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index a6a0fa516268..a99e642a68b5 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -729,7 +729,7 @@ static int snd_sb16_dma_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_valu } static snd_kcontrol_new_t snd_sb16_dma_control = { - .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = "16-bit DMA Allocation", .info = snd_sb16_dma_control_info, .get = snd_sb16_dma_control_get, diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index cc5a2c6dec16..ff4b59968027 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -688,7 +688,7 @@ static struct sbmix_elem snd_als4000_ctl_3d_poweroff_switch = SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01); static struct sbmix_elem snd_als4000_ctl_3d_delay = SB_SINGLE("3D Delay", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f); -#if NOT_AVAILABLE +#ifdef NOT_AVAILABLE static struct sbmix_elem snd_als4000_ctl_fmdac = SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01); static struct sbmix_elem snd_als4000_ctl_qsound = @@ -723,7 +723,7 @@ static struct sbmix_elem *snd_als4000_controls[] = { &snd_als4000_ctl_3d_output_ratio, &snd_als4000_ctl_3d_delay, &snd_als4000_ctl_3d_poweroff_switch, -#if NOT_AVAILABLE +#ifdef NOT_AVAILABLE &snd_als4000_ctl_fmdac, &snd_als4000_ctl_qsound, #endif diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index 0e13623f69f0..32379688eed4 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -34,7 +34,7 @@ /* weird stuff, derived from port I/O tracing with dosemu */ -unsigned char page_zero[] __initdata = { +static unsigned char page_zero[] __initdata = { 0x01, 0x7c, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x11, 0x00, 0x20, 0x00, 0x32, 0x00, 0x40, 0x00, 0x13, 0x00, 0x00, 0x00, 0x14, 0x02, 0x76, 0x00, 0x60, 0x00, 0x80, 0x02, 0x00, 0x00, @@ -61,7 +61,7 @@ unsigned char page_zero[] __initdata = { 0x1d, 0x02, 0xdf }; -unsigned char page_one[] __initdata = { +static unsigned char page_one[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x19, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xd8, 0x00, 0x00, 0x02, 0x20, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, @@ -88,7 +88,7 @@ unsigned char page_one[] __initdata = { 0x60, 0x00, 0x1b }; -unsigned char page_two[] __initdata = { +static unsigned char page_two[] __initdata = { 0xc4, 0x00, 0x44, 0x07, 0x44, 0x00, 0x40, 0x25, 0x01, 0x06, 0xc4, 0x07, 0x40, 0x25, 0x01, 0x00, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -103,7 +103,7 @@ unsigned char page_two[] __initdata = { 0x46, 0x05, 0x46, 0x07, 0x46, 0x07, 0x44 }; -unsigned char page_three[] __initdata = { +static unsigned char page_three[] __initdata = { 0x07, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x40, 0x00, 0x40, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -118,7 +118,7 @@ unsigned char page_three[] __initdata = { 0x02, 0x00, 0x42, 0x00, 0xc0, 0x00, 0x40 }; -unsigned char page_four[] __initdata = { +static unsigned char page_four[] __initdata = { 0x63, 0x03, 0x26, 0x02, 0x2c, 0x00, 0x24, 0x00, 0x2e, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -133,7 +133,7 @@ unsigned char page_four[] __initdata = { 0x02, 0x62, 0x02, 0x20, 0x01, 0x21, 0x01 }; -unsigned char page_six[] __initdata = { +static unsigned char page_six[] __initdata = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x00, 0x12, 0x00, 0x00, 0x14, 0x00, 0x00, @@ -154,7 +154,7 @@ unsigned char page_six[] __initdata = { 0x80, 0x00, 0x7e, 0x80, 0x80 }; -unsigned char page_seven[] __initdata = { +static unsigned char page_seven[] __initdata = { 0x0f, 0xff, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x0f, @@ -181,7 +181,7 @@ unsigned char page_seven[] __initdata = { 0x00, 0x02, 0x00 }; -unsigned char page_zero_v2[] __initdata = { +static unsigned char page_zero_v2[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -193,7 +193,7 @@ unsigned char page_zero_v2[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -unsigned char page_one_v2[] __initdata = { +static unsigned char page_one_v2[] __initdata = { 0x01, 0xc0, 0x01, 0xfa, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -205,21 +205,21 @@ unsigned char page_one_v2[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -unsigned char page_two_v2[] __initdata = { +static unsigned char page_two_v2[] __initdata = { 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -unsigned char page_three_v2[] __initdata = { +static unsigned char page_three_v2[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -unsigned char page_four_v2[] __initdata = { +static unsigned char page_four_v2[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -227,7 +227,7 @@ unsigned char page_four_v2[] __initdata = { 0x00, 0x00, 0x00, 0x00 }; -unsigned char page_seven_v2[] __initdata = { +static unsigned char page_seven_v2[] __initdata = { 0x0f, 0xff, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -239,7 +239,7 @@ unsigned char page_seven_v2[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -unsigned char mod_v2[] __initdata = { +static unsigned char mod_v2[] __initdata = { 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x03, 0x02, 0x00, 0x01, 0x04, 0x02, 0x00, 0x01, 0x05, 0x02, 0x00, 0x01, 0x06, 0x02, 0x00, 0x01, 0x07, 0x02, 0x00, 0xb0, @@ -269,7 +269,7 @@ unsigned char mod_v2[] __initdata = { 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x05, 0x02, 0x01, 0x01, 0x06, 0x02, 0x01, 0x01, 0x07, 0x02, 0x01 }; -unsigned char coefficients[] __initdata = { +static unsigned char coefficients[] __initdata = { 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x00, 0x4b, 0x03, 0x11, 0x00, 0x4d, 0x01, 0x32, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x07, 0x41, 0x00, 0x00, 0x01, @@ -305,14 +305,14 @@ unsigned char coefficients[] __initdata = { 0x06, 0x6c, 0x4c, 0x6c, 0x06, 0x50, 0x52, 0xe2, 0x06, 0x42, 0x02, 0xba }; -unsigned char coefficients2[] __initdata = { +static unsigned char coefficients2[] __initdata = { 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x45, 0x0f, 0xff, 0x07, 0x48, 0x0f, 0xff, 0x07, 0x7b, 0x04, 0xcc, 0x07, 0x7d, 0x04, 0xcc, 0x07, 0x7c, 0x00, 0x00, 0x07, 0x7e, 0x00, 0x00, 0x07, 0x46, 0x00, 0x00, 0x07, 0x49, 0x00, 0x00, 0x07, 0x47, 0x00, 0x00, 0x07, 0x4a, 0x00, 0x00, 0x07, 0x4c, 0x00, 0x00, 0x07, 0x4e, 0x00, 0x00 }; -unsigned char coefficients3[] __initdata = { +static unsigned char coefficients3[] __initdata = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x51, 0x00, 0x51, 0x00, 0x7a, 0x00, 0x7a, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xf5, 0x00, 0xf5, 0x01, 0x1e, 0x01, 0x1e, 0x01, diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index 7bd95ceab7cc..953e5f3ea03d 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -6,7 +6,7 @@ # Prompt user for primary drivers. config SOUND_BT878 tristate "BT878 audio dma" - depends on SOUND_PRIME + depends on SOUND_PRIME && PCI ---help--- Audio DMA support for bt878 based grabber boards. As you might have already noticed, bt878 is listed with two functions in /proc/pci. @@ -80,14 +80,14 @@ config SOUND_EMU10K1 config MIDI_EMU10K1 bool "Creative SBLive! MIDI (EXPERIMENTAL)" - depends on SOUND_EMU10K1 && EXPERIMENTAL + depends on SOUND_EMU10K1 && EXPERIMENTAL && ISA_DMA_API help Say Y if you want to be able to use the OSS /dev/sequencer interface. This code is still experimental. config SOUND_FUSION tristate "Crystal SoundFusion (CS4280/461x)" - depends on SOUND_PRIME + depends on SOUND_PRIME && PCI help This module drives the Crystal SoundFusion devices (CS4280/46xx series) when wired as native sound drivers with AC97 codecs. If @@ -95,7 +95,7 @@ config SOUND_FUSION config SOUND_CS4281 tristate "Crystal Sound CS4281" - depends on SOUND_PRIME + depends on SOUND_PRIME && PCI help Picture and feature list at <http://www.pcbroker.com/crystal4281.html>. @@ -179,7 +179,7 @@ config SOUND_HARMONY config SOUND_SONICVIBES tristate "S3 SonicVibes" - depends on SOUND_PRIME + depends on SOUND_PRIME && PCI help Say Y or M if you have a PCI sound card utilizing the S3 SonicVibes chipset. To find out if your sound card uses a @@ -226,7 +226,7 @@ config SOUND_AU1550_AC97 config SOUND_TRIDENT tristate "Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core" - depends on SOUND_PRIME + depends on SOUND_PRIME && PCI ---help--- Say Y or M if you have a PCI sound card utilizing the Trident 4DWave-DX/NX chipset or your mother board chipset has SiS 7018 @@ -503,7 +503,7 @@ config SOUND_VIA82CXXX config MIDI_VIA82CXXX bool "VIA 82C686 MIDI" - depends on SOUND_VIA82CXXX + depends on SOUND_VIA82CXXX && ISA_DMA_API help Answer Y to use the MIDI interface of the Via686. You may need to enable this in the BIOS before it will work. This is for connection @@ -512,7 +512,7 @@ config MIDI_VIA82CXXX config SOUND_OSS tristate "OSS sound modules" - depends on SOUND_PRIME + depends on SOUND_PRIME && ISA_DMA_API help OSS is the Open Sound System suite of sound card drivers. They make sound programming easier since they provide a common API. Say Y or diff --git a/sound/oss/Makefile b/sound/oss/Makefile index db9afb61d6ff..9bf3ee544d86 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -80,7 +80,7 @@ obj-$(CONFIG_SOUND_ALI5455) += ali5455.o ac97_codec.o obj-$(CONFIG_SOUND_IT8172) += ite8172.o ac97_codec.o obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o -obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o +obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o ac97_codec.o obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o ifeq ($(CONFIG_MIDI_EMU10K1),y) diff --git a/sound/oss/dmasound/dmasound_awacs.c b/sound/oss/dmasound/dmasound_awacs.c index 2704e1598add..2ceb46f1d40f 100644 --- a/sound/oss/dmasound/dmasound_awacs.c +++ b/sound/oss/dmasound/dmasound_awacs.c @@ -1557,7 +1557,7 @@ static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when) /* All the burgundy functions: */ /* Waits for busy flag to clear */ -inline static void +static inline void awacs_burgundy_busy_wait(void) { int count = 50; /* > 2 samples at 44k1 */ @@ -1565,7 +1565,7 @@ awacs_burgundy_busy_wait(void) udelay(1) ; } -inline static void +static inline void awacs_burgundy_extend_wait(void) { int count = 50 ; /* > 2 samples at 44k1 */ diff --git a/sound/oss/i810_audio.c b/sound/oss/i810_audio.c index 7e9f667cf7a7..b9a640fe48b1 100644 --- a/sound/oss/i810_audio.c +++ b/sound/oss/i810_audio.c @@ -3430,9 +3430,9 @@ out_iospace: release_mem_region(card->iobase_mmio_phys, 256); } out_pio: - release_region(card->iobase, 64); -out_region2: release_region(card->ac97base, 256); +out_region2: + release_region(card->iobase, 64); out_region1: pci_free_consistent(pci_dev, sizeof(struct i810_channel)*NR_HW_CH, card->channel, card->chandma); diff --git a/sound/oss/pss.c b/sound/oss/pss.c index 3ed38765dcc4..a617ccb40e00 100644 --- a/sound/oss/pss.c +++ b/sound/oss/pss.c @@ -714,7 +714,7 @@ static int __init attach_pss(struct address_info *hw_config) disable_all_emulations(); -#if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES +#ifdef YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES if (sound_alloc_dma(hw_config->dma, "PSS")) { printk("pss.c: Can't allocate DMA channel.\n"); diff --git a/sound/oss/vidc.h b/sound/oss/vidc.h index bab7044572d3..d5b8064dc565 100644 --- a/sound/oss/vidc.h +++ b/sound/oss/vidc.h @@ -10,10 +10,6 @@ * VIDC sound function prototypes */ -/* vidc.c */ - -extern int vidc_busy; - /* vidc_fill.S */ /* diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 6d7a00f34d82..1e458919cce6 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -1,11 +1,15 @@ # ALSA PCI drivers -menu "PCI devices" - depends on SND!=n && PCI - config SND_AC97_CODEC tristate select SND_PCM + select SND_AC97_BUS + +config SND_AC97_BUS + tristate + +menu "PCI devices" + depends on SND!=n && PCI config SND_ALI5451 tristate "ALi M5451 PCI Audio Controller" @@ -314,7 +318,7 @@ config SND_YMFPCI config SND_ALS4000 tristate "Avance Logic ALS4000" - depends on SND + depends on SND && ISA_DMA_API select SND_OPL3_LIB select SND_MPU401_UART select SND_PCM diff --git a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile index 3c3222122d8b..77b3482cb133 100644 --- a/sound/pci/ac97/Makefile +++ b/sound/pci/ac97/Makefile @@ -10,9 +10,11 @@ snd-ac97-codec-objs += ac97_proc.o endif snd-ak4531-codec-objs := ak4531_codec.o +snd-ac97-bus-objs := ac97_bus.o # Toplevel Module Dependency obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o +obj-$(CONFIG_SND_AC97_BUS) += snd-ac97-bus.o obj-m := $(sort $(obj-m)) diff --git a/sound/pci/ac97/ac97_bus.c b/sound/pci/ac97/ac97_bus.c new file mode 100644 index 000000000000..227f8b9f67ce --- /dev/null +++ b/sound/pci/ac97/ac97_bus.c @@ -0,0 +1,79 @@ +/* + * Linux driver model AC97 bus interface + * + * Author: Nicolas Pitre + * Created: Jan 14, 2005 + * Copyright: (C) 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 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/init.h> +#include <linux/device.h> +#include <linux/string.h> + +/* + * Codec families have names seperated by commas, so we search for an + * individual codec name within the family string. + */ +static int ac97_bus_match(struct device *dev, struct device_driver *drv) +{ + return (strstr(dev->bus_id, drv->name) != NULL); +} + +static int ac97_bus_suspend(struct device *dev, pm_message_t state) +{ + int ret = 0; + + if (dev->driver && dev->driver->suspend) { + ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE); + if (ret == 0) + ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN); + } + return ret; +} + +static int ac97_bus_resume(struct device *dev) +{ + int ret = 0; + + if (dev->driver && dev->driver->resume) { + ret = dev->driver->resume(dev, RESUME_POWER_ON); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_RESTORE_STATE); + if (ret == 0) + ret = dev->driver->resume(dev, RESUME_ENABLE); + } + return ret; +} + +struct bus_type ac97_bus_type = { + .name = "ac97", + .match = ac97_bus_match, + .suspend = ac97_bus_suspend, + .resume = ac97_bus_resume, +}; + +static int __init ac97_bus_init(void) +{ + return bus_register(&ac97_bus_type); +} + +subsys_initcall(ac97_bus_init); + +static void __exit ac97_bus_exit(void) +{ + bus_unregister(&ac97_bus_type); +} + +module_exit(ac97_bus_exit); + +EXPORT_SYMBOL(ac97_bus_type); + +MODULE_LICENSE("GPL"); diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index a4b72cd2eea0..5501f4440c92 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -157,6 +157,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = { { 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] { 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 { 0x57454301, 0xffffffff, "W83971D", NULL, NULL }, { 0x574d4c00, 0xffffffff, "WM9701A", NULL, NULL }, { 0x574d4C03, 0xffffffff, "WM9703,WM9707,WM9708,WM9717", patch_wolfson03, NULL}, @@ -367,6 +368,7 @@ int snd_ac97_update(ac97_t *ac97, unsigned short reg, unsigned short value) ac97->regs[reg] = value; ac97->bus->ops->write(ac97, reg, value); } + set_bit(reg, ac97->reg_accessed); up(&ac97->reg_mutex); return change; } @@ -410,6 +412,7 @@ int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, ac97->regs[reg] = new; ac97->bus->ops->write(ac97, reg, new); } + set_bit(reg, ac97->reg_accessed); return change; } @@ -1076,6 +1079,11 @@ static void check_volume_resolution(ac97_t *ac97, int reg, unsigned char *lo_max for (i = 0 ; i < ARRAY_SIZE(cbit); i++) { unsigned short val; snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8)); + /* Do the read twice due to buffers on some ac97 codecs. + * e.g. The STAC9704 returns exactly what you wrote the the register + * if you read it immediately. This causes the detect routine to fail. + */ + val = snd_ac97_read(ac97, reg); val = snd_ac97_read(ac97, reg); if (! *lo_max && (val & 0x7f) == cbit[i]) *lo_max = max[i]; @@ -1300,16 +1308,18 @@ static int snd_ac97_mixer_build(ac97_t * ac97) } /* build master tone controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) { - for (idx = 0; idx < 2; idx++) { - if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0) - return err; - if (ac97->id == AC97_ID_YMF753) { - kctl->private_value &= ~(0xff << 16); - kctl->private_value |= 7 << 16; + if (!(ac97->flags & AC97_HAS_NO_TONE)) { + if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_TONE)) { + for (idx = 0; idx < 2; idx++) { + if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_tone[idx], ac97))) < 0) + return err; + if (ac97->id == AC97_ID_YMF753) { + kctl->private_value &= ~(0xff << 16); + kctl->private_value |= 7 << 16; + } } + snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f); } - snd_ac97_write_cache(ac97, AC97_MASTER_TONE, 0x0f0f); } /* build PC Speaker controls */ @@ -1332,11 +1342,13 @@ static int snd_ac97_mixer_build(ac97_t * ac97) } /* build MIC controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) { - if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0) - return err; - if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0) - return err; + if (!(ac97->flags & AC97_HAS_NO_MIC)) { + if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) { + if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0) + return err; + if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0) + return err; + } } /* build Line controls */ @@ -1395,12 +1407,14 @@ static int snd_ac97_mixer_build(ac97_t * ac97) } snd_ac97_write_cache(ac97, AC97_PCM, init_val); } else { - if (ac97->flags & AC97_HAS_NO_PCM_VOL) - err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97); - else - err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97); - if (err < 0) - return err; + if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) { + if (ac97->flags & AC97_HAS_NO_PCM_VOL) + err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97); + else + err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97); + if (err < 0) + return err; + } } /* build Capture controls */ @@ -1800,6 +1814,39 @@ int snd_ac97_bus(snd_card_t *card, int num, ac97_bus_ops_t *ops, return 0; } +/* stop no dev release warning */ +static void ac97_device_release(struct device * dev) +{ +} + +/* register ac97 codec to bus */ +static int snd_ac97_dev_register(snd_device_t *device) +{ + ac97_t *ac97 = device->device_data; + int err; + + ac97->dev.bus = &ac97_bus_type; + ac97->dev.parent = ac97->bus->card->dev; + ac97->dev.platform_data = ac97; + ac97->dev.release = ac97_device_release; + snprintf(ac97->dev.bus_id, BUS_ID_SIZE, "card%d-%d", ac97->bus->card->number, ac97->num); + if ((err = device_register(&ac97->dev)) < 0) { + snd_printk(KERN_ERR "Can't register ac97 bus\n"); + ac97->dev.bus = NULL; + return err; + } + return 0; +} + +/* unregister ac97 codec */ +static int snd_ac97_dev_unregister(snd_device_t *device) +{ + ac97_t *ac97 = device->device_data; + if (ac97->dev.bus) + device_unregister(&ac97->dev); + return snd_ac97_free(ac97); +} + /* build_ops to do nothing */ static struct snd_ac97_build_ops null_build_ops; @@ -1833,6 +1880,8 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97) const ac97_codec_id_t *pid; static snd_device_ops_t ops = { .dev_free = snd_ac97_dev_free, + .dev_register = snd_ac97_dev_register, + .dev_unregister = snd_ac97_dev_unregister, }; snd_assert(rac97 != NULL, return -EINVAL); @@ -2224,7 +2273,7 @@ void snd_ac97_restore_iec958(ac97_t *ac97) */ void snd_ac97_resume(ac97_t *ac97) { - int i; + unsigned long end_time; if (ac97->bus->ops->reset) { ac97->bus->ops->reset(ac97); @@ -2242,26 +2291,26 @@ void snd_ac97_resume(ac97_t *ac97) snd_ac97_write(ac97, AC97_POWERDOWN, ac97->regs[AC97_POWERDOWN]); if (ac97_is_audio(ac97)) { ac97->bus->ops->write(ac97, AC97_MASTER, 0x8101); - for (i = HZ/10; i >= 0; i--) { + end_time = jiffies + msecs_to_jiffies(100); + do { if (snd_ac97_read(ac97, AC97_MASTER) == 0x8101) break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - } + } while (time_after_eq(end_time, jiffies)); /* FIXME: extra delay */ ac97->bus->ops->write(ac97, AC97_MASTER, 0x8000); - if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/4); - } + if (snd_ac97_read(ac97, AC97_MASTER) != 0x8000) + msleep(250); } else { - for (i = HZ/10; i >= 0; i--) { + end_time = jiffies + msecs_to_jiffies(100); + do { unsigned short val = snd_ac97_read(ac97, AC97_EXTENDED_MID); if (val != 0xffff && (val & 1) != 0) break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - } + } while (time_after_eq(end_time, jiffies)); } __reset_ready: @@ -2532,8 +2581,6 @@ int snd_ac97_tune_hardware(ac97_t *ac97, struct ac97_quirk *quirk, const char *o { int result; - snd_assert(quirk, return -EINVAL); - /* quirk overriden? */ if (override && strcmp(override, "-1") && strcmp(override, "default")) { result = apply_quirk_str(ac97, override); @@ -2542,6 +2589,9 @@ int snd_ac97_tune_hardware(ac97_t *ac97, struct ac97_quirk *quirk, const char *o return result; } + if (! quirk) + return -EINVAL; + for (; quirk->subvendor; quirk++) { if (quirk->subvendor != ac97->subsystem_vendor) continue; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index a15eb8522b7c..b584172c1104 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -370,141 +370,387 @@ int patch_yamaha_ymf753(ac97_t * ac97) * added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717. */ -int patch_wolfson03(ac97_t * ac97) +static const snd_kcontrol_new_t wm97xx_snd_ac97_controls[] = { +AC97_DOUBLE("Front Playback Volume", AC97_WM97XX_FMIXER_VOL, 8, 0, 31, 1), +AC97_SINGLE("Front Playback Switch", AC97_WM97XX_FMIXER_VOL, 15, 1, 1), +}; + +static int patch_wolfson_wm9703_specific(ac97_t * ac97) { /* This is known to work for the ViewSonic ViewPad 1000 - Randolph Bentson <bentson@holmsjoen.com> */ + * Randolph Bentson <bentson@holmsjoen.com> + * WM9703/9707/9708/9717 + */ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) { + if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0) + return err; + } + snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808); + return 0; +} + +static struct snd_ac97_build_ops patch_wolfson_wm9703_ops = { + .build_specific = patch_wolfson_wm9703_specific, +}; - // WM9703/9707/9708/9717 - snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808); - snd_ac97_write_cache(ac97, AC97_GENERAL_PURPOSE, 0x8000); +int patch_wolfson03(ac97_t * ac97) +{ + ac97->build_ops = &patch_wolfson_wm9703_ops; return 0; } - -int patch_wolfson04(ac97_t * ac97) + +static const snd_kcontrol_new_t wm9704_snd_ac97_controls[] = { +AC97_DOUBLE("Front Playback Volume", AC97_WM97XX_FMIXER_VOL, 8, 0, 31, 1), +AC97_SINGLE("Front Playback Switch", AC97_WM97XX_FMIXER_VOL, 15, 1, 1), +AC97_DOUBLE("Rear Playback Volume", AC97_WM9704_RMIXER_VOL, 8, 0, 31, 1), +AC97_SINGLE("Rear Playback Switch", AC97_WM9704_RMIXER_VOL, 15, 1, 1), +AC97_DOUBLE("Rear DAC Volume", AC97_WM9704_RPCM_VOL, 8, 0, 31, 1), +AC97_DOUBLE("Surround Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1), +}; + +static int patch_wolfson_wm9704_specific(ac97_t * ac97) { - // WM9704M/9704Q - // set front and rear mixer volume - snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808); - snd_ac97_write_cache(ac97, AC97_WM9704_RMIXER_VOL, 0x0808); - - // patch for DVD noise + int err, i; + for (i = 0; i < ARRAY_SIZE(wm9704_snd_ac97_controls); i++) { + if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9704_snd_ac97_controls[i], ac97))) < 0) + return err; + } + /* patch for DVD noise */ snd_ac97_write_cache(ac97, AC97_WM9704_TEST, 0x0200); - - // init vol - snd_ac97_write_cache(ac97, AC97_WM9704_RPCM_VOL, 0x0808); - - // set rear surround volume - snd_ac97_write_cache(ac97, AC97_SURROUND_MASTER, 0x0000); return 0; } - + +static struct snd_ac97_build_ops patch_wolfson_wm9704_ops = { + .build_specific = patch_wolfson_wm9704_specific, +}; + +int patch_wolfson04(ac97_t * ac97) +{ + /* WM9704M/9704Q */ + ac97->build_ops = &patch_wolfson_wm9704_ops; + return 0; +} + +static int patch_wolfson_wm9705_specific(ac97_t * ac97) +{ + int err, i; + for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) { + if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0) + return err; + } + snd_ac97_write_cache(ac97, 0x72, 0x0808); + return 0; +} + +static struct snd_ac97_build_ops patch_wolfson_wm9705_ops = { + .build_specific = patch_wolfson_wm9705_specific, +}; + int patch_wolfson05(ac97_t * ac97) { - // WM9705, WM9710 - // set front mixer volume - snd_ac97_write_cache(ac97, AC97_WM97XX_FMIXER_VOL, 0x0808); + /* WM9705, WM9710 */ + ac97->build_ops = &patch_wolfson_wm9705_ops; + return 0; +} + +static const char* wm9711_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char* wm9711_alc_mix[] = {"Stereo", "Right", "Left", "None"}; +static const char* wm9711_out3_src[] = {"Left", "VREF", "Left + Right", "Mono"}; +static const char* wm9711_out3_lrsrc[] = {"Master Mix", "Headphone Mix"}; +static const char* wm9711_rec_adc[] = {"Stereo", "Left", "Right", "Mute"}; +static const char* wm9711_base[] = {"Linear Control", "Adaptive Boost"}; +static const char* wm9711_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char* wm9711_mic[] = {"Mic 1", "Differential", "Mic 2", "Stereo"}; +static const char* wm9711_rec_sel[] = + {"Mic 1", "NC", "NC", "Master Mix", "Line", "Headphone Mix", "Phone Mix", "Phone"}; +static const char* wm9711_ng_type[] = {"Constant Gain", "Mute"}; + +static const struct ac97_enum wm9711_enum[] = { +AC97_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9711_alc_select), +AC97_ENUM_SINGLE(AC97_VIDEO, 10, 4, wm9711_alc_mix), +AC97_ENUM_SINGLE(AC97_AUX, 9, 4, wm9711_out3_src), +AC97_ENUM_SINGLE(AC97_AUX, 8, 2, wm9711_out3_lrsrc), +AC97_ENUM_SINGLE(AC97_REC_SEL, 12, 4, wm9711_rec_adc), +AC97_ENUM_SINGLE(AC97_MASTER_TONE, 15, 2, wm9711_base), +AC97_ENUM_DOUBLE(AC97_REC_GAIN, 14, 6, 2, wm9711_rec_gain), +AC97_ENUM_SINGLE(AC97_MIC, 5, 4, wm9711_mic), +AC97_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, wm9711_rec_sel), +AC97_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9711_ng_type), +}; + +static const snd_kcontrol_new_t wm9711_snd_ac97_controls[] = { +AC97_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +AC97_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +AC97_SINGLE("ALC Decay Time", AC97_CODEC_CLASS_REV, 4, 15, 0), +AC97_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +AC97_ENUM("ALC Function", wm9711_enum[0]), +AC97_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 1), +AC97_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 1), +AC97_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +AC97_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +AC97_ENUM("ALC NG Type", wm9711_enum[9]), +AC97_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 1), + +AC97_SINGLE("Side Tone Switch", AC97_VIDEO, 15, 1, 1), +AC97_SINGLE("Side Tone Volume", AC97_VIDEO, 12, 7, 1), +AC97_ENUM("ALC Headphone Mux", wm9711_enum[1]), +AC97_SINGLE("ALC Headphone Volume", AC97_VIDEO, 7, 7, 1), + +AC97_SINGLE("Out3 Switch", AC97_AUX, 15, 1, 1), +AC97_SINGLE("Out3 ZC Switch", AC97_AUX, 7, 1, 1), +AC97_ENUM("Out3 Mux", wm9711_enum[2]), +AC97_ENUM("Out3 LR Mux", wm9711_enum[3]), +AC97_SINGLE("Out3 Volume", AC97_AUX, 0, 31, 1), + +AC97_SINGLE("Beep to Headphone Switch", AC97_PC_BEEP, 15, 1, 1), +AC97_SINGLE("Beep to Headphone Volume", AC97_PC_BEEP, 12, 7, 1), +AC97_SINGLE("Beep to Side Tone Switch", AC97_PC_BEEP, 11, 1, 1), +AC97_SINGLE("Beep to Side Tone Volume", AC97_PC_BEEP, 8, 7, 1), +AC97_SINGLE("Beep to Phone Switch", AC97_PC_BEEP, 7, 1, 1), +AC97_SINGLE("Beep to Phone Volume", AC97_PC_BEEP, 4, 7, 1), + +AC97_SINGLE("Aux to Headphone Switch", AC97_CD, 15, 1, 1), +AC97_SINGLE("Aux to Headphone Volume", AC97_CD, 12, 7, 1), +AC97_SINGLE("Aux to Side Tone Switch", AC97_CD, 11, 1, 1), +AC97_SINGLE("Aux to Side Tone Volume", AC97_CD, 8, 7, 1), +AC97_SINGLE("Aux to Phone Switch", AC97_CD, 7, 1, 1), +AC97_SINGLE("Aux to Phone Volume", AC97_CD, 4, 7, 1), + +AC97_SINGLE("Phone to Headphone Switch", AC97_PHONE, 15, 1, 1), +AC97_SINGLE("Phone to Master Switch", AC97_PHONE, 14, 1, 1), + +AC97_SINGLE("Line to Headphone Switch", AC97_LINE, 15, 1, 1), +AC97_SINGLE("Line to Master Switch", AC97_LINE, 14, 1, 1), +AC97_SINGLE("Line to Phone Switch", AC97_LINE, 13, 1, 1), + +AC97_SINGLE("PCM Playback to Headphone Switch", AC97_PCM, 15, 1, 1), +AC97_SINGLE("PCM Playback to Master Switch", AC97_PCM, 14, 1, 1), +AC97_SINGLE("PCM Playback to Phone Switch", AC97_PCM, 13, 1, 1), + +AC97_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0), +AC97_ENUM("Capture to Phone Mux", wm9711_enum[4]), +AC97_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1), +AC97_ENUM("Capture Select", wm9711_enum[8]), + +AC97_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1), +AC97_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1), + +AC97_ENUM("Bass Control", wm9711_enum[5]), +AC97_SINGLE("Bass Cut-off Switch", AC97_MASTER_TONE, 12, 1, 1), +AC97_SINGLE("Tone Cut-off Switch", AC97_MASTER_TONE, 4, 1, 1), +AC97_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0), + +AC97_SINGLE("ADC Switch", AC97_REC_GAIN, 15, 1, 1), +AC97_ENUM("Capture Volume Steps", wm9711_enum[6]), +AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 1), +AC97_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0), + +AC97_SINGLE("Mic 1 to Phone Switch", AC97_MIC, 14, 1, 1), +AC97_SINGLE("Mic 2 to Phone Switch", AC97_MIC, 13, 1, 1), +AC97_ENUM("Mic Select Source", wm9711_enum[7]), +AC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 32, 1), +AC97_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0), + +AC97_SINGLE("Master ZC Switch", AC97_MASTER, 7, 1, 0), +AC97_SINGLE("Headphone ZC Switch", AC97_HEADPHONE, 7, 1, 0), +AC97_SINGLE("Mono ZC Switch", AC97_MASTER_MONO, 7, 1, 0), +}; + +static int patch_wolfson_wm9711_specific(ac97_t * ac97) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm9711_snd_ac97_controls); i++) { + if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm9711_snd_ac97_controls[i], ac97))) < 0) + return err; + } + snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x0808); + snd_ac97_write_cache(ac97, AC97_PCI_SVID, 0x0808); + snd_ac97_write_cache(ac97, AC97_VIDEO, 0x0808); + snd_ac97_write_cache(ac97, AC97_AUX, 0x0808); + snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808); + snd_ac97_write_cache(ac97, AC97_CD, 0x0000); return 0; } +static struct snd_ac97_build_ops patch_wolfson_wm9711_ops = { + .build_specific = patch_wolfson_wm9711_specific, +}; + int patch_wolfson11(ac97_t * ac97) { - // WM9711, WM9712 - // set out3 volume - snd_ac97_write_cache(ac97, AC97_WM9711_OUT3VOL, 0x0808); + /* WM9711, WM9712 */ + ac97->build_ops = &patch_wolfson_wm9711_ops; + + ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_MIC | + AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD; + return 0; } -static const char* wm9713_mic_mixer[] = {"Stereo", "Mic1", "Mic2", "Mute"}; +static const char* wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"}; static const char* wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"}; -static const char* wm9713_rec_src_l[] = {"Mic1", "Mic2", "Line L", "Mono In", "HP Mix L", "Spk Mix", "Mono Mix", "Zh"}; -static const char* wm9713_rec_src_r[] = {"Mic1", "Mic2", "Line R", "Mono In", "HP Mix R", "Spk Mix", "Mono Mix", "Zh"}; +static const char* wm9713_rec_src[] = + {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone Mix", "Master Mix", + "Mono Mix", "Zh"}; +static const char* wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"}; +static const char* wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"}; +static const char* wm9713_mono_pga[] = {"Vmid", "Zh", "Mono Mix", "Inv 1"}; +static const char* wm9713_spk_pga[] = + {"Vmid", "Zh", "Headphone Mix", "Master Mix", "Inv", "NC", "NC", "NC"}; +static const char* wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone Mix", "NC"}; +static const char* wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "NC"}; +static const char* wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "NC"}; +static const char* wm9713_dac_inv[] = + {"Off", "Mono Mix", "Master Mix", "Headphone Mix L", "Headphone Mix R", + "Headphone Mix Mono", "NC", "Vmid"}; +static const char* wm9713_base[] = {"Linear Control", "Adaptive Boost"}; +static const char* wm9713_ng_type[] = {"Constant Gain", "Mute"}; static const struct ac97_enum wm9713_enum[] = { AC97_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), AC97_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), AC97_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), -AC97_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src_l), -AC97_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src_r), +AC97_ENUM_DOUBLE(AC97_VIDEO, 3, 0, 8, wm9713_rec_src), +AC97_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), +AC97_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), +AC97_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), +AC97_ENUM_DOUBLE(AC97_REC_GAIN, 11, 8, 8, wm9713_spk_pga), +AC97_ENUM_DOUBLE(AC97_REC_GAIN, 6, 4, 4, wm9713_hp_pga), +AC97_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), +AC97_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), +AC97_ENUM_DOUBLE(AC97_REC_GAIN_MIC, 13, 10, 8, wm9713_dac_inv), +AC97_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_base), +AC97_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), }; -static const snd_kcontrol_new_t wm13_snd_ac97_controls_line_in[] = { +static const snd_kcontrol_new_t wm13_snd_ac97_controls[] = { AC97_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1), -AC97_SINGLE("Line In to Headphone Mute", AC97_PC_BEEP, 15, 1, 1), -AC97_SINGLE("Line In to Speaker Mute", AC97_PC_BEEP, 14, 1, 1), -AC97_SINGLE("Line In to Mono Mute", AC97_PC_BEEP, 13, 1, 1), -}; - -static const snd_kcontrol_new_t wm13_snd_ac97_controls_dac[] = { -AC97_DOUBLE("DAC Volume", AC97_PHONE, 8, 0, 31, 1), -AC97_SINGLE("DAC to Headphone Mute", AC97_PHONE, 15, 1, 1), -AC97_SINGLE("DAC to Speaker Mute", AC97_PHONE, 14, 1, 1), -AC97_SINGLE("DAC to Mono Mute", AC97_PHONE, 13, 1, 1), -}; - -static const snd_kcontrol_new_t wm13_snd_ac97_controls_mic[] = { -AC97_SINGLE("MICA Volume", AC97_MIC, 8, 31, 1), -AC97_SINGLE("MICB Volume", AC97_MIC, 0, 31, 1), -AC97_SINGLE("MICA to Mono Mute", AC97_LINE, 7, 1, 1), -AC97_SINGLE("MICB to Mono Mute", AC97_LINE, 6, 1, 1), -AC97_SINGLE("MIC Boost (+20dB)", AC97_LINE, 5, 1, 1), -AC97_ENUM("MIC Headphone Routing", wm9713_enum[0]), -AC97_SINGLE("MIC Headphone Mixer Volume", AC97_LINE, 0, 7, 1) +AC97_SINGLE("Line In to Headphone Switch", AC97_PC_BEEP, 15, 1, 1), +AC97_SINGLE("Line In to Master Switch", AC97_PC_BEEP, 14, 1, 1), +AC97_SINGLE("Line In to Mono Switch", AC97_PC_BEEP, 13, 1, 1), + +AC97_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1), +AC97_SINGLE("PCM Playback to Headphone Switch", AC97_PHONE, 15, 1, 1), +AC97_SINGLE("PCM Playback to Master Switch", AC97_PHONE, 14, 1, 1), +AC97_SINGLE("PCM Playback to Mono Switch", AC97_PHONE, 13, 1, 1), + +AC97_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1), +AC97_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1), +AC97_SINGLE("Mic 1 to Mono Switch", AC97_LINE, 7, 1, 1), +AC97_SINGLE("Mic 2 to Mono Switch", AC97_LINE, 6, 1, 1), +AC97_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0), +AC97_ENUM("Mic to Headphone Mux", wm9713_enum[0]), +AC97_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1), + +AC97_SINGLE("Capture Switch", AC97_CD, 15, 1, 1), +AC97_ENUM("Capture Volume Steps", wm9713_enum[4]), +AC97_DOUBLE("Capture Volume", AC97_CD, 8, 0, 15, 0), +AC97_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0), + +AC97_ENUM("Capture to Headphone Mux", wm9713_enum[1]), +AC97_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1), +AC97_ENUM("Capture to Mono Mux", wm9713_enum[2]), +AC97_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0), +AC97_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0), +AC97_ENUM("Capture Select", wm9713_enum[3]), + +AC97_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0), +AC97_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0), +AC97_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0), +AC97_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0), +AC97_ENUM("ALC Function", wm9713_enum[5]), +AC97_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0), +AC97_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0), +AC97_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0), +AC97_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0), +AC97_ENUM("ALC NG Type", wm9713_enum[13]), +AC97_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0), + +AC97_DOUBLE("Master ZC Switch", AC97_MASTER, 14, 6, 1, 0), +AC97_DOUBLE("Headphone ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0), +AC97_DOUBLE("Out3/4 ZC Switch", AC97_MASTER_MONO, 14, 6, 1, 0), +AC97_SINGLE("Master Right Switch", AC97_MASTER, 7, 1, 1), +AC97_SINGLE("Headphone Right Switch", AC97_HEADPHONE, 7, 1, 1), +AC97_SINGLE("Out3/4 Right Switch", AC97_MASTER_MONO, 7, 1, 1), + +AC97_SINGLE("Mono In to Headphone Switch", AC97_MASTER_TONE, 15, 1, 1), +AC97_SINGLE("Mono In to Master Switch", AC97_MASTER_TONE, 14, 1, 1), +AC97_SINGLE("Mono In Volume", AC97_MASTER_TONE, 8, 31, 1), +AC97_SINGLE("Mono Switch", AC97_MASTER_TONE, 7, 1, 1), +AC97_SINGLE("Mono ZC Switch", AC97_MASTER_TONE, 6, 1, 0), +AC97_SINGLE("Mono Volume", AC97_MASTER_TONE, 0, 31, 1), + +AC97_SINGLE("PC Beep to Headphone Switch", AC97_AUX, 15, 1, 1), +AC97_SINGLE("PC Beep to Headphone Volume", AC97_AUX, 12, 7, 1), +AC97_SINGLE("PC Beep to Master Switch", AC97_AUX, 11, 1, 1), +AC97_SINGLE("PC Beep to Master Volume", AC97_AUX, 8, 7, 1), +AC97_SINGLE("PC Beep to Mono Switch", AC97_AUX, 7, 1, 1), +AC97_SINGLE("PC Beep to Mono Volume", AC97_AUX, 4, 7, 1), + +AC97_SINGLE("Voice to Headphone Switch", AC97_PCM, 15, 1, 1), +AC97_SINGLE("Voice to Headphone Volume", AC97_PCM, 12, 7, 1), +AC97_SINGLE("Voice to Master Switch", AC97_PCM, 11, 1, 1), +AC97_SINGLE("Voice to Master Volume", AC97_PCM, 8, 7, 1), +AC97_SINGLE("Voice to Mono Switch", AC97_PCM, 7, 1, 1), +AC97_SINGLE("Voice to Mono Volume", AC97_PCM, 4, 7, 1), + +AC97_SINGLE("Aux to Headphone Switch", AC97_REC_SEL, 15, 1, 1), +AC97_SINGLE("Aux to Headphone Volume", AC97_REC_SEL, 12, 7, 1), +AC97_SINGLE("Aux to Master Switch", AC97_REC_SEL, 11, 1, 1), +AC97_SINGLE("Aux to Master Volume", AC97_REC_SEL, 8, 7, 1), +AC97_SINGLE("Aux to Mono Switch", AC97_REC_SEL, 7, 1, 1), +AC97_SINGLE("Aux to Mono Volume", AC97_REC_SEL, 4, 7, 1), + +AC97_ENUM("Mono Input Mux", wm9713_enum[6]), +AC97_ENUM("Master Input Mux", wm9713_enum[7]), +AC97_ENUM("Headphone Input Mux", wm9713_enum[8]), +AC97_ENUM("Out 3 Input Mux", wm9713_enum[9]), +AC97_ENUM("Out 4 Input Mux", wm9713_enum[10]), + +AC97_ENUM("Bass Control", wm9713_enum[12]), +AC97_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1), +AC97_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1), +AC97_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0), +AC97_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1), +AC97_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1), }; -static const snd_kcontrol_new_t wm13_snd_ac97_controls_adc[] = { -AC97_SINGLE("ADC Mute", AC97_CD, 15, 1, 1), -AC97_DOUBLE("Gain Step Size (1.5dB/0.75dB)", AC97_CD, 14, 6, 1, 1), -AC97_DOUBLE("ADC Volume",AC97_CD, 8, 0, 15, 0), -AC97_SINGLE("ADC Zero Cross", AC97_CD, 7, 1, 1), +static const snd_kcontrol_new_t wm13_snd_ac97_controls_3d[] = { +AC97_ENUM("Inv Input Mux", wm9713_enum[11]), +AC97_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0), +AC97_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0), +AC97_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1), }; -static const snd_kcontrol_new_t wm13_snd_ac97_controls_recsel[] = { -AC97_ENUM("Record to Headphone Path", wm9713_enum[1]), -AC97_SINGLE("Record to Headphone Volume", AC97_VIDEO, 11, 7, 0), -AC97_ENUM("Record to Mono Path", wm9713_enum[2]), -AC97_SINGLE("Record to Mono Boost (+20dB)", AC97_VIDEO, 8, 1, 0), -AC97_SINGLE("Record ADC Boost (+20dB)", AC97_VIDEO, 6, 1, 0), -AC97_ENUM("Record Select Left", wm9713_enum[3]), -AC97_ENUM("Record Select Right", wm9713_enum[4]), -}; +static int patch_wolfson_wm9713_3d (ac97_t * ac97) +{ + int err, i; + + for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_3d); i++) { + if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_3d[i], ac97))) < 0) + return err; + } + return 0; +} static int patch_wolfson_wm9713_specific(ac97_t * ac97) { int err, i; - for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_line_in); i++) { - if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_line_in[i], ac97))) < 0) + for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls); i++) { + if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls[i], ac97))) < 0) return err; } snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x0808); - - for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_dac); i++) { - if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_dac[i], ac97))) < 0) - return err; - } snd_ac97_write_cache(ac97, AC97_PHONE, 0x0808); - - for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_mic); i++) { - if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_mic[i], ac97))) < 0) - return err; - } snd_ac97_write_cache(ac97, AC97_MIC, 0x0808); snd_ac97_write_cache(ac97, AC97_LINE, 0x00da); - - for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_adc); i++) { - if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_adc[i], ac97))) < 0) - return err; - } snd_ac97_write_cache(ac97, AC97_CD, 0x0808); - - for (i = 0; i < ARRAY_SIZE(wm13_snd_ac97_controls_recsel); i++) { - if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm13_snd_ac97_controls_recsel[i], ac97))) < 0) - return err; - } snd_ac97_write_cache(ac97, AC97_VIDEO, 0xd612); snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x1ba0); - return 0; } @@ -525,6 +771,7 @@ static void patch_wolfson_wm9713_resume (ac97_t * ac97) static struct snd_ac97_build_ops patch_wolfson_wm9713_ops = { .build_specific = patch_wolfson_wm9713_specific, + .build_3d = patch_wolfson_wm9713_3d, #ifdef CONFIG_PM .suspend = patch_wolfson_wm9713_suspend, .resume = patch_wolfson_wm9713_resume @@ -533,10 +780,13 @@ static struct snd_ac97_build_ops patch_wolfson_wm9713_ops = { int patch_wolfson13(ac97_t * ac97) { + /* WM9713, WM9714 */ ac97->build_ops = &patch_wolfson_wm9713_ops; ac97->flags |= AC97_HAS_NO_REC_GAIN | AC97_STEREO_MUTES | AC97_HAS_NO_PHONE | - AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD; + AC97_HAS_NO_PC_BEEP | AC97_HAS_NO_VIDEO | AC97_HAS_NO_CD | AC97_HAS_NO_TONE | + AC97_HAS_NO_STD_PCM; + ac97->scaps &= ~AC97_SCAP_MODEM; snd_ac97_write_cache(ac97, AC97_EXTENDED_MID, 0xda00); snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0x3810); @@ -1379,6 +1629,7 @@ static void check_ad1981_hp_jack_sense(ac97_t *ac97) u32 subid = ((u32)ac97->subsystem_vendor << 16) | ac97->subsystem_device; switch (subid) { case 0x103c0890: /* HP nc6000 */ + case 0x103c099c: /* HP nx6110 */ case 0x103c006d: /* HP nx9105 */ case 0x17340088: /* FSC Scenic-W */ /* enable headphone jack sense */ @@ -1528,6 +1779,9 @@ static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = { }, AC97_SURROUND_JACK_MODE_CTL, AC97_CHANNEL_MODE_CTL, + + AC97_SINGLE("Headphone Jack Sense", AC97_AD_JACK_SPDIF, 10, 1, 0), + AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 12, 1, 0), }; static int patch_ad1888_specific(ac97_t *ac97) @@ -1703,7 +1957,7 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = { }; static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = { - AC97_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0), + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_ALC650_MULTICH, 11, 1, 0), AC97_SINGLE("Analog to IEC958 Output", AC97_ALC650_MULTICH, 12, 1, 0), /* disable this controls since it doesn't work as expected */ /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */ @@ -1846,12 +2100,12 @@ static int alc655_iec958_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_ } static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc655[] = { - AC97_PAGE_SINGLE("IEC958 Capture Switch", AC97_ALC650_MULTICH, 11, 1, 0, 0), + AC97_PAGE_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_ALC650_MULTICH, 11, 1, 0, 0), /* disable this controls since it doesn't work as expected */ /* AC97_PAGE_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 14, 1, 0, 0), */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Route", + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", .info = alc655_iec958_route_info, .get = alc655_iec958_route_get, .put = alc655_iec958_route_put, @@ -2413,6 +2667,16 @@ int patch_vt1616(ac97_t * ac97) } /* + * VT1617A codec + */ +int patch_vt1617a(ac97_t * ac97) +{ + ac97->ext_id |= AC97_EI_SPDIF; /* force the detection of spdif */ + ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000; + return 0; +} + +/* */ static void it2646_update_jacks(ac97_t *ac97) { @@ -2430,7 +2694,7 @@ static const snd_kcontrol_new_t snd_ac97_controls_it2646[] = { }; static const snd_kcontrol_new_t snd_ac97_spdif_controls_it2646[] = { - AC97_SINGLE("IEC958 Capture Switch", 0x76, 11, 1, 0), + AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0x76, 11, 1, 0), AC97_SINGLE("Analog to IEC958 Output", 0x76, 12, 1, 0), AC97_SINGLE("IEC958 Input Monitor", 0x76, 13, 1, 0), }; diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index 7b7377d0f2ae..ec1811320106 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -56,5 +56,6 @@ int patch_cm9739(ac97_t * ac97); int patch_cm9761(ac97_t * ac97); int patch_cm9780(ac97_t * ac97); int patch_vt1616(ac97_t * ac97); +int patch_vt1617a(ac97_t * ac97); int patch_it2646(ac97_t * ac97); int mpatch_si3036(ac97_t * ac97); diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index eb5c36d31a52..ce6c9fadb594 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -399,7 +399,7 @@ static int snd_ali_codec_ready( ali_t *codec, unsigned long end_time; unsigned int res; - end_time = jiffies + 10 * (HZ >> 2); + end_time = jiffies + 10 * msecs_to_jiffies(250); do { res = snd_ali_5451_peek(codec,port); if (! (res & 0x8000)) @@ -422,7 +422,7 @@ static int snd_ali_stimer_ready(ali_t *codec, int sched) dwChk1 = snd_ali_5451_peek(codec, ALI_STIMER); dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); - end_time = jiffies + 10 * (HZ >> 2); + end_time = jiffies + 10 * msecs_to_jiffies(250); do { dwChk2 = snd_ali_5451_peek(codec, ALI_STIMER); if (dwChk2 != dwChk1) @@ -1842,7 +1842,7 @@ static int __devinit snd_ali_pcm(ali_t * codec, int device, struct ali_pcm_descr return 0; } -struct ali_pcm_description ali_pcms[] = { +static struct ali_pcm_description ali_pcms[] = { { "ALI 5451", ALI_CHANNELS, 1, &snd_ali_playback_ops, &snd_ali_capture_ops }, { "ALI 5451 modem", 1, 1, &snd_ali_modem_playback_ops, &snd_ali_modem_capture_ops } }; @@ -1959,9 +1959,9 @@ static int snd_ali5451_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t static snd_kcontrol_new_t snd_ali5451_mixer_spdif[] __devinitdata = { /* spdif aplayback switch */ /* FIXME: "IEC958 Playback Switch" may conflict with one on ac97_codec */ - ALI5451_SPDIF("IEC958 Output switch", 0, 0), + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), 0, 0), /* spdif out to spdif channel */ - ALI5451_SPDIF("IEC958 Channel Output Switch", 0, 1), + ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("Channel Output ",NONE,SWITCH), 0, 1), /* spdif in from spdif channel */ ALI5451_SPDIF(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, 2) }; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index cafab4af5c57..188df085b7ee 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -248,6 +248,7 @@ struct snd_atiixp_dma { unsigned int period_bytes, periods; int opened; int running; + int suspended; int pcm_open_flag; int ac97_pcm_type; /* index # of ac97_pcm to access, -1 = not used */ unsigned int saved_curptr; @@ -699,12 +700,18 @@ static int snd_atiixp_pcm_trigger(snd_pcm_substream_t *substream, int cmd) spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: dma->ops->enable_transfer(chip, 1); dma->running = 1; + dma->suspended = 0; break; case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: dma->ops->enable_transfer(chip, 0); dma->running = 0; + dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND; break; default: err = -EINVAL; @@ -975,6 +982,7 @@ static snd_pcm_hardware_t snd_atiixp_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, @@ -1419,7 +1427,7 @@ static int snd_atiixp_suspend(snd_card_t *card, pm_message_t state) snd_atiixp_aclink_down(chip); snd_atiixp_chip_stop(chip); - pci_set_power_state(chip->pci, 3); + pci_set_power_state(chip->pci, PCI_D3hot); pci_disable_device(chip->pci); return 0; } @@ -1430,7 +1438,7 @@ static int snd_atiixp_resume(snd_card_t *card) int i; pci_enable_device(chip->pci); - pci_set_power_state(chip->pci, 0); + pci_set_power_state(chip->pci, PCI_D0); pci_set_master(chip->pci); snd_atiixp_aclink_reset(chip); @@ -1443,7 +1451,7 @@ static int snd_atiixp_resume(snd_card_t *card) for (i = 0; i < NUM_ATI_PCMDEVS; i++) if (chip->pcmdevs[i]) { atiixp_dma_t *dma = &chip->dmas[i]; - if (dma->substream && dma->running) { + if (dma->substream && dma->suspended) { dma->ops->enable_dma(chip, 1); writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN, chip->remap_addr + dma->ops->llp_offset); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index a6b4b8d589fd..8d2002951bd7 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -265,6 +265,7 @@ struct snd_atiixp { */ static struct pci_device_id snd_atiixp_ids[] = { { 0x1002, 0x434d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB200 */ + { 0x1002, 0x4378, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB400 */ { 0, } }; diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 04dcefd8b8ff..38bd2b5dd434 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -33,7 +33,7 @@ /* hardware definition */ static snd_pcm_hardware_t snd_vortex_playback_hw_adb = { .info = - (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME | + (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID), .formats = @@ -58,7 +58,7 @@ static snd_pcm_hardware_t snd_vortex_playback_hw_adb = { #ifndef CHIP_AU8820 static snd_pcm_hardware_t snd_vortex_playback_hw_a3d = { .info = - (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME | + (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID), .formats = @@ -78,7 +78,7 @@ static snd_pcm_hardware_t snd_vortex_playback_hw_a3d = { #endif static snd_pcm_hardware_t snd_vortex_playback_hw_spdif = { .info = - (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_RESUME | + (SNDRV_PCM_INFO_MMAP | /* SNDRV_PCM_INFO_RESUME | */ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID), .formats = @@ -220,8 +220,10 @@ snd_vortex_pcm_hw_params(snd_pcm_substream_t * substream, vortex_adb_allocroute(chip, -1, params_channels(hw_params), substream->stream, type); - if (dma < 0) + if (dma < 0) { + spin_unlock_irq(&chip->lock); return dma; + } stream = substream->runtime->private_data = &chip->dma_adb[dma]; stream->substream = substream; /* Setup Buffers. */ diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index 95c289284267..7e27bfc37439 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -188,6 +188,14 @@ static ca0106_details_t ca0106_chip_details[] = { .name = "MSI K8N Diamond MB [SB0438]", .gpio_type = 1, .i2c_adc = 1 } , + /* Shuttle XPC SD31P which has an onboard Creative Labs Sound Blaster Live! 24-bit EAX + * high-definition 7.1 audio processor". + * Added using info from andrewvegan in alsa bug #1298 + */ + { .serial = 0x30381297, + .name = "Shuttle XPC SD31P [SD31P]", + .gpio_type = 1, + .i2c_adc = 1 } , { .serial = 0, .name = "AudigyLS [Unknown]" } }; diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 0e5e9ce0ff28..b6b8882ce704 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -297,7 +297,7 @@ static int snd_ca0106_spdif_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_ca0106_spdif_mask_control = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), .count = 4, .info = snd_ca0106_spdif_info, @@ -306,7 +306,7 @@ static snd_kcontrol_new_t snd_ca0106_spdif_mask_control = static snd_kcontrol_new_t snd_ca0106_spdif_control = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .count = 4, .info = snd_ca0106_spdif_info, diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index b4503385ea69..b098b51099c2 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -306,7 +306,7 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address."); #define CM_REG_FM_PCI 0x50 /* - * for CMI-8338 .. this is not valid for CMI-8738. + * access from SB-mixer port */ #define CM_REG_EXTENT_IND 0xf0 #define CM_VPHONE_MASK 0xe0 /* Phone volume control (0-3) << 5 */ @@ -315,6 +315,7 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address."); #define CM_VSPKM 0x08 /* Speaker mute control, default high */ #define CM_RLOOPREN 0x04 /* Rec. R-channel enable */ #define CM_RLOOPLEN 0x02 /* Rec. L-channel enable */ +#define CM_VADMIC3 0x01 /* Mic record boost */ /* * CMI-8338 spec ver 0.5 (this is not valid for CMI-8738): @@ -488,32 +489,34 @@ struct snd_stru_cmipci { /* read/write operations for dword register */ -inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data) +static inline void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data) { outl(data, cm->iobase + cmd); } -inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd) + +static inline unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd) { return inl(cm->iobase + cmd); } /* read/write operations for word register */ -inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data) +static inline void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data) { outw(data, cm->iobase + cmd); } -inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd) + +static inline unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd) { return inw(cm->iobase + cmd); } /* read/write operations for byte register */ -inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data) +static inline void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data) { outb(data, cm->iobase + cmd); } -inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd) +static inline unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd) { return inb(cm->iobase + cmd); } @@ -1026,7 +1029,7 @@ static int snd_cmipci_spdif_mask_get(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_cmipci_spdif_mask_info, .get = snd_cmipci_spdif_mask_get, @@ -2133,8 +2136,12 @@ static snd_kcontrol_new_t snd_cmipci_mixers[] __devinitdata = { CMIPCI_MIXER_VOL_STEREO("Aux Playback Volume", CM_REG_AUX_VOL, 4, 0, 15), CMIPCI_MIXER_SW_STEREO("Aux Playback Switch", CM_REG_MIXER2, CM_VAUXLM_SHIFT, CM_VAUXRM_SHIFT, 0), CMIPCI_MIXER_SW_STEREO("Aux Capture Switch", CM_REG_MIXER2, CM_RAUXLEN_SHIFT, CM_RAUXREN_SHIFT, 0), - CMIPCI_MIXER_SW_MONO("Mic Boost", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), + CMIPCI_MIXER_SW_MONO("Mic Boost Playback Switch", CM_REG_MIXER2, CM_MICGAINZ_SHIFT, 1), CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), + CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7), + CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0), + CMIPCI_DOUBLE("PC Speaker Playnack Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), + CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0), }; /* diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index eb3c52b03af3..c7a370d4f923 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -542,7 +542,7 @@ static void snd_cs4281_delay(unsigned int delay) } } -inline static void snd_cs4281_delay_long(void) +static inline void snd_cs4281_delay_long(void) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index db212ecd792a..b9fff4ee6f9d 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -113,7 +113,7 @@ static int __devinit snd_card_cs46xx_probe(struct pci_dev *pci, return err; } #endif - if ((err = snd_cs46xx_mixer(chip)) < 0) { + if ((err = snd_cs46xx_mixer(chip, 2)) < 0) { snd_card_free(card); return err; } diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index fd4c50c88bc9..4b052158ee33 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -1243,8 +1243,8 @@ static snd_pcm_hardware_t snd_cs46xx_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/ + /*SNDRV_PCM_INFO_RESUME*/), .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE), @@ -1265,8 +1265,8 @@ static snd_pcm_hardware_t snd_cs46xx_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/ + /*SNDRV_PCM_INFO_RESUME*/), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5500, @@ -2231,7 +2231,7 @@ static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Output Switch", + .name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), .info = snd_mixer_boolean_info, .get = snd_cs46xx_iec958_get, .put = snd_cs46xx_iec958_put, @@ -2239,7 +2239,7 @@ static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Input Switch", + .name = SNDRV_CTL_NAME_IEC958("Input ",NONE,SWITCH), .info = snd_mixer_boolean_info, .get = snd_cs46xx_iec958_get, .put = snd_cs46xx_iec958_put, @@ -2249,7 +2249,7 @@ static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = { /* Input IEC958 volume does not work for the moment. (Benny) */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Input Volume", + .name = SNDRV_CTL_NAME_IEC958("Input ",NONE,VOLUME), .info = snd_cs46xx_vol_info, .get = snd_cs46xx_vol_iec958_get, .put = snd_cs46xx_vol_iec958_put, @@ -2400,8 +2400,7 @@ static void snd_cs46xx_codec_reset (ac97_t * ac97) if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05) return; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/100); + msleep(10); } while (time_after_eq(end_time, jiffies)); snd_printk("CS46xx secondary codec dont respond!\n"); @@ -2435,14 +2434,13 @@ static int __devinit cs46xx_detect_codec(cs46xx_t *chip, int codec) err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97[codec]); return err; } - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/100); + msleep(10); } snd_printdd("snd_cs46xx: codec %d detection timeout\n", codec); return -ENXIO; } -int __devinit snd_cs46xx_mixer(cs46xx_t *chip) +int __devinit snd_cs46xx_mixer(cs46xx_t *chip, int spdif_device) { snd_card_t *card = chip->card; snd_ctl_elem_id_t id; @@ -2478,6 +2476,8 @@ int __devinit snd_cs46xx_mixer(cs46xx_t *chip) for (idx = 0; idx < ARRAY_SIZE(snd_cs46xx_controls); idx++) { snd_kcontrol_t *kctl; kctl = snd_ctl_new1(&snd_cs46xx_controls[idx], chip); + if (kctl && kctl->id.iface == SNDRV_CTL_ELEM_IFACE_PCM) + kctl->id.device = spdif_device; if ((err = snd_ctl_add(card, kctl)) < 0) return err; } @@ -3018,8 +3018,7 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip) /* * Wait until the PLL has stabilized. */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/10); /* 100ms */ + msleep(100); /* * Turn on clocking of the core so that we can setup the serial ports. @@ -3072,8 +3071,7 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip) */ if (snd_cs46xx_peekBA0(chip, BA0_ACSTS) & ACSTS_CRDY) goto ok1; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((HZ+99)/100); + msleep(10); } @@ -3122,8 +3120,7 @@ static int snd_cs46xx_chip_init(cs46xx_t *chip) */ if ((snd_cs46xx_peekBA0(chip, BA0_ACISV) & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) goto ok2; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((HZ+99)/100); + msleep(10); } #ifndef CONFIG_SND_CS46XX_NEW_DSP diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 2085a998eaeb..fc377c4b666c 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -52,6 +52,7 @@ static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64}; static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128}; static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static uint subsystem[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* Force card subsystem model */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard."); @@ -71,7 +72,8 @@ module_param_array(max_buffer_size, int, NULL, 0444); MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB."); module_param_array(enable_ir, bool, NULL, 0444); MODULE_PARM_DESC(enable_ir, "Enable IR."); - +module_param_array(subsystem, uint, NULL, 0444); +MODULE_PARM_DESC(subsystem, "Force card subsystem model."); /* * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400 */ @@ -122,7 +124,7 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, max_buffer_size[dev] = 1024; if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev], (long)max_buffer_size[dev] * 1024 * 1024, - enable_ir[dev], + enable_ir[dev], subsystem[dev], &emu)) < 0) { snd_card_free(card); return err; @@ -140,14 +142,14 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, return err; } /* This stores the periods table. */ - if (emu->audigy && emu->revision == 4) { /* P16V */ + if (emu->card_capabilities->ca0151_chip) { /* P16V */ if(snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), 1024, &emu->p16v_buffer) < 0) { snd_p16v_free(emu); return -ENOMEM; } } - if ((err = snd_emu10k1_mixer(emu)) < 0) { + if ((err = snd_emu10k1_mixer(emu, 0, 3)) < 0) { snd_card_free(card); return err; } @@ -161,7 +163,7 @@ static int __devinit snd_card_emu10k1_probe(struct pci_dev *pci, snd_card_free(card); return err; } - if (emu->audigy && emu->revision == 4) { /* P16V */ + if (emu->card_capabilities->ca0151_chip) { /* P16V */ if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index a341e758acde..e69d5b739e80 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -191,7 +191,7 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) /* Set playback routing. */ snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4); } - if (emu->audigy && (emu->serial == 0x10011102) ) { /* audigy2 Value */ + if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */ /* Hacks for Alice3 to work independent of haP16V driver */ u32 tmp; @@ -253,6 +253,8 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); else outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); + /* FIXME: Remove all these emu->model and replace it with a card recognition parameter, + * e.g. card_capabilities->joystick */ } else if (emu->model == 0x20 || emu->model == 0xc400 || (emu->model == 0x21 && emu->revision < 6)) @@ -299,12 +301,12 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir) if (emu->audigy) { outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); - if (emu->revision == 4) { /* audigy2 */ + if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ /* Unmute Analog now. Set GPO6 to 1 for Apollo. * This has to be done after init ALice3 I2SOut beyond 48KHz. * So, sequence is important. */ outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); - } else if (emu->serial == 0x10011102) { /* audigy2 value */ + } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */ /* Unmute Analog now. */ outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG); } else { @@ -614,6 +616,7 @@ static int snd_emu10k1_dev_free(snd_device_t *device) static emu_chip_details_t emu_chip_details[] = { /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/ + /* Tested by James@superbug.co.uk 3rd July 2005 */ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102, .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", .id = "Audigy2", @@ -627,6 +630,14 @@ static emu_chip_details_t emu_chip_details[] = { .emu10k2_chip = 1, .ca0108_chip = 1, .ac97_chip = 1} , + /* Tested by James@superbug.co.uk 8th July 2005. No sound available yet. */ + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x40011102, + .driver = "Audigy2", .name = "E-mu 1212m [4001]", + .id = "EMU1212m", + .emu10k2_chip = 1, + .ca0102_chip = 1, + .ecard = 1} , + /* Tested by James@superbug.co.uk 3rd July 2005 */ {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102, .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", .id = "Audigy2", @@ -687,18 +698,18 @@ static emu_chip_details_t emu_chip_details[] = { .ca0151_chip = 1, .spdif_bug = 1, .ac97_chip = 1} , - {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10020052, - .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102, + .driver = "Audigy", .name = "Audigy 1 [SB0090]", .id = "Audigy", .emu10k2_chip = 1, .ca0102_chip = 1, - .spdif_bug = 1, .ac97_chip = 1} , - {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102, - .driver = "Audigy", .name = "Audigy 1 [SB0090]", + {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00521102, + .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", .id = "Audigy", .emu10k2_chip = 1, .ca0102_chip = 1, + .spdif_bug = 1, .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102, .driver = "Audigy", .name = "Audigy 1 [SB0090]", @@ -712,54 +723,57 @@ static emu_chip_details_t emu_chip_details[] = { .emu10k2_chip = 1, .ca0102_chip = 1, .ac97_chip = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102, - .driver = "EMU10K1", .name = "E-mu APS [4001]", - .id = "APS", - .emu10k1_chip = 1, - .ecard = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102, - .driver = "EMU10K1", .name = "SBLive! Player 5.1 [SB0060]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102, + .driver = "EMU10K1", .name = "SBLive! [SB0105]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102, - .driver = "EMU10K1", .name = "SB Live 5.1", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102, + .driver = "EMU10K1", .name = "SBLive! Value [SB0103]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102, - .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102, + .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", .id = "Live", .emu10k1_chip = 1, - .ac97_chip = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102, - .driver = "EMU10K1", .name = "SBLive! [CT4620]", + .ac97_chip = 1, + .sblive51 = 1} , + /* Tested by Thomas Zehetbauer 27th Aug 2005 */ + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80651102, + .driver = "EMU10K1", .name = "SB Live 5.1 [SB0220]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102, - .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102, + .driver = "EMU10K1", .name = "SB Live 5.1", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102, - .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", + /* Tested by alsa bugtrack user "hus" 12th Sept 2005 */ + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102, + .driver = "EMU10K1", .name = "SBLive! Player 5.1 [SB0060]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102, - .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102, - .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102, + .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", + .id = "Live", + .emu10k1_chip = 1, + .ac97_chip = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, @@ -770,50 +784,50 @@ static emu_chip_details_t emu_chip_details[] = { .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102, - .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102, - .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", + /* Tested by James@superbug.co.uk 3rd July 2005 */ + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102, - .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102, - .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102, + .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102, - .driver = "EMU10K1", .name = "SBLive! Value [SB0060]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102, - .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", - .id = "Live", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102, + .driver = "EMU10K1", .name = "E-mu APS [4001]", + .id = "APS", .emu10k1_chip = 1, - .ac97_chip = 1, - .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102, - .driver = "EMU10K1", .name = "SBLive! Value [SB0103]", + .ecard = 1} , + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102, + .driver = "EMU10K1", .name = "SBLive! [CT4620]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, .sblive51 = 1} , - {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102, - .driver = "EMU10K1", .name = "SBLive! [SB0105]", + {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102, + .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", .id = "Live", .emu10k1_chip = 1, .ac97_chip = 1, @@ -833,6 +847,7 @@ int __devinit snd_emu10k1_create(snd_card_t * card, unsigned short extout_mask, long max_cache_bytes, int enable_ir, + uint subsystem, emu10k1_t ** remu) { emu10k1_t *emu; @@ -878,10 +893,16 @@ int __devinit snd_emu10k1_create(snd_card_t * card, for (c = emu_chip_details; c->vendor; c++) { if (c->vendor == pci->vendor && c->device == pci->device) { - if (c->subsystem && c->subsystem != emu->serial) - continue; - if (c->revision && c->revision != emu->revision) - continue; + if (subsystem) { + if (c->subsystem && (c->subsystem == subsystem) ) { + break; + } else continue; + } else { + if (c->subsystem && (c->subsystem != emu->serial) ) + continue; + if (c->revision && c->revision != emu->revision) + continue; + } break; } } @@ -892,10 +913,14 @@ int __devinit snd_emu10k1_create(snd_card_t * card, return -ENOENT; } emu->card_capabilities = c; - if (c->subsystem != 0) + if (c->subsystem && !subsystem) snd_printdd("Sound card name=%s\n", c->name); - else - snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x\n", c->name, pci->vendor, pci->device, emu->serial); + else if (subsystem) + snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x. Forced to subsytem=0x%x\n", + c->name, pci->vendor, pci->device, emu->serial, c->subsystem); + else + snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x.\n", + c->name, pci->vendor, pci->device, emu->serial); if (!*card->id && c->id) { int i, n = 0; diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index e90c5ddd1d17..52c7826df440 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1183,7 +1183,7 @@ static int snd_emu10k1x_spdif_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_emu10k1x_spdif_mask_control = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), .count = 3, .info = snd_emu10k1x_spdif_info, @@ -1192,7 +1192,7 @@ static snd_kcontrol_new_t snd_emu10k1x_spdif_mask_control = static snd_kcontrol_new_t snd_emu10k1x_spdif_control = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .count = 3, .info = snd_emu10k1x_spdif_info, diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 0529fb281125..637c555cfdb1 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -1159,12 +1159,12 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) /* Optical SPDIF Playback Volume */ A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_OPT_SPDIF_L); A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_OPT_SPDIF_R); - snd_emu10k1_init_stereo_control(&controls[nctl++], "IEC958 Optical Playback Volume", gpr, 0); + snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",PLAYBACK,VOLUME), gpr, 0); gpr += 2; /* Optical SPDIF Capture Volume */ A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L); A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R); - snd_emu10k1_init_stereo_control(&controls[nctl++], "IEC958 Optical Capture Volume", gpr, 0); + snd_emu10k1_init_stereo_control(&controls[nctl++], SNDRV_CTL_NAME_IEC958("Optical ",CAPTURE,VOLUME), gpr, 0); gpr += 2; /* Line2 Playback Volume */ @@ -1389,7 +1389,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) A_OP(icode, &ptr, iACC3, A_EXTOUT(A_EXTOUT_FRONT_L + z), A_GPR(tmp + 0), A_GPR(tmp + 1), A_C_00000000); } } - snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "IEC958 Optical Raw Playback Switch", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + nctl++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0); gpr += 2; A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS); @@ -1716,7 +1716,7 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) /* IEC958 TTL Playback Volume */ for (z = 0; z < 2; z++) VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_SPDIF_CD_L + z, gpr + z); - snd_emu10k1_init_stereo_control(controls + i++, "IEC958 TTL Playback Volume", gpr, 0); + snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",PLAYBACK,VOLUME), gpr, 0); gpr += 2; /* IEC958 TTL Capture Volume + Switch */ @@ -1724,8 +1724,8 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_SPDIF_CD_L + z, gpr + 2 + z); VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); } - snd_emu10k1_init_stereo_control(controls + i++, "IEC958 TTL Capture Volume", gpr, 0); - snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 TTL Capture Switch", gpr + 2, 0); + snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",CAPTURE,VOLUME), gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("TTL ",CAPTURE,SWITCH), gpr + 2, 0); gpr += 4; } @@ -1750,7 +1750,7 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) /* IEC958 Optical Playback Volume */ for (z = 0; z < 2; z++) VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_TOSLINK_L + z, gpr + z); - snd_emu10k1_init_stereo_control(controls + i++, "IEC958 LiveDrive Playback Volume", gpr, 0); + snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",PLAYBACK,VOLUME), gpr, 0); gpr += 2; /* IEC958 Optical Capture Volume */ @@ -1758,8 +1758,8 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_TOSLINK_L + z, gpr + 2 + z); VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); } - snd_emu10k1_init_stereo_control(controls + i++, "IEC958 LiveDrive Capture Volume", gpr, 0); - snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 LiveDrive Capture Switch", gpr + 2, 0); + snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",CAPTURE,VOLUME), gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("LiveDrive ",CAPTURE,SWITCH), gpr + 2, 0); gpr += 4; } @@ -1784,7 +1784,7 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) /* IEC958 Coax Playback Volume */ for (z = 0; z < 2; z++) VOLUME_ADDIN(icode, &ptr, playback + z, EXTIN_COAX_SPDIF_L + z, gpr + z); - snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Coaxial Playback Volume", gpr, 0); + snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",PLAYBACK,VOLUME), gpr, 0); gpr += 2; /* IEC958 Coax Capture Volume + Switch */ @@ -1792,8 +1792,8 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) SWITCH_IN(icode, &ptr, tmp + 0, EXTIN_COAX_SPDIF_L + z, gpr + 2 + z); VOLUME_ADD(icode, &ptr, capture + z, tmp + 0, gpr + z); } - snd_emu10k1_init_stereo_control(controls + i++, "IEC958 Coaxial Capture Volume", gpr, 0); - snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 Coaxial Capture Switch", gpr + 2, 0); + snd_emu10k1_init_stereo_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",CAPTURE,VOLUME), gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("Coaxial ",CAPTURE,SWITCH), gpr + 2, 0); gpr += 4; } @@ -1920,7 +1920,7 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu) #endif } - snd_emu10k1_init_stereo_onoff_control(controls + i++, "IEC958 Optical Raw Playback Switch", gpr, 0); + snd_emu10k1_init_stereo_onoff_control(controls + i++, SNDRV_CTL_NAME_IEC958("Optical Raw ",PLAYBACK,SWITCH), gpr, 0); gpr += 2; } diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 6be82c5fe138..d71a72e84bcc 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -181,7 +181,7 @@ static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK), .count = 4, .info = snd_emu10k1_spdif_info, @@ -190,7 +190,7 @@ static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control = static snd_kcontrol_new_t snd_emu10k1_spdif_control = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .count = 4, .info = snd_emu10k1_spdif_info, @@ -295,7 +295,7 @@ static int snd_emu10k1_send_routing_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_emu10k1_send_routing_control = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "EMU10K1 PCM Send Routing", .count = 32, .info = snd_emu10k1_send_routing_info, @@ -364,7 +364,7 @@ static int snd_emu10k1_send_volume_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_emu10k1_send_volume_control = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "EMU10K1 PCM Send Volume", .count = 32, .info = snd_emu10k1_send_volume_info, @@ -427,7 +427,7 @@ static int snd_emu10k1_attn_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_emu10k1_attn_control = { .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "EMU10K1 PCM Volume", .count = 32, .info = snd_emu10k1_attn_info, @@ -737,7 +737,8 @@ static int rename_ctl(snd_card_t *card, const char *src, const char *dst) return -ENOENT; } -int __devinit snd_emu10k1_mixer(emu10k1_t *emu) +int __devinit snd_emu10k1_mixer(emu10k1_t *emu, + int pcm_device, int multi_device) { int err, pcm; snd_kcontrol_t *kctl; @@ -852,29 +853,35 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL) return -ENOMEM; + kctl->id.device = pcm_device; if ((err = snd_ctl_add(card, kctl))) return err; if ((kctl = emu->ctl_send_volume = snd_ctl_new1(&snd_emu10k1_send_volume_control, emu)) == NULL) return -ENOMEM; + kctl->id.device = pcm_device; if ((err = snd_ctl_add(card, kctl))) return err; if ((kctl = emu->ctl_attn = snd_ctl_new1(&snd_emu10k1_attn_control, emu)) == NULL) return -ENOMEM; + kctl->id.device = pcm_device; if ((err = snd_ctl_add(card, kctl))) return err; if ((kctl = emu->ctl_efx_send_routing = snd_ctl_new1(&snd_emu10k1_efx_send_routing_control, emu)) == NULL) return -ENOMEM; + kctl->id.device = multi_device; if ((err = snd_ctl_add(card, kctl))) return err; if ((kctl = emu->ctl_efx_send_volume = snd_ctl_new1(&snd_emu10k1_efx_send_volume_control, emu)) == NULL) return -ENOMEM; + kctl->id.device = multi_device; if ((err = snd_ctl_add(card, kctl))) return err; if ((kctl = emu->ctl_efx_attn = snd_ctl_new1(&snd_emu10k1_efx_attn_control, emu)) == NULL) return -ENOMEM; + kctl->id.device = multi_device; if ((err = snd_ctl_add(card, kctl))) return err; @@ -924,10 +931,14 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu) /* sb live! and audigy */ if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL) return -ENOMEM; + if (!emu->audigy) + kctl->id.device = emu->pcm_efx->device; if ((err = snd_ctl_add(card, kctl))) return err; if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL) return -ENOMEM; + if (!emu->audigy) + kctl->id.device = emu->pcm_efx->device; if ((err = snd_ctl_add(card, kctl))) return err; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 520b99af5f55..9c35f6dde1b5 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1682,6 +1682,7 @@ static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm) int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm) { snd_pcm_t *pcm; + snd_kcontrol_t *kctl; int err; if (rpcm) @@ -1714,7 +1715,11 @@ int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm emu->efx_voices_mask[0] = 0xffff0000; emu->efx_voices_mask[1] = 0; } - snd_ctl_add(emu->card, snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu)); + kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu); + if (!kctl) + return -ENOMEM; + kctl->id.device = device; + snd_ctl_add(emu->card, kctl); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index 7a595f0dd7a1..6afeaeab3e13 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -495,7 +495,7 @@ static int synth_free_pages(emu10k1_t *emu, emu10k1_memblk_t *blk) } /* calculate buffer pointer from offset address */ -inline static void *offset_ptr(emu10k1_t *emu, int page, int offset) +static inline void *offset_ptr(emu10k1_t *emu, int page, int offset) { char *ptr; snd_assert(page >= 0 && page < emu->max_cache_pages, return NULL); diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index 98f980189892..a1691330d3b6 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -822,7 +822,7 @@ static int snd_p16v_volume_put_analog_unknown(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_p16v_volume_control_analog_front = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD Analog Front Volume", + .name = "HD Analog Front Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_analog_front, .put = snd_p16v_volume_put_analog_front @@ -831,7 +831,7 @@ static snd_kcontrol_new_t snd_p16v_volume_control_analog_front = static snd_kcontrol_new_t snd_p16v_volume_control_analog_center_lfe = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD Analog Center/LFE Volume", + .name = "HD Analog Center/LFE Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_analog_center_lfe, .put = snd_p16v_volume_put_analog_center_lfe @@ -840,7 +840,7 @@ static snd_kcontrol_new_t snd_p16v_volume_control_analog_center_lfe = static snd_kcontrol_new_t snd_p16v_volume_control_analog_unknown = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD Analog Unknown Volume", + .name = "HD Analog Unknown Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_analog_unknown, .put = snd_p16v_volume_put_analog_unknown @@ -849,7 +849,7 @@ static snd_kcontrol_new_t snd_p16v_volume_control_analog_unknown = static snd_kcontrol_new_t snd_p16v_volume_control_analog_rear = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD Analog Rear Volume", + .name = "HD Analog Rear Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_analog_rear, .put = snd_p16v_volume_put_analog_rear @@ -858,7 +858,7 @@ static snd_kcontrol_new_t snd_p16v_volume_control_analog_rear = static snd_kcontrol_new_t snd_p16v_volume_control_spdif_front = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD SPDIF Front Volume", + .name = "HD SPDIF Front Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_spdif_front, .put = snd_p16v_volume_put_spdif_front @@ -867,7 +867,7 @@ static snd_kcontrol_new_t snd_p16v_volume_control_spdif_front = static snd_kcontrol_new_t snd_p16v_volume_control_spdif_center_lfe = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD SPDIF Center/LFE Volume", + .name = "HD SPDIF Center/LFE Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_spdif_center_lfe, .put = snd_p16v_volume_put_spdif_center_lfe @@ -876,7 +876,7 @@ static snd_kcontrol_new_t snd_p16v_volume_control_spdif_center_lfe = static snd_kcontrol_new_t snd_p16v_volume_control_spdif_unknown = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD SPDIF Unknown Volume", + .name = "HD SPDIF Unknown Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_spdif_unknown, .put = snd_p16v_volume_put_spdif_unknown @@ -885,7 +885,7 @@ static snd_kcontrol_new_t snd_p16v_volume_control_spdif_unknown = static snd_kcontrol_new_t snd_p16v_volume_control_spdif_rear = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD SPDIF Rear Volume", + .name = "HD SPDIF Rear Playback Volume", .info = snd_p16v_volume_info, .get = snd_p16v_volume_get_spdif_rear, .put = snd_p16v_volume_put_spdif_rear @@ -936,7 +936,7 @@ static int snd_p16v_capture_source_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_p16v_capture_source __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD Capture source", + .name = "HD source Capture", .info = snd_p16v_capture_source_info, .get = snd_p16v_capture_source_get, .put = snd_p16v_capture_source_put @@ -985,7 +985,7 @@ static int snd_p16v_capture_channel_put(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_p16v_capture_channel __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "HD Capture channel", + .name = "HD channel Capture", .info = snd_p16v_capture_channel_info, .get = snd_p16v_capture_channel_get, .put = snd_p16v_capture_channel_put diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 4e63498a58b2..f06b95f41a1d 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -685,6 +685,15 @@ static unsigned short snd_es1371_codec_read(ac97_t *ac97, return 0; } +static void snd_es1371_codec_wait(ac97_t *ac97) +{ + msleep(750); + snd_es1371_codec_read(ac97, AC97_RESET); + snd_es1371_codec_read(ac97, AC97_VENDOR_ID1); + snd_es1371_codec_read(ac97, AC97_VENDOR_ID2); + msleep(50); +} + static void snd_es1371_adc_rate(ensoniq_t * ensoniq, unsigned int rate) { unsigned int n, truncm, freq, result; @@ -1435,7 +1444,7 @@ static int snd_es1371_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t /* spdif controls */ static snd_kcontrol_new_t snd_es1371_mixer_spdif[] __devinitdata = { - ES1371_SPDIF("IEC958 Playback Switch"), + ES1371_SPDIF(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH)), { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), @@ -1585,6 +1594,7 @@ static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq) static ac97_bus_ops_t ops = { .write = snd_es1371_codec_write, .read = snd_es1371_codec_read, + .wait = snd_es1371_codec_wait, }; if ((err = snd_ac97_bus(card, 0, &ops, NULL, &pbus)) < 0) @@ -2008,21 +2018,11 @@ static int __devinit snd_ensoniq_create(snd_card_t * card, if (pci->vendor == es1371_ac97_reset_hack[idx].vid && pci->device == es1371_ac97_reset_hack[idx].did && ensoniq->rev == es1371_ac97_reset_hack[idx].rev) { - unsigned long tmo; - signed long tmo2; - ensoniq->cssr |= ES_1371_ST_AC97_RST; outl(ensoniq->cssr, ES_REG(ensoniq, STATUS)); /* need to delay around 20ms(bleech) to give some CODECs enough time to wakeup */ - tmo = jiffies + (HZ / 50) + 1; - while (1) { - tmo2 = tmo - jiffies; - if (tmo2 <= 0) - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(tmo2); - } + msleep(20); break; } /* AC'97 warm reset to start the bitclk */ diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index ea889b311390..9d7a28783930 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -636,7 +636,7 @@ static void __maestro_write(es1968_t *chip, u16 reg, u16 data) chip->maestro_map[reg] = data; } -inline static void maestro_write(es1968_t *chip, u16 reg, u16 data) +static inline void maestro_write(es1968_t *chip, u16 reg, u16 data) { unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); @@ -654,7 +654,7 @@ static u16 __maestro_read(es1968_t *chip, u16 reg) return chip->maestro_map[reg]; } -inline static u16 maestro_read(es1968_t *chip, u16 reg) +static inline u16 maestro_read(es1968_t *chip, u16 reg) { unsigned long flags; u16 result; @@ -664,11 +664,6 @@ inline static u16 maestro_read(es1968_t *chip, u16 reg) return result; } -#define big_mdelay(msec) do {\ - set_current_state(TASK_UNINTERRUPTIBLE);\ - schedule_timeout(((msec) * HZ + 999) / 1000);\ -} while (0) - /* Wait for the codec bus to be free */ static int snd_es1968_ac97_wait(es1968_t *chip) { @@ -755,7 +750,7 @@ static void __apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) apu_data_set(chip, data); } -inline static void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) +static inline void apu_set_register(es1968_t *chip, u16 channel, u8 reg, u16 data) { unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); @@ -771,7 +766,7 @@ static u16 __apu_get_register(es1968_t *chip, u16 channel, u8 reg) return __maestro_read(chip, IDR0_DATA_PORT); } -inline static u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg) +static inline u16 apu_get_register(es1968_t *chip, u16 channel, u8 reg) { unsigned long flags; u16 v; @@ -957,7 +952,7 @@ static u32 snd_es1968_compute_rate(es1968_t *chip, u32 freq) } /* get current pointer */ -inline static unsigned int +static inline unsigned int snd_es1968_get_dma_ptr(es1968_t *chip, esschan_t *es) { unsigned int offset; @@ -978,7 +973,7 @@ static void snd_es1968_apu_set_freq(es1968_t *chip, int apu, int freq) } /* spin lock held */ -inline static void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode) +static inline void snd_es1968_trigger_apu(es1968_t *esm, int apu, int mode) { /* set the APU mode */ __apu_set_register(esm, apu, 0, @@ -1809,8 +1804,7 @@ static void __devinit es1968_measure_clock(es1968_t *chip) snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR); do_gettimeofday(&start_time); spin_unlock_irq(&chip->reg_lock); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 20); /* 50 msec */ + msleep(50); spin_lock_irq(&chip->reg_lock); offset = __apu_get_register(chip, apu, 5); do_gettimeofday(&stop_time); @@ -2093,7 +2087,7 @@ static void snd_es1968_ac97_reset(es1968_t *chip) outw(0x0000, ioaddr + 0x60); /* write 0 to gpio 0 */ udelay(20); outw(0x0001, ioaddr + 0x60); /* write 1 to gpio 1 */ - big_mdelay(20); + msleep(20); outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38); @@ -2109,7 +2103,7 @@ static void snd_es1968_ac97_reset(es1968_t *chip) outw(0x0001, ioaddr + 0x60); /* write 1 to gpio */ udelay(20); outw(0x0009, ioaddr + 0x60); /* write 9 to gpio */ - big_mdelay(500); + msleep(500); //outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); @@ -2135,7 +2129,7 @@ static void snd_es1968_ac97_reset(es1968_t *chip) if (w > 10000) { outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ - big_mdelay(500); /* oh my.. */ + msleep(500); /* oh my.. */ outb(inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); udelay(1); diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index ff10e637a95e..36b2f62e8573 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -1155,10 +1155,10 @@ FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1), static snd_kcontrol_new_t snd_fm801_controls_multi[] __devinitdata = { FM801_SINGLE("AC97 2ch->4ch Copy Switch", FM801_CODEC_CTRL, 7, 1, 0), FM801_SINGLE("AC97 18-bit Switch", FM801_CODEC_CTRL, 10, 1, 0), -FM801_SINGLE("IEC958 Capture Switch", FM801_I2S_MODE, 8, 1, 0), -FM801_SINGLE("IEC958 Raw Data Playback Switch", FM801_I2S_MODE, 9, 1, 0), -FM801_SINGLE("IEC958 Raw Data Capture Switch", FM801_I2S_MODE, 10, 1, 0), -FM801_SINGLE("IEC958 Playback Switch", FM801_GEN_CTRL, 2, 1, 0), +FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), FM801_I2S_MODE, 8, 1, 0), +FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",PLAYBACK,SWITCH), FM801_I2S_MODE, 9, 1, 0), +FM801_SINGLE(SNDRV_CTL_NAME_IEC958("Raw Data ",CAPTURE,SWITCH), FM801_I2S_MODE, 10, 1, 0), +FM801_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), FM801_GEN_CTRL, 2, 1, 0), }; static void snd_fm801_mixer_free_ac97_bus(ac97_bus_t *bus) diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index bd8cb33c4fb4..ddfb5ff7fb8f 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o +snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o ifdef CONFIG_PROC_FS snd-hda-codec-objs += hda_proc.o endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e2cf02387289..20f7762f7144 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -432,22 +432,26 @@ void snd_hda_get_codec_name(struct hda_codec *codec, } /* - * look for an AFG node - * - * return 0 if not found + * look for an AFG and MFG nodes */ -static int look_for_afg_node(struct hda_codec *codec) +static void setup_fg_nodes(struct hda_codec *codec) { int i, total_nodes; hda_nid_t nid; total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); for (i = 0; i < total_nodes; i++, nid++) { - if ((snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE) & 0xff) == - AC_GRP_AUDIO_FUNCTION) - return nid; + switch((snd_hda_param_read(codec, nid, AC_PAR_FUNCTION_TYPE) & 0xff)) { + case AC_GRP_AUDIO_FUNCTION: + codec->afg = nid; + break; + case AC_GRP_MODEM_FUNCTION: + codec->mfg = nid; + break; + default: + break; + } } - return 0; } /* @@ -507,10 +511,9 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID); codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, AC_PAR_REV_ID); - /* FIXME: support for multiple AFGs? */ - codec->afg = look_for_afg_node(codec); - if (! codec->afg) { - snd_printdd("hda_codec: no AFG node found\n"); + setup_fg_nodes(codec); + if (! codec->afg && ! codec->mfg) { + snd_printdd("hda_codec: no AFG or MFG node found\n"); snd_hda_codec_free(codec); return -ENODEV; } @@ -749,12 +752,14 @@ int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t long *valp = ucontrol->value.integer.value; int change = 0; - if (chs & 1) + if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 0x7f, *valp); + valp++; + } if (chs & 2) change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, - 0x7f, valp[1]); + 0x7f, *valp); return change; } @@ -796,12 +801,15 @@ int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t long *valp = ucontrol->value.integer.value; int change = 0; - if (chs & 1) + if (chs & 1) { change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx, 0x80, *valp ? 0 : 0x80); + valp++; + } if (chs & 2) change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx, - 0x80, valp[1] ? 0 : 0x80); + 0x80, *valp ? 0 : 0x80); + return change; } @@ -1155,8 +1163,16 @@ int snd_hda_build_controls(struct hda_bus *bus) /* * stream formats */ -static unsigned int rate_bits[][3] = { +struct hda_rate_tbl { + unsigned int hz; + unsigned int alsa_bits; + unsigned int hda_fmt; +}; + +static struct hda_rate_tbl rate_bits[] = { /* rate in Hz, ALSA rate bitmask, HDA format value */ + + /* autodetected value used in snd_hda_query_supported_pcm */ { 8000, SNDRV_PCM_RATE_8000, 0x0500 }, /* 1/6 x 48 */ { 11025, SNDRV_PCM_RATE_11025, 0x4300 }, /* 1/4 x 44 */ { 16000, SNDRV_PCM_RATE_16000, 0x0200 }, /* 1/3 x 48 */ @@ -1168,7 +1184,11 @@ static unsigned int rate_bits[][3] = { { 96000, SNDRV_PCM_RATE_96000, 0x0800 }, /* 2 x 48 */ { 176400, SNDRV_PCM_RATE_176400, 0x5800 },/* 4 x 44 */ { 192000, SNDRV_PCM_RATE_192000, 0x1800 }, /* 4 x 48 */ - { 0 } + + /* not autodetected value */ + { 9600, SNDRV_PCM_RATE_KNOT, 0x0400 }, /* 1/5 x 48 */ + + { 0 } /* terminator */ }; /** @@ -1190,12 +1210,12 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate, int i; unsigned int val = 0; - for (i = 0; rate_bits[i][0]; i++) - if (rate_bits[i][0] == rate) { - val = rate_bits[i][2]; + for (i = 0; rate_bits[i].hz; i++) + if (rate_bits[i].hz == rate) { + val = rate_bits[i].hda_fmt; break; } - if (! rate_bits[i][0]) { + if (! rate_bits[i].hz) { snd_printdd("invalid rate %d\n", rate); return 0; } @@ -1258,9 +1278,9 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, if (ratesp) { u32 rates = 0; - for (i = 0; rate_bits[i][0]; i++) { + for (i = 0; rate_bits[i].hz; i++) { if (val & (1 << i)) - rates |= rate_bits[i][1]; + rates |= rate_bits[i].alsa_bits; } *ratesp = rates; } @@ -1352,13 +1372,13 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, } rate = format & 0xff00; - for (i = 0; rate_bits[i][0]; i++) - if (rate_bits[i][2] == rate) { + for (i = 0; rate_bits[i].hz; i++) + if (rate_bits[i].hda_fmt == rate) { if (val & (1 << i)) break; return 0; } - if (! rate_bits[i][0]) + if (! rate_bits[i].hz) return 0; stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); @@ -1541,8 +1561,11 @@ int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_c for (c = tbl; c->modelname || c->pci_subvendor; c++) { if (c->pci_subvendor == subsystem_vendor && (! c->pci_subdevice /* all match */|| - (c->pci_subdevice == subsystem_device))) + (c->pci_subdevice == subsystem_device))) { + snd_printdd(KERN_INFO "hda_codec: PCI %x:%x, codec config %d is selected\n", + subsystem_vendor, subsystem_device, c->config); return c->config; + } } } return -1; @@ -1803,11 +1826,25 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *c cfg->line_out_pins[j] = nid; } - /* Swap surround and CLFE: the association order is front/CLFE/surr/back */ - if (cfg->line_outs >= 3) { + /* Reorder the surround channels + * ALSA sequence is front/surr/clfe/side + * HDA sequence is: + * 4-ch: front/surr => OK as it is + * 6-ch: front/clfe/surr + * 8-ch: front/clfe/side/surr + */ + switch (cfg->line_outs) { + case 3: nid = cfg->line_out_pins[1]; cfg->line_out_pins[1] = cfg->line_out_pins[2]; cfg->line_out_pins[2] = nid; + break; + case 4: + nid = cfg->line_out_pins[1]; + cfg->line_out_pins[1] = cfg->line_out_pins[3]; + cfg->line_out_pins[3] = cfg->line_out_pins[2]; + cfg->line_out_pins[2] = nid; + break; } return 0; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 59991560d492..63a29a8a2860 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -262,6 +262,9 @@ enum { #define AC_PINCTL_OUT_EN (1<<6) #define AC_PINCTL_HP_EN (1<<7) +/* Unsolicited response - 8bit */ +#define AC_USRSP_EN (1<<7) + /* configuration default - 32bit */ #define AC_DEFCFG_SEQUENCE (0xf<<0) #define AC_DEFCFG_DEF_ASSOC (0xf<<4) @@ -511,6 +514,7 @@ struct hda_codec { struct list_head list; /* list point */ hda_nid_t afg; /* AFG node id */ + hda_nid_t mfg; /* MFG node id */ /* ids */ u32 vendor_id; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2d046abb5911..1229227af5b5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -881,6 +881,11 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec) struct hda_gspec *spec; int err; + if(!codec->afg) { + snd_printdd("hda_generic: no generic modem yet\n"); + return -ENODEV; + } + spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); if (spec == NULL) { printk(KERN_ERR "hda_generic: can't allocate spec\n"); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 5e0cca36ed57..15107df1f490 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -71,7 +71,9 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," "{Intel, ESB2}," "{ATI, SB450}," "{VIA, VT8251}," - "{VIA, VT8237A}}"); + "{VIA, VT8237A}," + "{SiS, SIS966}," + "{ULI, M5461}}"); MODULE_DESCRIPTION("Intel HDA driver"); #define SFX "hda-intel: " @@ -141,9 +143,24 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; */ /* max number of SDs */ -#define MAX_ICH6_DEV 8 +/* ICH, ATI and VIA have 4 playback and 4 capture */ +#define ICH6_CAPTURE_INDEX 0 +#define ICH6_NUM_CAPTURE 4 +#define ICH6_PLAYBACK_INDEX 4 +#define ICH6_NUM_PLAYBACK 4 + +/* ULI has 6 playback and 5 capture */ +#define ULI_CAPTURE_INDEX 0 +#define ULI_NUM_CAPTURE 5 +#define ULI_PLAYBACK_INDEX 5 +#define ULI_NUM_PLAYBACK 6 + +/* this number is statically defined for simplicity */ +#define MAX_AZX_DEV 16 + /* max number of fragments - we may use more if allocating more pages for BDL */ -#define AZX_MAX_FRAG (PAGE_SIZE / (MAX_ICH6_DEV * 16)) +#define BDL_SIZE PAGE_ALIGN(8192) +#define AZX_MAX_FRAG (BDL_SIZE / (MAX_AZX_DEV * 16)) /* max buffer size - no h/w limit, you can increase as you like */ #define AZX_MAX_BUF_SIZE (1024*1024*1024) /* max number of PCM devics per card */ @@ -178,6 +195,9 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ #define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ +/* GCTL unsolicited response enable bit */ +#define ICH6_GCTL_UREN (1<<8) + /* GCTL reset bit */ #define ICH6_GCTL_RESET (1<<0) @@ -197,7 +217,6 @@ enum { }; /* Defines for ATI HD Audio support in SB450 south bridge */ -#define ATI_SB450_HDAUDIO_PCI_DEVICE_ID 0x437b #define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 #define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 @@ -255,6 +274,14 @@ struct snd_azx { snd_card_t *card; struct pci_dev *pci; + /* chip type specific */ + int driver_type; + int playback_streams; + int playback_index_offset; + int capture_streams; + int capture_index_offset; + int num_streams; + /* pci resources */ unsigned long addr; void __iomem *remap_addr; @@ -264,8 +291,8 @@ struct snd_azx { spinlock_t reg_lock; struct semaphore open_mutex; - /* streams */ - azx_dev_t azx_dev[MAX_ICH6_DEV]; + /* streams (x num_streams) */ + azx_dev_t *azx_dev; /* PCM */ unsigned int pcm_devs; @@ -289,6 +316,23 @@ struct snd_azx { unsigned int initialized: 1; }; +/* driver types */ +enum { + AZX_DRIVER_ICH, + AZX_DRIVER_ATI, + AZX_DRIVER_VIA, + AZX_DRIVER_SIS, + AZX_DRIVER_ULI, +}; + +static char *driver_short_names[] __devinitdata = { + [AZX_DRIVER_ICH] = "HDA Intel", + [AZX_DRIVER_ATI] = "HDA ATI SB", + [AZX_DRIVER_VIA] = "HDA VIA VT82xx", + [AZX_DRIVER_SIS] = "HDA SIS966", + [AZX_DRIVER_ULI] = "HDA ULI M5461" +}; + /* * macros for easy use */ @@ -357,6 +401,8 @@ static void azx_init_cmd_io(azx_t *chip) azx_writel(chip, CORBLBASE, (u32)chip->corb.addr); azx_writel(chip, CORBUBASE, upper_32bit(chip->corb.addr)); + /* set the corb size to 256 entries (ULI requires explicitly) */ + azx_writeb(chip, CORBSIZE, 0x02); /* set the corb write pointer to 0 */ azx_writew(chip, CORBWP, 0); /* reset the corb hw read pointer */ @@ -370,6 +416,8 @@ static void azx_init_cmd_io(azx_t *chip) azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr); azx_writel(chip, RIRBUBASE, upper_32bit(chip->rirb.addr)); + /* set the rirb size to 256 entries (ULI requires explicitly) */ + azx_writeb(chip, RIRBSIZE, 0x02); /* reset the rirb hw write pointer */ azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR); /* set N=1, get RIRB response interrupt for new entry */ @@ -562,6 +610,9 @@ static int azx_reset(azx_t *chip) return -EBUSY; } + /* Accept unsolicited responses */ + azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN); + /* detect codecs */ if (! chip->codec_mask) { chip->codec_mask = azx_readw(chip, STATESTS); @@ -590,7 +641,7 @@ static void azx_int_disable(azx_t *chip) int i; /* disable interrupts in stream descriptor */ - for (i = 0; i < MAX_ICH6_DEV; i++) { + for (i = 0; i < chip->num_streams; i++) { azx_dev_t *azx_dev = &chip->azx_dev[i]; azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) & ~SD_INT_MASK); @@ -610,7 +661,7 @@ static void azx_int_clear(azx_t *chip) int i; /* clear stream status */ - for (i = 0; i < MAX_ICH6_DEV; i++) { + for (i = 0; i < chip->num_streams; i++) { azx_dev_t *azx_dev = &chip->azx_dev[i]; azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); } @@ -680,8 +731,7 @@ static void azx_init_chip(azx_t *chip) } /* For ATI SB450 azalia HD audio, we need to enable snoop */ - if (chip->pci->vendor == PCI_VENDOR_ID_ATI && - chip->pci->device == ATI_SB450_HDAUDIO_PCI_DEVICE_ID) { + if (chip->driver_type == AZX_DRIVER_ATI) { pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, &ati_misc_cntl2); pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, @@ -708,7 +758,7 @@ static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs) return IRQ_NONE; } - for (i = 0; i < MAX_ICH6_DEV; i++) { + for (i = 0; i < chip->num_streams; i++) { azx_dev = &chip->azx_dev[i]; if (status & azx_dev->sd_int_sta_mask) { azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK); @@ -873,9 +923,15 @@ static int __devinit azx_codec_create(azx_t *chip, const char *model) /* assign a stream for the PCM */ static inline azx_dev_t *azx_assign_device(azx_t *chip, int stream) { - int dev, i; - dev = stream == SNDRV_PCM_STREAM_PLAYBACK ? 4 : 0; - for (i = 0; i < 4; i++, dev++) + int dev, i, nums; + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev = chip->playback_index_offset; + nums = chip->playback_streams; + } else { + dev = chip->capture_index_offset; + nums = chip->capture_streams; + } + for (i = 0; i < nums; i++, dev++) if (! chip->azx_dev[dev].opened) { chip->azx_dev[dev].opened = 1; return &chip->azx_dev[dev]; @@ -893,8 +949,8 @@ static snd_pcm_hardware_t azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE /*|*/ + /*SNDRV_PCM_INFO_RESUME*/), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, @@ -1043,6 +1099,7 @@ static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd) azx_dev->running = 1; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: azx_stream_stop(chip, azx_dev); azx_dev->running = 0; @@ -1052,6 +1109,7 @@ static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd) } spin_unlock(&chip->reg_lock); if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH || + cmd == SNDRV_PCM_TRIGGER_SUSPEND || cmd == SNDRV_PCM_TRIGGER_STOP) { int timeout = 5000; while (azx_sd_readb(azx_dev, SD_CTL) & SD_CTL_DMA_START && --timeout) @@ -1130,6 +1188,7 @@ static int __devinit create_codec_pcm(azx_t *chip, struct hda_codec *codec, snd_dma_pci_data(chip->pci), 1024 * 64, 1024 * 128); chip->pcm[pcm_dev] = pcm; + chip->pcm_devs = pcm_dev + 1; return 0; } @@ -1180,7 +1239,7 @@ static int __devinit azx_init_stream(azx_t *chip) /* initialize each stream (aka device) * assign the starting bdl address to each stream (device) and initialize */ - for (i = 0; i < MAX_ICH6_DEV; i++) { + for (i = 0; i < chip->num_streams; i++) { unsigned int off = sizeof(u32) * (i * AZX_MAX_FRAG * 4); azx_dev_t *azx_dev = &chip->azx_dev[i]; azx_dev->bdl = (u32 *)(chip->bdl.area + off); @@ -1239,7 +1298,7 @@ static int azx_free(azx_t *chip) if (chip->initialized) { int i; - for (i = 0; i < MAX_ICH6_DEV; i++) + for (i = 0; i < chip->num_streams; i++) azx_stream_stop(chip, &chip->azx_dev[i]); /* disable interrupts */ @@ -1255,10 +1314,10 @@ static int azx_free(azx_t *chip) /* wait a little for interrupts to finish */ msleep(1); - - iounmap(chip->remap_addr); } + if (chip->remap_addr) + iounmap(chip->remap_addr); if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); @@ -1270,6 +1329,7 @@ static int azx_free(azx_t *chip) snd_dma_free_pages(&chip->posbuf); pci_release_regions(chip->pci); pci_disable_device(chip->pci); + kfree(chip->azx_dev); kfree(chip); return 0; @@ -1284,7 +1344,8 @@ static int azx_dev_free(snd_device_t *device) * constructor */ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, - int posfix, azx_t **rchip) + int posfix, int driver_type, + azx_t **rchip) { azx_t *chip; int err = 0; @@ -1310,9 +1371,20 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, chip->card = card; chip->pci = pci; chip->irq = -1; + chip->driver_type = driver_type; chip->position_fix = posfix; +#if BITS_PER_LONG != 64 + /* Fix up base address on ULI M5461 */ + if (chip->driver_type == AZX_DRIVER_ULI) { + u16 tmp3; + pci_read_config_word(pci, 0x40, &tmp3); + pci_write_config_word(pci, 0x40, tmp3 | 0x10); + pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0); + } +#endif + if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) { kfree(chip); pci_disable_device(pci); @@ -1338,16 +1410,37 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, pci_set_master(pci); synchronize_irq(chip->irq); + switch (chip->driver_type) { + case AZX_DRIVER_ULI: + chip->playback_streams = ULI_NUM_PLAYBACK; + chip->capture_streams = ULI_NUM_CAPTURE; + chip->playback_index_offset = ULI_PLAYBACK_INDEX; + chip->capture_index_offset = ULI_CAPTURE_INDEX; + break; + default: + chip->playback_streams = ICH6_NUM_PLAYBACK; + chip->capture_streams = ICH6_NUM_CAPTURE; + chip->playback_index_offset = ICH6_PLAYBACK_INDEX; + chip->capture_index_offset = ICH6_CAPTURE_INDEX; + break; + } + chip->num_streams = chip->playback_streams + chip->capture_streams; + chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); + if (! chip->azx_dev) { + snd_printk(KERN_ERR "cannot malloc azx_dev\n"); + goto errout; + } + /* allocate memory for the BDL for each stream */ if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), - PAGE_SIZE, &chip->bdl)) < 0) { + BDL_SIZE, &chip->bdl)) < 0) { snd_printk(KERN_ERR SFX "cannot allocate BDL\n"); goto errout; } if (chip->position_fix == POS_FIX_POSBUF) { /* allocate memory for the position buffer */ if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), - MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) { + chip->num_streams * 8, &chip->posbuf)) < 0) { snd_printk(KERN_ERR SFX "cannot allocate posbuf\n"); goto errout; } @@ -1376,6 +1469,10 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, goto errout; } + strcpy(card->driver, "HDA-Intel"); + strcpy(card->shortname, driver_short_names[chip->driver_type]); + sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq); + *rchip = chip; return 0; @@ -1404,15 +1501,12 @@ static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id * return -ENOMEM; } - if ((err = azx_create(card, pci, position_fix[dev], &chip)) < 0) { + if ((err = azx_create(card, pci, position_fix[dev], pci_id->driver_data, + &chip)) < 0) { snd_card_free(card); return err; } - strcpy(card->driver, "HDA-Intel"); - strcpy(card->shortname, "HDA Intel"); - sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->addr, chip->irq); - /* create codec instances */ if ((err = azx_codec_create(chip, model[dev])) < 0) { snd_card_free(card); @@ -1453,12 +1547,13 @@ static void __devexit azx_remove(struct pci_dev *pci) /* PCI IDs */ static struct pci_device_id azx_ids[] = { - { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH6 */ - { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH7 */ - { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ESB2 */ - { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ATI SB450 */ - { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* VIA VT8251/VT8237A */ - { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ALI 5461? */ + { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */ + { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */ + { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */ + { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */ + { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */ + { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */ + { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */ { 0, } }; MODULE_DEVICE_TABLE(pci, azx_ids); diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index a5de684b6944..acaef3c811b8 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -10,11 +10,14 @@ extern struct hda_codec_preset snd_hda_preset_cmedia[]; extern struct hda_codec_preset snd_hda_preset_analog[]; /* SigmaTel codecs */ extern struct hda_codec_preset snd_hda_preset_sigmatel[]; +/* SiLabs 3054/3055 modem codecs */ +extern struct hda_codec_preset snd_hda_preset_si3054[]; static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_realtek, snd_hda_preset_cmedia, snd_hda_preset_analog, snd_hda_preset_sigmatel, + snd_hda_preset_si3054, NULL }; diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2fd05bb84136..bceb83a42a38 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -572,7 +572,7 @@ static snd_kcontrol_new_t ad1983_mixers[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Route", + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", .info = ad1983_spdif_route_info, .get = ad1983_spdif_route_get, .put = ad1983_spdif_route_put, @@ -705,7 +705,7 @@ static snd_kcontrol_new_t ad1981_mixers[] = { /* identical with AD1983 */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback Route", + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", .info = ad1983_spdif_route_info, .get = ad1983_spdif_route_get, .put = ad1983_spdif_route_put, diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index 2d6e3e3d0a38..07fb4f5a54b3 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -408,7 +408,7 @@ static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct aut /* search for an empty channel */ for (j = 0; j < cfg->line_outs; j++) { if (! assigned[j]) { - spec->dac_nids[i] = i + 0x03; + spec->dac_nids[i] = j + 0x03; assigned[j] = 1; break; } @@ -444,11 +444,10 @@ static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pi len = snd_hda_get_connections(codec, nid, conn, 4); for (k = 0; k < len; k++) if (conn[k] == spec->dac_nids[i]) { - spec->multi_init[j].param = j; + spec->multi_init[j].param = k; break; } j++; - break; } } return 0; @@ -648,6 +647,7 @@ static struct hda_board_config cmi9880_cfg_tbl[] = { { .modelname = "min_fp", .config = CMI_MIN_FP }, { .modelname = "full", .config = CMI_FULL }, { .modelname = "full_dig", .config = CMI_FULL_DIG }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x813d, .config = CMI_FULL_DIG }, /* ASUS P5AD2 */ { .modelname = "allout", .config = CMI_ALLOUT }, { .modelname = "auto", .config = CMI_AUTO }, {} /* terminator */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bab89843d850..eeb900ab79af 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -40,6 +40,7 @@ enum { ALC880_W810, ALC880_Z71V, ALC880_AUTO, + ALC880_6ST, ALC880_6ST_DIG, ALC880_F1734, ALC880_ASUS, @@ -119,6 +120,7 @@ struct alc_spec { unsigned int num_kctl_alloc, num_kctl_used; snd_kcontrol_new_t *kctl_alloc; struct hda_input_mux private_imux; + hda_nid_t private_dac_nids[4]; }; @@ -685,6 +687,12 @@ static snd_kcontrol_new_t alc880_asus_w1v_mixer[] = { { } /* end */ }; +/* additional mixers to alc880_asus_mixer */ +static snd_kcontrol_new_t alc880_pcbeep_mixer[] = { + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + { } /* end */ +}; /* * build control elements @@ -1522,6 +1530,7 @@ static struct hda_board_config alc880_cfg_tbl[] = { /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */ { .modelname = "3stack-digout", .config = ALC880_3ST_DIG }, { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG }, + { .pci_subvendor = 0x1025, .pci_subdevice = 0x0070, .config = ALC880_3ST_DIG }, /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/ { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG }, @@ -1547,9 +1556,10 @@ static struct hda_board_config alc880_cfg_tbl[] = { { .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG }, { .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG }, { .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, + /* { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG }, */ /* conflict with 6stack */ { .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG }, - { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, + /* note subvendor = 0 below */ + /* { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG }, */ { .modelname = "w810", .config = ALC880_W810 }, { .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 }, @@ -1557,7 +1567,10 @@ static struct hda_board_config alc880_cfg_tbl[] = { { .modelname = "z71v", .config = ALC880_Z71V }, { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V }, - { .modelname = "6statack-digout", .config = ALC880_6ST_DIG }, + { .modelname = "6stack", .config = ALC880_6ST }, + { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_6ST }, /* Acer APFV */ + + { .modelname = "6stack-digout", .config = ALC880_6ST_DIG }, { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG }, { .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG }, { .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG }, @@ -1644,6 +1657,15 @@ static struct alc_config_preset alc880_presets[] = { .channel_mode = alc880_fivestack_modes, .input_mux = &alc880_capture_source, }, + [ALC880_6ST] = { + .mixers = { alc880_six_stack_mixer }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids), + .dac_nids = alc880_6st_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes), + .channel_mode = alc880_sixstack_modes, + .input_mux = &alc880_6stack_capture_source, + }, [ALC880_6ST_DIG] = { .mixers = { alc880_six_stack_mixer }, .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs }, @@ -1656,7 +1678,8 @@ static struct alc_config_preset alc880_presets[] = { }, [ALC880_W810] = { .mixers = { alc880_w810_base_mixer }, - .init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs, + alc880_gpio2_init_verbs }, .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids), .dac_nids = alc880_w810_dac_nids, .dig_out_nid = ALC880_DIGOUT_NID, @@ -1666,8 +1689,7 @@ static struct alc_config_preset alc880_presets[] = { }, [ALC880_Z71V] = { .mixers = { alc880_z71v_mixer }, - .init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs, - alc880_gpio2_init_verbs }, + .init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs }, .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids), .dac_nids = alc880_z71v_dac_nids, .dig_out_nid = ALC880_DIGOUT_NID, @@ -1719,7 +1741,7 @@ static struct alc_config_preset alc880_presets[] = { .input_mux = &alc880_capture_source, }, [ALC880_UNIWILL_DIG] = { - .mixers = { alc880_asus_mixer }, + .mixers = { alc880_asus_mixer, alc880_pcbeep_mixer }, .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs }, .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids), .dac_nids = alc880_asus_dac_nids, @@ -1809,6 +1831,7 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pi int i, j; memset(assigned, 0, sizeof(assigned)); + spec->multiout.dac_nids = spec->private_dac_nids; /* check the pins hardwired to audio widget */ for (i = 0; i < cfg->line_outs; i++) { diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c new file mode 100644 index 000000000000..b0270d1b64ce --- /dev/null +++ b/sound/pci/hda/patch_si3054.c @@ -0,0 +1,300 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for Silicon Labs 3054/5 modem codec + * + * Copyright (c) 2005 Sasha Khapyorsky <sashak@smlink.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 + * 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 <sound/driver.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + + +/* si3054 verbs */ +#define SI3054_VERB_READ_NODE 0x900 +#define SI3054_VERB_WRITE_NODE 0x100 + +/* si3054 nodes (registers) */ +#define SI3054_EXTENDED_MID 2 +#define SI3054_LINE_RATE 3 +#define SI3054_LINE_LEVEL 4 +#define SI3054_GPIO_CFG 5 +#define SI3054_GPIO_POLARITY 6 +#define SI3054_GPIO_STICKY 7 +#define SI3054_GPIO_WAKEUP 8 +#define SI3054_GPIO_STATUS 9 +#define SI3054_GPIO_CONTROL 10 +#define SI3054_MISC_AFE 11 +#define SI3054_CHIPID 12 +#define SI3054_LINE_CFG1 13 +#define SI3054_LINE_STATUS 14 +#define SI3054_DC_TERMINATION 15 +#define SI3054_LINE_CONFIG 16 +#define SI3054_CALLPROG_ATT 17 +#define SI3054_SQ_CONTROL 18 +#define SI3054_MISC_CONTROL 19 +#define SI3054_RING_CTRL1 20 +#define SI3054_RING_CTRL2 21 + +/* extended MID */ +#define SI3054_MEI_READY 0xf + +/* line level */ +#define SI3054_ATAG_MASK 0x00f0 +#define SI3054_DTAG_MASK 0xf000 + +/* GPIO bits */ +#define SI3054_GPIO_OH 0x0001 +#define SI3054_GPIO_CID 0x0002 + +/* chipid and revisions */ +#define SI3054_CHIPID_CODEC_REV_MASK 0x000f +#define SI3054_CHIPID_DAA_REV_MASK 0x00f0 +#define SI3054_CHIPID_INTERNATIONAL 0x0100 +#define SI3054_CHIPID_DAA_ID 0x0f00 +#define SI3054_CHIPID_CODEC_ID (1<<12) + +/* si3054 codec registers (nodes) access macros */ +#define GET_REG(codec,reg) (snd_hda_codec_read(codec,reg,0,SI3054_VERB_READ_NODE,0)) +#define SET_REG(codec,reg,val) (snd_hda_codec_write(codec,reg,0,SI3054_VERB_WRITE_NODE,val)) + + +struct si3054_spec { + unsigned international; + struct hda_pcm pcm; +}; + + +/* + * Modem mixer + */ + +#define PRIVATE_VALUE(reg,mask) ((reg<<16)|(mask&0xffff)) +#define PRIVATE_REG(val) ((val>>16)&0xffff) +#define PRIVATE_MASK(val) (val&0xffff) + +static int si3054_switch_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int si3054_switch_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *uvalue) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + u16 reg = PRIVATE_REG(kcontrol->private_value); + u16 mask = PRIVATE_MASK(kcontrol->private_value); + uvalue->value.integer.value[0] = (GET_REG(codec, reg)) & mask ? 1 : 0 ; + return 0; +} + +static int si3054_switch_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *uvalue) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + u16 reg = PRIVATE_REG(kcontrol->private_value); + u16 mask = PRIVATE_MASK(kcontrol->private_value); + if (uvalue->value.integer.value[0]) + SET_REG(codec, reg, (GET_REG(codec, reg)) | mask); + else + SET_REG(codec, reg, (GET_REG(codec, reg)) & ~mask); + return 0; +} + +#define SI3054_KCONTROL(kname,reg,mask) { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = kname, \ + .info = si3054_switch_info, \ + .get = si3054_switch_get, \ + .put = si3054_switch_put, \ + .private_value = PRIVATE_VALUE(reg,mask), \ +} + + +static snd_kcontrol_new_t si3054_modem_mixer[] = { + SI3054_KCONTROL("Off-hook Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_OH), + SI3054_KCONTROL("Caller ID Switch", SI3054_GPIO_CONTROL, SI3054_GPIO_CID), + {} +}; + +static int si3054_build_controls(struct hda_codec *codec) +{ + return snd_hda_add_new_ctls(codec, si3054_modem_mixer); +} + + +/* + * PCM callbacks + */ + +static int si3054_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) +{ + u16 val; + + SET_REG(codec, SI3054_LINE_RATE, substream->runtime->rate); + val = GET_REG(codec, SI3054_LINE_LEVEL); + val &= 0xff << (8 * (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)); + val |= ((stream_tag & 0xf) << 4) << (8 * (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)); + SET_REG(codec, SI3054_LINE_LEVEL, val); + + snd_hda_codec_setup_stream(codec, hinfo->nid, + stream_tag, 0, format); + return 0; +} + +static int si3054_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) +{ + static unsigned int rates[] = { 8000, 9600, 16000 }; + static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, + }; + substream->runtime->hw.period_bytes_min = 80; + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates); +} + + +static struct hda_pcm_stream si3054_pcm = { + .substreams = 1, + .channels_min = 1, + .channels_max = 1, + .nid = 0x1, + .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .maxbps = 16, + .ops = { + .open = si3054_pcm_open, + .prepare = si3054_pcm_prepare, + }, +}; + + +static int si3054_build_pcms(struct hda_codec *codec) +{ + struct si3054_spec *spec = codec->spec; + struct hda_pcm *info = &spec->pcm; + si3054_pcm.nid = codec->mfg; + codec->num_pcms = 1; + codec->pcm_info = info; + info->name = "Si3054 Modem"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; + return 0; +} + + +/* + * Init part + */ + +static int si3054_init(struct hda_codec *codec) +{ + struct si3054_spec *spec = codec->spec; + unsigned wait_count; + u16 val; + + snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); + snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); + SET_REG(codec, SI3054_LINE_RATE, 9600); + SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK); + SET_REG(codec, SI3054_EXTENDED_MID, 0); + + wait_count = 10; + do { + msleep(2); + val = GET_REG(codec, SI3054_EXTENDED_MID); + } while ((val & SI3054_MEI_READY) != SI3054_MEI_READY && wait_count--); + + if((val&SI3054_MEI_READY) != SI3054_MEI_READY) { + snd_printk(KERN_ERR "si3054: cannot initialize. EXT MID = %04x\n", val); + return -EACCES; + } + + SET_REG(codec, SI3054_GPIO_POLARITY, 0xffff); + SET_REG(codec, SI3054_GPIO_CFG, 0x0); + SET_REG(codec, SI3054_MISC_AFE, 0); + SET_REG(codec, SI3054_LINE_CFG1,0x200); + + if((GET_REG(codec,SI3054_LINE_STATUS) & (1<<6)) == 0) { + snd_printd("Link Frame Detect(FDT) is not ready (line status: %04x)\n", + GET_REG(codec,SI3054_LINE_STATUS)); + } + + spec->international = GET_REG(codec, SI3054_CHIPID) & SI3054_CHIPID_INTERNATIONAL; + + return 0; +} + +static void si3054_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + + +/* + */ + +static struct hda_codec_ops si3054_patch_ops = { + .build_controls = si3054_build_controls, + .build_pcms = si3054_build_pcms, + .init = si3054_init, + .free = si3054_free, +#ifdef CONFIG_PM + //.suspend = si3054_suspend, + .resume = si3054_init, +#endif +}; + +static int patch_si3054(struct hda_codec *codec) +{ + struct si3054_spec *spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + codec->spec = spec; + codec->patch_ops = si3054_patch_ops; + return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_si3054[] = { + { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 }, + {} +}; + diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 013be2ea513a..9d503da7320d 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -30,32 +30,37 @@ #include <linux/slab.h> #include <linux/pci.h> #include <sound/core.h> +#include <sound/asoundef.h> #include "hda_codec.h" #include "hda_local.h" #undef STAC_TEST +#define NUM_CONTROL_ALLOC 32 +#define STAC_HP_EVENT 0x37 +#define STAC_UNSOL_ENABLE (AC_USRSP_EN | STAC_HP_EVENT) + struct sigmatel_spec { + snd_kcontrol_new_t *mixers[4]; + unsigned int num_mixers; + + unsigned int surr_switch: 1; + /* playback */ struct hda_multi_out multiout; - hda_nid_t playback_nid; + hda_nid_t dac_nids[4]; /* capture */ hda_nid_t *adc_nids; unsigned int num_adcs; hda_nid_t *mux_nids; unsigned int num_muxes; - hda_nid_t capture_nid; hda_nid_t dig_in_nid; - /* power management*/ - hda_nid_t *pstate_nids; - unsigned int num_pstates; - +#ifdef STAC_TEST /* pin widgets */ hda_nid_t *pin_nids; unsigned int num_pins; -#ifdef STAC_TEST unsigned int *pin_configs; #endif @@ -64,16 +69,20 @@ struct sigmatel_spec { snd_kcontrol_new_t *mixer; /* capture source */ - struct hda_input_mux input_mux; - char input_labels[HDA_MAX_NUM_INPUTS][16]; + struct hda_input_mux *input_mux; unsigned int cur_mux[2]; /* channel mode */ unsigned int num_ch_modes; unsigned int cur_ch_mode; - const struct sigmatel_channel_mode *channel_modes; - struct hda_pcm pcm_rec[1]; /* PCM information */ + struct hda_pcm pcm_rec[2]; /* PCM information */ + + /* dynamic controls and input_mux */ + struct auto_pin_cfg autocfg; + unsigned int num_kctl_alloc, num_kctl_used; + snd_kcontrol_new_t *kctl_alloc; + struct hda_input_mux private_imux; }; static hda_nid_t stac9200_adc_nids[1] = { @@ -88,14 +97,6 @@ static hda_nid_t stac9200_dac_nids[1] = { 0x02, }; -static hda_nid_t stac9200_pstate_nids[3] = { - 0x01, 0x02, 0x03, -}; - -static hda_nid_t stac9200_pin_nids[8] = { - 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, -}; - static hda_nid_t stac922x_adc_nids[2] = { 0x06, 0x07, }; @@ -104,24 +105,22 @@ static hda_nid_t stac922x_mux_nids[2] = { 0x12, 0x13, }; -static hda_nid_t stac922x_dac_nids[4] = { - 0x02, 0x03, 0x04, 0x05, -}; - -static hda_nid_t stac922x_pstate_nids[8] = { - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x11, +#ifdef STAC_TEST +static hda_nid_t stac9200_pin_nids[8] = { + 0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, }; static hda_nid_t stac922x_pin_nids[10] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x15, 0x1b, }; +#endif static int stac92xx_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); + return snd_hda_input_mux_info(spec->input_mux, uinfo); } static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) @@ -140,26 +139,64 @@ static int stac92xx_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t struct sigmatel_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); } -static struct hda_verb stac9200_ch2_init[] = { +static struct hda_verb stac9200_core_init[] = { /* set dac0mux for dac converter */ - { 0x07, 0x701, 0x00}, + { 0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, {} }; -static struct hda_verb stac922x_ch2_init[] = { +static struct hda_verb stac922x_core_init[] = { /* set master volume and direct control */ - { 0x16, 0x70f, 0xff}, + { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, {} }; -struct sigmatel_channel_mode { - unsigned int channels; - const struct hda_verb *sequence; -}; +static int stac922x_channel_modes[3] = {2, 6, 8}; + +static int stac922x_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->num_ch_modes; + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + sprintf(uinfo->value.enumerated.name, "%dch", + stac922x_channel_modes[uinfo->value.enumerated.item]); + return 0; +} + +static int stac922x_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->cur_ch_mode; + return 0; +} + +static int stac922x_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + if (ucontrol->value.enumerated.item[0] >= spec->num_ch_modes) + ucontrol->value.enumerated.item[0] = spec->num_ch_modes; + if (ucontrol->value.enumerated.item[0] == spec->cur_ch_mode && + ! codec->in_resume) + return 0; + + spec->cur_ch_mode = ucontrol->value.enumerated.item[0]; + spec->multiout.max_channels = stac922x_channel_modes[spec->cur_ch_mode]; + + return 1; +} static snd_kcontrol_new_t stac9200_mixer[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT), @@ -174,13 +211,12 @@ static snd_kcontrol_new_t stac9200_mixer[] = { }, HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Input Mux Volume", 0x0c, 0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Capture Mux Volume", 0x0c, 0, HDA_OUTPUT), { } /* end */ }; +/* This needs to be generated dynamically based on sequence */ static snd_kcontrol_new_t stac922x_mixer[] = { - HDA_CODEC_VOLUME("PCM Playback Volume", 0x2, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x2, 0x0, HDA_OUTPUT), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", @@ -195,14 +231,38 @@ static snd_kcontrol_new_t stac922x_mixer[] = { { } /* end */ }; +static snd_kcontrol_new_t stac922x_ch_mode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = stac922x_ch_mode_info, + .get = stac922x_ch_mode_get, + .put = stac922x_ch_mode_put, + }, + { } /* end */ +}; + static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; int err; + int i; err = snd_hda_add_new_ctls(codec, spec->mixer); if (err < 0) return err; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; + } + + if (spec->surr_switch) { + err = snd_hda_add_new_ctls(codec, stac922x_ch_mode_mixer); + if (err < 0) + return err; + } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); if (err < 0) @@ -222,9 +282,9 @@ static unsigned int stac9200_pin_configs[8] = { 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, }; -static unsigned int stac922x_pin_configs[14] = { - 0x40000100, 0x40000100, 0x40000100, 0x01114010, - 0x01813122, 0x40000100, 0x01447010, 0x01c47010, +static unsigned int stac922x_pin_configs[10] = { + 0x01014010, 0x01014011, 0x01014012, 0x0221401f, + 0x01813122, 0x01014014, 0x01441030, 0x01c41030, 0x40000100, 0x40000100, }; @@ -255,180 +315,66 @@ static void stac92xx_set_config_regs(struct hda_codec *codec) } #endif -static int stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int value) -{ - unsigned int pin_ctl; - - pin_ctl = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, - 0x00); - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, - pin_ctl | value); - - return 0; -} - -static int stac92xx_set_vref(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int vref_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) >> AC_PINCAP_VREF_SHIFT; - unsigned int vref_ctl = AC_PINCTL_VREF_HIZ; - - if (vref_caps & AC_PINCAP_VREF_100) - vref_ctl = AC_PINCTL_VREF_100; - else if (vref_caps & AC_PINCAP_VREF_80) - vref_ctl = AC_PINCTL_VREF_80; - else if (vref_caps & AC_PINCAP_VREF_50) - vref_ctl = AC_PINCTL_VREF_50; - else if (vref_caps & AC_PINCAP_VREF_GRD) - vref_ctl = AC_PINCTL_VREF_GRD; - - stac92xx_set_pinctl(codec, nid, vref_ctl); - - return 0; -} - /* - * retrieve the default device type from the default config value + * Analog playback callbacks */ -#define get_defcfg_type(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT) -#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT) - -static int stac92xx_config_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_cfg) +static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + snd_pcm_substream_t *substream) { struct sigmatel_spec *spec = codec->spec; - u32 location = get_defcfg_location(pin_cfg); - char *label; - const char *type = NULL; - int ainput = 0; - - switch(get_defcfg_type(pin_cfg)) { - case AC_JACK_HP_OUT: - /* Enable HP amp */ - stac92xx_set_pinctl(codec, nid, AC_PINCTL_HP_EN); - /* Fall through */ - case AC_JACK_SPDIF_OUT: - case AC_JACK_LINE_OUT: - case AC_JACK_SPEAKER: - /* Enable output */ - stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); - break; - case AC_JACK_SPDIF_IN: - stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); - break; - case AC_JACK_MIC_IN: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - type = "Front Mic"; - else - type = "Mic"; - ainput = 1; - /* Set vref */ - stac92xx_set_vref(codec, nid); - stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); - break; - case AC_JACK_CD: - type = "CD"; - ainput = 1; - stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); - break; - case AC_JACK_LINE_IN: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - type = "Front Line"; - else - type = "Line"; - ainput = 1; - stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); - break; - case AC_JACK_AUX: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - type = "Front Aux"; - else - type = "Aux"; - ainput = 1; - stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN); - break; - } - - if (ainput) { - hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - int i, j, num_cons, index = -1; - if (!type) - type = "Input"; - label = spec->input_labels[spec->input_mux.num_items]; - strcpy(label, type); - spec->input_mux.items[spec->input_mux.num_items].label = label; - for (i=0; i<spec->num_muxes; i++) { - num_cons = snd_hda_get_connections(codec, spec->mux_nids[i], con_lst, HDA_MAX_NUM_INPUTS); - for (j=0; j<num_cons; j++) - if (con_lst[j] == nid) { - index = j; - break; - } - if (index >= 0) - break; - } - spec->input_mux.items[spec->input_mux.num_items].index = index; - spec->input_mux.num_items++; - } - - return 0; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); } -static int stac92xx_config_pins(struct hda_codec *codec) +/* + * set up the i/o for analog out + * when the digital out is available, copy the front out to digital out, too. + */ +static int stac92xx_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_out *mout, + unsigned int stream_tag, + unsigned int format, + snd_pcm_substream_t *substream) { - struct sigmatel_spec *spec = codec->spec; + hda_nid_t *nids = mout->dac_nids; + int chs = substream->runtime->channels; int i; - unsigned int pin_cfg; - for (i=0; i < spec->num_pins; i++) { - /* Default to disabled */ - snd_hda_codec_write(codec, spec->pin_nids[i], 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - 0x00); - - pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0, - AC_VERB_GET_CONFIG_DEFAULT, - 0x00); - if (((pin_cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) == AC_JACK_PORT_NONE) - continue; /* Move on */ - - stac92xx_config_pin(codec, spec->pin_nids[i], pin_cfg); + down(&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; + /* setup digital receiver */ + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, + stream_tag, 0, format); + } else { + mout->dig_out_used = 0; + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0); + } } - - return 0; -} - -static int stac92xx_init(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - int i; - - for (i=0; i < spec->num_pstates; i++) - snd_hda_codec_write(codec, spec->pstate_nids[i], 0, - AC_VERB_SET_POWER_STATE, 0x00); - - mdelay(100); - - snd_hda_sequence_write(codec, spec->init); - -#ifdef STAC_TEST - stac92xx_set_config_regs(codec); -#endif - - stac92xx_config_pins(codec); - + up(&codec->spdif_mutex); + + /* front */ + snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 0, format); + if (mout->hp_nid) + /* headphone out will just decode front left/right (stereo) */ + snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); + /* surrounds */ + if (mout->max_channels > 2) + for (i = 1; i < mout->num_dacs; i++) { + if ((mout->max_channels == 6) && (i == 3)) + break; + 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); + } return 0; } -/* - * Analog playback callbacks - */ -static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - snd_pcm_substream_t *substream) -{ - struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream); -} static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct hda_codec *codec, @@ -437,7 +383,7 @@ static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo, snd_pcm_substream_t *substream) { struct sigmatel_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, + return stac92xx_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, format, substream); } @@ -516,7 +462,7 @@ static struct hda_pcm_stream stac92xx_pcm_digital_capture = { static struct hda_pcm_stream stac92xx_pcm_analog_playback = { .substreams = 1, .channels_min = 2, - .channels_max = 2, + .channels_max = 8, .nid = 0x02, /* NID to query formats and rates */ .ops = { .open = stac92xx_playback_pcm_open, @@ -544,11 +490,9 @@ static int stac92xx_build_pcms(struct hda_codec *codec) codec->num_pcms = 1; codec->pcm_info = info; - info->name = "STAC92xx"; + info->name = "STAC92xx Analog"; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->playback_nid; info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->capture_nid; if (spec->multiout.dig_out_nid || spec->dig_in_nid) { codec->num_pcms++; @@ -567,21 +511,413 @@ static int stac92xx_build_pcms(struct hda_codec *codec) return 0; } +enum { + STAC_CTL_WIDGET_VOL, + STAC_CTL_WIDGET_MUTE, +}; + +static snd_kcontrol_new_t stac92xx_control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), +}; + +/* add dynamic controls */ +static int stac92xx_add_control(struct sigmatel_spec *spec, int type, const char *name, unsigned long val) +{ + snd_kcontrol_new_t *knew; + + if (spec->num_kctl_used >= spec->num_kctl_alloc) { + int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC; + + knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */ + if (! knew) + return -ENOMEM; + if (spec->kctl_alloc) { + memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc); + kfree(spec->kctl_alloc); + } + spec->kctl_alloc = knew; + spec->num_kctl_alloc = num; + } + + knew = &spec->kctl_alloc[spec->num_kctl_used]; + *knew = stac92xx_control_templates[type]; + knew->name = kstrdup(name, GFP_KERNEL); + if (! knew->name) + return -ENOMEM; + knew->private_value = val; + spec->num_kctl_used++; + return 0; +} + +/* fill in the dac_nids table from the parsed pin configuration */ +static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid; + int i; + + /* check the pins hardwired to audio widget */ + for (i = 0; i < cfg->line_outs; i++) { + nid = cfg->line_out_pins[i]; + spec->multiout.dac_nids[i] = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONNECT_LIST, 0) & 0xff; + } + + spec->multiout.num_dacs = cfg->line_outs; + + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const struct auto_pin_cfg *cfg) +{ + char name[32]; + static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; + hda_nid_t nid; + int i, err; + + for (i = 0; i < cfg->line_outs; i++) { + if (! spec->multiout.dac_nids[i]) + continue; + + nid = spec->multiout.dac_nids[i]; + + if (i == 2) { + /* Center/LFE */ + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Center Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0) + return err; + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "LFE Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0) + return err; + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Center Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0) + return err; + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "LFE Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0) + return err; + } else { + sprintf(name, "%s Playback Volume", chname[i]); + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0) + return err; + sprintf(name, "%s Playback Switch", chname[i]); + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0) + return err; + } + } + + return 0; +} + +/* add playback controls for HP output */ +static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t pin = cfg->hp_pin; + hda_nid_t nid; + int i, err; + unsigned int wid_caps; + + if (! pin) + return 0; + + wid_caps = snd_hda_param_read(codec, pin, AC_PAR_AUDIO_WIDGET_CAP); + if (wid_caps & AC_WCAP_UNSOL_CAP) + /* Enable unsolicited responses on the HP widget */ + snd_hda_codec_write(codec, pin, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + STAC_UNSOL_ENABLE); + + nid = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff; + for (i = 0; i < cfg->line_outs; i++) { + if (! spec->multiout.dac_nids[i]) + continue; + if (spec->multiout.dac_nids[i] == nid) + return 0; + } + + spec->multiout.hp_nid = nid; + + /* control HP volume/switch on the output mixer amp */ + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, "Headphone Playback Volume", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0) + return err; + if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, "Headphone Playback Switch", + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0) + return err; + + return 0; +} + +/* create playback/capture controls for input pins */ +static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +{ + struct sigmatel_spec *spec = codec->spec; + static char *labels[AUTO_PIN_LAST] = { + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux" + }; + struct hda_input_mux *imux = &spec->private_imux; + hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; + int i, j, k; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + int index = -1; + if (cfg->input_pins[i]) { + imux->items[imux->num_items].label = labels[i]; + + for (j=0; j<spec->num_muxes; j++) { + int num_cons = snd_hda_get_connections(codec, spec->mux_nids[j], con_lst, HDA_MAX_NUM_INPUTS); + for (k=0; k<num_cons; k++) + if (con_lst[k] == cfg->input_pins[i]) { + index = k; + break; + } + if (index >= 0) + break; + } + imux->items[imux->num_items].index = index; + imux->num_items++; + } + } + + return 0; +} + +static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type) + +{ + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); +} + +static void stac92xx_auto_init_multi_out(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->autocfg.line_outs; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + } +} + +static void stac92xx_auto_init_hp_out(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pin; + if (pin) /* connect to front */ + stac92xx_auto_set_pinctl(codec, pin, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); +} + +static int stac922x_parse_auto_config(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + + if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0) + return err; + if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) + return err; + if (! spec->autocfg.line_outs && ! spec->autocfg.hp_pin) + return 0; /* can't find valid pin config */ + + if ((err = stac92xx_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || + (err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg)) < 0 || + (err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) + return err; + + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + if (spec->multiout.max_channels > 2) { + spec->surr_switch = 1; + spec->cur_ch_mode = 1; + spec->num_ch_modes = 2; + if (spec->multiout.max_channels == 8) { + spec->cur_ch_mode++; + spec->num_ch_modes++; + } + } + + if (spec->autocfg.dig_out_pin) { + spec->multiout.dig_out_nid = 0x08; + stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_out_pin, AC_PINCTL_OUT_EN); + } + if (spec->autocfg.dig_in_pin) { + spec->dig_in_nid = 0x09; + stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_in_pin, AC_PINCTL_IN_EN); + } + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +static int stac9200_parse_auto_config(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int err; + + if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0) + return err; + + if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0) + return err; + + if (spec->autocfg.dig_out_pin) { + spec->multiout.dig_out_nid = 0x05; + stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_out_pin, AC_PINCTL_OUT_EN); + } + if (spec->autocfg.dig_in_pin) { + spec->dig_in_nid = 0x04; + stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_in_pin, AC_PINCTL_IN_EN); + } + + if (spec->kctl_alloc) + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; + + spec->input_mux = &spec->private_imux; + + return 1; +} + +static int stac92xx_init_pstate(struct hda_codec *codec) +{ + hda_nid_t nid, nid_start; + int nodes; + + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_POWER_STATE, 0x00); + + nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start); + for (nid = nid_start; nid < nodes + nid_start; nid++) { + unsigned int wid_caps = snd_hda_param_read(codec, nid, + AC_PAR_AUDIO_WIDGET_CAP); + if (wid_caps & AC_WCAP_POWER) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, 0x00); + } + + mdelay(100); + + return 0; +} + +static int stac92xx_init(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + + stac92xx_init_pstate(codec); + + snd_hda_sequence_write(codec, spec->init); + + stac92xx_auto_init_multi_out(codec); + stac92xx_auto_init_hp_out(codec); + + return 0; +} + static void stac92xx_free(struct hda_codec *codec) { - kfree(codec->spec); + struct sigmatel_spec *spec = codec->spec; + int i; + + if (! spec) + return; + + if (spec->kctl_alloc) { + for (i = 0; i < spec->num_kctl_used; i++) + kfree(spec->kctl_alloc[i].name); + kfree(spec->kctl_alloc); + } + + kfree(spec); +} + +static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, + unsigned int flag) +{ + unsigned int pin_ctl = snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_ctl | flag); +} + +static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, + unsigned int flag) +{ + unsigned int pin_ctl = snd_hda_codec_read(codec, nid, + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_ctl & ~flag); +} + +static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) +{ + struct sigmatel_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, presence; + + if ((res >> 26) != STAC_HP_EVENT) + return; + + presence = snd_hda_codec_read(codec, cfg->hp_pin, 0, + AC_VERB_GET_PIN_SENSE, 0x00) >> 31; + + if (presence) { + /* disable lineouts, enable hp */ + for (i = 0; i < cfg->line_outs; i++) + stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], + AC_PINCTL_OUT_EN); + stac92xx_set_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN); + } else { + /* enable lineouts, disable hp */ + for (i = 0; i < cfg->line_outs; i++) + stac92xx_set_pinctl(codec, cfg->line_out_pins[i], + AC_PINCTL_OUT_EN); + stac92xx_reset_pinctl(codec, cfg->hp_pin, AC_PINCTL_OUT_EN); + } +} + +#ifdef CONFIG_PM +static int stac92xx_resume(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + int i; + + stac92xx_init(codec); + for (i = 0; i < spec->num_mixers; i++) + snd_hda_resume_ctls(codec, spec->mixers[i]); + if (spec->multiout.dig_out_nid) + snd_hda_resume_spdif_out(codec); + if (spec->dig_in_nid) + snd_hda_resume_spdif_in(codec); + + return 0; } +#endif static struct hda_codec_ops stac92xx_patch_ops = { .build_controls = stac92xx_build_controls, .build_pcms = stac92xx_build_pcms, .init = stac92xx_init, .free = stac92xx_free, + .unsol_event = stac92xx_unsol_event, +#ifdef CONFIG_PM + .resume = stac92xx_resume, +#endif }; static int patch_stac9200(struct hda_codec *codec) { struct sigmatel_spec *spec; + int err; spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -589,26 +925,27 @@ static int patch_stac9200(struct hda_codec *codec) codec->spec = spec; +#ifdef STAC_TEST + spec->pin_nids = stac9200_pin_nids; + spec->num_pins = 8; + spec->pin_configs = stac9200_pin_configs; + stac92xx_set_config_regs(codec); +#endif spec->multiout.max_channels = 2; spec->multiout.num_dacs = 1; spec->multiout.dac_nids = stac9200_dac_nids; - spec->multiout.dig_out_nid = 0x05; - spec->dig_in_nid = 0x04; spec->adc_nids = stac9200_adc_nids; spec->mux_nids = stac9200_mux_nids; spec->num_muxes = 1; - spec->input_mux.num_items = 0; - spec->pstate_nids = stac9200_pstate_nids; - spec->num_pstates = 3; - spec->pin_nids = stac9200_pin_nids; -#ifdef STAC_TEST - spec->pin_configs = stac9200_pin_configs; -#endif - spec->num_pins = 8; - spec->init = stac9200_ch2_init; + + spec->init = stac9200_core_init; spec->mixer = stac9200_mixer; - spec->playback_nid = 0x02; - spec->capture_nid = 0x03; + + err = stac9200_parse_auto_config(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } codec->patch_ops = stac92xx_patch_ops; @@ -618,6 +955,7 @@ static int patch_stac9200(struct hda_codec *codec) static int patch_stac922x(struct hda_codec *codec) { struct sigmatel_spec *spec; + int err; spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); if (spec == NULL) @@ -625,26 +963,26 @@ static int patch_stac922x(struct hda_codec *codec) codec->spec = spec; - spec->multiout.max_channels = 2; - spec->multiout.num_dacs = 4; - spec->multiout.dac_nids = stac922x_dac_nids; - spec->multiout.dig_out_nid = 0x08; - spec->dig_in_nid = 0x09; - spec->adc_nids = stac922x_adc_nids; - spec->mux_nids = stac922x_mux_nids; - spec->num_muxes = 2; - spec->input_mux.num_items = 0; - spec->pstate_nids = stac922x_pstate_nids; - spec->num_pstates = 8; - spec->pin_nids = stac922x_pin_nids; #ifdef STAC_TEST + spec->num_pins = 10; + spec->pin_nids = stac922x_pin_nids; spec->pin_configs = stac922x_pin_configs; + stac92xx_set_config_regs(codec); #endif - spec->num_pins = 10; - spec->init = stac922x_ch2_init; + spec->adc_nids = stac922x_adc_nids; + spec->mux_nids = stac922x_mux_nids; + spec->num_muxes = 2; + + spec->init = stac922x_core_init; spec->mixer = stac922x_mixer; - spec->playback_nid = 0x02; - spec->capture_nid = 0x06; + + spec->multiout.dac_nids = spec->dac_nids; + + err = stac922x_parse_auto_config(codec); + if (err < 0) { + stac92xx_free(codec); + return err; + } codec->patch_ops = stac92xx_patch_ops; diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c index eb20f73be61a..39fbe662965d 100644 --- a/sound/pci/ice1712/delta.c +++ b/sound/pci/ice1712/delta.c @@ -618,15 +618,15 @@ static int __devinit snd_ice1712_delta_init(ice1712_t *ice) */ static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata = -ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0); +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0); static snd_kcontrol_new_t snd_ice1712_delta1010lt_wordclock_select __devinitdata = -ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 1, 0); +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Sync", 0, ICE1712_DELTA_1010LT_WORDCLOCK, 1, 0); static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata = -ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata = -ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0); +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0); static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata = -ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); +ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_MIXER, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE); static int __devinit snd_ice1712_delta_add_controls(ice1712_t *ice) diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index a2545a5b26c4..b97f50d10ba3 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -1422,7 +1422,7 @@ static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_switch __devinitdata static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_switch __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .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, @@ -1441,7 +1441,7 @@ static snd_kcontrol_new_t snd_ice1712_multi_capture_analog_volume __devinitdata static snd_kcontrol_new_t snd_ice1712_multi_capture_spdif_volume __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .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, @@ -1715,7 +1715,7 @@ static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_ice1712_spdif_info, .get = snd_ice1712_spdif_maskc_get, @@ -1724,7 +1724,7 @@ static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata = static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), .info = snd_ice1712_spdif_info, .get = snd_ice1712_spdif_maskp_get, @@ -2203,7 +2203,7 @@ static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = { static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "IEC958 Playback 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, diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 79b5f12e06fc..c7af5e5fee13 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1414,7 +1414,7 @@ static int snd_vt1724_spdif_maskp_get(snd_kcontrol_t * kcontrol, static snd_kcontrol_new_t snd_vt1724_spdif_maskc __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_vt1724_spdif_info, .get = snd_vt1724_spdif_maskc_get, @@ -1423,7 +1423,7 @@ static snd_kcontrol_new_t snd_vt1724_spdif_maskc __devinitdata = static snd_kcontrol_new_t snd_vt1724_spdif_maskp __devinitdata = { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), .info = snd_vt1724_spdif_info, .get = snd_vt1724_spdif_maskp_get, @@ -1466,7 +1466,7 @@ static snd_kcontrol_new_t 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 = "IEC958 Output 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 diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index cc16f95f9cef..7b548416dcef 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -389,6 +389,7 @@ typedef struct { struct ac97_pcm *pcm; int pcm_open_flag; unsigned int page_attr_changed: 1; + unsigned int suspended: 1; } ichdev_t; typedef struct _snd_intel8x0 intel8x0_t; @@ -424,6 +425,7 @@ struct _snd_intel8x0 { unsigned xbox: 1; /* workaround for Xbox AC'97 detection */ int spdif_idx; /* SPDIF BAR index; *_SPBAR or -1 if use PCMOUT */ + unsigned int sdm_saved; /* SDM reg value */ ac97_bus_t *ac97_bus; ac97_t *ac97[3]; @@ -861,12 +863,16 @@ static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd) unsigned long port = ichdev->reg_offset; switch (cmd) { - case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: + ichdev->suspended = 0; + /* fallthru */ + case SNDRV_PCM_TRIGGER_START: val = ICH_IOCE | ICH_STARTBM; break; - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: + ichdev->suspended = 1; + /* fallthru */ + case SNDRV_PCM_TRIGGER_STOP: val = 0; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -898,9 +904,11 @@ static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd) val = igetdword(chip, ICHREG(ALI_DMACR)); switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + ichdev->suspended = 0; + /* fallthru */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - case SNDRV_PCM_TRIGGER_RESUME: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* clear FIFO for synchronization of channels */ fifo = igetdword(chip, fiforeg[ichdev->ali_slot / 4]); @@ -912,9 +920,11 @@ static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd) val &= ~(1 << (ichdev->ali_slot + 16)); /* clear PAUSE flag */ iputdword(chip, ICHREG(ALI_DMACR), val | (1 << ichdev->ali_slot)); /* start DMA */ break; + case SNDRV_PCM_TRIGGER_SUSPEND: + ichdev->suspended = 1; + /* fallthru */ case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - case SNDRV_PCM_TRIGGER_SUSPEND: iputdword(chip, ICHREG(ALI_DMACR), val | (1 << (ichdev->ali_slot + 16))); /* pause */ iputbyte(chip, port + ICH_REG_OFF_CR, 0); while (igetbyte(chip, port + ICH_REG_OFF_CR)) @@ -993,6 +1003,8 @@ static void snd_intel8x0_setup_pcm_out(intel8x0_t *chip, { unsigned int cnt; int dbl = runtime->rate > 48000; + + spin_lock_irq(&chip->reg_lock); switch (chip->device_type) { case DEVICE_ALI: cnt = igetdword(chip, ICHREG(ALI_SCR)); @@ -1036,6 +1048,7 @@ static void snd_intel8x0_setup_pcm_out(intel8x0_t *chip, iputdword(chip, ICHREG(GLOB_CNT), cnt); break; } + spin_unlock_irq(&chip->reg_lock); } static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream) @@ -1047,15 +1060,12 @@ static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream) ichdev->physbuf = runtime->dma_addr; ichdev->size = snd_pcm_lib_buffer_bytes(substream); ichdev->fragsize = snd_pcm_lib_period_bytes(substream); - spin_lock_irq(&chip->reg_lock); if (ichdev->ichd == ICHD_PCMOUT) { snd_intel8x0_setup_pcm_out(chip, runtime); - if (chip->device_type == DEVICE_INTEL_ICH4) { + if (chip->device_type == DEVICE_INTEL_ICH4) ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; - } } snd_intel8x0_setup_periods(chip, ichdev); - spin_unlock_irq(&chip->reg_lock); return 0; } @@ -1816,6 +1826,18 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { }, { .subvendor = 0x103c, + .subdevice = 0x0934, + .name = "HP nx8220", + .type = AC97_TUNE_MUTE_LED + }, + { + .subvendor = 0x103c, + .subdevice = 0x099c, + .name = "HP nx6110", /* AD1981B */ + .type = AC97_TUNE_HP_ONLY + }, + { + .subvendor = 0x103c, .subdevice = 0x129d, .name = "HP xw8000", .type = AC97_TUNE_HP_ONLY @@ -1869,6 +1891,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = { .type = AC97_TUNE_HP_ONLY }, { + .subvendor = 0x10cf, + .subdevice = 0x12ec, + .name = "Fujitsu-Siemens 4010", + .type = AC97_TUNE_HP_ONLY + }, + { .subvendor = 0x10f1, .subdevice = 0x2665, .name = "Fujitsu-Siemens Celsius", /* AD1981? */ @@ -2373,6 +2401,11 @@ static int intel8x0_suspend(snd_card_t *card, pm_message_t state) for (i = 0; i < 3; i++) if (chip->ac97[i]) snd_ac97_suspend(chip->ac97[i]); + if (chip->device_type == DEVICE_INTEL_ICH4) + chip->sdm_saved = igetbyte(chip, ICHREG(SDM)); + + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); pci_disable_device(chip->pci); return 0; } @@ -2384,7 +2417,19 @@ static int intel8x0_resume(snd_card_t *card) pci_enable_device(chip->pci); pci_set_master(chip->pci); - snd_intel8x0_chip_init(chip, 0); + request_irq(chip->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip); + synchronize_irq(chip->irq); + snd_intel8x0_chip_init(chip, 1); + + /* re-initialize mixer stuff */ + if (chip->device_type == DEVICE_INTEL_ICH4) { + /* enable separate SDINs for ICH4 */ + iputbyte(chip, ICHREG(SDM), chip->sdm_saved); + /* use slot 10/11 for SPDIF */ + iputdword(chip, ICHREG(GLOB_CNT), + (igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_SPDIF_MASK) | + ICH_PCM_SPDIF_1011); + } /* refill nocache */ if (chip->fix_nocache) @@ -2406,6 +2451,20 @@ static int intel8x0_resume(snd_card_t *card) } } + /* resume status */ + for (i = 0; i < chip->bdbars_count; i++) { + ichdev_t *ichdev = &chip->ichd[i]; + unsigned long port = ichdev->reg_offset; + if (! ichdev->substream || ! ichdev->suspended) + continue; + if (ichdev->ichd == ICHD_PCMOUT) + snd_intel8x0_setup_pcm_out(chip, ichdev->substream->runtime); + iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr); + iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi); + iputbyte(chip, port + ICH_REG_OFF_CIV, ichdev->civ); + iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI); + } + return 0; } #endif /* CONFIG_PM */ @@ -2451,8 +2510,7 @@ static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip) } do_gettimeofday(&start_time); spin_unlock_irq(&chip->reg_lock); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ / 20); + msleep(50); spin_lock_irq(&chip->reg_lock); /* check the position */ pos = ichdev->fragsize1; diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 79d8eda54f0d..d2aa9c82d41e 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2067,7 +2067,7 @@ static int snd_korg1212_control_sync_put(snd_kcontrol_t * kcontrol, snd_ctl_elem }, \ { \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, \ - .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = c_name " Monitor Phase Invert", \ .info = snd_korg1212_control_phase_info, \ .get = snd_korg1212_control_phase_get, \ @@ -2082,7 +2082,7 @@ static snd_kcontrol_new_t snd_korg1212_controls[] = { MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"), { .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Sync Source", .info = snd_korg1212_control_sync_info, .get = snd_korg1212_control_sync_get, diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 096f15132853..39b5e7db1543 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -1050,27 +1050,22 @@ static struct m3_hv_quirk m3_hv_quirk_list[] = { * lowlevel functions */ -#define big_mdelay(msec) do {\ - set_current_state(TASK_UNINTERRUPTIBLE);\ - schedule_timeout(((msec) * HZ) / 1000);\ -} while (0) - -inline static void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg) +static inline void snd_m3_outw(m3_t *chip, u16 value, unsigned long reg) { outw(value, chip->iobase + reg); } -inline static u16 snd_m3_inw(m3_t *chip, unsigned long reg) +static inline u16 snd_m3_inw(m3_t *chip, unsigned long reg) { return inw(chip->iobase + reg); } -inline static void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg) +static inline void snd_m3_outb(m3_t *chip, u8 value, unsigned long reg) { outb(value, chip->iobase + reg); } -inline static u8 snd_m3_inb(m3_t *chip, unsigned long reg) +static inline u8 snd_m3_inb(m3_t *chip, unsigned long reg) { return inb(chip->iobase + reg); } @@ -1096,7 +1091,7 @@ static void snd_m3_assp_write(m3_t *chip, u16 region, u16 index, u16 data) static void snd_m3_assp_halt(m3_t *chip) { chip->reset_state = snd_m3_inb(chip, DSP_PORT_CONTROL_REG_B) & ~REGB_STOP_CLOCK; - big_mdelay(10); + msleep(10); snd_m3_outb(chip, chip->reset_state & ~REGB_ENABLE_RESET, DSP_PORT_CONTROL_REG_B); } @@ -2108,9 +2103,9 @@ static void snd_m3_ac97_reset(m3_t *chip) */ tmp = inw(io + RING_BUS_CTRL_A); outw(RAC_SDFS_ENABLE|LAC_SDFS_ENABLE, io + RING_BUS_CTRL_A); - big_mdelay(20); + msleep(20); outw(tmp, io + RING_BUS_CTRL_A); - big_mdelay(50); + msleep(50); #endif } @@ -2525,9 +2520,13 @@ static void snd_m3_enable_ints(m3_t *chip) { unsigned long io = chip->iobase; + unsigned short val; /* TODO: MPU401 not supported yet */ - outw(ASSP_INT_ENABLE | HV_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL); + val = ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/; + if (chip->hv_quirk && (chip->hv_quirk->config & HV_CTRL_ENABLE)) + val |= HV_INT_ENABLE; + outw(val, io + HOST_INT_CTRL); outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, io + ASSP_CONTROL_C); } @@ -2589,7 +2588,7 @@ static int m3_suspend(snd_card_t *card, pm_message_t state) snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); - big_mdelay(10); /* give the assp a chance to idle.. */ + msleep(10); /* give the assp a chance to idle.. */ snd_m3_assp_halt(chip); @@ -2697,6 +2696,8 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, } spin_lock_init(&chip->reg_lock); + spin_lock_init(&chip->ac97_lock); + switch (pci->device) { case PCI_DEVICE_ID_ESS_ALLEGRO: case PCI_DEVICE_ID_ESS_ALLEGRO_1: @@ -2765,6 +2766,8 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, snd_m3_assp_init(chip); snd_m3_amp_enable(chip, 1); + tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); + if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ, card->driver, (void *)chip)) { snd_printk("unable to grab IRQ %d\n", pci->irq); @@ -2786,9 +2789,6 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, return err; } - spin_lock_init(&chip->ac97_lock); - tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); - if ((err = snd_m3_mixer(chip)) < 0) return err; diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 082c0d0f73d2..6c868d913634 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -445,9 +445,9 @@ static int snd_mixart_trigger(snd_pcm_substream_t *subs, int cmd) static int mixart_sync_nonblock_events(mixart_mgr_t *mgr) { - int timeout = HZ; + unsigned long timeout = jiffies + HZ; while (atomic_read(&mgr->msg_processed) > 0) { - if (! timeout--) { + if (time_after(jiffies, timeout)) { snd_printk(KERN_ERR "mixart: cannot process nonblock events!\n"); return -EBUSY; } diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 8a52091f8552..2bbeb10ff7c4 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -189,6 +189,7 @@ struct snd_nm256_stream { nm256_t *chip; snd_pcm_substream_t *substream; int running; + int suspended; u32 buf; /* offset from chip->buffer */ int bufsize; /* buffer size in bytes */ @@ -231,8 +232,10 @@ struct snd_nm256 { int mixer_status_mask; /* bit mask to test the mixer status */ int irq; + int irq_acks; irqreturn_t (*interrupt)(int, void *, struct pt_regs *); int badintrcount; /* counter to check bogus interrupts */ + struct semaphore irq_mutex; nm256_stream_t streams[2]; @@ -285,43 +288,43 @@ MODULE_DEVICE_TABLE(pci, snd_nm256_ids); * lowlvel stuffs */ -inline static u8 +static inline u8 snd_nm256_readb(nm256_t *chip, int offset) { return readb(chip->cport + offset); } -inline static u16 +static inline u16 snd_nm256_readw(nm256_t *chip, int offset) { return readw(chip->cport + offset); } -inline static u32 +static inline u32 snd_nm256_readl(nm256_t *chip, int offset) { return readl(chip->cport + offset); } -inline static void +static inline void snd_nm256_writeb(nm256_t *chip, int offset, u8 val) { writeb(val, chip->cport + offset); } -inline static void +static inline void snd_nm256_writew(nm256_t *chip, int offset, u16 val) { writew(val, chip->cport + offset); } -inline static void +static inline void snd_nm256_writel(nm256_t *chip, int offset, u32 val) { writel(val, chip->cport + offset); } -inline static void +static inline void snd_nm256_write_buffer(nm256_t *chip, void *src, int offset, int size) { offset -= chip->buffer_start; @@ -464,6 +467,37 @@ snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *subs } } +/* acquire interrupt */ +static int snd_nm256_acquire_irq(nm256_t *chip) +{ + down(&chip->irq_mutex); + if (chip->irq < 0) { + if (request_irq(chip->pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, + chip->card->driver, (void*)chip)) { + snd_printk("unable to grab IRQ %d\n", chip->pci->irq); + up(&chip->irq_mutex); + return -EBUSY; + } + chip->irq = chip->pci->irq; + } + chip->irq_acks++; + up(&chip->irq_mutex); + return 0; +} + +/* release interrupt */ +static void snd_nm256_release_irq(nm256_t *chip) +{ + down(&chip->irq_mutex); + if (chip->irq_acks > 0) + chip->irq_acks--; + if (chip->irq_acks == 0 && chip->irq >= 0) { + free_irq(chip->irq, (void*)chip); + chip->irq = -1; + } + up(&chip->irq_mutex); +} + /* * start / stop */ @@ -538,15 +572,19 @@ snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd) spin_lock(&chip->reg_lock); switch (cmd) { - case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: + s->suspended = 0; + /* fallthru */ + case SNDRV_PCM_TRIGGER_START: if (! s->running) { snd_nm256_playback_start(chip, s, substream); s->running = 1; } break; - case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: + s->suspended = 1; + /* fallthru */ + case SNDRV_PCM_TRIGGER_STOP: if (s->running) { snd_nm256_playback_stop(chip); s->running = 0; @@ -818,6 +856,8 @@ snd_nm256_playback_open(snd_pcm_substream_t *substream) { nm256_t *chip = snd_pcm_substream_chip(substream); + if (snd_nm256_acquire_irq(chip) < 0) + return -EBUSY; snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], substream, &snd_nm256_playback); return 0; @@ -828,6 +868,8 @@ snd_nm256_capture_open(snd_pcm_substream_t *substream) { nm256_t *chip = snd_pcm_substream_chip(substream); + if (snd_nm256_acquire_irq(chip) < 0) + return -EBUSY; snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], substream, &snd_nm256_capture); return 0; @@ -839,6 +881,9 @@ snd_nm256_capture_open(snd_pcm_substream_t *substream) static int snd_nm256_playback_close(snd_pcm_substream_t *substream) { + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_release_irq(chip); return 0; } @@ -846,6 +891,9 @@ snd_nm256_playback_close(snd_pcm_substream_t *substream) static int snd_nm256_capture_close(snd_pcm_substream_t *substream) { + nm256_t *chip = snd_pcm_substream_chip(substream); + + snd_nm256_release_irq(chip); return 0; } @@ -915,18 +963,16 @@ snd_nm256_pcm(nm256_t *chip, int device) static void snd_nm256_init_chip(nm256_t *chip) { - spin_lock_irq(&chip->reg_lock); /* Reset everything. */ snd_nm256_writeb(chip, 0x0, 0x11); snd_nm256_writew(chip, 0x214, 0); /* stop sounds.. */ //snd_nm256_playback_stop(chip); //snd_nm256_capture_stop(chip); - spin_unlock_irq(&chip->reg_lock); } -inline static void +static irqreturn_t snd_nm256_intr_check(nm256_t *chip) { if (chip->badintrcount++ > 1000) { @@ -947,7 +993,9 @@ snd_nm256_intr_check(nm256_t *chip) if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) snd_nm256_capture_stop(chip); chip->badintrcount = 0; + return IRQ_HANDLED; } + return IRQ_NONE; } /* @@ -969,10 +1017,8 @@ snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy) status = snd_nm256_readw(chip, NM_INT_REG); /* Not ours. */ - if (status == 0) { - snd_nm256_intr_check(chip); - return IRQ_NONE; - } + if (status == 0) + return snd_nm256_intr_check(chip); chip->badintrcount = 0; @@ -1036,10 +1082,8 @@ snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy) status = snd_nm256_readl(chip, NM_INT_REG); /* Not ours. */ - if (status == 0) { - snd_nm256_intr_check(chip); - return IRQ_NONE; - } + if (status == 0) + return snd_nm256_intr_check(chip); chip->badintrcount = 0; @@ -1192,7 +1236,7 @@ snd_nm256_mixer(nm256_t *chip) AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD, AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, - AC97_EXTENDED_ID, + /*AC97_EXTENDED_ID,*/ AC97_VENDOR_ID1, AC97_VENDOR_ID2, -1 }; @@ -1206,6 +1250,7 @@ snd_nm256_mixer(nm256_t *chip) for (i = 0; mixer_regs[i] >= 0; i++) set_bit(mixer_regs[i], ac97.reg_accessed); ac97.private_data = chip; + pbus->no_vra = 1; err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); if (err < 0) return err; @@ -1281,6 +1326,7 @@ static int nm256_suspend(snd_card_t *card, pm_message_t state) static int nm256_resume(snd_card_t *card) { nm256_t *chip = card->pm_private_data; + int i; /* Perform a full reset on the hardware */ pci_enable_device(chip->pci); @@ -1289,6 +1335,15 @@ static int nm256_resume(snd_card_t *card) /* restore ac97 */ snd_ac97_resume(chip->ac97); + for (i = 0; i < 2; i++) { + nm256_stream_t *s = &chip->streams[i]; + if (s->substream && s->suspended) { + spin_lock_irq(&chip->reg_lock); + snd_nm256_set_format(chip, s, s->substream); + spin_unlock_irq(&chip->reg_lock); + } + } + return 0; } #endif /* CONFIG_PM */ @@ -1360,6 +1415,7 @@ snd_nm256_create(snd_card_t *card, struct pci_dev *pci, chip->use_cache = usecache; spin_lock_init(&chip->reg_lock); chip->irq = -1; + init_MUTEX(&chip->irq_mutex); chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize; chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize; @@ -1470,15 +1526,6 @@ snd_nm256_create(snd_card_t *card, struct pci_dev *pci, chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; } - /* acquire interrupt */ - if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, - card->driver, (void*)chip)) { - err = -EBUSY; - snd_printk("unable to grab IRQ %d\n", pci->irq); - goto __error; - } - chip->irq = pci->irq; - /* Fixed setting. */ chip->mixer_base = NM_MIXER_OFFSET; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index b7b554df6705..456be39e8e4a 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1900,7 +1900,7 @@ static snd_kcontrol_new_t snd_rme32_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), .info = snd_rme32_control_spdif_mask_info, .get = snd_rme32_control_spdif_mask_get, @@ -1908,7 +1908,7 @@ static snd_kcontrol_new_t snd_rme32_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), .info = snd_rme32_control_spdif_mask_info, .get = snd_rme32_control_spdif_mask_get, diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 10c4f45a913c..9645e9004a48 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -2266,7 +2266,7 @@ static snd_kcontrol_new_t snd_rme96_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_rme96_control_spdif_mask_info, .get = snd_rme96_control_spdif_mask_get, @@ -2276,7 +2276,7 @@ static snd_kcontrol_new_t snd_rme96_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), .info = snd_rme96_control_spdif_mask_info, .get = snd_rme96_control_spdif_mask_get, diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index a673cc438b91..6694866089b5 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -445,6 +445,7 @@ struct _hdsp { u32 control2_register; /* cached value */ u32 creg_spdif; u32 creg_spdif_stream; + int clock_source_locked; char *card_name; /* digiface/multiface */ HDSP_IO_Type io_type; /* ditto, but for code use */ unsigned short firmware_rev; @@ -678,8 +679,7 @@ static int snd_hdsp_load_firmware_from_cache(hdsp_t *hdsp) { } if ((1000 / HZ) < 3000) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((3000 * HZ + 999) / 1000); + ssleep(3); } else { mdelay(3000); } @@ -1524,7 +1524,7 @@ static int snd_hdsp_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_el } #define HDSP_SPDIF_IN(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_spdif_in, \ @@ -1584,7 +1584,7 @@ static int snd_hdsp_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t } #define HDSP_SPDIF_OUT(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_hdsp_info_spdif_bits, \ .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out } @@ -1638,7 +1638,7 @@ static int snd_hdsp_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ } #define HDSP_SPDIF_PROFESSIONAL(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_hdsp_info_spdif_bits, \ .get = snd_hdsp_get_spdif_professional, .put = snd_hdsp_put_spdif_professional } @@ -1683,7 +1683,7 @@ static int snd_hdsp_put_spdif_professional(snd_kcontrol_t * kcontrol, snd_ctl_el } #define HDSP_SPDIF_EMPHASIS(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_hdsp_info_spdif_bits, \ .get = snd_hdsp_get_spdif_emphasis, .put = snd_hdsp_put_spdif_emphasis } @@ -1728,7 +1728,7 @@ static int snd_hdsp_put_spdif_emphasis(snd_kcontrol_t * kcontrol, snd_ctl_elem_v } #define HDSP_SPDIF_NON_AUDIO(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_hdsp_info_spdif_bits, \ .get = snd_hdsp_get_spdif_nonaudio, .put = snd_hdsp_put_spdif_nonaudio } @@ -1773,7 +1773,7 @@ static int snd_hdsp_put_spdif_nonaudio(snd_kcontrol_t * kcontrol, snd_ctl_elem_v } #define HDSP_SPDIF_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1834,7 +1834,7 @@ static int snd_hdsp_get_spdif_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_ele } #define HDSP_SYSTEM_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1858,7 +1858,7 @@ static int snd_hdsp_get_system_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_el } #define HDSP_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1918,7 +1918,7 @@ static int snd_hdsp_get_autosync_sample_rate(snd_kcontrol_t * kcontrol, snd_ctl_ } #define HDSP_SYSTEM_CLOCK_MODE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1958,7 +1958,7 @@ static int snd_hdsp_get_system_clock_mode(snd_kcontrol_t * kcontrol, snd_ctl_ele } #define HDSP_CLOCK_SOURCE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_clock_source, \ @@ -2095,8 +2095,36 @@ static int snd_hdsp_put_clock_source(snd_kcontrol_t * kcontrol, snd_ctl_elem_val return change; } +static int snd_hdsp_info_clock_source_lock(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_hdsp_get_clock_source_lock(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = hdsp->clock_source_locked; + return 0; +} + +static int snd_hdsp_put_clock_source_lock(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + hdsp_t *hdsp = snd_kcontrol_chip(kcontrol); + int change; + + change = (int)ucontrol->value.integer.value[0] != hdsp->clock_source_locked; + if (change) + hdsp->clock_source_locked = ucontrol->value.integer.value[0]; + return change; +} + #define HDSP_DA_GAIN(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_da_gain, \ @@ -2182,7 +2210,7 @@ static int snd_hdsp_put_da_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t } #define HDSP_AD_GAIN(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_ad_gain, \ @@ -2268,7 +2296,7 @@ static int snd_hdsp_put_ad_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t } #define HDSP_PHONE_GAIN(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_phone_gain, \ @@ -2354,7 +2382,7 @@ static int snd_hdsp_put_phone_gain(snd_kcontrol_t * kcontrol, snd_ctl_elem_value } #define HDSP_XLR_BREAKOUT_CABLE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_xlr_breakout_cable, \ @@ -2419,7 +2447,7 @@ static int snd_hdsp_put_xlr_breakout_cable(snd_kcontrol_t * kcontrol, snd_ctl_el Switching this on desactivates external ADAT */ #define HDSP_AEB(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_aeb, \ @@ -2480,7 +2508,7 @@ static int snd_hdsp_put_aeb(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * uc } #define HDSP_PREF_SYNC_REF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_pref_sync_ref, \ @@ -2613,7 +2641,7 @@ static int snd_hdsp_put_pref_sync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_va } #define HDSP_AUTOSYNC_REF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -2669,7 +2697,7 @@ static int snd_hdsp_get_autosync_ref(snd_kcontrol_t * kcontrol, snd_ctl_elem_val } #define HDSP_LINE_OUT(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_line_out, \ @@ -2729,7 +2757,7 @@ static int snd_hdsp_put_line_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t } #define HDSP_PRECISE_POINTER(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_precise_pointer, \ @@ -2783,7 +2811,7 @@ static int snd_hdsp_put_precise_pointer(snd_kcontrol_t * kcontrol, snd_ctl_elem_ } #define HDSP_USE_MIDI_TASKLET(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ .name = xname, \ .index = xindex, \ .info = snd_hdsp_info_use_midi_tasklet, \ @@ -2840,6 +2868,7 @@ static int snd_hdsp_put_use_midi_tasklet(snd_kcontrol_t * kcontrol, snd_ctl_elem { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ .name = xname, \ .index = xindex, \ + .device = 0, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ .info = snd_hdsp_info_mixer, \ @@ -2911,7 +2940,7 @@ static int snd_hdsp_put_mixer(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * } #define HDSP_WC_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ @@ -2955,7 +2984,7 @@ static int snd_hdsp_get_wc_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem_va } #define HDSP_SPDIF_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ @@ -2987,7 +3016,7 @@ static int snd_hdsp_get_spdif_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_elem } #define HDSP_ADATSYNC_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ @@ -3018,7 +3047,7 @@ static int snd_hdsp_get_adatsync_sync_check(snd_kcontrol_t * kcontrol, snd_ctl_e } #define HDSP_ADAT_SYNC_CHECK \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ .info = snd_hdsp_info_sync_check, \ .get = snd_hdsp_get_adat_sync_check \ @@ -3091,7 +3120,7 @@ static snd_kcontrol_new_t snd_hdsp_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_hdsp_control_spdif_mask_info, .get = snd_hdsp_control_spdif_mask_get, @@ -3101,7 +3130,7 @@ static snd_kcontrol_new_t snd_hdsp_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), .info = snd_hdsp_control_spdif_mask_info, .get = snd_hdsp_control_spdif_mask_get, @@ -3117,6 +3146,13 @@ HDSP_SPDIF_EMPHASIS("IEC958 Emphasis Bit", 0), HDSP_SPDIF_NON_AUDIO("IEC958 Non-audio Bit", 0), /* 'Sample Clock Source' complies with the alsa control naming scheme */ HDSP_CLOCK_SOURCE("Sample Clock Source", 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sample Clock Source Locking", + .info = snd_hdsp_info_clock_source_lock, + .get = snd_hdsp_get_clock_source_lock, + .put = snd_hdsp_put_clock_source_lock, +}, HDSP_SYSTEM_CLOCK_MODE("System Clock Mode", 0), HDSP_PREF_SYNC_REF("Preferred Sync Reference", 0), HDSP_AUTOSYNC_REF("AutoSync Reference", 0), @@ -3349,6 +3385,7 @@ snd_hdsp_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer) snd_iprintf (buffer, "System Clock Mode: %s\n", system_clock_mode); snd_iprintf (buffer, "System Clock Frequency: %d\n", hdsp->system_sample_rate); + snd_iprintf (buffer, "System Clock Locked: %s\n", hdsp->clock_source_locked ? "Yes" : "No"); snd_iprintf(buffer, "\n"); @@ -3853,13 +3890,14 @@ static int snd_hdsp_hw_params(snd_pcm_substream_t *substream, */ spin_lock_irq(&hdsp->lock); - if ((err = hdsp_set_rate(hdsp, params_rate(params), 0)) < 0) { - spin_unlock_irq(&hdsp->lock); - _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); - return err; - } else { - spin_unlock_irq(&hdsp->lock); + if (! hdsp->clock_source_locked) { + if ((err = hdsp_set_rate(hdsp, params_rate(params), 0)) < 0) { + spin_unlock_irq(&hdsp->lock); + _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_RATE); + return err; + } } + spin_unlock_irq(&hdsp->lock); if ((err = hdsp_set_interrupt_interval(hdsp, params_period_size(params))) < 0) { _snd_pcm_hw_param_setempty(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); @@ -4284,13 +4322,17 @@ static int snd_hdsp_playback_open(snd_pcm_substream_t *substream) snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, &hdsp_hw_constraints_period_sizes); - if (hdsp->io_type == H9632) { - runtime->hw.channels_min = hdsp->qs_out_channels; - runtime->hw.channels_max = hdsp->ss_out_channels; + if (hdsp->clock_source_locked) { + runtime->hw.rate_min = runtime->hw.rate_max = hdsp->system_sample_rate; + } else if (hdsp->io_type == H9632) { runtime->hw.rate_max = 192000; runtime->hw.rates = SNDRV_PCM_RATE_KNOT; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hdsp_hw_constraints_9632_sample_rates); } + if (hdsp->io_type == H9632) { + runtime->hw.channels_min = hdsp->qs_out_channels; + runtime->hw.channels_max = hdsp->ss_out_channels; + } snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, snd_hdsp_hw_rule_out_channels, hdsp, @@ -5036,8 +5078,7 @@ static int __devinit snd_hdsp_create(snd_card_t *card, if (!is_9652 && !is_9632) { /* we wait 2 seconds to let freshly inserted cardbus cards do their hardware init */ if ((1000 / HZ) < 2000) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((2000 * HZ + 999) / 1000); + ssleep(2); } else { mdelay(2000); } diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 9e86d0eb41ce..5d786d113b25 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -65,7 +65,7 @@ module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards."); module_param_array(precise_ptr, bool, NULL, 0444); -MODULE_PARM_DESC(precise_ptr, "Enable precise pointer, or disable."); +MODULE_PARM_DESC(precise_ptr, "Enable or disable precise pointer."); module_param_array(line_outs_monitor, bool, NULL, 0444); MODULE_PARM_DESC(line_outs_monitor, @@ -1104,14 +1104,14 @@ static int snd_hdspm_midi_output_close(snd_rawmidi_substream_t * substream) return 0; } -snd_rawmidi_ops_t snd_hdspm_midi_output = +static snd_rawmidi_ops_t snd_hdspm_midi_output = { .open = snd_hdspm_midi_output_open, .close = snd_hdspm_midi_output_close, .trigger = snd_hdspm_midi_output_trigger, }; -snd_rawmidi_ops_t snd_hdspm_midi_input = +static snd_rawmidi_ops_t snd_hdspm_midi_input = { .open = snd_hdspm_midi_input_open, .close = snd_hdspm_midi_input_close, @@ -1168,7 +1168,7 @@ static void hdspm_midi_tasklet(unsigned long arg) /* get the system sample rate which is set */ #define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1195,7 +1195,7 @@ static int snd_hdspm_get_system_sample_rate(snd_kcontrol_t * kcontrol, } #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1264,7 +1264,7 @@ static int snd_hdspm_get_autosync_sample_rate(snd_kcontrol_t * kcontrol, } #define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1310,7 +1310,7 @@ static int snd_hdspm_get_system_clock_mode(snd_kcontrol_t * kcontrol, } #define HDSPM_CLOCK_SOURCE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdspm_info_clock_source, \ @@ -1457,7 +1457,7 @@ static int snd_hdspm_put_clock_source(snd_kcontrol_t * kcontrol, } #define HDSPM_PREF_SYNC_REF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdspm_info_pref_sync_ref, \ @@ -1547,7 +1547,7 @@ static int snd_hdspm_put_pref_sync_ref(snd_kcontrol_t * kcontrol, } #define HDSPM_AUTOSYNC_REF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ, \ @@ -1604,7 +1604,7 @@ static int snd_hdspm_get_autosync_ref(snd_kcontrol_t * kcontrol, } #define HDSPM_LINE_OUT(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdspm_info_line_out, \ @@ -1668,7 +1668,7 @@ static int snd_hdspm_put_line_out(snd_kcontrol_t * kcontrol, } #define HDSPM_TX_64(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdspm_info_tx_64, \ @@ -1731,7 +1731,7 @@ static int snd_hdspm_put_tx_64(snd_kcontrol_t * kcontrol, } #define HDSPM_C_TMS(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdspm_info_c_tms, \ @@ -1794,7 +1794,7 @@ static int snd_hdspm_put_c_tms(snd_kcontrol_t * kcontrol, } #define HDSPM_SAFE_MODE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdspm_info_safe_mode, \ @@ -1857,7 +1857,7 @@ static int snd_hdspm_put_safe_mode(snd_kcontrol_t * kcontrol, } #define HDSPM_INPUT_SELECT(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_hdspm_info_input_select, \ @@ -1941,6 +1941,7 @@ static int snd_hdspm_put_input_select(snd_kcontrol_t * kcontrol, { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ .name = xname, \ .index = xindex, \ + .device = 0, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ .info = snd_hdspm_info_mixer, \ @@ -2124,7 +2125,7 @@ static int snd_hdspm_put_playback_mixer(snd_kcontrol_t * kcontrol, } #define HDSPM_WC_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ @@ -2170,7 +2171,7 @@ static int snd_hdspm_get_wc_sync_check(snd_kcontrol_t * kcontrol, #define HDSPM_MADI_SYNC_CHECK(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index f3037402d58f..8ee4d6fd6ea7 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -893,7 +893,7 @@ static int snd_rme9652_control_spdif_mask_get(snd_kcontrol_t * kcontrol, snd_ctl } #define RME9652_ADAT1_IN(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_rme9652_info_adat1_in, \ .get = snd_rme9652_get_adat1_in, \ .put = snd_rme9652_put_adat1_in } @@ -971,7 +971,7 @@ static int snd_rme9652_put_adat1_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_valu } #define RME9652_SPDIF_IN(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_rme9652_info_spdif_in, \ .get = snd_rme9652_get_spdif_in, .put = snd_rme9652_put_spdif_in } @@ -1042,7 +1042,7 @@ static int snd_rme9652_put_spdif_in(snd_kcontrol_t * kcontrol, snd_ctl_elem_valu } #define RME9652_SPDIF_OUT(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_rme9652_info_spdif_out, \ .get = snd_rme9652_get_spdif_out, .put = snd_rme9652_put_spdif_out } @@ -1110,7 +1110,7 @@ static int snd_rme9652_put_spdif_out(snd_kcontrol_t * kcontrol, snd_ctl_elem_val } #define RME9652_SYNC_MODE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_rme9652_info_sync_mode, \ .get = snd_rme9652_get_sync_mode, .put = snd_rme9652_put_sync_mode } @@ -1195,7 +1195,7 @@ static int snd_rme9652_put_sync_mode(snd_kcontrol_t * kcontrol, snd_ctl_elem_val } #define RME9652_SYNC_PREF(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_rme9652_info_sync_pref, \ .get = snd_rme9652_get_sync_pref, .put = snd_rme9652_put_sync_pref } @@ -1340,7 +1340,7 @@ static int snd_rme9652_put_thru(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t } #define RME9652_PASSTHRU(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_rme9652_info_passthru, \ .put = snd_rme9652_put_passthru, \ .get = snd_rme9652_get_passthru } @@ -1386,7 +1386,7 @@ static int snd_rme9652_put_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_valu /* Read-only switches */ #define RME9652_SPDIF_RATE(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ .info = snd_rme9652_info_spdif_rate, \ .get = snd_rme9652_get_spdif_rate } @@ -1411,7 +1411,7 @@ static int snd_rme9652_get_spdif_rate(snd_kcontrol_t * kcontrol, snd_ctl_elem_va } #define RME9652_ADAT_SYNC(xname, xindex, xidx) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ .info = snd_rme9652_info_adat_sync, \ .get = snd_rme9652_get_adat_sync, .private_value = xidx } @@ -1447,7 +1447,7 @@ static int snd_rme9652_get_adat_sync(snd_kcontrol_t * kcontrol, snd_ctl_elem_val } #define RME9652_TC_VALID(xname, xindex) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ .info = snd_rme9652_info_tc_valid, \ .get = snd_rme9652_get_tc_valid } @@ -1470,7 +1470,7 @@ static int snd_rme9652_get_tc_valid(snd_kcontrol_t * kcontrol, snd_ctl_elem_valu return 0; } -#if ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE +#ifdef ALSA_HAS_STANDARD_WAY_OF_RETURNING_TIMECODE /* FIXME: this routine needs a port to the new control API --jk */ @@ -1545,7 +1545,7 @@ static snd_kcontrol_new_t snd_rme9652_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), .info = snd_rme9652_control_spdif_mask_info, .get = snd_rme9652_control_spdif_mask_get, @@ -1555,7 +1555,7 @@ static snd_kcontrol_new_t snd_rme9652_controls[] = { }, { .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK), .info = snd_rme9652_control_spdif_mask_info, .get = snd_rme9652_control_spdif_mask_get, @@ -1568,7 +1568,7 @@ RME9652_SPDIF_OUT("IEC958 Output also on ADAT1", 0), RME9652_SYNC_MODE("Sync Mode", 0), RME9652_SYNC_PREF("Preferred Sync Source", 0), { - .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Channels Thru", .index = 0, .info = snd_rme9652_info_thru, diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index ccd5ca2ba16f..f30d9d947862 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -472,6 +472,7 @@ void snd_trident_write_voice_regs(trident_t * trident, break; default: snd_BUG(); + return; } outb(voice->number, TRID_REG(trident, T4D_LFO_GC_CIR)); @@ -1688,7 +1689,7 @@ static snd_pcm_hardware_t snd_trident_playback = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, @@ -1713,7 +1714,7 @@ static snd_pcm_hardware_t snd_trident_capture = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), .formats = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE), .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, @@ -1738,7 +1739,7 @@ static snd_pcm_hardware_t snd_trident_foldback = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, @@ -1762,7 +1763,7 @@ static snd_pcm_hardware_t snd_trident_spdif = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), @@ -1783,7 +1784,7 @@ static snd_pcm_hardware_t snd_trident_spdif_7018 = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME), + SNDRV_PCM_INFO_PAUSE /* | SNDRV_PCM_INFO_RESUME */), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, @@ -3152,8 +3153,7 @@ static int snd_trident_gameport_open(struct gameport *gameport, int mode) switch (mode) { case GAMEPORT_MODE_COOKED: outb(GAMEPORT_MODE_ADC, TRID_REG(chip, GAMEPORT_GCR)); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1 + 20 * HZ / 1000); /* 20msec */ + msleep(20); return 0; case GAMEPORT_MODE_RAW: outb(0, TRID_REG(chip, GAMEPORT_GCR)); @@ -3204,7 +3204,7 @@ static inline void snd_trident_free_gameport(trident_t *chip) { } /* * delay for 1 tick */ -inline static void do_delay(trident_t *chip) +static inline void do_delay(trident_t *chip) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index 6cc282681e09..333d3790692a 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -118,7 +118,7 @@ static inline void set_silent_tlb(trident_t *trident, int page) #endif /* PAGE_SIZE */ /* calculate buffer pointer from offset address */ -inline static void *offset_ptr(trident_t *trident, int offset) +static inline void *offset_ptr(trident_t *trident, int offset) { char *ptr; ptr = page_to_ptr(trident, get_aligned_page(offset)); diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 42c48f0ce8e8..56c6e52d7264 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -547,8 +547,7 @@ static void snd_via82xx_codec_wait(ac97_t *ac97) int err; err = snd_via82xx_codec_ready(chip, ac97->num); /* here we need to wait fairly for long time.. */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/2); + msleep(500); } static void snd_via82xx_codec_write(ac97_t *ac97, @@ -664,10 +663,12 @@ static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd) val = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: val |= VIA_REG_CTRL_START; viadev->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: val = VIA_REG_CTRL_TERMINATE; viadev->running = 0; break; @@ -930,12 +931,12 @@ static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream) if ((rate_changed = via_lock_rate(&chip->rates[0], ac97_rate)) < 0) return rate_changed; - if (rate_changed) { + if (rate_changed) snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, chip->no_vra ? 48000 : runtime->rate); - snd_ac97_set_rate(chip->ac97, AC97_SPDIF, - chip->no_vra ? 48000 : runtime->rate); - } + if (chip->spdif_on && viadev->reg_offset == 0x30) + snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate); + if (runtime->rate == 48000) rbits = 0xfffff; else @@ -1036,7 +1037,7 @@ static snd_pcm_hardware_t snd_via82xx_hw = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_RESUME | + /* SNDRV_PCM_INFO_RESUME | */ SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, @@ -1485,7 +1486,7 @@ static int snd_via8233_dxs3_spdif_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_val } static snd_kcontrol_new_t snd_via8233_dxs3_spdif_control __devinitdata = { - .name = "IEC958 Output Switch", + .name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH), .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .info = snd_via8233_dxs3_spdif_info, .get = snd_via8233_dxs3_spdif_get, @@ -1847,7 +1848,7 @@ static void __devinit snd_via82xx_proc_init(via82xx_t *chip) static int snd_via82xx_chip_init(via82xx_t *chip) { unsigned int val; - int max_count; + unsigned long end_time; unsigned char pval; #if 0 /* broken on K7M? */ @@ -1889,14 +1890,14 @@ static int snd_via82xx_chip_init(via82xx_t *chip) } /* wait until codec ready */ - max_count = ((3 * HZ) / 4) + 1; + end_time = jiffies + msecs_to_jiffies(750); do { pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - } while (--max_count > 0); + } while (time_before(jiffies, end_time)); if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) snd_printk("AC'97 codec is not ready [0x%x]\n", val); @@ -1905,7 +1906,7 @@ static int snd_via82xx_chip_init(via82xx_t *chip) snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | VIA_REG_AC97_SECONDARY_VALID | (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); - max_count = ((3 * HZ) / 4) + 1; + end_time = jiffies + msecs_to_jiffies(750); snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | VIA_REG_AC97_SECONDARY_VALID | (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); @@ -1916,7 +1917,7 @@ static int snd_via82xx_chip_init(via82xx_t *chip) } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); - } while (--max_count > 0); + } while (time_before(jiffies, end_time)); /* This is ok, the most of motherboards have only one codec */ __ac97_ok2: @@ -2154,6 +2155,7 @@ static int __devinit check_dxs_list(struct pci_dev *pci) { .subvendor = 0x1019, .subdevice = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */ { .subvendor = 0x1019, .subdevice = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */ { .subvendor = 0x1025, .subdevice = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */ + { .subvendor = 0x1025, .subdevice = 0x0046, .action = VIA_DXS_SRC }, /* Acer Aspire 1524 WLMi */ { .subvendor = 0x1043, .subdevice = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/ { .subvendor = 0x1043, .subdevice = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */ { .subvendor = 0x1043, .subdevice = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ @@ -2169,16 +2171,18 @@ static int __devinit check_dxs_list(struct pci_dev *pci) { .subvendor = 0x1297, .subdevice = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */ { .subvendor = 0x1458, .subdevice = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */ { .subvendor = 0x1462, .subdevice = 0x0080, .action = VIA_DXS_SRC }, /* MSI K8T Neo-FIS2R */ + { .subvendor = 0x1462, .subdevice = 0x0430, .action = VIA_DXS_SRC }, /* MSI 7142 (K8MM-V) */ { .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */ { .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ + { .subvendor = 0x1462, .subdevice = 0x7142, .action = VIA_DXS_ENABLE }, /* MSI K8MM-V */ { .subvendor = 0x147b, .subdevice = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */ { .subvendor = 0x147b, .subdevice = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */ { .subvendor = 0x147b, .subdevice = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */ { .subvendor = 0x147b, .subdevice = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */ { .subvendor = 0x14ff, .subdevice = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */ - { .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_NO_VRA }, /* Twinhead mobo */ + { .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_SRC }, /* Twinhead laptop */ { .subvendor = 0x1584, .subdevice = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */ { .subvendor = 0x1584, .subdevice = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */ { .subvendor = 0x161f, .subdevice = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */ @@ -2187,6 +2191,7 @@ static int __devinit check_dxs_list(struct pci_dev *pci) { .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */ { .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */ { .subvendor = 0x1919, .subdevice = 0x200a, .action = VIA_DXS_NO_VRA }, /* Soltek SL-K8Tpro-939 */ + { .subvendor = 0x4005, .subdevice = 0x4710, .action = VIA_DXS_SRC }, /* MSI K7T266 Pro2 (MS-6380 V2.0) BIOS 3.7 */ { } /* terminator */ }; struct dxs_whitelist *w; diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index 5896d289f9ac..5872d438a04a 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -408,8 +408,7 @@ static void snd_via82xx_codec_wait(ac97_t *ac97) int err; err = snd_via82xx_codec_ready(chip, ac97->num); /* here we need to wait fairly for long time.. */ - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/2); + msleep(500); } static void snd_via82xx_codec_write(ac97_t *ac97, @@ -522,6 +521,7 @@ static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_SUSPEND: val |= VIA_REG_CTRL_START; viadev->running = 1; break; @@ -698,7 +698,7 @@ static snd_pcm_hardware_t snd_via82xx_hw = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_RESUME | + /* SNDRV_PCM_INFO_RESUME | */ SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_KNOT, @@ -923,7 +923,7 @@ static void __devinit snd_via82xx_proc_init(via82xx_t *chip) static int snd_via82xx_chip_init(via82xx_t *chip) { unsigned int val; - int max_count; + unsigned long end_time; unsigned char pval; pci_read_config_byte(chip->pci, VIA_MC97_CTRL, &pval); @@ -962,14 +962,14 @@ static int snd_via82xx_chip_init(via82xx_t *chip) } /* wait until codec ready */ - max_count = ((3 * HZ) / 4) + 1; + end_time = jiffies + msecs_to_jiffies(750); do { pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval); if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */ break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - } while (--max_count > 0); + } while (time_before(jiffies, end_time)); if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY) snd_printk("AC'97 codec is not ready [0x%x]\n", val); @@ -977,7 +977,7 @@ static int snd_via82xx_chip_init(via82xx_t *chip) snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | VIA_REG_AC97_SECONDARY_VALID | (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); - max_count = ((3 * HZ) / 4) + 1; + end_time = jiffies + msecs_to_jiffies(750); snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ | VIA_REG_AC97_SECONDARY_VALID | (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT)); @@ -988,7 +988,7 @@ static int snd_via82xx_chip_init(via82xx_t *chip) } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); - } while (--max_count > 0); + } while (time_before(jiffies, end_time)); /* This is ok, the most of motherboards have only one codec */ __ac97_ok2: diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 683e9799976f..967bd5e6b23c 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -82,7 +82,7 @@ static int vx2_reg_index[VX_REG_MAX] = { [VX_GPIOC] = 0, /* on the PLX */ }; -inline static unsigned long vx2_reg_addr(vx_core_t *_chip, int reg) +static inline unsigned long vx2_reg_addr(vx_core_t *_chip, int reg) { struct snd_vx222 *chip = (struct snd_vx222 *)_chip; return chip->port[vx2_reg_index[reg]] + vx2_reg_offset[reg]; @@ -235,7 +235,7 @@ static void vx2_setup_pseudo_dma(vx_core_t *chip, int do_write) /* * vx_release_pseudo_dma - disable the pseudo-DMA mode */ -inline static void vx2_release_pseudo_dma(vx_core_t *chip) +static inline void vx2_release_pseudo_dma(vx_core_t *chip) { /* HREQ pin disabled. */ vx_outl(chip, ICR, 0); diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 2ae79610ecb5..054836412dc4 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -84,16 +84,16 @@ static inline void snd_ymfpci_writel(ymfpci_t *chip, u32 offset, u32 val) static int snd_ymfpci_codec_ready(ymfpci_t *chip, int secondary) { - signed long end_time; + unsigned long end_time; u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; - end_time = (jiffies + ((3 * HZ) / 4)) + 1; + end_time = jiffies + msecs_to_jiffies(750); do { if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0) return 0; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - } while (end_time - (signed long)jiffies >= 0); + } while (time_before(jiffies, end_time)); snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg)); return -EBUSY; } @@ -321,6 +321,26 @@ static void snd_ymfpci_pcm_interrupt(ymfpci_t *chip, ymfpci_voice_t *voice) snd_pcm_period_elapsed(ypcm->substream); spin_lock(&chip->reg_lock); } + + if (unlikely(ypcm->update_pcm_vol)) { + unsigned int subs = ypcm->substream->number; + unsigned int next_bank = 1 - chip->active_bank; + snd_ymfpci_playback_bank_t *bank; + u32 volume; + + bank = &voice->bank[next_bank]; + volume = cpu_to_le32(chip->pcm_mixer[subs].left << 15); + bank->left_gain_end = volume; + if (ypcm->output_rear) + bank->eff2_gain_end = volume; + if (ypcm->voices[1]) + bank = &ypcm->voices[1]->bank[next_bank]; + volume = cpu_to_le32(chip->pcm_mixer[subs].right << 15); + bank->right_gain_end = volume; + if (ypcm->output_rear) + bank->eff3_gain_end = volume; + ypcm->update_pcm_vol--; + } } spin_unlock(&chip->reg_lock); } @@ -451,87 +471,74 @@ static int snd_ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices) return 0; } -static void snd_ymfpci_pcm_init_voice(ymfpci_voice_t *voice, int stereo, - int rate, int w_16, unsigned long addr, - unsigned int end, - int output_front, int output_rear) +static void snd_ymfpci_pcm_init_voice(ymfpci_pcm_t *ypcm, unsigned int voiceidx, + snd_pcm_runtime_t *runtime, + int has_pcm_volume) { + ymfpci_voice_t *voice = ypcm->voices[voiceidx]; u32 format; - u32 delta = snd_ymfpci_calc_delta(rate); - u32 lpfQ = snd_ymfpci_calc_lpfQ(rate); - u32 lpfK = snd_ymfpci_calc_lpfK(rate); + u32 delta = snd_ymfpci_calc_delta(runtime->rate); + u32 lpfQ = snd_ymfpci_calc_lpfQ(runtime->rate); + u32 lpfK = snd_ymfpci_calc_lpfK(runtime->rate); snd_ymfpci_playback_bank_t *bank; unsigned int nbank; + u32 vol_left, vol_right; + u8 use_left, use_right; snd_assert(voice != NULL, return); - format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); + if (runtime->channels == 1) { + use_left = 1; + use_right = 1; + } else { + use_left = (voiceidx & 1) == 0; + use_right = !use_left; + } + if (has_pcm_volume) { + vol_left = cpu_to_le32(ypcm->chip->pcm_mixer + [ypcm->substream->number].left << 15); + vol_right = cpu_to_le32(ypcm->chip->pcm_mixer + [ypcm->substream->number].right << 15); + } else { + vol_left = cpu_to_le32(0x40000000); + vol_right = cpu_to_le32(0x40000000); + } + format = runtime->channels == 2 ? 0x00010000 : 0; + if (snd_pcm_format_width(runtime->format) == 8) + format |= 0x80000000; + if (runtime->channels == 2 && (voiceidx & 1) != 0) + format |= 1; for (nbank = 0; nbank < 2; nbank++) { bank = &voice->bank[nbank]; + memset(bank, 0, sizeof(*bank)); bank->format = cpu_to_le32(format); - bank->loop_default = 0; - bank->base = cpu_to_le32(addr); - bank->loop_start = 0; - bank->loop_end = cpu_to_le32(end); - bank->loop_frac = 0; - bank->eg_gain_end = cpu_to_le32(0x40000000); + bank->base = cpu_to_le32(runtime->dma_addr); + bank->loop_end = cpu_to_le32(ypcm->buffer_size); bank->lpfQ = cpu_to_le32(lpfQ); - bank->status = 0; - bank->num_of_frames = 0; - bank->loop_count = 0; - bank->start = 0; - bank->start_frac = 0; bank->delta = bank->delta_end = cpu_to_le32(delta); bank->lpfK = bank->lpfK_end = cpu_to_le32(lpfK); - bank->eg_gain = cpu_to_le32(0x40000000); - bank->lpfD1 = - bank->lpfD2 = 0; - - bank->left_gain = - bank->right_gain = - bank->left_gain_end = - bank->right_gain_end = - bank->eff1_gain = - bank->eff2_gain = - bank->eff3_gain = - bank->eff1_gain_end = - bank->eff2_gain_end = - bank->eff3_gain_end = 0; - - if (!stereo) { - if (output_front) { - bank->left_gain = + bank->eg_gain = + bank->eg_gain_end = cpu_to_le32(0x40000000); + + if (ypcm->output_front) { + if (use_left) { + bank->left_gain = + bank->left_gain_end = vol_left; + } + if (use_right) { bank->right_gain = - bank->left_gain_end = - bank->right_gain_end = cpu_to_le32(0x40000000); + bank->right_gain_end = vol_right; } - if (output_rear) { + } + if (ypcm->output_rear) { + if (use_left) { bank->eff2_gain = - bank->eff2_gain_end = - bank->eff3_gain = - bank->eff3_gain_end = cpu_to_le32(0x40000000); - } - } else { - if (output_front) { - if ((voice->number & 1) == 0) { - bank->left_gain = - bank->left_gain_end = cpu_to_le32(0x40000000); - } else { - bank->format |= cpu_to_le32(1); - bank->right_gain = - bank->right_gain_end = cpu_to_le32(0x40000000); - } + bank->eff2_gain_end = vol_left; } - if (output_rear) { - if ((voice->number & 1) == 0) { - bank->eff3_gain = - bank->eff3_gain_end = cpu_to_le32(0x40000000); - } else { - bank->format |= cpu_to_le32(1); - bank->eff2_gain = - bank->eff2_gain_end = cpu_to_le32(0x40000000); - } + if (use_right) { + bank->eff3_gain = + bank->eff3_gain_end = vol_right; } } } @@ -613,7 +620,7 @@ static int snd_ymfpci_playback_hw_free(snd_pcm_substream_t * substream) static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream) { - // ymfpci_t *chip = snd_pcm_substream_chip(substream); + ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm = runtime->private_data; unsigned int nvoice; @@ -623,14 +630,8 @@ static int snd_ymfpci_playback_prepare(snd_pcm_substream_t * substream) ypcm->period_pos = 0; ypcm->last_pos = 0; for (nvoice = 0; nvoice < runtime->channels; nvoice++) - snd_ymfpci_pcm_init_voice(ypcm->voices[nvoice], - runtime->channels == 2, - runtime->rate, - snd_pcm_format_width(runtime->format) == 16, - runtime->dma_addr, - ypcm->buffer_size, - ypcm->output_front, - ypcm->output_rear); + snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime, + substream->pcm == chip->pcm); return 0; } @@ -882,6 +883,7 @@ static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream) ymfpci_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm; + snd_kcontrol_t *kctl; int err; if ((err = snd_ymfpci_playback_open_1(substream)) < 0) @@ -895,6 +897,10 @@ static int snd_ymfpci_playback_open(snd_pcm_substream_t * substream) chip->rear_opened++; } spin_unlock_irq(&chip->reg_lock); + + kctl = chip->pcm_mixer[substream->number].ctl; + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); return 0; } @@ -987,6 +993,7 @@ static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream) { ymfpci_t *chip = snd_pcm_substream_chip(substream); ymfpci_pcm_t *ypcm = substream->runtime->private_data; + snd_kcontrol_t *kctl; spin_lock_irq(&chip->reg_lock); if (ypcm->output_rear && chip->rear_opened > 0) { @@ -994,6 +1001,9 @@ static int snd_ymfpci_playback_close(snd_pcm_substream_t * substream) ymfpci_close_extension(chip); } spin_unlock_irq(&chip->reg_lock); + kctl = chip->pcm_mixer[substream->number].ctl; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); return snd_ymfpci_playback_close_1(substream); } @@ -1665,6 +1675,66 @@ static snd_kcontrol_new_t snd_ymfpci_rear_shared __devinitdata = { .private_value = 2, }; +/* + * PCM voice volume + */ + +static int snd_ymfpci_pcm_vol_info(snd_kcontrol_t *kcontrol, + snd_ctl_elem_info_t *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x8000; + return 0; +} + +static int snd_ymfpci_pcm_vol_get(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int subs = kcontrol->id.subdevice; + + ucontrol->value.integer.value[0] = chip->pcm_mixer[subs].left; + ucontrol->value.integer.value[1] = chip->pcm_mixer[subs].right; + return 0; +} + +static int snd_ymfpci_pcm_vol_put(snd_kcontrol_t *kcontrol, + snd_ctl_elem_value_t *ucontrol) +{ + ymfpci_t *chip = snd_kcontrol_chip(kcontrol); + unsigned int subs = kcontrol->id.subdevice; + snd_pcm_substream_t *substream; + unsigned long flags; + + if (ucontrol->value.integer.value[0] != chip->pcm_mixer[subs].left || + ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) { + chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0]; + chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1]; + + substream = (snd_pcm_substream_t *)kcontrol->private_value; + spin_lock_irqsave(&chip->voice_lock, flags); + if (substream->runtime && substream->runtime->private_data) { + ymfpci_pcm_t *ypcm = substream->runtime->private_data; + ypcm->update_pcm_vol = 2; + } + spin_unlock_irqrestore(&chip->voice_lock, flags); + return 1; + } + return 0; +} + +static snd_kcontrol_new_t snd_ymfpci_pcm_volume __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "PCM Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, + .info = snd_ymfpci_pcm_vol_info, + .get = snd_ymfpci_pcm_vol_get, + .put = snd_ymfpci_pcm_vol_put, +}; + /* * Mixer routines @@ -1686,6 +1756,7 @@ int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch) { ac97_template_t ac97; snd_kcontrol_t *kctl; + snd_pcm_substream_t *substream; unsigned int idx; int err; static ac97_bus_ops_t ops = { @@ -1739,6 +1810,23 @@ int __devinit snd_ymfpci_mixer(ymfpci_t *chip, int rear_switch) return err; } + /* per-voice volume */ + substream = chip->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + for (idx = 0; idx < 32; ++idx) { + kctl = snd_ctl_new1(&snd_ymfpci_pcm_volume, chip); + if (!kctl) + return -ENOMEM; + kctl->id.device = chip->pcm->device; + kctl->id.subdevice = idx; + kctl->private_value = (unsigned long)substream; + if ((err = snd_ctl_add(chip->card, kctl)) < 0) + return err; + chip->pcm_mixer[idx].left = 0x8000; + chip->pcm_mixer[idx].right = 0x8000; + chip->pcm_mixer[idx].ctl = kctl; + substream = substream->next; + } + return 0; } diff --git a/sound/pcmcia/Kconfig b/sound/pcmcia/Kconfig index 3611e298834f..5d1b0b762efa 100644 --- a/sound/pcmcia/Kconfig +++ b/sound/pcmcia/Kconfig @@ -8,23 +8,12 @@ config SND_VXPOCKET depends on SND && PCMCIA && ISA select SND_VX_LIB help - Say Y here to include support for Digigram VXpocket - soundcards. + Say Y here to include support for Digigram VXpocket and + VXpocket 440 soundcards. To compile this driver as a module, choose M here: the module will be called snd-vxpocket. -config SND_VXP440 - tristate "Digigram VXpocket 440" - depends on SND && PCMCIA && ISA - select SND_VX_LIB - help - Say Y here to include support for Digigram VXpocket 440 - soundcards. - - To compile this driver as a module, choose M here: the module - will be called snd-vxp440. - config SND_PDAUDIOCF tristate "Sound Core PDAudioCF" depends on SND && PCMCIA && ISA diff --git a/sound/pcmcia/vx/Makefile b/sound/pcmcia/vx/Makefile index f35dfa1af094..54971f01e968 100644 --- a/sound/pcmcia/vx/Makefile +++ b/sound/pcmcia/vx/Makefile @@ -3,9 +3,6 @@ # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> # -snd-vx-cs-objs := vx_entry.o vxp_ops.o vxp_mixer.o -snd-vxpocket-objs := vxpocket.o -snd-vxp440-objs := vxp440.o +snd-vxpocket-objs := vxpocket.o vxp_ops.o vxp_mixer.o -obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o snd-vx-cs.o -obj-$(CONFIG_SND_VXP440) += snd-vxp440.o snd-vx-cs.o +obj-$(CONFIG_SND_VXPOCKET) += snd-vxpocket.o diff --git a/sound/pcmcia/vx/vx_entry.c b/sound/pcmcia/vx/vx_entry.c deleted file mode 100644 index df7a39ba9680..000000000000 --- a/sound/pcmcia/vx/vx_entry.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Driver for Digigram VXpocket soundcards - * - * PCMCIA entry part - * - * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <sound/driver.h> -#include <sound/core.h> -#include "vxpocket.h" -#include <pcmcia/ciscode.h> -#include <pcmcia/cisreg.h> - - -MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); -MODULE_DESCRIPTION("Common routines for Digigram PCMCIA VX drivers"); -MODULE_LICENSE("GPL"); - -/* - * prototypes - */ -static void vxpocket_config(dev_link_t *link); - - -static void vxpocket_release(dev_link_t *link) -{ - if (link->state & DEV_CONFIG) { - /* release cs resources */ - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; - } -} - -/* - * destructor - */ -static int snd_vxpocket_free(vx_core_t *chip) -{ - struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; - struct snd_vxp_entry *hw; - dev_link_t *link = &vxp->link; - - vxpocket_release(link); - - /* Break the link with Card Services */ - if (link->handle) - pcmcia_deregister_client(link->handle); - - hw = vxp->hw_entry; - if (hw) - hw->card_list[vxp->index] = NULL; - chip->card = NULL; - kfree(chip->dev); - - snd_vx_free_firmware(chip); - kfree(chip); - return 0; -} - -static int snd_vxpocket_dev_free(snd_device_t *device) -{ - vx_core_t *chip = device->device_data; - return snd_vxpocket_free(chip); -} - -/* - * snd_vxpocket_attach - attach callback for cs - * @hw: the hardware information - */ -dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw) -{ - client_reg_t client_reg; /* Register with cardmgr */ - dev_link_t *link; /* Info for cardmgr */ - int i, ret; - vx_core_t *chip; - struct snd_vxpocket *vxp; - snd_card_t *card; - static snd_device_ops_t ops = { - .dev_free = snd_vxpocket_dev_free, - }; - - snd_printdd(KERN_DEBUG "vxpocket_attach called\n"); - /* find an empty slot from the card list */ - for (i = 0; i < SNDRV_CARDS; i++) { - if (! hw->card_list[i]) - break; - } - if (i >= SNDRV_CARDS) { - snd_printk(KERN_ERR "vxpocket: too many cards found\n"); - return NULL; - } - if (! hw->enable_table[i]) - return NULL; /* disabled explicitly */ - - /* ok, create a card instance */ - card = snd_card_new(hw->index_table[i], hw->id_table[i], THIS_MODULE, 0); - if (card == NULL) { - snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n"); - return NULL; - } - - chip = snd_vx_create(card, hw->hardware, hw->ops, - sizeof(struct snd_vxpocket) - sizeof(vx_core_t)); - if (! chip) - return NULL; - -#ifdef SND_VX_FW_LOADER - /* fake a device here since pcmcia doesn't give a valid device... */ - chip->dev = kcalloc(1, sizeof(*chip->dev), GFP_KERNEL); - if (! chip->dev) { - snd_printk(KERN_ERR "vxp: can't malloc chip->dev\n"); - kfree(chip); - snd_card_free(card); - return NULL; - } - device_initialize(chip->dev); - sprintf(chip->dev->bus_id, "vxpocket%d", i); -#endif - - if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) { - kfree(chip); - snd_card_free(card); - return NULL; - } - - vxp = (struct snd_vxpocket *)chip; - vxp->index = i; - vxp->hw_entry = hw; - chip->ibl.size = hw->ibl[i]; - hw->card_list[i] = chip; - - link = &vxp->link; - link->priv = chip; - - link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; - link->io.NumPorts1 = 16; - - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; - // link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; - - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.Handler = &snd_vx_irq_handler; - link->irq.Instance = chip; - - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - link->conf.Present = PRESENT_OPTION; - - /* Register with Card Services */ - memset(&client_reg, 0, sizeof(client_reg)); - client_reg.dev_info = hw->dev_info; - client_reg.Version = 0x0210; - client_reg.event_callback_args.client_data = link; - - ret = pcmcia_register_client(&link->handle, &client_reg); - if (ret != CS_SUCCESS) { - cs_error(link->handle, RegisterClient, ret); - snd_card_free(card); - return NULL; - } - - /* Chain drivers */ - link->next = hw->dev_list; - hw->dev_list = link; - - /* snd_card_set_pm_callback(card, snd_vxpocket_suspend, snd_vxpocket_resume, chip); */ - - return link; -} - - -/** - * snd_vxpocket_assign_resources - initialize the hardware and card instance. - * @port: i/o port for the card - * @irq: irq number for the card - * - * this function assigns the specified port and irq, boot the card, - * create pcm and control instances, and initialize the rest hardware. - * - * returns 0 if successful, or a negative error code. - */ -static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq) -{ - int err; - snd_card_t *card = chip->card; - struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; - - snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq); - vxp->port = port; - - sprintf(card->shortname, "Digigram %s", card->driver); - sprintf(card->longname, "%s at 0x%x, irq %i", - card->shortname, port, irq); - - chip->irq = irq; - - if ((err = snd_vx_setup_firmware(chip)) < 0) - return err; - - return 0; -} - - -/* - * snd_vxpocket_detach - detach callback for cs - * @hw: the hardware information - */ -void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link) -{ - vx_core_t *chip; - - if (! link) - return; - - chip = link->priv; - - snd_printdd(KERN_DEBUG "vxpocket_detach called\n"); - /* Remove the interface data from the linked list */ - if (hw) { - dev_link_t **linkp; - /* Locate device structure */ - for (linkp = &hw->dev_list; *linkp; linkp = &(*linkp)->next) - if (*linkp == link) { - *linkp = link->next; - break; - } - } - chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */ - snd_card_disconnect(chip->card); - snd_card_free_in_thread(chip->card); -} - -/* - * configuration callback - */ - -#define CS_CHECK(fn, ret) \ -do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) - -static void vxpocket_config(dev_link_t *link) -{ - client_handle_t handle = link->handle; - vx_core_t *chip = link->priv; - struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; - tuple_t tuple; - cisparse_t *parse = NULL; - u_short buf[32]; - int last_fn, last_ret; - - snd_printdd(KERN_DEBUG "vxpocket_config called\n"); - parse = kmalloc(sizeof(*parse), GFP_KERNEL); - if (! parse) { - snd_printk(KERN_ERR "vx: cannot allocate\n"); - return; - } - tuple.Attributes = 0; - tuple.TupleData = (cisdata_t *)buf; - tuple.TupleDataMax = sizeof(buf); - tuple.TupleOffset = 0; - tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse)); - link->conf.ConfigBase = parse->config.base; - link->conf.Present = parse->config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - - CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); - - if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0) - goto failed; - - link->dev = &vxp->node; - link->state &= ~DEV_CONFIG_PENDING; - kfree(parse); - return; - -cs_failed: - cs_error(link->handle, last_fn, last_ret); -failed: - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; - kfree(parse); -} - - -/* - * event callback - */ -int vxpocket_event(event_t event, int priority, event_callback_args_t *args) -{ - dev_link_t *link = args->client_data; - vx_core_t *chip = link->priv; - - switch (event) { - case CS_EVENT_CARD_REMOVAL: - snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n"); - link->state &= ~DEV_PRESENT; - if (link->state & DEV_CONFIG) { - chip->chip_status |= VX_STAT_IS_STALE; - } - break; - case CS_EVENT_CARD_INSERTION: - snd_printdd(KERN_DEBUG "CARD_INSERTION..\n"); - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - vxpocket_config(link); - break; -#ifdef CONFIG_PM - case CS_EVENT_PM_SUSPEND: - snd_printdd(KERN_DEBUG "SUSPEND\n"); - link->state |= DEV_SUSPEND; - if (chip && chip->card->pm_suspend) { - snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n"); - chip->card->pm_suspend(chip->card, PMSG_SUSPEND); - } - /* Fall through... */ - case CS_EVENT_RESET_PHYSICAL: - snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n"); - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - break; - case CS_EVENT_PM_RESUME: - snd_printdd(KERN_DEBUG "RESUME\n"); - link->state &= ~DEV_SUSPEND; - /* Fall through... */ - case CS_EVENT_CARD_RESET: - snd_printdd(KERN_DEBUG "CARD_RESET\n"); - if (DEV_OK(link)) { - //struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; - snd_printdd(KERN_DEBUG "requestconfig...\n"); - pcmcia_request_configuration(link->handle, &link->conf); - if (chip && chip->card->pm_resume) { - snd_printdd(KERN_DEBUG "calling snd_vx_resume\n"); - chip->card->pm_resume(chip->card); - } - } - snd_printdd(KERN_DEBUG "resume done!\n"); - break; -#endif - } - return 0; -} - -/* - * exported stuffs - */ -EXPORT_SYMBOL(snd_vxpocket_ops); -EXPORT_SYMBOL(snd_vxpocket_attach); -EXPORT_SYMBOL(vxpocket_event); -EXPORT_SYMBOL(snd_vxpocket_detach); diff --git a/sound/pcmcia/vx/vxp440.c b/sound/pcmcia/vx/vxp440.c deleted file mode 100644 index 59190a833001..000000000000 --- a/sound/pcmcia/vx/vxp440.c +++ /dev/null @@ -1,14 +0,0 @@ -#define COMPILE_VXP440 - -/* - add the following as /etc/pcmcia/vxp440.conf: - - device "snd-vxp440" - class "audio" module "snd-vxp440" - - card "Digigram VX-POCKET440" - manfid 0x01f1, 0x0100 - bind "snd-vxp440" -*/ - -#include "vxpocket.c" diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index ef6734271607..6f15c3d03ab5 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -49,7 +49,7 @@ static int vxp_reg_offset[VX_REG_MAX] = { }; -inline static unsigned long vxp_reg_addr(vx_core_t *_chip, int reg) +static inline unsigned long vxp_reg_addr(vx_core_t *_chip, int reg) { struct snd_vxpocket *chip = (struct snd_vxpocket *)_chip; return chip->port + vxp_reg_offset[reg]; diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 62d6fa128148..1e8f16b4c073 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -24,21 +24,17 @@ #include <linux/moduleparam.h> #include <sound/core.h> #include "vxpocket.h" +#include <pcmcia/ciscode.h> +#include <pcmcia/cisreg.h> #include <sound/initval.h> /* */ -#ifdef COMPILE_VXP440 -#define CARD_NAME "VXPocket440" -#else -#define CARD_NAME "VXPocket" -#endif - MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); -MODULE_DESCRIPTION("Digigram " CARD_NAME); +MODULE_DESCRIPTION("Digigram VXPocket"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Digigram," CARD_NAME "}}"); +MODULE_SUPPORTED_DEVICE("{{Digigram,VXPocket},{Digigram,VXPocket440}}"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -46,82 +42,406 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable switches */ static int ibl[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); +MODULE_PARM_DESC(index, "Index value for VXPocket soundcard."); module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); +MODULE_PARM_DESC(id, "ID string for VXPocket soundcard."); module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); +MODULE_PARM_DESC(enable, "Enable VXPocket soundcard."); module_param_array(ibl, int, NULL, 0444); -MODULE_PARM_DESC(ibl, "Capture IBL size for " CARD_NAME " soundcard."); +MODULE_PARM_DESC(ibl, "Capture IBL size for VXPocket soundcard."); /* */ -#ifdef COMPILE_VXP440 +static unsigned int card_alloc; +static dev_link_t *dev_list; /* Linked list of devices */ +static dev_info_t dev_info = "snd-vxpocket"; -/* 1 DSP, 1 sync UER, 1 sync World Clock (NIY) */ -/* SMPTE (NIY) */ -/* 2 stereo analog input (line/micro) */ -/* 2 stereo analog output */ -/* Only output levels can be modified */ -/* UER, but only for the first two inputs and outputs. */ -#define NUM_CODECS 2 -#define CARD_TYPE VX_TYPE_VXP440 -#define DEV_INFO "snd-vxp440" +static int vxpocket_event(event_t event, int priority, event_callback_args_t *args); -#else -/* 1 DSP, 1 sync UER */ -/* 1 programmable clock (NIY) */ -/* 1 stereo analog input (line/micro) */ -/* 1 stereo analog output */ -/* Only output levels can be modified */ +/* + */ +static void vxpocket_release(dev_link_t *link) +{ + if (link->state & DEV_CONFIG) { + /* release cs resources */ + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + } + if (link->handle) { + /* Break the link with Card Services */ + pcmcia_deregister_client(link->handle); + link->handle = NULL; + } +} -#define NUM_CODECS 1 -#define CARD_TYPE VX_TYPE_VXPOCKET -#define DEV_INFO "snd-vxpocket" +/* + * destructor, called from snd_card_free_in_thread() + */ +static int snd_vxpocket_dev_free(snd_device_t *device) +{ + vx_core_t *chip = device->device_data; -#endif + snd_vx_free_firmware(chip); + kfree(chip); + return 0; +} -static dev_info_t dev_info = DEV_INFO; -static struct snd_vx_hardware vxp_hw = { - .name = CARD_NAME, - .type = CARD_TYPE, +/* + * Hardware information + */ + +/* VX-pocket V2 + * + * 1 DSP, 1 sync UER + * 1 programmable clock (NIY) + * 1 stereo analog input (line/micro) + * 1 stereo analog output + * Only output levels can be modified + */ + +static struct snd_vx_hardware vxpocket_hw = { + .name = "VXPocket", + .type = VX_TYPE_VXPOCKET, /* hardware specs */ - .num_codecs = NUM_CODECS, - .num_ins = NUM_CODECS, - .num_outs = NUM_CODECS, + .num_codecs = 1, + .num_ins = 1, + .num_outs = 1, .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, }; -static struct snd_vxp_entry hw_entry = { - .dev_info = &dev_info, +/* VX-pocket 440 + * + * 1 DSP, 1 sync UER, 1 sync World Clock (NIY) + * SMPTE (NIY) + * 2 stereo analog input (line/micro) + * 2 stereo analog output + * Only output levels can be modified + * UER, but only for the first two inputs and outputs. + */ - /* module parameters */ - .index_table = index, - .id_table = id, - .enable_table = enable, - .ibl = ibl, +static struct snd_vx_hardware vxp440_hw = { + .name = "VXPocket440", + .type = VX_TYPE_VXP440, + + /* hardware specs */ + .num_codecs = 2, + .num_ins = 2, + .num_outs = 2, + .output_level_max = VX_ANALOG_OUT_LEVEL_MAX, +}; + + +/* + * create vxpocket instance + */ +static struct snd_vxpocket *snd_vxpocket_new(snd_card_t *card, int ibl) +{ + client_reg_t client_reg; /* Register with cardmgr */ + dev_link_t *link; /* Info for cardmgr */ + vx_core_t *chip; + struct snd_vxpocket *vxp; + int ret; + static snd_device_ops_t ops = { + .dev_free = snd_vxpocket_dev_free, + }; + + chip = snd_vx_create(card, &vxpocket_hw, &snd_vxpocket_ops, + sizeof(struct snd_vxpocket) - sizeof(vx_core_t)); + if (! chip) + return NULL; + + if (snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops) < 0) { + kfree(chip); + return NULL; + } + chip->ibl.size = ibl; + + vxp = (struct snd_vxpocket *)chip; + + link = &vxp->link; + link->priv = chip; + + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.NumPorts1 = 16; + + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->irq.Handler = &snd_vx_irq_handler; + link->irq.Instance = chip; + + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.ConfigIndex = 1; + link->conf.Present = PRESENT_OPTION; + + /* Register with Card Services */ + memset(&client_reg, 0, sizeof(client_reg)); + client_reg.dev_info = &dev_info; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL +#ifdef CONFIG_PM + | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET + | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME +#endif + ; + client_reg.event_handler = &vxpocket_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + return NULL; + } + + return vxp; +} + + +/** + * snd_vxpocket_assign_resources - initialize the hardware and card instance. + * @port: i/o port for the card + * @irq: irq number for the card + * + * this function assigns the specified port and irq, boot the card, + * create pcm and control instances, and initialize the rest hardware. + * + * returns 0 if successful, or a negative error code. + */ +static int snd_vxpocket_assign_resources(vx_core_t *chip, int port, int irq) +{ + int err; + snd_card_t *card = chip->card; + struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + + snd_printdd(KERN_DEBUG "vxpocket assign resources: port = 0x%x, irq = %d\n", port, irq); + vxp->port = port; + + sprintf(card->shortname, "Digigram %s", card->driver); + sprintf(card->longname, "%s at 0x%x, irq %i", + card->shortname, port, irq); + + chip->irq = irq; + + if ((err = snd_vx_setup_firmware(chip)) < 0) + return err; + + return 0; +} + + +/* + * configuration callback + */ + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +static void vxpocket_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + vx_core_t *chip = link->priv; + struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + tuple_t tuple; + cisparse_t *parse; + u_short buf[32]; + int last_fn, last_ret; + + snd_printdd(KERN_DEBUG "vxpocket_config called\n"); + parse = kmalloc(sizeof(*parse), GFP_KERNEL); + if (! parse) { + snd_printk(KERN_ERR "vx: cannot allocate\n"); + return; + } + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse)); + link->conf.ConfigBase = parse->config.base; + link->conf.Present = parse->config.rmask[0]; + + /* redefine hardware record according to the VERSION1 string */ + tuple.DesiredTuple = CISTPL_VERS_1; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, parse)); + if (! strcmp(parse->version_1.str + parse->version_1.ofs[1], "VX-POCKET")) { + snd_printdd("VX-pocket is detected\n"); + } else { + snd_printdd("VX-pocket 440 is detected\n"); + /* overwrite the hardware information */ + chip->hw = &vxp440_hw; + chip->type = vxp440_hw.type; + strcpy(chip->card->driver, vxp440_hw.name); + } + + /* Configure card */ + link->state |= DEV_CONFIG; + + CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + + chip->dev = &handle_to_dev(link->handle); + snd_card_set_dev(chip->card, chip->dev); + + if (snd_vxpocket_assign_resources(chip, link->io.BasePort1, link->irq.AssignedIRQ) < 0) + goto failed; + + link->dev = &vxp->node; + link->state &= ~DEV_CONFIG_PENDING; + kfree(parse); + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + pcmcia_release_configuration(link->handle); + pcmcia_release_io(link->handle, &link->io); + pcmcia_release_irq(link->handle, &link->irq); + link->state &= ~DEV_CONFIG; + kfree(parse); +} + + +/* + * event callback + */ +static int vxpocket_event(event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + vx_core_t *chip = link->priv; + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + snd_printdd(KERN_DEBUG "CARD_REMOVAL..\n"); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + chip->chip_status |= VX_STAT_IS_STALE; + break; + case CS_EVENT_CARD_INSERTION: + snd_printdd(KERN_DEBUG "CARD_INSERTION..\n"); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + vxpocket_config(link); + break; +#ifdef CONFIG_PM + case CS_EVENT_PM_SUSPEND: + snd_printdd(KERN_DEBUG "SUSPEND\n"); + link->state |= DEV_SUSPEND; + if (chip && chip->card->pm_suspend) { + snd_printdd(KERN_DEBUG "snd_vx_suspend calling\n"); + chip->card->pm_suspend(chip->card, PMSG_SUSPEND); + } + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + snd_printdd(KERN_DEBUG "RESET_PHYSICAL\n"); + if (link->state & DEV_CONFIG) + pcmcia_release_configuration(link->handle); + break; + case CS_EVENT_PM_RESUME: + snd_printdd(KERN_DEBUG "RESUME\n"); + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + snd_printdd(KERN_DEBUG "CARD_RESET\n"); + if (DEV_OK(link)) { + //struct snd_vxpocket *vxp = (struct snd_vxpocket *)chip; + snd_printdd(KERN_DEBUG "requestconfig...\n"); + pcmcia_request_configuration(link->handle, &link->conf); + if (chip && chip->card->pm_resume) { + snd_printdd(KERN_DEBUG "calling snd_vx_resume\n"); + chip->card->pm_resume(chip->card); + } + } + snd_printdd(KERN_DEBUG "resume done!\n"); + break; +#endif + } + return 0; +} - /* h/w config */ - .hardware = &vxp_hw, - .ops = &snd_vxpocket_ops, -}; /* */ -static dev_link_t *vxp_attach(void) +static dev_link_t *vxpocket_attach(void) { - return snd_vxpocket_attach(&hw_entry); + snd_card_t *card; + struct snd_vxpocket *vxp; + int i; + + /* find an empty slot from the card list */ + for (i = 0; i < SNDRV_CARDS; i++) { + if (! card_alloc & (1 << i)) + break; + } + if (i >= SNDRV_CARDS) { + snd_printk(KERN_ERR "vxpocket: too many cards found\n"); + return NULL; + } + if (! enable[i]) + return NULL; /* disabled explicitly */ + + /* ok, create a card instance */ + card = snd_card_new(index[i], id[i], THIS_MODULE, 0); + if (card == NULL) { + snd_printk(KERN_ERR "vxpocket: cannot create a card instance\n"); + return NULL; + } + + vxp = snd_vxpocket_new(card, ibl[i]); + if (! vxp) { + snd_card_free(card); + return NULL; + } + + vxp->index = i; + card_alloc |= 1 << i; + + /* Chain drivers */ + vxp->link.next = dev_list; + dev_list = &vxp->link; + + return &vxp->link; } -static void vxp_detach(dev_link_t *link) +static void vxpocket_detach(dev_link_t *link) { - snd_vxpocket_detach(&hw_entry, link); + struct snd_vxpocket *vxp; + vx_core_t *chip; + dev_link_t **linkp; + + if (! link) + return; + + vxp = link->priv; + chip = (vx_core_t *)vxp; + card_alloc &= ~(1 << vxp->index); + + /* Remove the interface data from the linked list */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) { + *linkp = link->next; + break; + } + + chip->chip_status |= VX_STAT_IS_STALE; /* to be sure */ + snd_card_disconnect(chip->card); + vxpocket_release(link); + snd_card_free_in_thread(chip->card); } /* @@ -137,10 +457,11 @@ MODULE_DEVICE_TABLE(pcmcia, vxp_ids); static struct pcmcia_driver vxp_cs_driver = { .owner = THIS_MODULE, .drv = { - .name = DEV_INFO, + .name = "snd-vxpocket", }, - .attach = vxp_attach, - .detach = vxp_detach, + .attach = vxpocket_attach, + .detach = vxpocket_detach, + .event = vxpocket_event, .id_table = vxp_ids, }; @@ -152,7 +473,7 @@ static int __init init_vxpocket(void) static void __exit exit_vxpocket(void) { pcmcia_unregister_driver(&vxp_cs_driver); - BUG_ON(hw_entry.dev_list != NULL); + BUG_ON(dev_list != NULL); } module_init(init_vxpocket); diff --git a/sound/pcmcia/vx/vxpocket.h b/sound/pcmcia/vx/vxpocket.h index 4462c04a4e8f..70754aa3dd11 100644 --- a/sound/pcmcia/vx/vxpocket.h +++ b/sound/pcmcia/vx/vxpocket.h @@ -28,24 +28,6 @@ #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> -struct snd_vxp_entry { - dev_info_t *dev_info; - - /* module parameters */ - int *index_table; - char **id_table; - int *enable_table; - int *ibl; - - /* h/w config */ - struct snd_vx_hardware *hardware; - struct snd_vx_ops *ops; - - /* slots */ - vx_core_t *card_list[SNDRV_CARDS]; - dev_link_t *dev_list; /* Linked list of devices */ -}; - struct snd_vxpocket { vx_core_t core; @@ -57,8 +39,7 @@ struct snd_vxpocket { unsigned int regCDSP; /* current CDSP register */ unsigned int regDIALOG; /* current DIALOG register */ - int index; - struct snd_vxp_entry *hw_entry; + int index; /* card index */ /* pcmcia stuff */ dev_link_t link; @@ -70,12 +51,6 @@ extern struct snd_vx_ops snd_vxpocket_ops; void vx_set_mic_boost(vx_core_t *chip, int boost); void vx_set_mic_level(vx_core_t *chip, int level); -/* - * pcmcia stuff - */ -dev_link_t *snd_vxpocket_attach(struct snd_vxp_entry *hw); -void snd_vxpocket_detach(struct snd_vxp_entry *hw, dev_link_t *link); - int vxp_add_mic_controls(vx_core_t *chip); /* Constants used to access the CDSP register (0x08). */ diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c index 061e52d3d771..758ca1bcbcf2 100644 --- a/sound/ppc/awacs.c +++ b/sound/ppc/awacs.c @@ -103,7 +103,7 @@ static void screamer_recalibrate(pmac_t *chip) snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); if (chip->manufacturer == 0x1) /* delay for broken crystal part */ - big_mdelay(750); + msleep(750); snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE); snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); @@ -653,10 +653,10 @@ static void snd_pmac_awacs_resume(pmac_t *chip) { if (machine_is_compatible("PowerBook3,1") || machine_is_compatible("PowerBook3,2")) { - big_mdelay(100); + msleep(100); snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1] & ~MASK_PAROUT); - big_mdelay(300); + msleep(300); } awacs_restore_all_regs(chip); diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c index 3f837d9f3eb1..edbc0484e22a 100644 --- a/sound/ppc/burgundy.c +++ b/sound/ppc/burgundy.c @@ -30,7 +30,7 @@ /* Waits for busy flag to clear */ -inline static void +static inline void snd_pmac_burgundy_busy_wait(pmac_t *chip) { int timeout = 50; @@ -40,7 +40,7 @@ snd_pmac_burgundy_busy_wait(pmac_t *chip) printk(KERN_DEBUG "burgundy_busy_wait: timeout\n"); } -inline static void +static inline void snd_pmac_burgundy_extend_wait(pmac_t *chip) { int timeout; diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 75b8b7423036..c89e82eb06a6 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -153,7 +153,7 @@ static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream) /* * wait while run status is on */ -inline static void +static inline void snd_pmac_wait_ack(pmac_stream_t *rec) { int timeout = 50000; @@ -177,7 +177,7 @@ static void snd_pmac_pcm_set_format(pmac_t *chip) /* * stop the DMA transfer */ -inline static void snd_pmac_dma_stop(pmac_stream_t *rec) +static inline void snd_pmac_dma_stop(pmac_stream_t *rec) { out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); snd_pmac_wait_ack(rec); @@ -186,7 +186,7 @@ inline static void snd_pmac_dma_stop(pmac_stream_t *rec) /* * set the command pointer address */ -inline static void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd) +static inline void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd) { out_le32(&rec->dma->cmdptr, cmd->addr); } @@ -194,7 +194,7 @@ inline static void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cm /* * start the DMA */ -inline static void snd_pmac_dma_run(pmac_stream_t *rec, int status) +static inline void snd_pmac_dma_run(pmac_stream_t *rec, int status) { out_le32(&rec->dma->control, status | (status << 16)); } @@ -765,7 +765,8 @@ snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) */ static void snd_pmac_sound_feature(pmac_t *chip, int enable) { - ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); + if (ppc_md.feature_call) + ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); } /* diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h index 582db5220119..ae3bb6c6edff 100644 --- a/sound/ppc/pmac.h +++ b/sound/ppc/pmac.h @@ -212,9 +212,4 @@ int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *ui int snd_pmac_add_automute(pmac_t *chip); -#define big_mdelay(msec) do {\ - set_current_state(TASK_UNINTERRUPTIBLE);\ - schedule_timeout(((msec) * HZ + 999) / 1000);\ -} while (0) - #endif /* __PMAC_H */ diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 36c5d5d45bb1..b94437c024b1 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -945,7 +945,7 @@ static void device_change_handler(void *self) check_mute(chip, &mix->line_mute, 0, mix->auto_mute_notify, chip->lineout_sw_ctl); if (mix->anded_reset) - big_mdelay(10); + msleep(10); check_mute(chip, &mix->amp_mute, 1, mix->auto_mute_notify, chip->speaker_sw_ctl); mix->drc_enable = 0; @@ -954,7 +954,7 @@ static void device_change_handler(void *self) check_mute(chip, &mix->amp_mute, 0, mix->auto_mute_notify, chip->speaker_sw_ctl); if (mix->anded_reset) - big_mdelay(10); + msleep(10); check_mute(chip, &mix->hp_mute, 1, mix->auto_mute_notify, chip->master_sw_ctl); if (mix->line_mute.addr != 0) @@ -1109,22 +1109,22 @@ static void tumbler_reset_audio(pmac_t *chip) DBG("(I) codec anded reset !\n"); write_audio_gpio(&mix->hp_mute, 0); write_audio_gpio(&mix->amp_mute, 0); - big_mdelay(200); + msleep(200); write_audio_gpio(&mix->hp_mute, 1); write_audio_gpio(&mix->amp_mute, 1); - big_mdelay(100); + msleep(100); write_audio_gpio(&mix->hp_mute, 0); write_audio_gpio(&mix->amp_mute, 0); - big_mdelay(100); + msleep(100); } else { DBG("(I) codec normal reset !\n"); write_audio_gpio(&mix->audio_reset, 0); - big_mdelay(200); + msleep(200); write_audio_gpio(&mix->audio_reset, 1); - big_mdelay(100); + msleep(100); write_audio_gpio(&mix->audio_reset, 0); - big_mdelay(100); + msleep(100); } } diff --git a/sound/sound_core.c b/sound/sound_core.c index 21a69e096225..954f994592ab 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -153,7 +153,7 @@ static DEFINE_SPINLOCK(sound_loader_lock); * list. Acquires locks as needed */ -static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode) +static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode, struct device *dev) { struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL); int r; @@ -175,7 +175,7 @@ static int sound_insert_unit(struct sound_unit **list, struct file_operations *f devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor), S_IFCHR | mode, s->name); class_device_create(sound_class, MKDEV(SOUND_MAJOR, s->unit_minor), - NULL, s->name+6); + dev, s->name+6); return r; fail: @@ -227,16 +227,18 @@ static void sound_remove_unit(struct sound_unit **list, int unit) static struct sound_unit *chains[SOUND_STEP]; /** - * register_sound_special - register a special sound node + * register_sound_special_device - register a special sound node * @fops: File operations for the driver * @unit: Unit number to allocate + * @dev: device pointer * * Allocate a special sound device by minor number from the sound * subsystem. The allocated number is returned on succes. On failure * a negative error code is returned. */ -int register_sound_special(struct file_operations *fops, int unit) +int register_sound_special_device(struct file_operations *fops, int unit, + struct device *dev) { const int chain = unit % SOUND_STEP; int max_unit = 128 + chain; @@ -294,9 +296,16 @@ int register_sound_special(struct file_operations *fops, int unit) break; } return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, - name, S_IRUSR | S_IWUSR); + name, S_IRUSR | S_IWUSR, dev); } +EXPORT_SYMBOL(register_sound_special_device); + +int register_sound_special(struct file_operations *fops, int unit) +{ + return register_sound_special_device(fops, unit, NULL); +} + EXPORT_SYMBOL(register_sound_special); /** @@ -312,7 +321,7 @@ EXPORT_SYMBOL(register_sound_special); int register_sound_mixer(struct file_operations *fops, int dev) { return sound_insert_unit(&chains[0], fops, dev, 0, 128, - "mixer", S_IRUSR | S_IWUSR); + "mixer", S_IRUSR | S_IWUSR, NULL); } EXPORT_SYMBOL(register_sound_mixer); @@ -330,7 +339,7 @@ EXPORT_SYMBOL(register_sound_mixer); int register_sound_midi(struct file_operations *fops, int dev) { return sound_insert_unit(&chains[2], fops, dev, 2, 130, - "midi", S_IRUSR | S_IWUSR); + "midi", S_IRUSR | S_IWUSR, NULL); } EXPORT_SYMBOL(register_sound_midi); @@ -356,7 +365,7 @@ EXPORT_SYMBOL(register_sound_midi); int register_sound_dsp(struct file_operations *fops, int dev) { return sound_insert_unit(&chains[3], fops, dev, 3, 131, - "dsp", S_IWUSR | S_IRUSR); + "dsp", S_IWUSR | S_IRUSR, NULL); } EXPORT_SYMBOL(register_sound_dsp); @@ -375,7 +384,7 @@ EXPORT_SYMBOL(register_sound_dsp); int register_sound_synth(struct file_operations *fops, int dev) { return sound_insert_unit(&chains[9], fops, dev, 9, 137, - "synth", S_IRUSR | S_IWUSR); + "synth", S_IRUSR | S_IWUSR, NULL); } EXPORT_SYMBOL(register_sound_synth); diff --git a/sound/sparc/Kconfig b/sound/sparc/Kconfig index 2358df1c45a9..25a8a558ef92 100644 --- a/sound/sparc/Kconfig +++ b/sound/sparc/Kconfig @@ -7,12 +7,30 @@ config SND_SUN_AMD7930 tristate "Sun AMD7930" depends on SBUS && SND select SND_PCM + help + Say Y here to include support for AMD7930 sound device on Sun. + + To compile this driver as a module, choose M here: the module + will be called snd-sun-amd7930. -# dep_tristate 'Sun DBRI' CONFIG_SND_SUN_DBRI $CONFIG_SND config SND_SUN_CS4231 tristate "Sun CS4231" depends on SND select SND_PCM + help + Say Y here to include support for CS4231 sound device on Sun. -endmenu + To compile this driver as a module, choose M here: the module + will be called snd-sun-cs4231. + +config SND_SUN_DBRI + tristate "Sun DBRI" + depends on SND && SBUS + select SND_PCM + help + Say Y here to include support for DBRI sound device on Sun. + + To compile this driver as a module, choose M here: the module + will be called snd-sun-dbri. +endmenu diff --git a/sound/sparc/Makefile b/sound/sparc/Makefile index 6809cc92d276..3cd89c67c2f2 100644 --- a/sound/sparc/Makefile +++ b/sound/sparc/Makefile @@ -4,9 +4,9 @@ # snd-sun-amd7930-objs := amd7930.o -#snd-sun-dbri-objs := dbri.o snd-sun-cs4231-objs := cs4231.o +snd-sun-dbri-objs := dbri.o obj-$(CONFIG_SND_SUN_AMD7930) += snd-sun-amd7930.o -#obj-$(CONFIG_SND_SUN_DBRI) += snd-sun-dbri.o obj-$(CONFIG_SND_SUN_CS4231) += snd-sun-cs4231.o +obj-$(CONFIG_SND_SUN_DBRI) += snd-sun-dbri.o diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c new file mode 100644 index 000000000000..941c7b1e7ebb --- /dev/null +++ b/sound/sparc/dbri.c @@ -0,0 +1,2729 @@ +/* + * Driver for DBRI sound chip found on Sparcs. + * Copyright (C) 2004 Martin Habets (mhabets@users.sourceforge.net) + * + * Based entirely upon drivers/sbus/audio/dbri.c which is: + * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de) + * Copyright (C) 1998, 1999 Brent Baccala (baccala@freesoft.org) + * + * This is the lowlevel driver for the DBRI & MMCODEC duo used for ISDN & AUDIO + * on Sun SPARCstation 10, 20, LX and Voyager models. + * + * - DBRI: AT&T T5900FX Dual Basic Rates ISDN Interface. It is a 32 channel + * data time multiplexer with ISDN support (aka T7259) + * Interfaces: SBus,ISDN NT & TE, CHI, 4 bits parallel. + * CHI: (spelled ki) Concentration Highway Interface (AT&T or Intel bus ?). + * Documentation: + * - "STP 4000SBus Dual Basic Rate ISDN (DBRI) Tranceiver" from + * Sparc Technology Business (courtesy of Sun Support) + * - Data sheet of the T7903, a newer but very similar ISA bus equivalent + * available from the Lucent (formarly AT&T microelectronics) home + * page. + * - http://www.freesoft.org/Linux/DBRI/ + * - MMCODEC: Crystal Semiconductor CS4215 16 bit Multimedia Audio Codec + * Interfaces: CHI, Audio In & Out, 2 bits parallel + * Documentation: from the Crystal Semiconductor home page. + * + * The DBRI is a 32 pipe machine, each pipe can transfer some bits between + * memory and a serial device (long pipes, nr 0-15) or between two serial + * devices (short pipes, nr 16-31), or simply send a fixed data to a serial + * device (short pipes). + * A timeslot defines the bit-offset and nr of bits read from a serial device. + * The timeslots are linked to 6 circular lists, one for each direction for + * each serial device (NT,TE,CHI). A timeslot is associated to 1 or 2 pipes + * (the second one is a monitor/tee pipe, valid only for serial input). + * + * The mmcodec is connected via the CHI bus and needs the data & some + * parameters (volume, balance, output selection) timemultiplexed in 8 byte + * chunks. It also has a control mode, which serves for audio format setting. + * + * Looking at the CS4215 data sheet it is easy to set up 2 or 4 codecs on + * the same CHI bus, so I thought perhaps it is possible to use the onboard + * & the speakerbox codec simultanously, giving 2 (not very independent :-) + * audio devices. But the SUN HW group decided against it, at least on my + * LX the speakerbox connector has at least 1 pin missing and 1 wrongly + * connected. + */ + +#include <sound/driver.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/control.h> +#include <sound/initval.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/sbus.h> +#include <asm/atomic.h> + +MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets"); +MODULE_DESCRIPTION("Sun DBRI"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Sun,DBRI}}"); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Sun DBRI soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Sun DBRI soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Sun DBRI soundcard."); + +#define DBRI_DEBUG + +#define D_INT (1<<0) +#define D_GEN (1<<1) +#define D_CMD (1<<2) +#define D_MM (1<<3) +#define D_USR (1<<4) +#define D_DESC (1<<5) + +static int dbri_debug = 0; +module_param(dbri_debug, int, 0444); +MODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard."); + +#ifdef DBRI_DEBUG +static char *cmds[] = { + "WAIT", "PAUSE", "JUMP", "IIQ", "REX", "SDP", "CDP", "DTS", + "SSP", "CHI", "NT", "TE", "CDEC", "TEST", "CDM", "RESRV" +}; + +#define dprintk(a, x...) if(dbri_debug & a) printk(KERN_DEBUG x) + +#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \ + (1 << 27) | \ + value) +#else +#define dprintk(a, x...) + +#define DBRI_CMD(cmd, intr, value) ((cmd << 28) | \ + (intr << 27) | \ + value) +#endif /* DBRI_DEBUG */ + +/*************************************************************************** + CS4215 specific definitions and structures +****************************************************************************/ + +struct cs4215 { + __u8 data[4]; /* Data mode: Time slots 5-8 */ + __u8 ctrl[4]; /* Ctrl mode: Time slots 1-4 */ + __u8 onboard; + __u8 offset; /* Bit offset from frame sync to time slot 1 */ + volatile __u32 status; + volatile __u32 version; + __u8 precision; /* In bits, either 8 or 16 */ + __u8 channels; /* 1 or 2 */ +}; + +/* + * Control mode first + */ + +/* Time Slot 1, Status register */ +#define CS4215_CLB (1<<2) /* Control Latch Bit */ +#define CS4215_OLB (1<<3) /* 1: line: 2.0V, speaker 4V */ + /* 0: line: 2.8V, speaker 8V */ +#define CS4215_MLB (1<<4) /* 1: Microphone: 20dB gain disabled */ +#define CS4215_RSRVD_1 (1<<5) + +/* Time Slot 2, Data Format Register */ +#define CS4215_DFR_LINEAR16 0 +#define CS4215_DFR_ULAW 1 +#define CS4215_DFR_ALAW 2 +#define CS4215_DFR_LINEAR8 3 +#define CS4215_DFR_STEREO (1<<2) +static struct { + unsigned short freq; + unsigned char xtal; + unsigned char csval; +} CS4215_FREQ[] = { + { 8000, (1 << 4), (0 << 3) }, + { 16000, (1 << 4), (1 << 3) }, + { 27429, (1 << 4), (2 << 3) }, /* Actually 24428.57 */ + { 32000, (1 << 4), (3 << 3) }, + /* { NA, (1 << 4), (4 << 3) }, */ + /* { NA, (1 << 4), (5 << 3) }, */ + { 48000, (1 << 4), (6 << 3) }, + { 9600, (1 << 4), (7 << 3) }, + { 5513, (2 << 4), (0 << 3) }, /* Actually 5512.5 */ + { 11025, (2 << 4), (1 << 3) }, + { 18900, (2 << 4), (2 << 3) }, + { 22050, (2 << 4), (3 << 3) }, + { 37800, (2 << 4), (4 << 3) }, + { 44100, (2 << 4), (5 << 3) }, + { 33075, (2 << 4), (6 << 3) }, + { 6615, (2 << 4), (7 << 3) }, + { 0, 0, 0} +}; + +#define CS4215_HPF (1<<7) /* High Pass Filter, 1: Enabled */ + +#define CS4215_12_MASK 0xfcbf /* Mask off reserved bits in slot 1 & 2 */ + +/* Time Slot 3, Serial Port Control register */ +#define CS4215_XEN (1<<0) /* 0: Enable serial output */ +#define CS4215_XCLK (1<<1) /* 1: Master mode: Generate SCLK */ +#define CS4215_BSEL_64 (0<<2) /* Bitrate: 64 bits per frame */ +#define CS4215_BSEL_128 (1<<2) +#define CS4215_BSEL_256 (2<<2) +#define CS4215_MCK_MAST (0<<4) /* Master clock */ +#define CS4215_MCK_XTL1 (1<<4) /* 24.576 MHz clock source */ +#define CS4215_MCK_XTL2 (2<<4) /* 16.9344 MHz clock source */ +#define CS4215_MCK_CLK1 (3<<4) /* Clockin, 256 x Fs */ +#define CS4215_MCK_CLK2 (4<<4) /* Clockin, see DFR */ + +/* Time Slot 4, Test Register */ +#define CS4215_DAD (1<<0) /* 0:Digital-Dig loop, 1:Dig-Analog-Dig loop */ +#define CS4215_ENL (1<<1) /* Enable Loopback Testing */ + +/* Time Slot 5, Parallel Port Register */ +/* Read only here and the same as the in data mode */ + +/* Time Slot 6, Reserved */ + +/* Time Slot 7, Version Register */ +#define CS4215_VERSION_MASK 0xf /* Known versions 0/C, 1/D, 2/E */ + +/* Time Slot 8, Reserved */ + +/* + * Data mode + */ +/* Time Slot 1-2: Left Channel Data, 2-3: Right Channel Data */ + +/* Time Slot 5, Output Setting */ +#define CS4215_LO(v) v /* Left Output Attenuation 0x3f: -94.5 dB */ +#define CS4215_LE (1<<6) /* Line Out Enable */ +#define CS4215_HE (1<<7) /* Headphone Enable */ + +/* Time Slot 6, Output Setting */ +#define CS4215_RO(v) v /* Right Output Attenuation 0x3f: -94.5 dB */ +#define CS4215_SE (1<<6) /* Speaker Enable */ +#define CS4215_ADI (1<<7) /* A/D Data Invalid: Busy in calibration */ + +/* Time Slot 7, Input Setting */ +#define CS4215_LG(v) v /* Left Gain Setting 0xf: 22.5 dB */ +#define CS4215_IS (1<<4) /* Input Select: 1=Microphone, 0=Line */ +#define CS4215_OVR (1<<5) /* 1: Overrange condition occurred */ +#define CS4215_PIO0 (1<<6) /* Parallel I/O 0 */ +#define CS4215_PIO1 (1<<7) + +/* Time Slot 8, Input Setting */ +#define CS4215_RG(v) v /* Right Gain Setting 0xf: 22.5 dB */ +#define CS4215_MA(v) (v<<4) /* Monitor Path Attenuation 0xf: mute */ + +/*************************************************************************** + DBRI specific definitions and structures +****************************************************************************/ + +/* DBRI main registers */ +#define REG0 0x00UL /* Status and Control */ +#define REG1 0x04UL /* Mode and Interrupt */ +#define REG2 0x08UL /* Parallel IO */ +#define REG3 0x0cUL /* Test */ +#define REG8 0x20UL /* Command Queue Pointer */ +#define REG9 0x24UL /* Interrupt Queue Pointer */ + +#define DBRI_NO_CMDS 64 +#define DBRI_NO_INTS 1 /* Note: the value of this define was + * originally 2. The ringbuffer to store + * interrupts in dma is currently broken. + * This is a temporary fix until the ringbuffer + * is fixed. + */ +#define DBRI_INT_BLK 64 +#define DBRI_NO_DESCS 64 +#define DBRI_NO_PIPES 32 + +#define DBRI_MM_ONB 1 +#define DBRI_MM_SB 2 + +#define DBRI_REC 0 +#define DBRI_PLAY 1 +#define DBRI_NO_STREAMS 2 + +/* One transmit/receive descriptor */ +struct dbri_mem { + volatile __u32 word1; + volatile __u32 ba; /* Transmit/Receive Buffer Address */ + volatile __u32 nda; /* Next Descriptor Address */ + volatile __u32 word4; +}; + +/* This structure is in a DMA region where it can accessed by both + * the CPU and the DBRI + */ +struct dbri_dma { + volatile s32 cmd[DBRI_NO_CMDS]; /* Place for commands */ + volatile s32 intr[DBRI_NO_INTS * DBRI_INT_BLK]; /* Interrupt field */ + struct dbri_mem desc[DBRI_NO_DESCS]; /* Xmit/receive descriptors */ +}; + +#define dbri_dma_off(member, elem) \ + ((u32)(unsigned long) \ + (&(((struct dbri_dma *)0)->member[elem]))) + +enum in_or_out { PIPEinput, PIPEoutput }; + +struct dbri_pipe { + u32 sdp; /* SDP command word */ + enum in_or_out direction; + int nextpipe; /* Next pipe in linked list */ + int prevpipe; + int cycle; /* Offset of timeslot (bits) */ + int length; /* Length of timeslot (bits) */ + int first_desc; /* Index of first descriptor */ + int desc; /* Index of active descriptor */ + volatile __u32 *recv_fixed_ptr; /* Ptr to receive fixed data */ +}; + +struct dbri_desc { + int inuse; /* Boolean flag */ + int next; /* Index of next desc, or -1 */ + unsigned int len; +}; + +/* Per stream (playback or record) information */ +typedef struct dbri_streaminfo { + snd_pcm_substream_t *substream; + u32 dvma_buffer; /* Device view of Alsa DMA buffer */ + int left; /* # of bytes left in DMA buffer */ + int size; /* Size of DMA buffer */ + size_t offset; /* offset in user buffer */ + int pipe; /* Data pipe used */ + int left_gain; /* mixer elements */ + int right_gain; + int balance; +} dbri_streaminfo_t; + +/* This structure holds the information for both chips (DBRI & CS4215) */ +typedef struct snd_dbri { + snd_card_t *card; /* ALSA card */ + snd_pcm_t *pcm; + + int regs_size, irq; /* Needed for unload */ + struct sbus_dev *sdev; /* SBUS device info */ + spinlock_t lock; + + volatile struct dbri_dma *dma; /* Pointer to our DMA block */ + u32 dma_dvma; /* DBRI visible DMA address */ + + void __iomem *regs; /* dbri HW regs */ + int dbri_version; /* 'e' and up is OK */ + int dbri_irqp; /* intr queue pointer */ + int wait_seen; + + struct dbri_pipe pipes[DBRI_NO_PIPES]; /* DBRI's 32 data pipes */ + struct dbri_desc descs[DBRI_NO_DESCS]; + + int chi_in_pipe; + int chi_out_pipe; + int chi_bpf; + + struct cs4215 mm; /* mmcodec special info */ + /* per stream (playback/record) info */ + struct dbri_streaminfo stream_info[DBRI_NO_STREAMS]; + + struct snd_dbri *next; +} snd_dbri_t; + +/* Needed for the ALSA macros to work */ +#define chip_t snd_dbri_t + +#define DBRI_MAX_VOLUME 63 /* Output volume */ +#define DBRI_MAX_GAIN 15 /* Input gain */ +#define DBRI_RIGHT_BALANCE 255 +#define DBRI_MID_BALANCE (DBRI_RIGHT_BALANCE >> 1) + +/* DBRI Reg0 - Status Control Register - defines. (Page 17) */ +#define D_P (1<<15) /* Program command & queue pointer valid */ +#define D_G (1<<14) /* Allow 4-Word SBus Burst */ +#define D_S (1<<13) /* Allow 16-Word SBus Burst */ +#define D_E (1<<12) /* Allow 8-Word SBus Burst */ +#define D_X (1<<7) /* Sanity Timer Disable */ +#define D_T (1<<6) /* Permit activation of the TE interface */ +#define D_N (1<<5) /* Permit activation of the NT interface */ +#define D_C (1<<4) /* Permit activation of the CHI interface */ +#define D_F (1<<3) /* Force Sanity Timer Time-Out */ +#define D_D (1<<2) /* Disable Master Mode */ +#define D_H (1<<1) /* Halt for Analysis */ +#define D_R (1<<0) /* Soft Reset */ + +/* DBRI Reg1 - Mode and Interrupt Register - defines. (Page 18) */ +#define D_LITTLE_END (1<<8) /* Byte Order */ +#define D_BIG_END (0<<8) /* Byte Order */ +#define D_MRR (1<<4) /* Multiple Error Ack on SBus (readonly) */ +#define D_MLE (1<<3) /* Multiple Late Error on SBus (readonly) */ +#define D_LBG (1<<2) /* Lost Bus Grant on SBus (readonly) */ +#define D_MBE (1<<1) /* Burst Error on SBus (readonly) */ +#define D_IR (1<<0) /* Interrupt Indicator (readonly) */ + +/* DBRI Reg2 - Parallel IO Register - defines. (Page 18) */ +#define D_ENPIO3 (1<<7) /* Enable Pin 3 */ +#define D_ENPIO2 (1<<6) /* Enable Pin 2 */ +#define D_ENPIO1 (1<<5) /* Enable Pin 1 */ +#define D_ENPIO0 (1<<4) /* Enable Pin 0 */ +#define D_ENPIO (0xf0) /* Enable all the pins */ +#define D_PIO3 (1<<3) /* Pin 3: 1: Data mode, 0: Ctrl mode */ +#define D_PIO2 (1<<2) /* Pin 2: 1: Onboard PDN */ +#define D_PIO1 (1<<1) /* Pin 1: 0: Reset */ +#define D_PIO0 (1<<0) /* Pin 0: 1: Speakerbox PDN */ + +/* DBRI Commands (Page 20) */ +#define D_WAIT 0x0 /* Stop execution */ +#define D_PAUSE 0x1 /* Flush long pipes */ +#define D_JUMP 0x2 /* New command queue */ +#define D_IIQ 0x3 /* Initialize Interrupt Queue */ +#define D_REX 0x4 /* Report command execution via interrupt */ +#define D_SDP 0x5 /* Setup Data Pipe */ +#define D_CDP 0x6 /* Continue Data Pipe (reread NULL Pointer) */ +#define D_DTS 0x7 /* Define Time Slot */ +#define D_SSP 0x8 /* Set short Data Pipe */ +#define D_CHI 0x9 /* Set CHI Global Mode */ +#define D_NT 0xa /* NT Command */ +#define D_TE 0xb /* TE Command */ +#define D_CDEC 0xc /* Codec setup */ +#define D_TEST 0xd /* No comment */ +#define D_CDM 0xe /* CHI Data mode command */ + +/* Special bits for some commands */ +#define D_PIPE(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */ + +/* Setup Data Pipe */ +/* IRM */ +#define D_SDP_2SAME (1<<18) /* Report 2nd time in a row value rcvd */ +#define D_SDP_CHANGE (2<<18) /* Report any changes */ +#define D_SDP_EVERY (3<<18) /* Report any changes */ +#define D_SDP_EOL (1<<17) /* EOL interrupt enable */ +#define D_SDP_IDLE (1<<16) /* HDLC idle interrupt enable */ + +/* Pipe data MODE */ +#define D_SDP_MEM (0<<13) /* To/from memory */ +#define D_SDP_HDLC (2<<13) +#define D_SDP_HDLC_D (3<<13) /* D Channel (prio control) */ +#define D_SDP_SER (4<<13) /* Serial to serial */ +#define D_SDP_FIXED (6<<13) /* Short only */ +#define D_SDP_MODE(v) ((v)&(7<<13)) + +#define D_SDP_TO_SER (1<<12) /* Direction */ +#define D_SDP_FROM_SER (0<<12) /* Direction */ +#define D_SDP_MSB (1<<11) /* Bit order within Byte */ +#define D_SDP_LSB (0<<11) /* Bit order within Byte */ +#define D_SDP_P (1<<10) /* Pointer Valid */ +#define D_SDP_A (1<<8) /* Abort */ +#define D_SDP_C (1<<7) /* Clear */ + +/* Define Time Slot */ +#define D_DTS_VI (1<<17) /* Valid Input Time-Slot Descriptor */ +#define D_DTS_VO (1<<16) /* Valid Output Time-Slot Descriptor */ +#define D_DTS_INS (1<<15) /* Insert Time Slot */ +#define D_DTS_DEL (0<<15) /* Delete Time Slot */ +#define D_DTS_PRVIN(v) ((v)<<10) /* Previous In Pipe */ +#define D_DTS_PRVOUT(v) ((v)<<5) /* Previous Out Pipe */ + +/* Time Slot defines */ +#define D_TS_LEN(v) ((v)<<24) /* Number of bits in this time slot */ +#define D_TS_CYCLE(v) ((v)<<14) /* Bit Count at start of TS */ +#define D_TS_DI (1<<13) /* Data Invert */ +#define D_TS_1CHANNEL (0<<10) /* Single Channel / Normal mode */ +#define D_TS_MONITOR (2<<10) /* Monitor pipe */ +#define D_TS_NONCONTIG (3<<10) /* Non contiguous mode */ +#define D_TS_ANCHOR (7<<10) /* Starting short pipes */ +#define D_TS_MON(v) ((v)<<5) /* Monitor Pipe */ +#define D_TS_NEXT(v) ((v)<<0) /* Pipe Nr: 0-15 long, 16-21 short */ + +/* Concentration Highway Interface Modes */ +#define D_CHI_CHICM(v) ((v)<<16) /* Clock mode */ +#define D_CHI_IR (1<<15) /* Immediate Interrupt Report */ +#define D_CHI_EN (1<<14) /* CHIL Interrupt enabled */ +#define D_CHI_OD (1<<13) /* Open Drain Enable */ +#define D_CHI_FE (1<<12) /* Sample CHIFS on Rising Frame Edge */ +#define D_CHI_FD (1<<11) /* Frame Drive */ +#define D_CHI_BPF(v) ((v)<<0) /* Bits per Frame */ + +/* NT: These are here for completeness */ +#define D_NT_FBIT (1<<17) /* Frame Bit */ +#define D_NT_NBF (1<<16) /* Number of bad frames to loose framing */ +#define D_NT_IRM_IMM (1<<15) /* Interrupt Report & Mask: Immediate */ +#define D_NT_IRM_EN (1<<14) /* Interrupt Report & Mask: Enable */ +#define D_NT_ISNT (1<<13) /* Configfure interface as NT */ +#define D_NT_FT (1<<12) /* Fixed Timing */ +#define D_NT_EZ (1<<11) /* Echo Channel is Zeros */ +#define D_NT_IFA (1<<10) /* Inhibit Final Activation */ +#define D_NT_ACT (1<<9) /* Activate Interface */ +#define D_NT_MFE (1<<8) /* Multiframe Enable */ +#define D_NT_RLB(v) ((v)<<5) /* Remote Loopback */ +#define D_NT_LLB(v) ((v)<<2) /* Local Loopback */ +#define D_NT_FACT (1<<1) /* Force Activation */ +#define D_NT_ABV (1<<0) /* Activate Bipolar Violation */ + +/* Codec Setup */ +#define D_CDEC_CK(v) ((v)<<24) /* Clock Select */ +#define D_CDEC_FED(v) ((v)<<12) /* FSCOD Falling Edge Delay */ +#define D_CDEC_RED(v) ((v)<<0) /* FSCOD Rising Edge Delay */ + +/* Test */ +#define D_TEST_RAM(v) ((v)<<16) /* RAM Pointer */ +#define D_TEST_SIZE(v) ((v)<<11) /* */ +#define D_TEST_ROMONOFF 0x5 /* Toggle ROM opcode monitor on/off */ +#define D_TEST_PROC 0x6 /* MicroProcessor test */ +#define D_TEST_SER 0x7 /* Serial-Controller test */ +#define D_TEST_RAMREAD 0x8 /* Copy from Ram to system memory */ +#define D_TEST_RAMWRITE 0x9 /* Copy into Ram from system memory */ +#define D_TEST_RAMBIST 0xa /* RAM Built-In Self Test */ +#define D_TEST_MCBIST 0xb /* Microcontroller Built-In Self Test */ +#define D_TEST_DUMP 0xe /* ROM Dump */ + +/* CHI Data Mode */ +#define D_CDM_THI (1<<8) /* Transmit Data on CHIDR Pin */ +#define D_CDM_RHI (1<<7) /* Receive Data on CHIDX Pin */ +#define D_CDM_RCE (1<<6) /* Receive on Rising Edge of CHICK */ +#define D_CDM_XCE (1<<2) /* Transmit Data on Rising Edge of CHICK */ +#define D_CDM_XEN (1<<1) /* Transmit Highway Enable */ +#define D_CDM_REN (1<<0) /* Receive Highway Enable */ + +/* The Interrupts */ +#define D_INTR_BRDY 1 /* Buffer Ready for processing */ +#define D_INTR_MINT 2 /* Marked Interrupt in RD/TD */ +#define D_INTR_IBEG 3 /* Flag to idle transition detected (HDLC) */ +#define D_INTR_IEND 4 /* Idle to flag transition detected (HDLC) */ +#define D_INTR_EOL 5 /* End of List */ +#define D_INTR_CMDI 6 /* Command has bean read */ +#define D_INTR_XCMP 8 /* Transmission of frame complete */ +#define D_INTR_SBRI 9 /* BRI status change info */ +#define D_INTR_FXDT 10 /* Fixed data change */ +#define D_INTR_CHIL 11 /* CHI lost frame sync (channel 36 only) */ +#define D_INTR_COLL 11 /* Unrecoverable D-Channel collision */ +#define D_INTR_DBYT 12 /* Dropped by frame slip */ +#define D_INTR_RBYT 13 /* Repeated by frame slip */ +#define D_INTR_LINT 14 /* Lost Interrupt */ +#define D_INTR_UNDR 15 /* DMA underrun */ + +#define D_INTR_TE 32 +#define D_INTR_NT 34 +#define D_INTR_CHI 36 +#define D_INTR_CMD 38 + +#define D_INTR_GETCHAN(v) (((v)>>24) & 0x3f) +#define D_INTR_GETCODE(v) (((v)>>20) & 0xf) +#define D_INTR_GETCMD(v) (((v)>>16) & 0xf) +#define D_INTR_GETVAL(v) ((v) & 0xffff) +#define D_INTR_GETRVAL(v) ((v) & 0xfffff) + +#define D_P_0 0 /* TE receive anchor */ +#define D_P_1 1 /* TE transmit anchor */ +#define D_P_2 2 /* NT transmit anchor */ +#define D_P_3 3 /* NT receive anchor */ +#define D_P_4 4 /* CHI send data */ +#define D_P_5 5 /* CHI receive data */ +#define D_P_6 6 /* */ +#define D_P_7 7 /* */ +#define D_P_8 8 /* */ +#define D_P_9 9 /* */ +#define D_P_10 10 /* */ +#define D_P_11 11 /* */ +#define D_P_12 12 /* */ +#define D_P_13 13 /* */ +#define D_P_14 14 /* */ +#define D_P_15 15 /* */ +#define D_P_16 16 /* CHI anchor pipe */ +#define D_P_17 17 /* CHI send */ +#define D_P_18 18 /* CHI receive */ +#define D_P_19 19 /* CHI receive */ +#define D_P_20 20 /* CHI receive */ +#define D_P_21 21 /* */ +#define D_P_22 22 /* */ +#define D_P_23 23 /* */ +#define D_P_24 24 /* */ +#define D_P_25 25 /* */ +#define D_P_26 26 /* */ +#define D_P_27 27 /* */ +#define D_P_28 28 /* */ +#define D_P_29 29 /* */ +#define D_P_30 30 /* */ +#define D_P_31 31 /* */ + +/* Transmit descriptor defines */ +#define DBRI_TD_F (1<<31) /* End of Frame */ +#define DBRI_TD_D (1<<30) /* Do not append CRC */ +#define DBRI_TD_CNT(v) ((v)<<16) /* Number of valid bytes in the buffer */ +#define DBRI_TD_B (1<<15) /* Final interrupt */ +#define DBRI_TD_M (1<<14) /* Marker interrupt */ +#define DBRI_TD_I (1<<13) /* Transmit Idle Characters */ +#define DBRI_TD_FCNT(v) (v) /* Flag Count */ +#define DBRI_TD_UNR (1<<3) /* Underrun: transmitter is out of data */ +#define DBRI_TD_ABT (1<<2) /* Abort: frame aborted */ +#define DBRI_TD_TBC (1<<0) /* Transmit buffer Complete */ +#define DBRI_TD_STATUS(v) ((v)&0xff) /* Transmit status */ + /* Maximum buffer size per TD: almost 8Kb */ +#define DBRI_TD_MAXCNT ((1 << 13) - 1) + +/* Receive descriptor defines */ +#define DBRI_RD_F (1<<31) /* End of Frame */ +#define DBRI_RD_C (1<<30) /* Completed buffer */ +#define DBRI_RD_B (1<<15) /* Final interrupt */ +#define DBRI_RD_M (1<<14) /* Marker interrupt */ +#define DBRI_RD_BCNT(v) (v) /* Buffer size */ +#define DBRI_RD_CRC (1<<7) /* 0: CRC is correct */ +#define DBRI_RD_BBC (1<<6) /* 1: Bad Byte received */ +#define DBRI_RD_ABT (1<<5) /* Abort: frame aborted */ +#define DBRI_RD_OVRN (1<<3) /* Overrun: data lost */ +#define DBRI_RD_STATUS(v) ((v)&0xff) /* Receive status */ +#define DBRI_RD_CNT(v) (((v)>>16)&0x1fff) /* Valid bytes in the buffer */ + +/* stream_info[] access */ +/* Translate the ALSA direction into the array index */ +#define DBRI_STREAMNO(substream) \ + (substream->stream == \ + SNDRV_PCM_STREAM_PLAYBACK? DBRI_PLAY: DBRI_REC) + +/* Return a pointer to dbri_streaminfo */ +#define DBRI_STREAM(dbri, substream) &dbri->stream_info[DBRI_STREAMNO(substream)] + +static snd_dbri_t *dbri_list = NULL; /* All DBRI devices */ + +/* + * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr. + * So we have to reverse the bits. Note: not all bit lengths are supported + */ +static __u32 reverse_bytes(__u32 b, int len) +{ + switch (len) { + case 32: + b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16); + case 16: + b = ((b & 0xff00ff00) >> 8) | ((b & 0x00ff00ff) << 8); + case 8: + b = ((b & 0xf0f0f0f0) >> 4) | ((b & 0x0f0f0f0f) << 4); + case 4: + b = ((b & 0xcccccccc) >> 2) | ((b & 0x33333333) << 2); + case 2: + b = ((b & 0xaaaaaaaa) >> 1) | ((b & 0x55555555) << 1); + case 1: + case 0: + break; + default: + printk(KERN_ERR "DBRI reverse_bytes: unsupported length\n"); + }; + + return b; +} + +/* +**************************************************************************** +************** DBRI initialization and command synchronization ************* +**************************************************************************** + +Commands are sent to the DBRI by building a list of them in memory, +then writing the address of the first list item to DBRI register 8. +The list is terminated with a WAIT command, which can generate a +CPU interrupt if required. + +Since the DBRI can run in parallel with the CPU, several means of +synchronization present themselves. The original scheme (Rudolf's) +was to set a flag when we "cmdlock"ed the DBRI, clear the flag when +an interrupt signaled completion, and wait on a wait_queue if a routine +attempted to cmdlock while the flag was set. The problems arose when +we tried to cmdlock from inside an interrupt handler, which might +cause scheduling in an interrupt (if we waited), etc, etc + +A more sophisticated scheme might involve a circular command buffer +or an array of command buffers. A routine could fill one with +commands and link it onto a list. When a interrupt signaled +completion of the current command buffer, look on the list for +the next one. + +I've decided to implement something much simpler - after each command, +the CPU waits for the DBRI to finish the command by polling the P bit +in DBRI register 0. I've tried to implement this in such a way +that might make implementing a more sophisticated scheme easier. + +Every time a routine wants to write commands to the DBRI, it must +first call dbri_cmdlock() and get an initial pointer into dbri->dma->cmd +in return. After the commands have been writen, dbri_cmdsend() is +called with the final pointer value. + +*/ + +enum dbri_lock_t { NoGetLock, GetLock }; + +static volatile s32 *dbri_cmdlock(snd_dbri_t * dbri, enum dbri_lock_t get) +{ +#ifndef SMP + if ((get == GetLock) && spin_is_locked(&dbri->lock)) { + printk(KERN_ERR "DBRI: cmdlock called while in spinlock."); + } +#endif + + /*if (get == GetLock) spin_lock(&dbri->lock); */ + return &dbri->dma->cmd[0]; +} + +static void dbri_process_interrupt_buffer(snd_dbri_t *); + +static void dbri_cmdsend(snd_dbri_t * dbri, volatile s32 * cmd) +{ + int MAXLOOPS = 1000000; + int maxloops = MAXLOOPS; + volatile s32 *ptr; + + for (ptr = &dbri->dma->cmd[0]; ptr < cmd; ptr++) { + dprintk(D_CMD, "cmd: %lx:%08x\n", (unsigned long)ptr, *ptr); + } + + if ((cmd - &dbri->dma->cmd[0]) >= DBRI_NO_CMDS - 1) { + printk("DBRI: Command buffer overflow! (bug in driver)\n"); + /* Ignore the last part. */ + cmd = &dbri->dma->cmd[DBRI_NO_CMDS - 3]; + } + + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + *(cmd++) = DBRI_CMD(D_WAIT, 1, 0); + dbri->wait_seen = 0; + sbus_writel(dbri->dma_dvma, dbri->regs + REG8); + while ((--maxloops) > 0 && (sbus_readl(dbri->regs + REG0) & D_P)) + barrier(); + if (maxloops == 0) { + printk(KERN_ERR "DBRI: Chip never completed command buffer\n"); + dprintk(D_CMD, "DBRI: Chip never completed command buffer\n"); + } else { + while ((--maxloops) > 0 && (!dbri->wait_seen)) + dbri_process_interrupt_buffer(dbri); + if (maxloops == 0) { + printk(KERN_ERR "DBRI: Chip never acked WAIT\n"); + dprintk(D_CMD, "DBRI: Chip never acked WAIT\n"); + } else { + dprintk(D_CMD, "Chip completed command " + "buffer (%d)\n", MAXLOOPS - maxloops); + } + } + + /*spin_unlock(&dbri->lock); */ +} + +/* Lock must be held when calling this */ +static void dbri_reset(snd_dbri_t * dbri) +{ + int i; + + dprintk(D_GEN, "reset 0:%x 2:%x 8:%x 9:%x\n", + sbus_readl(dbri->regs + REG0), + sbus_readl(dbri->regs + REG2), + sbus_readl(dbri->regs + REG8), sbus_readl(dbri->regs + REG9)); + + sbus_writel(D_R, dbri->regs + REG0); /* Soft Reset */ + for (i = 0; (sbus_readl(dbri->regs + REG0) & D_R) && i < 64; i++) + udelay(10); +} + +/* Lock must not be held before calling this */ +static void dbri_initialize(snd_dbri_t * dbri) +{ + volatile s32 *cmd; + u32 dma_addr, tmp; + unsigned long flags; + int n; + + spin_lock_irqsave(&dbri->lock, flags); + + dbri_reset(dbri); + + cmd = dbri_cmdlock(dbri, NoGetLock); + dprintk(D_GEN, "init: cmd: %p, int: %p\n", + &dbri->dma->cmd[0], &dbri->dma->intr[0]); + + /* + * Initialize the interrupt ringbuffer. + */ + for (n = 0; n < DBRI_NO_INTS - 1; n++) { + dma_addr = dbri->dma_dvma; + dma_addr += dbri_dma_off(intr, ((n + 1) & DBRI_INT_BLK)); + dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr; + } + dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0); + dbri->dma->intr[n * DBRI_INT_BLK] = dma_addr; + dbri->dbri_irqp = 1; + + /* Initialize pipes */ + for (n = 0; n < DBRI_NO_PIPES; n++) + dbri->pipes[n].desc = dbri->pipes[n].first_desc = -1; + + /* We should query the openprom to see what burst sizes this + * SBus supports. For now, just disable all SBus bursts */ + tmp = sbus_readl(dbri->regs + REG0); + tmp &= ~(D_G | D_S | D_E); + sbus_writel(tmp, dbri->regs + REG0); + + /* + * Set up the interrupt queue + */ + dma_addr = dbri->dma_dvma + dbri_dma_off(intr, 0); + *(cmd++) = DBRI_CMD(D_IIQ, 0, 0); + *(cmd++) = dma_addr; + + dbri_cmdsend(dbri, cmd); + spin_unlock_irqrestore(&dbri->lock, flags); +} + +/* +**************************************************************************** +************************** DBRI data pipe management *********************** +**************************************************************************** + +While DBRI control functions use the command and interrupt buffers, the +main data path takes the form of data pipes, which can be short (command +and interrupt driven), or long (attached to DMA buffers). These functions +provide a rudimentary means of setting up and managing the DBRI's pipes, +but the calling functions have to make sure they respect the pipes' linked +list ordering, among other things. The transmit and receive functions +here interface closely with the transmit and receive interrupt code. + +*/ +static int pipe_active(snd_dbri_t * dbri, int pipe) +{ + return ((pipe >= 0) && (dbri->pipes[pipe].desc != -1)); +} + +/* reset_pipe(dbri, pipe) + * + * Called on an in-use pipe to clear anything being transmitted or received + * Lock must be held before calling this. + */ +static void reset_pipe(snd_dbri_t * dbri, int pipe) +{ + int sdp; + int desc; + volatile int *cmd; + + if (pipe < 0 || pipe > 31) { + printk("DBRI: reset_pipe called with illegal pipe number\n"); + return; + } + + sdp = dbri->pipes[pipe].sdp; + if (sdp == 0) { + printk("DBRI: reset_pipe called on uninitialized pipe\n"); + return; + } + + cmd = dbri_cmdlock(dbri, NoGetLock); + *(cmd++) = DBRI_CMD(D_SDP, 0, sdp | D_SDP_C | D_SDP_P); + *(cmd++) = 0; + dbri_cmdsend(dbri, cmd); + + desc = dbri->pipes[pipe].first_desc; + while (desc != -1) { + dbri->descs[desc].inuse = 0; + desc = dbri->descs[desc].next; + } + + dbri->pipes[pipe].desc = -1; + dbri->pipes[pipe].first_desc = -1; +} + +/* FIXME: direction as an argument? */ +static void setup_pipe(snd_dbri_t * dbri, int pipe, int sdp) +{ + if (pipe < 0 || pipe > 31) { + printk("DBRI: setup_pipe called with illegal pipe number\n"); + return; + } + + if ((sdp & 0xf800) != sdp) { + printk("DBRI: setup_pipe called with strange SDP value\n"); + /* sdp &= 0xf800; */ + } + + /* If this is a fixed receive pipe, arrange for an interrupt + * every time its data changes + */ + if (D_SDP_MODE(sdp) == D_SDP_FIXED && !(sdp & D_SDP_TO_SER)) + sdp |= D_SDP_CHANGE; + + sdp |= D_PIPE(pipe); + dbri->pipes[pipe].sdp = sdp; + dbri->pipes[pipe].desc = -1; + dbri->pipes[pipe].first_desc = -1; + if (sdp & D_SDP_TO_SER) + dbri->pipes[pipe].direction = PIPEoutput; + else + dbri->pipes[pipe].direction = PIPEinput; + + reset_pipe(dbri, pipe); +} + +/* FIXME: direction not needed */ +static void link_time_slot(snd_dbri_t * dbri, int pipe, + enum in_or_out direction, int basepipe, + int length, int cycle) +{ + volatile s32 *cmd; + int val; + int prevpipe; + int nextpipe; + + if (pipe < 0 || pipe > 31 || basepipe < 0 || basepipe > 31) { + printk + ("DBRI: link_time_slot called with illegal pipe number\n"); + return; + } + + if (dbri->pipes[pipe].sdp == 0 || dbri->pipes[basepipe].sdp == 0) { + printk("DBRI: link_time_slot called on uninitialized pipe\n"); + return; + } + + /* Deal with CHI special case: + * "If transmission on edges 0 or 1 is desired, then cycle n + * (where n = # of bit times per frame...) must be used." + * - DBRI data sheet, page 11 + */ + if (basepipe == 16 && direction == PIPEoutput && cycle == 0) + cycle = dbri->chi_bpf; + + if (basepipe == pipe) { + prevpipe = pipe; + nextpipe = pipe; + } else { + /* We're not initializing a new linked list (basepipe != pipe), + * so run through the linked list and find where this pipe + * should be sloted in, based on its cycle. CHI confuses + * things a bit, since it has a single anchor for both its + * transmit and receive lists. + */ + if (basepipe == 16) { + if (direction == PIPEinput) { + prevpipe = dbri->chi_in_pipe; + } else { + prevpipe = dbri->chi_out_pipe; + } + } else { + prevpipe = basepipe; + } + + nextpipe = dbri->pipes[prevpipe].nextpipe; + + while (dbri->pipes[nextpipe].cycle < cycle + && dbri->pipes[nextpipe].nextpipe != basepipe) { + prevpipe = nextpipe; + nextpipe = dbri->pipes[nextpipe].nextpipe; + } + } + + if (prevpipe == 16) { + if (direction == PIPEinput) { + dbri->chi_in_pipe = pipe; + } else { + dbri->chi_out_pipe = pipe; + } + } else { + dbri->pipes[prevpipe].nextpipe = pipe; + } + + dbri->pipes[pipe].nextpipe = nextpipe; + dbri->pipes[pipe].cycle = cycle; + dbri->pipes[pipe].length = length; + + cmd = dbri_cmdlock(dbri, NoGetLock); + + if (direction == PIPEinput) { + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = + D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + *(cmd++) = 0; + } else { + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = + D_TS_LEN(length) | D_TS_CYCLE(cycle) | D_TS_NEXT(nextpipe); + } + + dbri_cmdsend(dbri, cmd); +} + +static void unlink_time_slot(snd_dbri_t * dbri, int pipe, + enum in_or_out direction, int prevpipe, + int nextpipe) +{ + volatile s32 *cmd; + int val; + + if (pipe < 0 || pipe > 31 || prevpipe < 0 || prevpipe > 31) { + printk + ("DBRI: unlink_time_slot called with illegal pipe number\n"); + return; + } + + cmd = dbri_cmdlock(dbri, NoGetLock); + + if (direction == PIPEinput) { + val = D_DTS_VI | D_DTS_DEL | D_DTS_PRVIN(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_NEXT(nextpipe); + *(cmd++) = 0; + } else { + val = D_DTS_VO | D_DTS_DEL | D_DTS_PRVOUT(prevpipe) | pipe; + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = D_TS_NEXT(nextpipe); + } + + dbri_cmdsend(dbri, cmd); +} + +/* xmit_fixed() / recv_fixed() + * + * Transmit/receive data on a "fixed" pipe - i.e, one whose contents are not + * expected to change much, and which we don't need to buffer. + * The DBRI only interrupts us when the data changes (receive pipes), + * or only changes the data when this function is called (transmit pipes). + * Only short pipes (numbers 16-31) can be used in fixed data mode. + * + * These function operate on a 32-bit field, no matter how large + * the actual time slot is. The interrupt handler takes care of bit + * ordering and alignment. An 8-bit time slot will always end up + * in the low-order 8 bits, filled either MSB-first or LSB-first, + * depending on the settings passed to setup_pipe() + */ +static void xmit_fixed(snd_dbri_t * dbri, int pipe, unsigned int data) +{ + volatile s32 *cmd; + + if (pipe < 16 || pipe > 31) { + printk("DBRI: xmit_fixed: Illegal pipe number\n"); + return; + } + + if (D_SDP_MODE(dbri->pipes[pipe].sdp) == 0) { + printk("DBRI: xmit_fixed: Uninitialized pipe %d\n", pipe); + return; + } + + if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { + printk("DBRI: xmit_fixed: Non-fixed pipe %d\n", pipe); + return; + } + + if (!(dbri->pipes[pipe].sdp & D_SDP_TO_SER)) { + printk("DBRI: xmit_fixed: Called on receive pipe %d\n", pipe); + return; + } + + /* DBRI short pipes always transmit LSB first */ + + if (dbri->pipes[pipe].sdp & D_SDP_MSB) + data = reverse_bytes(data, dbri->pipes[pipe].length); + + cmd = dbri_cmdlock(dbri, GetLock); + + *(cmd++) = DBRI_CMD(D_SSP, 0, pipe); + *(cmd++) = data; + + dbri_cmdsend(dbri, cmd); +} + +static void recv_fixed(snd_dbri_t * dbri, int pipe, volatile __u32 * ptr) +{ + if (pipe < 16 || pipe > 31) { + printk("DBRI: recv_fixed called with illegal pipe number\n"); + return; + } + + if (D_SDP_MODE(dbri->pipes[pipe].sdp) != D_SDP_FIXED) { + printk("DBRI: recv_fixed called on non-fixed pipe %d\n", pipe); + return; + } + + if (dbri->pipes[pipe].sdp & D_SDP_TO_SER) { + printk("DBRI: recv_fixed called on transmit pipe %d\n", pipe); + return; + } + + dbri->pipes[pipe].recv_fixed_ptr = ptr; +} + +/* setup_descs() + * + * Setup transmit/receive data on a "long" pipe - i.e, one associated + * with a DMA buffer. + * + * Only pipe numbers 0-15 can be used in this mode. + * + * This function takes a stream number pointing to a data buffer, + * and work by building chains of descriptors which identify the + * data buffers. Buffers too large for a single descriptor will + * be spread across multiple descriptors. + */ +static int setup_descs(snd_dbri_t * dbri, int streamno, unsigned int period) +{ + dbri_streaminfo_t *info = &dbri->stream_info[streamno]; + __u32 dvma_buffer; + int desc = 0; + int len; + int first_desc = -1; + int last_desc = -1; + + if (info->pipe < 0 || info->pipe > 15) { + printk("DBRI: setup_descs: Illegal pipe number\n"); + return -2; + } + + if (dbri->pipes[info->pipe].sdp == 0) { + printk("DBRI: setup_descs: Uninitialized pipe %d\n", + info->pipe); + return -2; + } + + dvma_buffer = info->dvma_buffer; + len = info->size; + + if (streamno == DBRI_PLAY) { + if (!(dbri->pipes[info->pipe].sdp & D_SDP_TO_SER)) { + printk("DBRI: setup_descs: Called on receive pipe %d\n", + info->pipe); + return -2; + } + } else { + if (dbri->pipes[info->pipe].sdp & D_SDP_TO_SER) { + printk + ("DBRI: setup_descs: Called on transmit pipe %d\n", + info->pipe); + return -2; + } + /* Should be able to queue multiple buffers to receive on a pipe */ + if (pipe_active(dbri, info->pipe)) { + printk("DBRI: recv_on_pipe: Called on active pipe %d\n", + info->pipe); + return -2; + } + + /* Make sure buffer size is multiple of four */ + len &= ~3; + } + + while (len > 0) { + int mylen; + + for (; desc < DBRI_NO_DESCS; desc++) { + if (!dbri->descs[desc].inuse) + break; + } + if (desc == DBRI_NO_DESCS) { + printk("DBRI: setup_descs: No descriptors\n"); + return -1; + } + + if (len > DBRI_TD_MAXCNT) { + mylen = DBRI_TD_MAXCNT; /* 8KB - 1 */ + } else { + mylen = len; + } + if (mylen > period) { + mylen = period; + } + + dbri->descs[desc].inuse = 1; + dbri->descs[desc].next = -1; + dbri->dma->desc[desc].ba = dvma_buffer; + dbri->dma->desc[desc].nda = 0; + + if (streamno == DBRI_PLAY) { + dbri->descs[desc].len = mylen; + dbri->dma->desc[desc].word1 = DBRI_TD_CNT(mylen); + dbri->dma->desc[desc].word4 = 0; + if (first_desc != -1) + dbri->dma->desc[desc].word1 |= DBRI_TD_M; + } else { + dbri->descs[desc].len = 0; + dbri->dma->desc[desc].word1 = 0; + dbri->dma->desc[desc].word4 = + DBRI_RD_B | DBRI_RD_BCNT(mylen); + } + + if (first_desc == -1) { + first_desc = desc; + } else { + dbri->descs[last_desc].next = desc; + dbri->dma->desc[last_desc].nda = + dbri->dma_dvma + dbri_dma_off(desc, desc); + } + + last_desc = desc; + dvma_buffer += mylen; + len -= mylen; + } + + if (first_desc == -1 || last_desc == -1) { + printk("DBRI: setup_descs: Not enough descriptors available\n"); + return -1; + } + + dbri->dma->desc[last_desc].word1 &= ~DBRI_TD_M; + if (streamno == DBRI_PLAY) { + dbri->dma->desc[last_desc].word1 |= + DBRI_TD_I | DBRI_TD_F | DBRI_TD_B; + } + dbri->pipes[info->pipe].first_desc = first_desc; + dbri->pipes[info->pipe].desc = first_desc; + + for (desc = first_desc; desc != -1; desc = dbri->descs[desc].next) { + dprintk(D_DESC, "DESC %d: %08x %08x %08x %08x\n", + desc, + dbri->dma->desc[desc].word1, + dbri->dma->desc[desc].ba, + dbri->dma->desc[desc].nda, dbri->dma->desc[desc].word4); + } + return 0; +} + +/* +**************************************************************************** +************************** DBRI - CHI interface **************************** +**************************************************************************** + +The CHI is a four-wire (clock, frame sync, data in, data out) time-division +multiplexed serial interface which the DBRI can operate in either master +(give clock/frame sync) or slave (take clock/frame sync) mode. + +*/ + +enum master_or_slave { CHImaster, CHIslave }; + +static void reset_chi(snd_dbri_t * dbri, enum master_or_slave master_or_slave, + int bits_per_frame) +{ + volatile s32 *cmd; + int val; + static int chi_initialized = 0; /* FIXME: mutex? */ + + if (!chi_initialized) { + + cmd = dbri_cmdlock(dbri, GetLock); + + /* Set CHI Anchor: Pipe 16 */ + + val = D_DTS_VI | D_DTS_INS | D_DTS_PRVIN(16) | D_PIPE(16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + *(cmd++) = 0; + + val = D_DTS_VO | D_DTS_INS | D_DTS_PRVOUT(16) | D_PIPE(16); + *(cmd++) = DBRI_CMD(D_DTS, 0, val); + *(cmd++) = 0; + *(cmd++) = D_TS_ANCHOR | D_TS_NEXT(16); + + dbri->pipes[16].sdp = 1; + dbri->pipes[16].nextpipe = 16; + dbri->chi_in_pipe = 16; + dbri->chi_out_pipe = 16; + +#if 0 + chi_initialized++; +#endif + } else { + int pipe; + + for (pipe = dbri->chi_in_pipe; + pipe != 16; pipe = dbri->pipes[pipe].nextpipe) { + unlink_time_slot(dbri, pipe, PIPEinput, + 16, dbri->pipes[pipe].nextpipe); + } + for (pipe = dbri->chi_out_pipe; + pipe != 16; pipe = dbri->pipes[pipe].nextpipe) { + unlink_time_slot(dbri, pipe, PIPEoutput, + 16, dbri->pipes[pipe].nextpipe); + } + + dbri->chi_in_pipe = 16; + dbri->chi_out_pipe = 16; + + cmd = dbri_cmdlock(dbri, GetLock); + } + + if (master_or_slave == CHIslave) { + /* Setup DBRI for CHI Slave - receive clock, frame sync (FS) + * + * CHICM = 0 (slave mode, 8 kHz frame rate) + * IR = give immediate CHI status interrupt + * EN = give CHI status interrupt upon change + */ + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(0)); + } else { + /* Setup DBRI for CHI Master - generate clock, FS + * + * BPF = bits per 8 kHz frame + * 12.288 MHz / CHICM_divisor = clock rate + * FD = 1 - drive CHIFS on rising edge of CHICK + */ + int clockrate = bits_per_frame * 8; + int divisor = 12288 / clockrate; + + if (divisor > 255 || divisor * clockrate != 12288) + printk("DBRI: illegal bits_per_frame in setup_chi\n"); + + *(cmd++) = DBRI_CMD(D_CHI, 0, D_CHI_CHICM(divisor) | D_CHI_FD + | D_CHI_BPF(bits_per_frame)); + } + + dbri->chi_bpf = bits_per_frame; + + /* CHI Data Mode + * + * RCE = 0 - receive on falling edge of CHICK + * XCE = 1 - transmit on rising edge of CHICK + * XEN = 1 - enable transmitter + * REN = 1 - enable receiver + */ + + *(cmd++) = DBRI_CMD(D_PAUSE, 0, 0); + *(cmd++) = DBRI_CMD(D_CDM, 0, D_CDM_XCE | D_CDM_XEN | D_CDM_REN); + + dbri_cmdsend(dbri, cmd); +} + +/* +**************************************************************************** +*********************** CS4215 audio codec management ********************** +**************************************************************************** + +In the standard SPARC audio configuration, the CS4215 codec is attached +to the DBRI via the CHI interface and few of the DBRI's PIO pins. + +*/ +static void cs4215_setup_pipes(snd_dbri_t * dbri) +{ + /* + * Data mode: + * Pipe 4: Send timeslots 1-4 (audio data) + * Pipe 20: Send timeslots 5-8 (part of ctrl data) + * Pipe 6: Receive timeslots 1-4 (audio data) + * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via + * interrupt, and the rest of the data (slot 5 and 8) is + * not relevant for us (only for doublechecking). + * + * Control mode: + * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly) + * Pipe 18: Receive timeslot 1 (clb). + * Pipe 19: Receive timeslot 7 (version). + */ + + setup_pipe(dbri, 4, D_SDP_MEM | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 20, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 6, D_SDP_MEM | D_SDP_FROM_SER | D_SDP_MSB); + setup_pipe(dbri, 21, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + + setup_pipe(dbri, 17, D_SDP_FIXED | D_SDP_TO_SER | D_SDP_MSB); + setup_pipe(dbri, 18, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); + setup_pipe(dbri, 19, D_SDP_FIXED | D_SDP_FROM_SER | D_SDP_MSB); +} + +static int cs4215_init_data(struct cs4215 *mm) +{ + /* + * No action, memory resetting only. + * + * Data Time Slot 5-8 + * Speaker,Line and Headphone enable. Gain set to the half. + * Input is mike. + */ + mm->data[0] = CS4215_LO(0x20) | CS4215_HE | CS4215_LE; + mm->data[1] = CS4215_RO(0x20) | CS4215_SE; + mm->data[2] = CS4215_LG(0x8) | CS4215_IS | CS4215_PIO0 | CS4215_PIO1; + mm->data[3] = CS4215_RG(0x8) | CS4215_MA(0xf); + + /* + * Control Time Slot 1-4 + * 0: Default I/O voltage scale + * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled + * 2: Serial enable, CHI master, 128 bits per frame, clock 1 + * 3: Tests disabled + */ + mm->ctrl[0] = CS4215_RSRVD_1 | CS4215_MLB; + mm->ctrl[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval; + mm->ctrl[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal; + mm->ctrl[3] = 0; + + mm->status = 0; + mm->version = 0xff; + mm->precision = 8; /* For ULAW */ + mm->channels = 2; + + return 0; +} + +static void cs4215_setdata(snd_dbri_t * dbri, int muted) +{ + if (muted) { + dbri->mm.data[0] |= 63; + dbri->mm.data[1] |= 63; + dbri->mm.data[2] &= ~15; + dbri->mm.data[3] &= ~15; + } else { + /* Start by setting the playback attenuation. */ + dbri_streaminfo_t *info = &dbri->stream_info[DBRI_PLAY]; + int left_gain = info->left_gain % 64; + int right_gain = info->right_gain % 64; + + if (info->balance < DBRI_MID_BALANCE) { + right_gain *= info->balance; + right_gain /= DBRI_MID_BALANCE; + } else { + left_gain *= DBRI_RIGHT_BALANCE - info->balance; + left_gain /= DBRI_MID_BALANCE; + } + + dbri->mm.data[0] &= ~0x3f; /* Reset the volume bits */ + dbri->mm.data[1] &= ~0x3f; + dbri->mm.data[0] |= (DBRI_MAX_VOLUME - left_gain); + dbri->mm.data[1] |= (DBRI_MAX_VOLUME - right_gain); + + /* Now set the recording gain. */ + info = &dbri->stream_info[DBRI_REC]; + left_gain = info->left_gain % 16; + right_gain = info->right_gain % 16; + dbri->mm.data[2] |= CS4215_LG(left_gain); + dbri->mm.data[3] |= CS4215_RG(right_gain); + } + + xmit_fixed(dbri, 20, *(int *)dbri->mm.data); +} + +/* + * Set the CS4215 to data mode. + */ +static void cs4215_open(snd_dbri_t * dbri) +{ + int data_width; + u32 tmp; + + dprintk(D_MM, "cs4215_open: %d channels, %d bits\n", + dbri->mm.channels, dbri->mm.precision); + + /* Temporarily mute outputs, and wait 1/8000 sec (125 us) + * to make sure this takes. This avoids clicking noises. + */ + + cs4215_setdata(dbri, 1); + udelay(125); + + /* + * Data mode: + * Pipe 4: Send timeslots 1-4 (audio data) + * Pipe 20: Send timeslots 5-8 (part of ctrl data) + * Pipe 6: Receive timeslots 1-4 (audio data) + * Pipe 21: Receive timeslots 6-7. We can only receive 20 bits via + * interrupt, and the rest of the data (slot 5 and 8) is + * not relevant for us (only for doublechecking). + * + * Just like in control mode, the time slots are all offset by eight + * bits. The CS4215, it seems, observes TSIN (the delayed signal) + * even if it's the CHI master. Don't ask me... + */ + tmp = sbus_readl(dbri->regs + REG0); + tmp &= ~(D_C); /* Disable CHI */ + sbus_writel(tmp, dbri->regs + REG0); + + /* Switch CS4215 to data mode - set PIO3 to 1 */ + sbus_writel(D_ENPIO | D_PIO1 | D_PIO3 | + (dbri->mm.onboard ? D_PIO0 : D_PIO2), dbri->regs + REG2); + + reset_chi(dbri, CHIslave, 128); + + /* Note: this next doesn't work for 8-bit stereo, because the two + * channels would be on timeslots 1 and 3, with 2 and 4 idle. + * (See CS4215 datasheet Fig 15) + * + * DBRI non-contiguous mode would be required to make this work. + */ + data_width = dbri->mm.channels * dbri->mm.precision; + + link_time_slot(dbri, 20, PIPEoutput, 16, 32, dbri->mm.offset + 32); + link_time_slot(dbri, 4, PIPEoutput, 16, data_width, dbri->mm.offset); + link_time_slot(dbri, 6, PIPEinput, 16, data_width, dbri->mm.offset); + link_time_slot(dbri, 21, PIPEinput, 16, 16, dbri->mm.offset + 40); + + /* FIXME: enable CHI after _setdata? */ + tmp = sbus_readl(dbri->regs + REG0); + tmp |= D_C; /* Enable CHI */ + sbus_writel(tmp, dbri->regs + REG0); + + cs4215_setdata(dbri, 0); +} + +/* + * Send the control information (i.e. audio format) + */ +static int cs4215_setctrl(snd_dbri_t * dbri) +{ + int i, val; + u32 tmp; + + /* FIXME - let the CPU do something useful during these delays */ + + /* Temporarily mute outputs, and wait 1/8000 sec (125 us) + * to make sure this takes. This avoids clicking noises. + */ + + cs4215_setdata(dbri, 1); + udelay(125); + + /* + * Enable Control mode: Set DBRI's PIO3 (4215's D/~C) to 0, then wait + * 12 cycles <= 12/(5512.5*64) sec = 34.01 usec + */ + val = D_ENPIO | D_PIO1 | (dbri->mm.onboard ? D_PIO0 : D_PIO2); + sbus_writel(val, dbri->regs + REG2); + dprintk(D_MM, "cs4215_setctrl: reg2=0x%x\n", val); + udelay(34); + + /* In Control mode, the CS4215 is a slave device, so the DBRI must + * operate as CHI master, supplying clocking and frame synchronization. + * + * In Data mode, however, the CS4215 must be CHI master to insure + * that its data stream is synchronous with its codec. + * + * The upshot of all this? We start by putting the DBRI into master + * mode, program the CS4215 in Control mode, then switch the CS4215 + * into Data mode and put the DBRI into slave mode. Various timing + * requirements must be observed along the way. + * + * Oh, and one more thing, on a SPARCStation 20 (and maybe + * others?), the addressing of the CS4215's time slots is + * offset by eight bits, so we add eight to all the "cycle" + * values in the Define Time Slot (DTS) commands. This is + * done in hardware by a TI 248 that delays the DBRI->4215 + * frame sync signal by eight clock cycles. Anybody know why? + */ + tmp = sbus_readl(dbri->regs + REG0); + tmp &= ~D_C; /* Disable CHI */ + sbus_writel(tmp, dbri->regs + REG0); + + reset_chi(dbri, CHImaster, 128); + + /* + * Control mode: + * Pipe 17: Send timeslots 1-4 (slots 5-8 are readonly) + * Pipe 18: Receive timeslot 1 (clb). + * Pipe 19: Receive timeslot 7 (version). + */ + + link_time_slot(dbri, 17, PIPEoutput, 16, 32, dbri->mm.offset); + link_time_slot(dbri, 18, PIPEinput, 16, 8, dbri->mm.offset); + link_time_slot(dbri, 19, PIPEinput, 16, 8, dbri->mm.offset + 48); + + /* Wait for the chip to echo back CLB (Control Latch Bit) as zero */ + dbri->mm.ctrl[0] &= ~CS4215_CLB; + xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); + + tmp = sbus_readl(dbri->regs + REG0); + tmp |= D_C; /* Enable CHI */ + sbus_writel(tmp, dbri->regs + REG0); + + for (i = 64; ((dbri->mm.status & 0xe4) != 0x20); --i) { + udelay(125); + } + if (i == 0) { + dprintk(D_MM, "CS4215 didn't respond to CLB (0x%02x)\n", + dbri->mm.status); + return -1; + } + + /* Disable changes to our copy of the version number, as we are about + * to leave control mode. + */ + recv_fixed(dbri, 19, NULL); + + /* Terminate CS4215 control mode - data sheet says + * "Set CLB=1 and send two more frames of valid control info" + */ + dbri->mm.ctrl[0] |= CS4215_CLB; + xmit_fixed(dbri, 17, *(int *)dbri->mm.ctrl); + + /* Two frames of control info @ 8kHz frame rate = 250 us delay */ + udelay(250); + + cs4215_setdata(dbri, 0); + + return 0; +} + +/* + * Setup the codec with the sampling rate, audio format and number of + * channels. + * As part of the process we resend the settings for the data + * timeslots as well. + */ +static int cs4215_prepare(snd_dbri_t * dbri, unsigned int rate, + snd_pcm_format_t format, unsigned int channels) +{ + int freq_idx; + int ret = 0; + + /* Lookup index for this rate */ + for (freq_idx = 0; CS4215_FREQ[freq_idx].freq != 0; freq_idx++) { + if (CS4215_FREQ[freq_idx].freq == rate) + break; + } + if (CS4215_FREQ[freq_idx].freq != rate) { + printk(KERN_WARNING "DBRI: Unsupported rate %d Hz\n", rate); + return -1; + } + + switch (format) { + case SNDRV_PCM_FORMAT_MU_LAW: + dbri->mm.ctrl[1] = CS4215_DFR_ULAW; + dbri->mm.precision = 8; + break; + case SNDRV_PCM_FORMAT_A_LAW: + dbri->mm.ctrl[1] = CS4215_DFR_ALAW; + dbri->mm.precision = 8; + break; + case SNDRV_PCM_FORMAT_U8: + dbri->mm.ctrl[1] = CS4215_DFR_LINEAR8; + dbri->mm.precision = 8; + break; + case SNDRV_PCM_FORMAT_S16_BE: + dbri->mm.ctrl[1] = CS4215_DFR_LINEAR16; + dbri->mm.precision = 16; + break; + default: + printk(KERN_WARNING "DBRI: Unsupported format %d\n", format); + return -1; + } + + /* Add rate parameters */ + dbri->mm.ctrl[1] |= CS4215_FREQ[freq_idx].csval; + dbri->mm.ctrl[2] = CS4215_XCLK | + CS4215_BSEL_128 | CS4215_FREQ[freq_idx].xtal; + + dbri->mm.channels = channels; + /* Stereo bit: 8 bit stereo not working yet. */ + if ((channels > 1) && (dbri->mm.precision == 16)) + dbri->mm.ctrl[1] |= CS4215_DFR_STEREO; + + ret = cs4215_setctrl(dbri); + if (ret == 0) + cs4215_open(dbri); /* set codec to data mode */ + + return ret; +} + +/* + * + */ +static int cs4215_init(snd_dbri_t * dbri) +{ + u32 reg2 = sbus_readl(dbri->regs + REG2); + dprintk(D_MM, "cs4215_init: reg2=0x%x\n", reg2); + + /* Look for the cs4215 chips */ + if (reg2 & D_PIO2) { + dprintk(D_MM, "Onboard CS4215 detected\n"); + dbri->mm.onboard = 1; + } + if (reg2 & D_PIO0) { + dprintk(D_MM, "Speakerbox detected\n"); + dbri->mm.onboard = 0; + + if (reg2 & D_PIO2) { + printk(KERN_INFO "DBRI: Using speakerbox / " + "ignoring onboard mmcodec.\n"); + sbus_writel(D_ENPIO2, dbri->regs + REG2); + } + } + + if (!(reg2 & (D_PIO0 | D_PIO2))) { + printk(KERN_ERR "DBRI: no mmcodec found.\n"); + return -EIO; + } + + cs4215_setup_pipes(dbri); + + cs4215_init_data(&dbri->mm); + + /* Enable capture of the status & version timeslots. */ + recv_fixed(dbri, 18, &dbri->mm.status); + recv_fixed(dbri, 19, &dbri->mm.version); + + dbri->mm.offset = dbri->mm.onboard ? 0 : 8; + if (cs4215_setctrl(dbri) == -1 || dbri->mm.version == 0xff) { + dprintk(D_MM, "CS4215 failed probe at offset %d\n", + dbri->mm.offset); + return -EIO; + } + dprintk(D_MM, "Found CS4215 at offset %d\n", dbri->mm.offset); + + return 0; +} + +/* +**************************************************************************** +*************************** DBRI interrupt handler ************************* +**************************************************************************** + +The DBRI communicates with the CPU mainly via a circular interrupt +buffer. When an interrupt is signaled, the CPU walks through the +buffer and calls dbri_process_one_interrupt() for each interrupt word. +Complicated interrupts are handled by dedicated functions (which +appear first in this file). Any pending interrupts can be serviced by +calling dbri_process_interrupt_buffer(), which works even if the CPU's +interrupts are disabled. This function is used by dbri_cmdsend() +to make sure we're synced up with the chip after each command sequence, +even if we're running cli'ed. + +*/ + +/* xmit_descs() + * + * Transmit the current TD's for recording/playing, if needed. + * For playback, ALSA has filled the DMA memory with new data (we hope). + */ +static void xmit_descs(unsigned long data) +{ + snd_dbri_t *dbri = (snd_dbri_t *) data; + dbri_streaminfo_t *info; + volatile s32 *cmd; + unsigned long flags; + int first_td; + + if (dbri == NULL) + return; /* Disabled */ + + /* First check the recording stream for buffer overflow */ + info = &dbri->stream_info[DBRI_REC]; + spin_lock_irqsave(&dbri->lock, flags); + + if ((info->left >= info->size) && (info->pipe >= 0)) { + first_td = dbri->pipes[info->pipe].first_desc; + + dprintk(D_DESC, "xmit_descs rec @ TD %d\n", first_td); + + /* Stream could be closed by the time we run. */ + if (first_td < 0) { + goto play; + } + + cmd = dbri_cmdlock(dbri, NoGetLock); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[info->pipe].sdp + | D_SDP_P | D_SDP_EVERY | D_SDP_C); + *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td); + dbri_cmdsend(dbri, cmd); + + /* Reset our admin of the pipe & bytes read. */ + dbri->pipes[info->pipe].desc = first_td; + info->left = 0; + } + +play: + spin_unlock_irqrestore(&dbri->lock, flags); + + /* Now check the playback stream for buffer underflow */ + info = &dbri->stream_info[DBRI_PLAY]; + spin_lock_irqsave(&dbri->lock, flags); + + if ((info->left <= 0) && (info->pipe >= 0)) { + first_td = dbri->pipes[info->pipe].first_desc; + + dprintk(D_DESC, "xmit_descs play @ TD %d\n", first_td); + + /* Stream could be closed by the time we run. */ + if (first_td < 0) { + spin_unlock_irqrestore(&dbri->lock, flags); + return; + } + + cmd = dbri_cmdlock(dbri, NoGetLock); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[info->pipe].sdp + | D_SDP_P | D_SDP_EVERY | D_SDP_C); + *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, first_td); + dbri_cmdsend(dbri, cmd); + + /* Reset our admin of the pipe & bytes written. */ + dbri->pipes[info->pipe].desc = first_td; + info->left = info->size; + } + spin_unlock_irqrestore(&dbri->lock, flags); +} + +DECLARE_TASKLET(xmit_descs_task, xmit_descs, 0); + +/* transmission_complete_intr() + * + * Called by main interrupt handler when DBRI signals transmission complete + * on a pipe (interrupt triggered by the B bit in a transmit descriptor). + * + * Walks through the pipe's list of transmit buffer descriptors, releasing + * each one's DMA buffer (if present), flagging the descriptor available, + * and signaling its callback routine (if present), before proceeding + * to the next one. Stops when the first descriptor is found without + * TBC (Transmit Buffer Complete) set, or we've run through them all. + */ + +static void transmission_complete_intr(snd_dbri_t * dbri, int pipe) +{ + dbri_streaminfo_t *info; + int td; + int status; + + info = &dbri->stream_info[DBRI_PLAY]; + + td = dbri->pipes[pipe].desc; + while (td >= 0) { + if (td >= DBRI_NO_DESCS) { + printk(KERN_ERR "DBRI: invalid td on pipe %d\n", pipe); + return; + } + + status = DBRI_TD_STATUS(dbri->dma->desc[td].word4); + if (!(status & DBRI_TD_TBC)) { + break; + } + + dprintk(D_INT, "TD %d, status 0x%02x\n", td, status); + + dbri->dma->desc[td].word4 = 0; /* Reset it for next time. */ + info->offset += dbri->descs[td].len; + info->left -= dbri->descs[td].len; + + /* On the last TD, transmit them all again. */ + if (dbri->descs[td].next == -1) { + if (info->left > 0) { + printk(KERN_WARNING + "%d bytes left after last transfer.\n", + info->left); + info->left = 0; + } + tasklet_schedule(&xmit_descs_task); + } + + td = dbri->descs[td].next; + dbri->pipes[pipe].desc = td; + } + + /* Notify ALSA */ + if (spin_is_locked(&dbri->lock)) { + spin_unlock(&dbri->lock); + snd_pcm_period_elapsed(info->substream); + spin_lock(&dbri->lock); + } else + snd_pcm_period_elapsed(info->substream); +} + +static void reception_complete_intr(snd_dbri_t * dbri, int pipe) +{ + dbri_streaminfo_t *info; + int rd = dbri->pipes[pipe].desc; + s32 status; + + if (rd < 0 || rd >= DBRI_NO_DESCS) { + printk(KERN_ERR "DBRI: invalid rd on pipe %d\n", pipe); + return; + } + + dbri->descs[rd].inuse = 0; + dbri->pipes[pipe].desc = dbri->descs[rd].next; + status = dbri->dma->desc[rd].word1; + dbri->dma->desc[rd].word1 = 0; /* Reset it for next time. */ + + info = &dbri->stream_info[DBRI_REC]; + info->offset += DBRI_RD_CNT(status); + info->left += DBRI_RD_CNT(status); + + /* FIXME: Check status */ + + dprintk(D_INT, "Recv RD %d, status 0x%02x, len %d\n", + rd, DBRI_RD_STATUS(status), DBRI_RD_CNT(status)); + + /* On the last TD, transmit them all again. */ + if (dbri->descs[rd].next == -1) { + if (info->left > info->size) { + printk(KERN_WARNING + "%d bytes recorded in %d size buffer.\n", + info->left, info->size); + } + tasklet_schedule(&xmit_descs_task); + } + + /* Notify ALSA */ + if (spin_is_locked(&dbri->lock)) { + spin_unlock(&dbri->lock); + snd_pcm_period_elapsed(info->substream); + spin_lock(&dbri->lock); + } else + snd_pcm_period_elapsed(info->substream); +} + +static void dbri_process_one_interrupt(snd_dbri_t * dbri, int x) +{ + int val = D_INTR_GETVAL(x); + int channel = D_INTR_GETCHAN(x); + int command = D_INTR_GETCMD(x); + int code = D_INTR_GETCODE(x); +#ifdef DBRI_DEBUG + int rval = D_INTR_GETRVAL(x); +#endif + + if (channel == D_INTR_CMD) { + dprintk(D_CMD, "INTR: Command: %-5s Value:%d\n", + cmds[command], val); + } else { + dprintk(D_INT, "INTR: Chan:%d Code:%d Val:%#x\n", + channel, code, rval); + } + + if (channel == D_INTR_CMD && command == D_WAIT) { + dbri->wait_seen++; + return; + } + + switch (code) { + case D_INTR_BRDY: + reception_complete_intr(dbri, channel); + break; + case D_INTR_XCMP: + case D_INTR_MINT: + transmission_complete_intr(dbri, channel); + break; + case D_INTR_UNDR: + /* UNDR - Transmission underrun + * resend SDP command with clear pipe bit (C) set + */ + { + volatile s32 *cmd; + + int pipe = channel; + int td = dbri->pipes[pipe].desc; + + dbri->dma->desc[td].word4 = 0; + cmd = dbri_cmdlock(dbri, NoGetLock); + *(cmd++) = DBRI_CMD(D_SDP, 0, + dbri->pipes[pipe].sdp + | D_SDP_P | D_SDP_C | D_SDP_2SAME); + *(cmd++) = dbri->dma_dvma + dbri_dma_off(desc, td); + dbri_cmdsend(dbri, cmd); + } + break; + case D_INTR_FXDT: + /* FXDT - Fixed data change */ + if (dbri->pipes[channel].sdp & D_SDP_MSB) + val = reverse_bytes(val, dbri->pipes[channel].length); + + if (dbri->pipes[channel].recv_fixed_ptr) + *(dbri->pipes[channel].recv_fixed_ptr) = val; + break; + default: + if (channel != D_INTR_CMD) + printk(KERN_WARNING + "DBRI: Ignored Interrupt: %d (0x%x)\n", code, x); + } +} + +/* dbri_process_interrupt_buffer advances through the DBRI's interrupt + * buffer until it finds a zero word (indicating nothing more to do + * right now). Non-zero words require processing and are handed off + * to dbri_process_one_interrupt AFTER advancing the pointer. This + * order is important since we might recurse back into this function + * and need to make sure the pointer has been advanced first. + */ +static void dbri_process_interrupt_buffer(snd_dbri_t * dbri) +{ + s32 x; + + while ((x = dbri->dma->intr[dbri->dbri_irqp]) != 0) { + dbri->dma->intr[dbri->dbri_irqp] = 0; + dbri->dbri_irqp++; + if (dbri->dbri_irqp == (DBRI_NO_INTS * DBRI_INT_BLK)) + dbri->dbri_irqp = 1; + else if ((dbri->dbri_irqp & (DBRI_INT_BLK - 1)) == 0) + dbri->dbri_irqp++; + + dbri_process_one_interrupt(dbri, x); + } +} + +static irqreturn_t snd_dbri_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + snd_dbri_t *dbri = dev_id; + static int errcnt = 0; + int x; + + if (dbri == NULL) + return IRQ_NONE; + spin_lock(&dbri->lock); + + /* + * Read it, so the interrupt goes away. + */ + x = sbus_readl(dbri->regs + REG1); + + if (x & (D_MRR | D_MLE | D_LBG | D_MBE)) { + u32 tmp; + + if (x & D_MRR) + printk(KERN_ERR + "DBRI: Multiple Error Ack on SBus reg1=0x%x\n", + x); + if (x & D_MLE) + printk(KERN_ERR + "DBRI: Multiple Late Error on SBus reg1=0x%x\n", + x); + if (x & D_LBG) + printk(KERN_ERR + "DBRI: Lost Bus Grant on SBus reg1=0x%x\n", x); + if (x & D_MBE) + printk(KERN_ERR + "DBRI: Burst Error on SBus reg1=0x%x\n", x); + + /* Some of these SBus errors cause the chip's SBus circuitry + * to be disabled, so just re-enable and try to keep going. + * + * The only one I've seen is MRR, which will be triggered + * if you let a transmit pipe underrun, then try to CDP it. + * + * If these things persist, we should probably reset + * and re-init the chip. + */ + if ((++errcnt) % 10 == 0) { + dprintk(D_INT, "Interrupt errors exceeded.\n"); + dbri_reset(dbri); + } else { + tmp = sbus_readl(dbri->regs + REG0); + tmp &= ~(D_D); + sbus_writel(tmp, dbri->regs + REG0); + } + } + + dbri_process_interrupt_buffer(dbri); + + /* FIXME: Write 0 into regs to ACK interrupt */ + + spin_unlock(&dbri->lock); + + return IRQ_HANDLED; +} + +/**************************************************************************** + PCM Interface +****************************************************************************/ +static snd_pcm_hardware_t snd_dbri_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_MU_LAW | + SNDRV_PCM_FMTBIT_A_LAW | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_BE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (64 * 1024), + .period_bytes_min = 1, + .period_bytes_max = DBRI_TD_MAXCNT, + .periods_min = 1, + .periods_max = 1024, +}; + +static int snd_dbri_open(snd_pcm_substream_t * substream) +{ + snd_dbri_t *dbri = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream); + unsigned long flags; + + dprintk(D_USR, "open audio output.\n"); + runtime->hw = snd_dbri_pcm_hw; + + spin_lock_irqsave(&dbri->lock, flags); + info->substream = substream; + info->left = 0; + info->offset = 0; + info->dvma_buffer = 0; + info->pipe = -1; + spin_unlock_irqrestore(&dbri->lock, flags); + + cs4215_open(dbri); + + return 0; +} + +static int snd_dbri_close(snd_pcm_substream_t * substream) +{ + snd_dbri_t *dbri = snd_pcm_substream_chip(substream); + dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream); + + dprintk(D_USR, "close audio output.\n"); + info->substream = NULL; + info->left = 0; + info->offset = 0; + + return 0; +} + +static int snd_dbri_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_pcm_runtime_t *runtime = substream->runtime; + snd_dbri_t *dbri = snd_pcm_substream_chip(substream); + dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream); + int direction; + int ret; + + /* set sampling rate, audio format and number of channels */ + ret = cs4215_prepare(dbri, params_rate(hw_params), + params_format(hw_params), + params_channels(hw_params)); + if (ret != 0) + return ret; + + if ((ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params))) < 0) { + snd_printk(KERN_ERR "malloc_pages failed with %d\n", ret); + return ret; + } + + /* hw_params can get called multiple times. Only map the DMA once. + */ + if (info->dvma_buffer == 0) { + if (DBRI_STREAMNO(substream) == DBRI_PLAY) + direction = SBUS_DMA_TODEVICE; + else + direction = SBUS_DMA_FROMDEVICE; + + info->dvma_buffer = sbus_map_single(dbri->sdev, + runtime->dma_area, + params_buffer_bytes(hw_params), + direction); + } + + direction = params_buffer_bytes(hw_params); + dprintk(D_USR, "hw_params: %d bytes, dvma=%x\n", + direction, info->dvma_buffer); + return 0; +} + +static int snd_dbri_hw_free(snd_pcm_substream_t * substream) +{ + snd_dbri_t *dbri = snd_pcm_substream_chip(substream); + dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream); + int direction; + dprintk(D_USR, "hw_free.\n"); + + /* hw_free can get called multiple times. Only unmap the DMA once. + */ + if (info->dvma_buffer) { + if (DBRI_STREAMNO(substream) == DBRI_PLAY) + direction = SBUS_DMA_TODEVICE; + else + direction = SBUS_DMA_FROMDEVICE; + + sbus_unmap_single(dbri->sdev, info->dvma_buffer, + substream->runtime->buffer_size, direction); + info->dvma_buffer = 0; + } + info->pipe = -1; + + return snd_pcm_lib_free_pages(substream); +} + +static int snd_dbri_prepare(snd_pcm_substream_t * substream) +{ + snd_dbri_t *dbri = snd_pcm_substream_chip(substream); + dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int ret; + + info->size = snd_pcm_lib_buffer_bytes(substream); + if (DBRI_STREAMNO(substream) == DBRI_PLAY) + info->pipe = 4; /* Send pipe */ + else { + info->pipe = 6; /* Receive pipe */ + info->left = info->size; /* To trigger submittal */ + } + + spin_lock_irq(&dbri->lock); + + /* Setup the all the transmit/receive desciptors to cover the + * whole DMA buffer. + */ + ret = setup_descs(dbri, DBRI_STREAMNO(substream), + snd_pcm_lib_period_bytes(substream)); + + runtime->stop_threshold = DBRI_TD_MAXCNT / runtime->channels; + + spin_unlock_irq(&dbri->lock); + + dprintk(D_USR, "prepare audio output. %d bytes\n", info->size); + return ret; +} + +static int snd_dbri_trigger(snd_pcm_substream_t * substream, int cmd) +{ + snd_dbri_t *dbri = snd_pcm_substream_chip(substream); + dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + dprintk(D_USR, "start audio, period is %d bytes\n", + (int)snd_pcm_lib_period_bytes(substream)); + /* Enable & schedule the tasklet that re-submits the TDs. */ + xmit_descs_task.data = (unsigned long)dbri; + tasklet_schedule(&xmit_descs_task); + break; + case SNDRV_PCM_TRIGGER_STOP: + dprintk(D_USR, "stop audio.\n"); + /* Make the tasklet bail out immediately. */ + xmit_descs_task.data = 0; + reset_pipe(dbri, info->pipe); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static snd_pcm_uframes_t snd_dbri_pointer(snd_pcm_substream_t * substream) +{ + snd_dbri_t *dbri = snd_pcm_substream_chip(substream); + dbri_streaminfo_t *info = DBRI_STREAM(dbri, substream); + snd_pcm_uframes_t ret; + + ret = bytes_to_frames(substream->runtime, info->offset) + % substream->runtime->buffer_size; + dprintk(D_USR, "I/O pointer: %ld frames, %d bytes left.\n", + ret, info->left); + return ret; +} + +static snd_pcm_ops_t snd_dbri_ops = { + .open = snd_dbri_open, + .close = snd_dbri_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_dbri_hw_params, + .hw_free = snd_dbri_hw_free, + .prepare = snd_dbri_prepare, + .trigger = snd_dbri_trigger, + .pointer = snd_dbri_pointer, +}; + +static int __devinit snd_dbri_pcm(snd_dbri_t * dbri) +{ + snd_pcm_t *pcm; + int err; + + if ((err = snd_pcm_new(dbri->card, + /* ID */ "sun_dbri", + /* device */ 0, + /* 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); + + pcm->private_data = dbri; + pcm->info_flags = 0; + strcpy(pcm->name, dbri->card->shortname); + dbri->pcm = pcm; + + if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + 64 * 1024, 64 * 1024)) < 0) { + return err; + } + + return 0; +} + +/***************************************************************************** + Mixer interface +*****************************************************************************/ + +static int snd_cs4215_info_volume(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + if (kcontrol->private_value == DBRI_PLAY) { + uinfo->value.integer.max = DBRI_MAX_VOLUME; + } else { + uinfo->value.integer.max = DBRI_MAX_GAIN; + } + return 0; +} + +static int snd_cs4215_get_volume(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol); + dbri_streaminfo_t *info; + snd_assert(dbri != NULL, 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; + return 0; +} + +static int snd_cs4215_put_volume(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol); + dbri_streaminfo_t *info = &dbri->stream_info[kcontrol->private_value]; + unsigned long flags; + int changed = 0; + + if (info->left_gain != ucontrol->value.integer.value[0]) { + info->left_gain = ucontrol->value.integer.value[0]; + changed = 1; + } + if (info->right_gain != ucontrol->value.integer.value[1]) { + info->right_gain = ucontrol->value.integer.value[1]; + changed = 1; + } + if (changed == 1) { + /* First mute outputs, and wait 1/8000 sec (125 us) + * to make sure this takes. This avoids clicking noises. + */ + spin_lock_irqsave(&dbri->lock, flags); + + cs4215_setdata(dbri, 1); + udelay(125); + cs4215_setdata(dbri, 0); + + spin_unlock_irqrestore(&dbri->lock, flags); + } + return changed; +} + +static int snd_cs4215_info_single(snd_kcontrol_t * kcontrol, + snd_ctl_elem_info_t * 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_cs4215_get_single(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol); + int elem = kcontrol->private_value & 0xff; + 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 (elem < 4) { + ucontrol->value.integer.value[0] = + (dbri->mm.data[elem] >> shift) & mask; + } else { + ucontrol->value.integer.value[0] = + (dbri->mm.ctrl[elem - 4] >> shift) & mask; + } + + if (invert == 1) { + ucontrol->value.integer.value[0] = + mask - ucontrol->value.integer.value[0]; + } + return 0; +} + +static int snd_cs4215_put_single(snd_kcontrol_t * kcontrol, + snd_ctl_elem_value_t * ucontrol) +{ + snd_dbri_t *dbri = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int elem = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 1; + int changed = 0; + unsigned short val; + snd_assert(dbri != NULL, return -EINVAL); + + val = (ucontrol->value.integer.value[0] & mask); + if (invert == 1) + val = mask - val; + val <<= shift; + + if (elem < 4) { + dbri->mm.data[elem] = (dbri->mm.data[elem] & + ~(mask << shift)) | val; + changed = (val != dbri->mm.data[elem]); + } else { + dbri->mm.ctrl[elem - 4] = (dbri->mm.ctrl[elem - 4] & + ~(mask << shift)) | val; + changed = (val != dbri->mm.ctrl[elem - 4]); + } + + dprintk(D_GEN, "put_single: mask=0x%x, changed=%d, " + "mixer-value=%ld, mm-value=0x%x\n", + mask, changed, ucontrol->value.integer.value[0], + dbri->mm.data[elem & 3]); + + if (changed) { + /* First mute outputs, and wait 1/8000 sec (125 us) + * to make sure this takes. This avoids clicking noises. + */ + spin_lock_irqsave(&dbri->lock, flags); + + cs4215_setdata(dbri, 1); + udelay(125); + cs4215_setdata(dbri, 0); + + spin_unlock_irqrestore(&dbri->lock, flags); + } + return changed; +} + +/* Entries 0-3 map to the 4 data timeslots, entries 4-7 map to the 4 control + timeslots. Shift is the bit offset in the timeslot, mask defines the + number of bits. invert is a boolean for use with attenuation. + */ +#define CS4215_SINGLE(xname, entry, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_cs4215_info_single, \ + .get = snd_cs4215_get_single, .put = snd_cs4215_put_single, \ + .private_value = entry | (shift << 8) | (mask << 16) | (invert << 24) }, + +static snd_kcontrol_new_t dbri_controls[] __devinitdata = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Playback Volume", + .info = snd_cs4215_info_volume, + .get = snd_cs4215_get_volume, + .put = snd_cs4215_put_volume, + .private_value = DBRI_PLAY, + }, + CS4215_SINGLE("Headphone switch", 0, 7, 1, 0) + CS4215_SINGLE("Line out switch", 0, 6, 1, 0) + CS4215_SINGLE("Speaker switch", 1, 6, 1, 0) + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = snd_cs4215_info_volume, + .get = snd_cs4215_get_volume, + .put = snd_cs4215_put_volume, + .private_value = DBRI_REC, + }, + /* FIXME: mic/line switch */ + CS4215_SINGLE("Line in switch", 2, 4, 1, 0) + CS4215_SINGLE("High Pass Filter switch", 5, 7, 1, 0) + CS4215_SINGLE("Monitor Volume", 3, 4, 0xf, 1) + CS4215_SINGLE("Mic boost", 4, 4, 1, 1) +}; + +#define NUM_CS4215_CONTROLS (sizeof(dbri_controls)/sizeof(snd_kcontrol_new_t)) + +static int __init snd_dbri_mixer(snd_dbri_t * dbri) +{ + snd_card_t *card; + int idx, err; + + snd_assert(dbri != NULL && dbri->card != NULL, return -EINVAL); + + card = dbri->card; + strcpy(card->mixername, card->shortname); + + for (idx = 0; idx < NUM_CS4215_CONTROLS; idx++) { + if ((err = snd_ctl_add(card, + snd_ctl_new1(&dbri_controls[idx], + dbri))) < 0) + return err; + } + + for (idx = DBRI_REC; idx < DBRI_NO_STREAMS; idx++) { + dbri->stream_info[idx].left_gain = 0; + dbri->stream_info[idx].right_gain = 0; + dbri->stream_info[idx].balance = DBRI_MID_BALANCE; + } + + return 0; +} + +/**************************************************************************** + /proc interface +****************************************************************************/ +static void dbri_regs_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer) +{ + snd_dbri_t *dbri = entry->private_data; + + snd_iprintf(buffer, "REG0: 0x%x\n", sbus_readl(dbri->regs + REG0)); + snd_iprintf(buffer, "REG2: 0x%x\n", sbus_readl(dbri->regs + REG2)); + snd_iprintf(buffer, "REG8: 0x%x\n", sbus_readl(dbri->regs + REG8)); + snd_iprintf(buffer, "REG9: 0x%x\n", sbus_readl(dbri->regs + REG9)); +} + +#ifdef DBRI_DEBUG +static void dbri_debug_read(snd_info_entry_t * entry, + snd_info_buffer_t * buffer) +{ + snd_dbri_t *dbri = entry->private_data; + int pipe; + snd_iprintf(buffer, "debug=%d\n", dbri_debug); + + snd_iprintf(buffer, "CHI pipe in=%d, out=%d\n", + dbri->chi_in_pipe, dbri->chi_out_pipe); + for (pipe = 0; pipe < 32; pipe++) { + if (pipe_active(dbri, pipe)) { + struct dbri_pipe *pptr = &dbri->pipes[pipe]; + snd_iprintf(buffer, + "Pipe %d: %s SDP=0x%x desc=%d, " + "len=%d @ %d prev: %d next %d\n", + pipe, + (pptr->direction == + PIPEinput ? "input" : "output"), pptr->sdp, + pptr->desc, pptr->length, pptr->cycle, + pptr->prevpipe, pptr->nextpipe); + } + } +} + +static void dbri_debug_write(snd_info_entry_t * entry, + snd_info_buffer_t * buffer) +{ + char line[80]; + int i; + + if (snd_info_get_line(buffer, line, 80) == 0) { + sscanf(line, "%d\n", &i); + dbri_debug = i & 0x3f; + } +} +#endif + +void snd_dbri_proc(snd_dbri_t * dbri) +{ + snd_info_entry_t *entry; + int err; + + err = snd_card_proc_new(dbri->card, "regs", &entry); + snd_info_set_text_ops(entry, dbri, 1024, dbri_regs_read); + +#ifdef DBRI_DEBUG + err = snd_card_proc_new(dbri->card, "debug", &entry); + snd_info_set_text_ops(entry, dbri, 4096, dbri_debug_read); + entry->mode = S_IFREG | S_IRUGO | S_IWUSR; /* Writable for root */ + entry->c.text.write_size = 256; + entry->c.text.write = dbri_debug_write; +#endif +} + +/* +**************************************************************************** +**************************** Initialization ******************************** +**************************************************************************** +*/ +static void snd_dbri_free(snd_dbri_t * dbri); + +static int __init snd_dbri_create(snd_card_t * card, + struct sbus_dev *sdev, + struct linux_prom_irqs *irq, int dev) +{ + snd_dbri_t *dbri = card->private_data; + int err; + + spin_lock_init(&dbri->lock); + dbri->card = card; + dbri->sdev = sdev; + dbri->irq = irq->pri; + dbri->dbri_version = sdev->prom_name[9]; + + dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma), + &dbri->dma_dvma); + 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"); + 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); + return -EIO; + } + + err = request_irq(dbri->irq, snd_dbri_interrupt, SA_SHIRQ, + "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); + return err; + } + + /* Do low level initialization of the DBRI and CS4215 chips */ + dbri_initialize(dbri); + err = cs4215_init(dbri); + if (err) { + snd_dbri_free(dbri); + return err; + } + + dbri->next = dbri_list; + dbri_list = dbri; + + return 0; +} + +static void snd_dbri_free(snd_dbri_t * dbri) +{ + dprintk(D_GEN, "snd_dbri_free\n"); + dbri_reset(dbri); + + if (dbri->irq) + free_irq(dbri->irq, dbri); + + if (dbri->regs) + sbus_iounmap(dbri->regs, dbri->regs_size); + + if (dbri->dma) + sbus_free_consistent(dbri->sdev, sizeof(struct dbri_dma), + (void *)dbri->dma, dbri->dma_dvma); +} + +static int __init dbri_attach(int prom_node, struct sbus_dev *sdev) +{ + snd_dbri_t *dbri; + struct linux_prom_irqs irq; + struct resource *rp; + snd_card_t *card; + static int dev = 0; + int err; + + if (sdev->prom_name[9] < 'e') { + printk(KERN_ERR "DBRI: unsupported chip version %c found.\n", + sdev->prom_name[9]); + return -EIO; + } + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + prom_getproperty(prom_node, "intr", (char *)&irq, sizeof(irq)); + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(snd_dbri_t)); + if (card == NULL) + return -ENOMEM; + + strcpy(card->driver, "DBRI"); + strcpy(card->shortname, "Sun DBRI"); + rp = &sdev->resource[0]; + sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s", + card->shortname, + rp->flags & 0xffL, rp->start, __irq_itoa(irq.pri)); + + if ((err = snd_dbri_create(card, sdev, &irq, dev)) < 0) { + snd_card_free(card); + return err; + } + + dbri = (snd_dbri_t *) card->private_data; + if ((err = snd_dbri_pcm(dbri)) < 0) { + snd_dbri_free(dbri); + snd_card_free(card); + return err; + } + + if ((err = snd_dbri_mixer(dbri)) < 0) { + snd_dbri_free(dbri); + snd_card_free(card); + return err; + } + + /* /proc file handling */ + snd_dbri_proc(dbri); + + if ((err = snd_card_register(card)) < 0) { + snd_dbri_free(dbri); + snd_card_free(card); + return err; + } + + printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n", + dev, dbri->regs, + dbri->irq, dbri->dbri_version, dbri->mm.version); + dev++; + + return 0; +} + +/* Probe for the dbri chip and then attach the driver. */ +static int __init dbri_init(void) +{ + struct sbus_bus *sbus; + struct sbus_dev *sdev; + int found = 0; + + /* Probe each SBUS for the DBRI chip(s). */ + for_all_sbusdev(sdev, sbus) { + /* + * The version is coded in the last character + */ + if (!strncmp(sdev->prom_name, "SUNW,DBRI", 9)) { + dprintk(D_GEN, "DBRI: Found %s in SBUS slot %d\n", + sdev->prom_name, sdev->slot); + + if (dbri_attach(sdev->prom_node, sdev) == 0) + found++; + } + } + + return (found > 0) ? 0 : -EIO; +} + +static void __exit dbri_exit(void) +{ + snd_dbri_t *this = dbri_list; + + while (this != NULL) { + snd_dbri_t *next = this->next; + snd_card_t *card = this->card; + + snd_dbri_free(this); + snd_card_free(card); + this = next; + } + dbri_list = NULL; +} + +module_init(dbri_init); +module_exit(dbri_exit); diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c index f13b038329eb..751bf1272af3 100644 --- a/sound/synth/emux/emux_synth.c +++ b/sound/synth/emux/emux_synth.c @@ -98,7 +98,6 @@ snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan) vp = emu->ops.get_voice(emu, port); if (vp == NULL || vp->ch < 0) continue; - snd_assert(vp->emu != NULL && vp->hw != NULL, return); if (STATE_IS_PLAYING(vp->state)) emu->ops.terminate(vp); diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index a75695045f29..5aa5fe651a8a 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -41,10 +41,12 @@ #include <sound/driver.h> #include <linux/bitops.h> #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/list.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/usb.h> +#include <linux/vmalloc.h> #include <linux/moduleparam.h> #include <sound/core.h> #include <sound/info.h> @@ -79,7 +81,7 @@ module_param_array(vid, int, NULL, 0444); MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); module_param_array(pid, int, NULL, 0444); MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); -module_param(nrpacks, int, 0444); +module_param(nrpacks, int, 0644); MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); module_param(async_unlink, bool, 0444); MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); @@ -97,7 +99,7 @@ MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); #define MAX_PACKS 10 #define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ -#define MAX_URBS 5 /* max. 20ms long packets */ +#define MAX_URBS 8 #define SYNC_URBS 4 /* always four urbs for sync */ #define MIN_PACKS_URB 1 /* minimum 1 packet per urb */ @@ -126,11 +128,10 @@ struct audioformat { struct snd_urb_ctx { struct urb *urb; + unsigned int buffer_size; /* size of data buffer, if data URB */ snd_usb_substream_t *subs; int index; /* index for urb array */ int packets; /* number of packets per urb */ - int transfer; /* transferred size */ - char *buf; /* buffer for capture */ }; struct snd_urb_ops { @@ -153,6 +154,7 @@ struct snd_usb_substream { unsigned int format; /* USB data format */ unsigned int datapipe; /* the data i/o pipe */ unsigned int syncpipe; /* 1 - async out or adaptive in */ + unsigned int datainterval; /* log_2 of data packet interval */ unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */ @@ -164,12 +166,11 @@ struct snd_usb_substream { unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int fill_max: 1; /* fill max packet size always */ unsigned int fmt_type; /* USB audio format type (1-3) */ + unsigned int packs_per_ms; /* packets per millisecond (for playback) */ unsigned int running: 1; /* running status */ - unsigned int hwptr; /* free frame position in the buffer (only for playback) */ unsigned int hwptr_done; /* processed frame position in the buffer */ - unsigned int transfer_sched; /* scheduled frames since last period (for playback) */ unsigned int transfer_done; /* processed frames since last period update */ unsigned long active_mask; /* bitmask of active urbs */ unsigned long unlink_mask; /* bitmask of unlinked urbs */ @@ -177,13 +178,14 @@ struct snd_usb_substream { unsigned int nurbs; /* # urbs */ snd_urb_ctx_t dataurb[MAX_URBS]; /* data urb table */ snd_urb_ctx_t syncurb[SYNC_URBS]; /* sync urb table */ - char syncbuf[SYNC_URBS * 4]; /* sync buffer; it's so small - let's get static */ - char *tmpbuf; /* temporary buffer for playback */ + char *syncbuf; /* sync buffer for all sync URBs */ + dma_addr_t sync_dma; /* DMA address of syncbuf */ u64 formats; /* format bitmasks (all or'ed) */ unsigned int num_formats; /* number of supported audio formats (list) */ struct list_head fmt_list; /* format list */ spinlock_t lock; + struct tasklet_struct start_period_elapsed; /* for start trigger */ struct snd_urb_ops ops; /* callbacks (must be filled at init) */ }; @@ -212,7 +214,7 @@ static snd_usb_audio_t *usb_chip[SNDRV_CARDS]; * convert a sampling rate into our full speed format (fs/1000 in Q16.16) * this will overflow at approx 524 kHz */ -inline static unsigned get_usb_full_speed_rate(unsigned int rate) +static inline unsigned get_usb_full_speed_rate(unsigned int rate) { return ((rate << 13) + 62) / 125; } @@ -221,19 +223,19 @@ inline static unsigned get_usb_full_speed_rate(unsigned int rate) * convert a sampling rate into USB high speed format (fs/8000 in Q16.16) * this will overflow at approx 4 MHz */ -inline static unsigned get_usb_high_speed_rate(unsigned int rate) +static inline unsigned get_usb_high_speed_rate(unsigned int rate) { return ((rate << 10) + 62) / 125; } /* convert our full speed USB rate into sampling rate in Hz */ -inline static unsigned get_full_speed_hz(unsigned int usb_rate) +static inline unsigned get_full_speed_hz(unsigned int usb_rate) { return (usb_rate * 125 + (1 << 12)) >> 13; } /* convert our high speed USB rate into sampling rate in Hz */ -inline static unsigned get_high_speed_hz(unsigned int usb_rate) +static inline unsigned get_high_speed_hz(unsigned int usb_rate) { return (usb_rate * 125 + (1 << 9)) >> 10; } @@ -310,27 +312,17 @@ static int prepare_capture_urb(snd_usb_substream_t *subs, struct urb *urb) { int i, offs; - unsigned long flags; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; offs = 0; urb->dev = ctx->subs->dev; /* we need to set this at each time */ - urb->number_of_packets = 0; - spin_lock_irqsave(&subs->lock, flags); for (i = 0; i < ctx->packets; i++) { urb->iso_frame_desc[i].offset = offs; urb->iso_frame_desc[i].length = subs->curpacksize; offs += subs->curpacksize; - urb->number_of_packets++; - subs->transfer_sched += subs->curframesize; - if (subs->transfer_sched >= runtime->period_size) { - subs->transfer_sched -= runtime->period_size; - break; - } } - spin_unlock_irqrestore(&subs->lock, flags); - urb->transfer_buffer = ctx->buf; urb->transfer_buffer_length = offs; + urb->number_of_packets = ctx->packets; #if 0 // for check if (! urb->bandwidth) { int bustime; @@ -358,6 +350,7 @@ static int retire_capture_urb(snd_usb_substream_t *subs, unsigned char *cp; int i; unsigned int stride, len, oldptr; + int period_elapsed = 0; stride = runtime->frame_bits >> 3; @@ -377,6 +370,10 @@ static int retire_capture_urb(snd_usb_substream_t *subs, if (subs->hwptr_done >= runtime->buffer_size) subs->hwptr_done -= runtime->buffer_size; subs->transfer_done += len; + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + period_elapsed = 1; + } spin_unlock_irqrestore(&subs->lock, flags); /* copy a data chunk */ if (oldptr + len > runtime->buffer_size) { @@ -387,15 +384,9 @@ static int retire_capture_urb(snd_usb_substream_t *subs, } else { memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); } - /* update the pointer, call callback if necessary */ - spin_lock_irqsave(&subs->lock, flags); - if (subs->transfer_done >= runtime->period_size) { - subs->transfer_done -= runtime->period_size; - spin_unlock_irqrestore(&subs->lock, flags); - snd_pcm_period_elapsed(subs->pcm_substream); - } else - spin_unlock_irqrestore(&subs->lock, flags); } + if (period_elapsed) + snd_pcm_period_elapsed(subs->pcm_substream); return 0; } @@ -491,12 +482,10 @@ static int retire_playback_sync_urb_hs(snd_usb_substream_t *subs, /* * prepare urb for playback data pipe * - * we copy the data directly from the pcm buffer. - * the current position to be copied is held in hwptr field. - * since a urb can handle only a single linear buffer, if the total - * transferred area overflows the buffer boundary, we cannot send - * it directly from the buffer. thus the data is once copied to - * a temporary buffer and urb points to that. + * Since a URB can handle only a single linear buffer, we must use double + * buffering when the data to be transferred overflows the buffer boundary. + * To avoid inconsistencies when updating hwptr_done, we use double buffering + * for all URBs. */ static int prepare_playback_urb(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, @@ -505,6 +494,7 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, int i, stride, offs; unsigned int counts; unsigned long flags; + int period_elapsed = 0; snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; stride = runtime->frame_bits >> 3; @@ -518,7 +508,8 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, if (subs->fill_max) counts = subs->maxframesize; /* fixed */ else { - subs->phase = (subs->phase & 0xffff) + subs->freqm; + subs->phase = (subs->phase & 0xffff) + + (subs->freqm << subs->datainterval); counts = subs->phase >> 16; if (counts > subs->maxframesize) counts = subs->maxframesize; @@ -528,80 +519,85 @@ static int prepare_playback_urb(snd_usb_substream_t *subs, urb->iso_frame_desc[i].length = counts * stride; offs += counts; urb->number_of_packets++; - subs->transfer_sched += counts; - if (subs->transfer_sched >= runtime->period_size) { - subs->transfer_sched -= runtime->period_size; + subs->transfer_done += counts; + if (subs->transfer_done >= runtime->period_size) { + subs->transfer_done -= runtime->period_size; + period_elapsed = 1; if (subs->fmt_type == USB_FORMAT_TYPE_II) { - if (subs->transfer_sched > 0) { - /* FIXME: fill-max mode is not supported yet */ - offs -= subs->transfer_sched; - counts -= subs->transfer_sched; - urb->iso_frame_desc[i].length = counts * stride; - subs->transfer_sched = 0; + if (subs->transfer_done > 0) { + /* FIXME: fill-max mode is not + * supported yet */ + offs -= subs->transfer_done; + counts -= subs->transfer_done; + urb->iso_frame_desc[i].length = + counts * stride; + subs->transfer_done = 0; } i++; if (i < ctx->packets) { /* add a transfer delimiter */ - urb->iso_frame_desc[i].offset = offs * stride; + urb->iso_frame_desc[i].offset = + offs * stride; urb->iso_frame_desc[i].length = 0; urb->number_of_packets++; } + break; } - break; } + /* finish at the frame boundary at/after the period boundary */ + if (period_elapsed && + (i & (subs->packs_per_ms - 1)) == subs->packs_per_ms - 1) + break; } - if (subs->hwptr + offs > runtime->buffer_size) { - /* err, the transferred area goes over buffer boundary. - * copy the data to the temp buffer. - */ - int len; - len = runtime->buffer_size - subs->hwptr; - urb->transfer_buffer = subs->tmpbuf; - memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * stride, len * stride); - memcpy(subs->tmpbuf + len * stride, runtime->dma_area, (offs - len) * stride); - subs->hwptr += offs; - subs->hwptr -= runtime->buffer_size; + if (subs->hwptr_done + offs > runtime->buffer_size) { + /* err, the transferred area goes over buffer boundary. */ + unsigned int len = runtime->buffer_size - subs->hwptr_done; + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done * stride, + len * stride); + memcpy(urb->transfer_buffer + len * stride, + runtime->dma_area, + (offs - len) * stride); } else { - /* set the buffer pointer */ - urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride; - subs->hwptr += offs; - if (subs->hwptr == runtime->buffer_size) - subs->hwptr = 0; + memcpy(urb->transfer_buffer, + runtime->dma_area + subs->hwptr_done * stride, + offs * stride); } + subs->hwptr_done += offs; + if (subs->hwptr_done >= runtime->buffer_size) + subs->hwptr_done -= runtime->buffer_size; spin_unlock_irqrestore(&subs->lock, flags); urb->transfer_buffer_length = offs * stride; - ctx->transfer = offs; - + if (period_elapsed) { + if (likely(subs->running)) + snd_pcm_period_elapsed(subs->pcm_substream); + else + tasklet_hi_schedule(&subs->start_period_elapsed); + } return 0; } /* * process after playback data complete - * - * update the current position and call callback if a period is processed. + * - nothing to do */ static int retire_playback_urb(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *urb) { - unsigned long flags; - snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context; - - spin_lock_irqsave(&subs->lock, flags); - subs->transfer_done += ctx->transfer; - subs->hwptr_done += ctx->transfer; - ctx->transfer = 0; - if (subs->hwptr_done >= runtime->buffer_size) - subs->hwptr_done -= runtime->buffer_size; - if (subs->transfer_done >= runtime->period_size) { - subs->transfer_done -= runtime->period_size; - spin_unlock_irqrestore(&subs->lock, flags); - snd_pcm_period_elapsed(subs->pcm_substream); - } else - spin_unlock_irqrestore(&subs->lock, flags); return 0; } +/* + * Delay the snd_pcm_period_elapsed() call until after the start trigger + * callback so that we're not longer in the substream's lock. + */ +static void start_period_elapsed(unsigned long data) +{ + snd_usb_substream_t *subs = (snd_usb_substream_t *)data; + snd_pcm_period_elapsed(subs->pcm_substream); +} + /* */ @@ -681,6 +677,42 @@ static void snd_complete_sync_urb(struct urb *urb, struct pt_regs *regs) } +/* get the physical page pointer at the given offset */ +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +/* allocate virtual buffer; may be called more than once */ +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + if (runtime->dma_area) { + if (runtime->dma_bytes >= size) + return 0; /* already large enough */ + vfree_nocheck(runtime->dma_area); + } + runtime->dma_area = vmalloc_nocheck(size); + if (! runtime->dma_area) + return -ENOMEM; + runtime->dma_bytes = size; + return 0; +} + +/* free virtual buffer; may be called more than once */ +static int snd_pcm_free_vmalloc_buffer(snd_pcm_substream_t *subs) +{ + snd_pcm_runtime_t *runtime = subs->runtime; + if (runtime->dma_area) { + vfree_nocheck(runtime->dma_area); + runtime->dma_area = NULL; + } + return 0; +} + + /* * unlink active urbs. */ @@ -790,7 +822,7 @@ static int start_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime) */ static int wait_clear_urbs(snd_usb_substream_t *subs) { - int timeout = HZ; + unsigned long end_time = jiffies + msecs_to_jiffies(1000); unsigned int i; int alive; @@ -810,7 +842,7 @@ static int wait_clear_urbs(snd_usb_substream_t *subs) break; set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); - } while (--timeout > 0); + } while (time_before(jiffies, end_time)); if (alive) snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); return 0; @@ -822,8 +854,14 @@ static int wait_clear_urbs(snd_usb_substream_t *subs) */ static snd_pcm_uframes_t snd_usb_pcm_pointer(snd_pcm_substream_t *substream) { - snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data; - return subs->hwptr_done; + snd_usb_substream_t *subs; + snd_pcm_uframes_t hwptr_done; + + subs = (snd_usb_substream_t *)substream->runtime->private_data; + spin_lock(&subs->lock); + hwptr_done = subs->hwptr_done; + spin_unlock(&subs->lock); + return hwptr_done; } @@ -856,11 +894,13 @@ static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd) static void release_urb_ctx(snd_urb_ctx_t *u) { if (u->urb) { + if (u->buffer_size) + usb_buffer_free(u->subs->dev, u->buffer_size, + u->urb->transfer_buffer, + u->urb->transfer_dma); usb_free_urb(u->urb); u->urb = NULL; } - kfree(u->buf); - u->buf = NULL; } /* @@ -878,8 +918,9 @@ static void release_substream_urbs(snd_usb_substream_t *subs, int force) release_urb_ctx(&subs->dataurb[i]); for (i = 0; i < SYNC_URBS; i++) release_urb_ctx(&subs->syncurb[i]); - kfree(subs->tmpbuf); - subs->tmpbuf = NULL; + usb_buffer_free(subs->dev, SYNC_URBS * 4, + subs->syncbuf, subs->sync_dma); + subs->syncbuf = NULL; subs->nurbs = 0; } @@ -891,7 +932,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by { unsigned int maxsize, n, i; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - unsigned int npacks[MAX_URBS], urb_packs, total_packs; + unsigned int npacks[MAX_URBS], urb_packs, total_packs, packs_per_ms; /* calculate the frequency in 16.16 format */ if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) @@ -899,40 +940,59 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by else subs->freqn = get_usb_high_speed_rate(rate); subs->freqm = subs->freqn; - subs->freqmax = subs->freqn + (subs->freqn >> 2); /* max. allowed frequency */ - subs->phase = 0; - - /* calculate the max. size of packet */ - maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) >> 16; - if (subs->maxpacksize && maxsize > subs->maxpacksize) { - //snd_printd(KERN_DEBUG "maxsize %d is greater than defined size %d\n", - // maxsize, subs->maxpacksize); + /* calculate max. frequency */ + if (subs->maxpacksize) { + /* whatever fits into a max. size packet */ maxsize = subs->maxpacksize; + subs->freqmax = (maxsize / (frame_bits >> 3)) + << (16 - subs->datainterval); + } else { + /* no max. packet size: just take 25% higher than nominal */ + subs->freqmax = subs->freqn + (subs->freqn >> 2); + maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3)) + >> (16 - subs->datainterval); } + subs->phase = 0; if (subs->fill_max) subs->curpacksize = subs->maxpacksize; else subs->curpacksize = maxsize; - if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) - urb_packs = nrpacks; + if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) + packs_per_ms = 8 >> subs->datainterval; else - urb_packs = nrpacks * 8; + packs_per_ms = 1; + subs->packs_per_ms = packs_per_ms; - /* allocate a temporary buffer for playback */ if (is_playback) { - subs->tmpbuf = kmalloc(maxsize * urb_packs, GFP_KERNEL); - if (! subs->tmpbuf) { - snd_printk(KERN_ERR "cannot malloc tmpbuf\n"); - return -ENOMEM; - } - } + urb_packs = nrpacks; + urb_packs = max(urb_packs, (unsigned int)MIN_PACKS_URB); + urb_packs = min(urb_packs, (unsigned int)MAX_PACKS); + } else + urb_packs = 1; + urb_packs *= packs_per_ms; /* decide how many packets to be used */ - total_packs = (period_bytes + maxsize - 1) / maxsize; - if (total_packs < 2 * MIN_PACKS_URB) - total_packs = 2 * MIN_PACKS_URB; + if (is_playback) { + unsigned int minsize; + /* determine how small a packet can be */ + minsize = (subs->freqn >> (16 - subs->datainterval)) + * (frame_bits >> 3); + /* with sync from device, assume it can be 12% lower */ + if (subs->syncpipe) + minsize -= minsize >> 3; + minsize = max(minsize, 1u); + total_packs = (period_bytes + minsize - 1) / minsize; + /* round up to multiple of packs_per_ms */ + total_packs = (total_packs + packs_per_ms - 1) + & ~(packs_per_ms - 1); + /* we need at least two URBs for queueing */ + if (total_packs < 2 * MIN_PACKS_URB * packs_per_ms) + total_packs = 2 * MIN_PACKS_URB * packs_per_ms; + } else { + total_packs = MAX_URBS * urb_packs; + } subs->nurbs = (total_packs + urb_packs - 1) / urb_packs; if (subs->nurbs > MAX_URBS) { /* too much... */ @@ -951,7 +1011,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by subs->nurbs = 2; npacks[0] = (total_packs + 1) / 2; npacks[1] = total_packs - npacks[0]; - } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB) { + } else if (npacks[subs->nurbs-1] < MIN_PACKS_URB * packs_per_ms) { /* the last packet is too small.. */ if (subs->nurbs > 2) { /* merge to the first one */ @@ -970,49 +1030,45 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by snd_urb_ctx_t *u = &subs->dataurb[i]; u->index = i; u->subs = subs; - u->transfer = 0; u->packets = npacks[i]; + u->buffer_size = maxsize * u->packets; if (subs->fmt_type == USB_FORMAT_TYPE_II) u->packets++; /* for transfer delimiter */ - if (! is_playback) { - /* allocate a capture buffer per urb */ - u->buf = kmalloc(maxsize * u->packets, GFP_KERNEL); - if (! u->buf) { - release_substream_urbs(subs, 0); - return -ENOMEM; - } - } u->urb = usb_alloc_urb(u->packets, GFP_KERNEL); - if (! u->urb) { - release_substream_urbs(subs, 0); - return -ENOMEM; - } - u->urb->dev = subs->dev; + if (! u->urb) + goto out_of_memory; + u->urb->transfer_buffer = + usb_buffer_alloc(subs->dev, u->buffer_size, GFP_KERNEL, + &u->urb->transfer_dma); + if (! u->urb->transfer_buffer) + goto out_of_memory; u->urb->pipe = subs->datapipe; - u->urb->transfer_flags = URB_ISO_ASAP; - u->urb->number_of_packets = u->packets; - u->urb->interval = 1; + u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + u->urb->interval = 1 << subs->datainterval; u->urb->context = u; u->urb->complete = snd_usb_complete_callback(snd_complete_urb); } if (subs->syncpipe) { /* allocate and initialize sync urbs */ + subs->syncbuf = usb_buffer_alloc(subs->dev, SYNC_URBS * 4, + GFP_KERNEL, &subs->sync_dma); + if (! subs->syncbuf) + goto out_of_memory; for (i = 0; i < SYNC_URBS; i++) { snd_urb_ctx_t *u = &subs->syncurb[i]; u->index = i; u->subs = subs; u->packets = 1; u->urb = usb_alloc_urb(1, GFP_KERNEL); - if (! u->urb) { - release_substream_urbs(subs, 0); - return -ENOMEM; - } + if (! u->urb) + goto out_of_memory; u->urb->transfer_buffer = subs->syncbuf + i * 4; + u->urb->transfer_dma = subs->sync_dma + i * 4; u->urb->transfer_buffer_length = 4; - u->urb->dev = subs->dev; u->urb->pipe = subs->syncpipe; - u->urb->transfer_flags = URB_ISO_ASAP; + u->urb->transfer_flags = URB_ISO_ASAP | + URB_NO_TRANSFER_DMA_MAP; u->urb->number_of_packets = 1; u->urb->interval = 1 << subs->syncinterval; u->urb->context = u; @@ -1020,6 +1076,10 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by } } return 0; + +out_of_memory: + release_substream_urbs(subs, 0); + return -ENOMEM; } @@ -1195,6 +1255,12 @@ static int set_format(snd_usb_substream_t *subs, struct audioformat *fmt) subs->datapipe = usb_sndisocpipe(dev, ep); else subs->datapipe = usb_rcvisocpipe(dev, ep); + if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH && + get_endpoint(alts, 0)->bInterval >= 1 && + get_endpoint(alts, 0)->bInterval <= 4) + subs->datainterval = get_endpoint(alts, 0)->bInterval - 1; + else + subs->datainterval = 0; subs->syncpipe = subs->syncinterval = 0; subs->maxpacksize = fmt->maxpacksize; subs->fill_max = 0; @@ -1282,7 +1348,8 @@ static int snd_usb_hw_params(snd_pcm_substream_t *substream, unsigned int channels, rate, format; int ret, changed; - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); if (ret < 0) return ret; @@ -1338,7 +1405,7 @@ static int snd_usb_hw_free(snd_pcm_substream_t *substream) subs->cur_rate = 0; subs->period_bytes = 0; release_substream_urbs(subs, 0); - return snd_pcm_lib_free_pages(substream); + return snd_pcm_free_vmalloc_buffer(substream); } /* @@ -1361,9 +1428,7 @@ static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream) subs->curframesize = bytes_to_frames(runtime, subs->curpacksize); /* reset the pointer */ - subs->hwptr = 0; subs->hwptr_done = 0; - subs->transfer_sched = 0; subs->transfer_done = 0; subs->phase = 0; @@ -1379,7 +1444,7 @@ static snd_pcm_hardware_t snd_usb_playback = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), - .buffer_bytes_max = (128*1024), + .buffer_bytes_max = (256*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, @@ -1391,7 +1456,7 @@ static snd_pcm_hardware_t snd_usb_capture = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), - .buffer_bytes_max = (128*1024), + .buffer_bytes_max = (256*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 2, @@ -1783,6 +1848,7 @@ static snd_pcm_ops_t snd_usb_playback_ops = { .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_pcm_trigger, .pointer = snd_usb_pcm_pointer, + .page = snd_pcm_get_vmalloc_page, }; static snd_pcm_ops_t snd_usb_capture_ops = { @@ -1794,6 +1860,7 @@ static snd_pcm_ops_t snd_usb_capture_ops = { .prepare = snd_usb_pcm_prepare, .trigger = snd_usb_pcm_trigger, .pointer = snd_usb_pcm_pointer, + .page = snd_pcm_get_vmalloc_page, }; @@ -2010,6 +2077,9 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat INIT_LIST_HEAD(&subs->fmt_list); spin_lock_init(&subs->lock); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tasklet_init(&subs->start_period_elapsed, start_period_elapsed, + (unsigned long)subs); subs->stream = as; subs->direction = stream; @@ -2018,10 +2088,6 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat subs->ops = audio_urb_ops[stream]; else subs->ops = audio_urb_ops_high_speed[stream]; - snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - 64 * 1024, 128 * 1024); snd_pcm_set_ops(as->pcm, stream, stream == SNDRV_PCM_STREAM_PLAYBACK ? &snd_usb_playback_ops : &snd_usb_capture_ops); @@ -2067,7 +2133,6 @@ static void snd_usb_audio_pcm_free(snd_pcm_t *pcm) snd_usb_stream_t *stream = pcm->private_data; if (stream) { stream->pcm = NULL; - snd_pcm_lib_preallocate_free_for_all(pcm); snd_usb_audio_stream_free(stream); } } @@ -2397,10 +2462,9 @@ static int parse_audio_format(snd_usb_audio_t *chip, struct audioformat *fp, if (chip->usb_id == USB_ID(0x041e, 0x3000) || chip->usb_id == USB_ID(0x041e, 0x3020)) { if (fmt[3] == USB_FORMAT_TYPE_I && - stream == SNDRV_PCM_STREAM_PLAYBACK && fp->rates != SNDRV_PCM_RATE_48000 && fp->rates != SNDRV_PCM_RATE_96000) - return -1; /* use 48k only */ + return -1; } #endif return 0; @@ -2492,8 +2556,10 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no) fp->altset_idx = i; fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress; fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; - /* FIXME: decode wMaxPacketSize of high bandwith endpoints */ fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); + if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) + fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) + * (fp->maxpacksize & 0x7ff); fp->attributes = csep[3]; /* some quirks for attributes here */ @@ -2723,7 +2789,8 @@ static int create_standard_interface_quirk(snd_usb_audio_t *chip, * to detect the sample rate is by looking at wMaxPacketSize. */ static int create_ua700_ua25_quirk(snd_usb_audio_t *chip, - struct usb_interface *iface) + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) { static const struct audioformat ua_format = { .format = SNDRV_PCM_FORMAT_S24_3LE, @@ -2814,7 +2881,9 @@ static int create_ua700_ua25_quirk(snd_usb_audio_t *chip, /* * Create a stream for an Edirol UA-1000 interface. */ -static int create_ua1000_quirk(snd_usb_audio_t *chip, struct usb_interface *iface) +static int create_ua1000_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) { static const struct audioformat ua1000_format = { .format = SNDRV_PCM_FORMAT_S32_LE, @@ -2891,6 +2960,13 @@ static int create_composite_quirk(snd_usb_audio_t *chip, return 0; } +static int ignore_interface_quirk(snd_usb_audio_t *chip, + struct usb_interface *iface, + const snd_usb_audio_quirk_t *quirk) +{ + return 0; +} + /* * boot quirks @@ -2926,8 +3002,6 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev) { -#if 0 - /* TODO: enable this when high speed synchronization actually works */ u8 buf = 1; snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a, @@ -2939,7 +3013,6 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev) 1, 2000, NULL, 0, 1000); return -ENODEV; } -#endif return 0; } @@ -2956,28 +3029,28 @@ static int snd_usb_create_quirk(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *quirk) { - switch (quirk->type) { - case QUIRK_MIDI_FIXED_ENDPOINT: - case QUIRK_MIDI_YAMAHA: - case QUIRK_MIDI_MIDIMAN: - case QUIRK_MIDI_NOVATION: - case QUIRK_MIDI_MOTU: - case QUIRK_MIDI_EMAGIC: - return snd_usb_create_midi_interface(chip, iface, quirk); - case QUIRK_COMPOSITE: - return create_composite_quirk(chip, iface, quirk); - case QUIRK_AUDIO_FIXED_ENDPOINT: - return create_fixed_stream_quirk(chip, iface, quirk); - case QUIRK_AUDIO_STANDARD_INTERFACE: - case QUIRK_MIDI_STANDARD_INTERFACE: - return create_standard_interface_quirk(chip, iface, quirk); - case QUIRK_AUDIO_EDIROL_UA700_UA25: - return create_ua700_ua25_quirk(chip, iface); - case QUIRK_AUDIO_EDIROL_UA1000: - return create_ua1000_quirk(chip, iface); - case QUIRK_IGNORE_INTERFACE: - return 0; - default: + typedef int (*quirk_func_t)(snd_usb_audio_t *, struct usb_interface *, + const snd_usb_audio_quirk_t *); + static const quirk_func_t quirk_funcs[] = { + [QUIRK_IGNORE_INTERFACE] = ignore_interface_quirk, + [QUIRK_COMPOSITE] = create_composite_quirk, + [QUIRK_MIDI_STANDARD_INTERFACE] = snd_usb_create_midi_interface, + [QUIRK_MIDI_FIXED_ENDPOINT] = snd_usb_create_midi_interface, + [QUIRK_MIDI_YAMAHA] = snd_usb_create_midi_interface, + [QUIRK_MIDI_MIDIMAN] = snd_usb_create_midi_interface, + [QUIRK_MIDI_NOVATION] = snd_usb_create_midi_interface, + [QUIRK_MIDI_RAW] = snd_usb_create_midi_interface, + [QUIRK_MIDI_EMAGIC] = snd_usb_create_midi_interface, + [QUIRK_MIDI_MIDITECH] = snd_usb_create_midi_interface, + [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_interface_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, + }; + + if (quirk->type < QUIRK_TYPE_COUNT) { + return quirk_funcs[quirk->type](chip, iface, quirk); + } else { snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); return -ENXIO; } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index aedb42aaa749..ad9eab211d8f 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -153,20 +153,24 @@ struct snd_usb_audio { #define QUIRK_NO_INTERFACE -2 #define QUIRK_ANY_INTERFACE -1 -/* quirk type */ -#define QUIRK_MIDI_FIXED_ENDPOINT 0 -#define QUIRK_MIDI_YAMAHA 1 -#define QUIRK_MIDI_MIDIMAN 2 -#define QUIRK_COMPOSITE 3 -#define QUIRK_AUDIO_FIXED_ENDPOINT 4 -#define QUIRK_AUDIO_STANDARD_INTERFACE 5 -#define QUIRK_MIDI_STANDARD_INTERFACE 6 -#define QUIRK_AUDIO_EDIROL_UA700_UA25 7 -#define QUIRK_AUDIO_EDIROL_UA1000 8 -#define QUIRK_IGNORE_INTERFACE 9 -#define QUIRK_MIDI_NOVATION 10 -#define QUIRK_MIDI_MOTU 11 -#define QUIRK_MIDI_EMAGIC 12 +enum quirk_type { + QUIRK_IGNORE_INTERFACE, + QUIRK_COMPOSITE, + QUIRK_MIDI_STANDARD_INTERFACE, + QUIRK_MIDI_FIXED_ENDPOINT, + QUIRK_MIDI_YAMAHA, + QUIRK_MIDI_MIDIMAN, + QUIRK_MIDI_NOVATION, + QUIRK_MIDI_RAW, + QUIRK_MIDI_EMAGIC, + QUIRK_MIDI_MIDITECH, + QUIRK_AUDIO_STANDARD_INTERFACE, + QUIRK_AUDIO_FIXED_ENDPOINT, + QUIRK_AUDIO_EDIROL_UA700_UA25, + QUIRK_AUDIO_EDIROL_UA1000, + + QUIRK_TYPE_COUNT +}; typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t; typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t; @@ -175,7 +179,7 @@ struct snd_usb_audio_quirk { const char *vendor_name; const char *product_name; int16_t ifnum; - int16_t type; + uint16_t type; const void *data; }; @@ -205,11 +209,13 @@ struct snd_usb_midi_endpoint_info { /* for QUIRK_IGNORE_INTERFACE, data is NULL */ -/* for QUIRK_MIDI_NOVATION and _MOTU, data is NULL */ +/* for QUIRK_MIDI_NOVATION and _RAW, data is NULL */ /* for QUIRK_MIDI_EMAGIC, data points to a snd_usb_midi_endpoint_info * structure (out_cables and in_cables only) */ +/* for QUIRK_MIDI_MIDITECH, data is NULL */ + /* */ diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index bee70068dce0..93dedde3c428 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -44,6 +44,7 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/timer.h> #include <linux/usb.h> #include <sound/core.h> #include <sound/minors.h> @@ -56,6 +57,12 @@ */ /* #define DUMP_PACKETS */ +/* + * how long to wait after some USB errors, so that khubd can disconnect() us + * without too many spurious errors + */ +#define ERROR_DELAY_JIFFIES (HZ / 10) + MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); MODULE_DESCRIPTION("USB Audio/MIDI helper module"); @@ -100,6 +107,7 @@ struct snd_usb_midi { snd_rawmidi_t* rmidi; struct usb_protocol_ops* usb_protocol_ops; struct list_head list; + struct timer_list error_timer; struct snd_usb_midi_endpoint { snd_usb_midi_out_endpoint_t *out; @@ -141,7 +149,8 @@ struct snd_usb_midi_in_endpoint { struct usbmidi_in_port { snd_rawmidi_substream_t* substream; } ports[0x10]; - int seen_f5; + u8 seen_f5; + u8 error_resubmit; int current_port; }; @@ -167,14 +176,22 @@ static int snd_usbmidi_submit_urb(struct urb* urb, int flags) */ static int snd_usbmidi_urb_error(int status) { - if (status == -ENOENT) - return status; /* killed */ - if (status == -EILSEQ || - status == -ECONNRESET || - status == -ETIMEDOUT) - return -ENODEV; /* device removed/shutdown */ - snd_printk(KERN_ERR "urb status %d\n", status); - return 0; /* continue */ + switch (status) { + /* manually unlinked, or device gone */ + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + case -ENODEV: + return -ENODEV; + /* errors that might occur during unplugging */ + case -EPROTO: /* EHCI */ + case -ETIMEDOUT: /* OHCI */ + case -EILSEQ: /* UHCI */ + return -EIO; + default: + snd_printk(KERN_ERR "urb status %d\n", status); + return 0; /* continue */ + } } /* @@ -218,8 +235,15 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb, struct pt_regs *regs) ep->umidi->usb_protocol_ops->input(ep, urb->transfer_buffer, urb->actual_length); } else { - if (snd_usbmidi_urb_error(urb->status) < 0) + int err = snd_usbmidi_urb_error(urb->status); + if (err < 0) { + if (err != -ENODEV) { + ep->error_resubmit = 1; + mod_timer(&ep->umidi->error_timer, + jiffies + ERROR_DELAY_JIFFIES); + } return; + } } if (usb_pipe_needs_resubmit(urb->pipe)) { @@ -236,8 +260,13 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb, struct pt_regs *regs) ep->urb_active = 0; spin_unlock(&ep->buffer_lock); if (urb->status < 0) { - if (snd_usbmidi_urb_error(urb->status) < 0) + int err = snd_usbmidi_urb_error(urb->status); + if (err < 0) { + if (err != -ENODEV) + mod_timer(&ep->umidi->error_timer, + jiffies + ERROR_DELAY_JIFFIES); return; + } } snd_usbmidi_do_output(ep); } @@ -276,6 +305,24 @@ static void snd_usbmidi_out_tasklet(unsigned long data) snd_usbmidi_do_output(ep); } +/* called after transfers had been interrupted due to some USB error */ +static void snd_usbmidi_error_timer(unsigned long data) +{ + snd_usb_midi_t *umidi = (snd_usb_midi_t *)data; + int i; + + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { + snd_usb_midi_in_endpoint_t *in = umidi->endpoints[i].in; + if (in && in->error_resubmit) { + in->error_resubmit = 0; + in->urb->dev = umidi->chip->dev; + snd_usbmidi_submit_urb(in->urb, GFP_ATOMIC); + } + if (umidi->endpoints[i].out) + snd_usbmidi_do_output(umidi->endpoints[i].out); + } +} + /* helper function to send static data that may not DMA-able */ static int send_bulk_static_data(snd_usb_midi_out_endpoint_t* ep, const void *data, int len) @@ -524,16 +571,16 @@ static struct usb_protocol_ops snd_usbmidi_novation_ops = { }; /* - * Mark of the Unicorn USB MIDI protocol: raw MIDI. + * "raw" protocol: used by the MOTU FastLane. */ -static void snd_usbmidi_motu_input(snd_usb_midi_in_endpoint_t* ep, - uint8_t* buffer, int buffer_length) +static void snd_usbmidi_raw_input(snd_usb_midi_in_endpoint_t* ep, + uint8_t* buffer, int buffer_length) { snd_usbmidi_input_data(ep, 0, buffer, buffer_length); } -static void snd_usbmidi_motu_output(snd_usb_midi_out_endpoint_t* ep) +static void snd_usbmidi_raw_output(snd_usb_midi_out_endpoint_t* ep) { int count; @@ -549,9 +596,9 @@ static void snd_usbmidi_motu_output(snd_usb_midi_out_endpoint_t* ep) ep->urb->transfer_buffer_length = count; } -static struct usb_protocol_ops snd_usbmidi_motu_ops = { - .input = snd_usbmidi_motu_input, - .output = snd_usbmidi_motu_output, +static struct usb_protocol_ops snd_usbmidi_raw_ops = { + .input = snd_usbmidi_raw_input, + .output = snd_usbmidi_raw_output, }; /* @@ -594,17 +641,20 @@ static void snd_usbmidi_emagic_finish_out(snd_usb_midi_out_endpoint_t* ep) static void snd_usbmidi_emagic_input(snd_usb_midi_in_endpoint_t* ep, uint8_t* buffer, int buffer_length) { - /* ignore padding bytes at end of buffer */ - while (buffer_length > 0 && buffer[buffer_length - 1] == 0xff) - --buffer_length; + int i; + + /* FF indicates end of valid data */ + for (i = 0; i < buffer_length; ++i) + if (buffer[i] == 0xff) { + buffer_length = i; + break; + } /* handle F5 at end of last buffer */ if (ep->seen_f5) goto switch_port; while (buffer_length > 0) { - int i; - /* determine size of data until next F5 */ for (i = 0; i < buffer_length; ++i) if (buffer[i] == 0xf5) @@ -671,6 +721,10 @@ static void snd_usbmidi_emagic_output(snd_usb_midi_out_endpoint_t* ep) break; } } + if (buf_free < ep->max_transfer && buf_free > 0) { + *buf = 0xff; + --buf_free; + } ep->urb->transfer_buffer_length = ep->max_transfer - buf_free; } @@ -765,7 +819,10 @@ static snd_rawmidi_ops_t snd_usbmidi_input_ops = { static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep) { if (ep->urb) { - kfree(ep->urb->transfer_buffer); + usb_buffer_free(ep->umidi->chip->dev, + ep->urb->transfer_buffer_length, + ep->urb->transfer_buffer, + ep->urb->transfer_dma); usb_free_urb(ep->urb); } kfree(ep); @@ -799,7 +856,8 @@ static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi, else pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->in_ep); length = usb_maxpacket(umidi->chip->dev, pipe, 0); - buffer = kmalloc(length, GFP_KERNEL); + buffer = usb_buffer_alloc(umidi->chip->dev, length, GFP_KERNEL, + &ep->urb->transfer_dma); if (!buffer) { snd_usbmidi_in_endpoint_delete(ep); return -ENOMEM; @@ -812,6 +870,7 @@ static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi, usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, length, snd_usb_complete_callback(snd_usbmidi_in_urb_complete), ep); + ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; rep->in = ep; return 0; @@ -832,10 +891,10 @@ static unsigned int snd_usbmidi_count_bits(unsigned int x) */ static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep) { - if (ep->tasklet.func) - tasklet_kill(&ep->tasklet); if (ep->urb) { - kfree(ep->urb->transfer_buffer); + usb_buffer_free(ep->umidi->chip->dev, ep->max_transfer, + ep->urb->transfer_buffer, + ep->urb->transfer_dma); usb_free_urb(ep->urb); } kfree(ep); @@ -867,7 +926,8 @@ static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi, /* we never use interrupt output pipes */ pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->out_ep); ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1); - buffer = kmalloc(ep->max_transfer, GFP_KERNEL); + buffer = usb_buffer_alloc(umidi->chip->dev, ep->max_transfer, + GFP_KERNEL, &ep->urb->transfer_dma); if (!buffer) { snd_usbmidi_out_endpoint_delete(ep); return -ENOMEM; @@ -875,6 +935,7 @@ static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi, usb_fill_bulk_urb(ep->urb, umidi->chip->dev, pipe, buffer, ep->max_transfer, snd_usb_complete_callback(snd_usbmidi_out_urb_complete), ep); + ep->urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; spin_lock_init(&ep->buffer_lock); tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep); @@ -918,8 +979,11 @@ void snd_usbmidi_disconnect(struct list_head* p) int i; umidi = list_entry(p, snd_usb_midi_t, list); + del_timer_sync(&umidi->error_timer); for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i]; + if (ep->out) + tasklet_kill(&ep->out->tasklet); if (ep->out && ep->out->urb) { usb_kill_urb(ep->out->urb); if (umidi->usb_protocol_ops->finish_out_endpoint) @@ -1480,6 +1544,9 @@ int snd_usb_create_midi_interface(snd_usb_audio_t* chip, umidi->iface = iface; umidi->quirk = quirk; umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; + init_timer(&umidi->error_timer); + umidi->error_timer.function = snd_usbmidi_error_timer; + umidi->error_timer.data = (unsigned long)umidi; /* detect the endpoint(s) to use */ memset(endpoints, 0, sizeof(endpoints)); @@ -1505,8 +1572,8 @@ int snd_usb_create_midi_interface(snd_usb_audio_t* chip, umidi->usb_protocol_ops = &snd_usbmidi_novation_ops; err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; - case QUIRK_MIDI_MOTU: - umidi->usb_protocol_ops = &snd_usbmidi_motu_ops; + case QUIRK_MIDI_RAW: + umidi->usb_protocol_ops = &snd_usbmidi_raw_ops; err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; case QUIRK_MIDI_EMAGIC: @@ -1515,6 +1582,9 @@ int snd_usb_create_midi_interface(snd_usb_audio_t* chip, sizeof(snd_usb_midi_endpoint_info_t)); err = snd_usbmidi_detect_endpoints(umidi, &endpoints[0], 1); break; + case QUIRK_MIDI_MIDITECH: + err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); + break; default: snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); err = -ENXIO; diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index e73c1c9d3e73..fa7056f5caaf 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -363,7 +363,7 @@ static int get_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int *value } /* channel = 0: master, 1 = first channel */ -inline static int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value) +static inline int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value) { return get_ctl_value(cval, GET_CUR, (cval->control << 8) | channel, value); } @@ -399,7 +399,7 @@ static int set_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int value) return set_ctl_value(cval, SET_CUR, validx, value); } -inline static int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value) +static inline int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value) { return set_ctl_value(cval, SET_CUR, (cval->control << 8) | channel, value); } diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h index f5135641b3e2..f74e652a1e51 100644 --- a/sound/usb/usbquirks.h +++ b/sound/usb/usbquirks.h @@ -116,6 +116,7 @@ YAMAHA_DEVICE(0x1039, NULL), YAMAHA_DEVICE(0x103a, NULL), YAMAHA_DEVICE(0x103b, NULL), YAMAHA_DEVICE(0x103c, NULL), +YAMAHA_DEVICE(0x103d, NULL), YAMAHA_DEVICE(0x2000, "DGP-7"), YAMAHA_DEVICE(0x2001, "DGP-5"), YAMAHA_DEVICE(0x2002, NULL), @@ -1259,7 +1260,12 @@ YAMAHA_DEVICE(0x7010, "UB99"), /* Mark of the Unicorn devices */ { /* thanks to Robert A. Lerche <ral 'at' msbit.com> */ - USB_DEVICE(0x07fd, 0x0001), + .match_flags = USB_DEVICE_ID_MATCH_VENDOR | + USB_DEVICE_ID_MATCH_PRODUCT | + USB_DEVICE_ID_MATCH_DEV_SUBCLASS, + .idVendor = 0x07fd, + .idProduct = 0x0001, + .bDeviceSubClass = 2, .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { .vendor_name = "MOTU", .product_name = "Fastlane", @@ -1268,7 +1274,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .data = & (const snd_usb_audio_quirk_t[]) { { .ifnum = 0, - .type = QUIRK_MIDI_MOTU + .type = QUIRK_MIDI_RAW }, { .ifnum = 1, @@ -1373,6 +1379,25 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, { + USB_DEVICE(0x4752, 0x0011), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Miditech", + .product_name = "Midistart-2", + .ifnum = 0, + .type = QUIRK_MIDI_MIDITECH + } +}, +{ + USB_DEVICE(0x7104, 0x2202), + .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) { + .vendor_name = "Miditech", + .product_name = "MidiStudio-2", + .ifnum = 0, + .type = QUIRK_MIDI_MIDITECH + } +}, + +{ /* * Some USB MIDI devices don't have an audio control interface, * so we have to grab MIDI streaming interfaces here. diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index bef9b0c142c4..0281a362857a 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -232,8 +232,7 @@ static int snd_usX2Y_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp) if (err) return err; if (dsp->index == 1) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(HZ/4); // give the device some time + msleep(250); // give the device some time err = usX2Y_AsyncSeq04_init(priv); if (err) { snd_printk("usX2Y_AsyncSeq04_init error \n"); diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index bb2c8e9000c6..d0199c4e5551 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -50,6 +50,7 @@ Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y. */ +#include <linux/delay.h> #include "usbusx2yaudio.c" #if defined(USX2Y_NRPACKS_VARIABLE) || (!defined(USX2Y_NRPACKS_VARIABLE) && USX2Y_NRPACKS == 1) @@ -520,11 +521,8 @@ static int snd_usX2Y_usbpcm_prepare(snd_pcm_substream_t *substream) usX2Y->hwdep_pcm_shm->playback_iso_start = -1; if (atomic_read(&subs->state) < state_PREPARED) { while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) > usX2Y->hwdep_pcm_shm->captured_iso_frames) { - signed long timeout; snd_printd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", usX2Y_iso_frames_per_buffer(runtime, usX2Y), usX2Y->hwdep_pcm_shm->captured_iso_frames); - set_current_state(TASK_INTERRUPTIBLE); - timeout = schedule_timeout(HZ/100 + 1); - if (signal_pending(current)) { + if (msleep_interruptible(10)) { err = -ERESTARTSYS; goto up_prepare_mutex; } @@ -626,7 +624,7 @@ static int usX2Y_pcms_lock_check(snd_card_t *card) for (s = 0; s < 2; ++s) { snd_pcm_substream_t *substream; substream = pcm->streams[s].substream; - if (substream && substream->open_flag) + if (substream && substream->ffile != NULL) err = -EBUSY; } } |