diff options
Diffstat (limited to 'sound')
201 files changed, 9507 insertions, 5439 deletions
diff --git a/sound/aoa/codecs/onyx.c b/sound/aoa/codecs/onyx.c index 401107b85d30..23c371ecfb6b 100644 --- a/sound/aoa/codecs/onyx.c +++ b/sound/aoa/codecs/onyx.c @@ -243,13 +243,7 @@ static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, { static const char * const texts[] = { "Line-In", "Microphone" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol, diff --git a/sound/aoa/codecs/tas.c b/sound/aoa/codecs/tas.c index cf3c6303b7e3..364c7c4416e8 100644 --- a/sound/aoa/codecs/tas.c +++ b/sound/aoa/codecs/tas.c @@ -478,15 +478,9 @@ static struct snd_kcontrol_new drc_switch_control = { static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Line-In", "Microphone" }; + static const char * const texts[] = { "Line-In", "Microphone" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol, diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index a80d5ea87ccd..4e2b4fbf2496 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -79,8 +79,7 @@ static void i2sbus_release_dev(struct device *dev) if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma); if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma); for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) - if (i2sdev->allocated_resource[i]) - release_and_free_resource(i2sdev->allocated_resource[i]); + release_and_free_resource(i2sdev->allocated_resource[i]); free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring); free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring); for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) @@ -323,8 +322,7 @@ static int i2sbus_add_dev(struct macio_dev *macio, if (dev->out.dbdma) iounmap(dev->out.dbdma); if (dev->in.dbdma) iounmap(dev->in.dbdma); for (i=0;i<3;i++) - if (dev->allocated_resource[i]) - release_and_free_resource(dev->allocated_resource[i]); + release_and_free_resource(dev->allocated_resource[i]); mutex_destroy(&dev->lock); kfree(dev); return 0; diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c index a61d7a9a995e..01f8fdc42b1b 100644 --- a/sound/arm/pxa2xx-pcm-lib.c +++ b/sound/arm/pxa2xx-pcm-lib.c @@ -200,9 +200,7 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) } else { printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", dma_ch, dcsr); - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); + snd_pcm_stop_xrun(substream); } } EXPORT_SYMBOL(pxa2xx_pcm_dma_irq); diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c index 31061e3521d4..023140504104 100644 --- a/sound/atmel/abdac.c +++ b/sound/atmel/abdac.c @@ -242,7 +242,7 @@ static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */ case SNDRV_PCM_TRIGGER_START: - clk_enable(dac->sample_clk); + clk_prepare_enable(dac->sample_clk); retval = dw_dma_cyclic_start(dac->dma.chan); if (retval) goto out; @@ -254,7 +254,7 @@ static int atmel_abdac_trigger(struct snd_pcm_substream *substream, int cmd) dw_dma_cyclic_stop(dac->dma.chan); dac_writel(dac, DATA, 0); dac_writel(dac, CTRL, 0); - clk_disable(dac->sample_clk); + clk_disable_unprepare(dac->sample_clk); break; default: retval = -EINVAL; @@ -429,7 +429,7 @@ static int atmel_abdac_probe(struct platform_device *pdev) retval = PTR_ERR(sample_clk); goto out_put_pclk; } - clk_enable(pclk); + clk_prepare_enable(pclk); retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, @@ -528,7 +528,7 @@ out_free_card: snd_card_free(card); out_put_sample_clk: clk_put(sample_clk); - clk_disable(pclk); + clk_disable_unprepare(pclk); out_put_pclk: clk_put(pclk); return retval; @@ -541,8 +541,8 @@ static int atmel_abdac_suspend(struct device *pdev) struct atmel_abdac *dac = card->private_data; dw_dma_cyclic_stop(dac->dma.chan); - clk_disable(dac->sample_clk); - clk_disable(dac->pclk); + clk_disable_unprepare(dac->sample_clk); + clk_disable_unprepare(dac->pclk); return 0; } @@ -552,8 +552,8 @@ static int atmel_abdac_resume(struct device *pdev) struct snd_card *card = dev_get_drvdata(pdev); struct atmel_abdac *dac = card->private_data; - clk_enable(dac->pclk); - clk_enable(dac->sample_clk); + clk_prepare_enable(dac->pclk); + clk_prepare_enable(dac->sample_clk); if (test_bit(DMA_READY, &dac->flags)) dw_dma_cyclic_start(dac->dma.chan); @@ -572,7 +572,7 @@ static int atmel_abdac_remove(struct platform_device *pdev) struct atmel_abdac *dac = get_dac(card); clk_put(dac->sample_clk); - clk_disable(dac->pclk); + clk_disable_unprepare(dac->pclk); clk_put(dac->pclk); dma_release_channel(dac->dma.chan); diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index b59427d5a697..cb44c74c9702 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -773,7 +773,7 @@ static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip) return err; } retval = snd_pcm_new(chip->card, chip->card->shortname, - chip->pdev->id, playback, capture, &pcm); + 0, playback, capture, &pcm); if (retval) return retval; @@ -944,7 +944,7 @@ static int atmel_ac97c_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "no peripheral clock\n"); return PTR_ERR(pclk); } - clk_enable(pclk); + clk_prepare_enable(pclk); retval = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, @@ -1122,7 +1122,7 @@ err_ioremap: err_request_irq: snd_card_free(card); err_snd_card_new: - clk_disable(pclk); + clk_disable_unprepare(pclk); clk_put(pclk); return retval; } @@ -1139,7 +1139,7 @@ static int atmel_ac97c_suspend(struct device *pdev) if (test_bit(DMA_TX_READY, &chip->flags)) dw_dma_cyclic_stop(chip->dma.tx_chan); } - clk_disable(chip->pclk); + clk_disable_unprepare(chip->pclk); return 0; } @@ -1149,7 +1149,7 @@ static int atmel_ac97c_resume(struct device *pdev) struct snd_card *card = dev_get_drvdata(pdev); struct atmel_ac97c *chip = card->private_data; - clk_enable(chip->pclk); + clk_prepare_enable(chip->pclk); if (cpu_is_at32ap7000()) { if (test_bit(DMA_RX_READY, &chip->flags)) dw_dma_cyclic_start(chip->dma.rx_chan); @@ -1177,7 +1177,7 @@ static int atmel_ac97c_remove(struct platform_device *pdev) ac97c_writel(chip, COMR, 0); ac97c_writel(chip, MR, 0); - clk_disable(chip->pclk); + clk_disable_unprepare(chip->pclk); clk_put(chip->pclk); iounmap(chip->regs); free_irq(chip->irq, chip); diff --git a/sound/core/Makefile b/sound/core/Makefile index 394a38909f6b..4daf2f58261c 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -14,6 +14,9 @@ snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o memalloc.o snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o +# for trace-points +CFLAGS_pcm_lib.o := -I$(src) + snd-pcm-dmaengine-objs := pcm_dmaengine.o snd-rawmidi-objs := rawmidi.o diff --git a/sound/core/control.c b/sound/core/control.c index b9611344ff9e..bb96a467e88d 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -141,6 +141,16 @@ static int snd_ctl_release(struct inode *inode, struct file *file) return 0; } +/** + * snd_ctl_notify - Send notification to user-space for a control change + * @card: the card to send notification + * @mask: the event mask, SNDRV_CTL_EVENT_* + * @id: the ctl element id to send notification + * + * This function adds an event record with the given id and mask, appends + * to the list and wakes up the user-space for notification. This can be + * called in the atomic context. + */ void snd_ctl_notify(struct snd_card *card, unsigned int mask, struct snd_ctl_elem_id *id) { @@ -179,7 +189,6 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, } read_unlock(&card->ctl_files_rwlock); } - EXPORT_SYMBOL(snd_ctl_notify); /** @@ -261,7 +270,6 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, kctl.private_data = private_data; return snd_ctl_new(&kctl, access); } - EXPORT_SYMBOL(snd_ctl_new1); /** @@ -280,7 +288,6 @@ void snd_ctl_free_one(struct snd_kcontrol *kcontrol) kfree(kcontrol); } } - EXPORT_SYMBOL(snd_ctl_free_one); static bool snd_ctl_remove_numid_conflict(struct snd_card *card, @@ -376,7 +383,6 @@ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol) snd_ctl_free_one(kcontrol); return err; } - EXPORT_SYMBOL(snd_ctl_add); /** @@ -471,7 +477,6 @@ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol) snd_ctl_free_one(kcontrol); return 0; } - EXPORT_SYMBOL(snd_ctl_remove); /** @@ -499,7 +504,6 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id) up_write(&card->controls_rwsem); return ret; } - EXPORT_SYMBOL(snd_ctl_remove_id); /** @@ -568,7 +572,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, ret = -ENOENT; goto unlock; } - index_offset = snd_ctl_get_ioff(kctl, &kctl->id); + index_offset = snd_ctl_get_ioff(kctl, id); vd = &kctl->vd[index_offset]; ret = 0; if (active) { @@ -617,7 +621,6 @@ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, up_write(&card->controls_rwsem); return 0; } - EXPORT_SYMBOL(snd_ctl_rename_id); /** @@ -645,7 +648,6 @@ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numi } return NULL; } - EXPORT_SYMBOL(snd_ctl_find_numid); /** @@ -687,7 +689,6 @@ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, } return NULL; } - EXPORT_SYMBOL(snd_ctl_find_id); static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, @@ -1526,19 +1527,28 @@ static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head * return 0; } +/** + * snd_ctl_register_ioctl - register the device-specific control-ioctls + * @fcn: ioctl callback function + * + * called from each device manager like pcm.c, hwdep.c, etc. + */ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls); } - EXPORT_SYMBOL(snd_ctl_register_ioctl); #ifdef CONFIG_COMPAT +/** + * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat + * control-ioctls + * @fcn: ioctl callback function + */ int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls); } - EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); #endif @@ -1566,19 +1576,26 @@ static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn, return -EINVAL; } +/** + * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls + * @fcn: ioctl callback function to unregister + */ int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls); } - EXPORT_SYMBOL(snd_ctl_unregister_ioctl); #ifdef CONFIG_COMPAT +/** + * snd_ctl_unregister_ioctl - de-register the device-specific compat 32bit + * control-ioctls + * @fcn: ioctl callback function to unregister + */ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls); } - EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); #endif @@ -1702,6 +1719,16 @@ int snd_ctl_create(struct snd_card *card) /* * Frequently used control callbacks/helpers */ + +/** + * snd_ctl_boolean_mono_info - Helper function for a standard boolean info + * callback with a mono channel + * @kcontrol: the kcontrol instance + * @uinfo: info to store + * + * This is a function that can be used as info callback for a standard + * boolean control with a single mono channel. + */ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1711,9 +1738,17 @@ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 1; return 0; } - EXPORT_SYMBOL(snd_ctl_boolean_mono_info); +/** + * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info + * callback with stereo two channels + * @kcontrol: the kcontrol instance + * @uinfo: info to store + * + * This is a function that can be used as info callback for a standard + * boolean control with stereo two channels. + */ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1723,7 +1758,6 @@ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, uinfo->value.integer.max = 1; return 0; } - EXPORT_SYMBOL(snd_ctl_boolean_stereo_info); /** @@ -1745,8 +1779,13 @@ int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels, info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; info->count = channels; info->value.enumerated.items = items; + if (!items) + return 0; if (info->value.enumerated.item >= items) info->value.enumerated.item = items - 1; + WARN(strlen(names[info->value.enumerated.item]) >= sizeof(info->value.enumerated.name), + "ALSA: too long item name '%s'\n", + names[info->value.enumerated.item]); strlcpy(info->value.enumerated.name, names[info->value.enumerated.item], sizeof(info->value.enumerated.name)); diff --git a/sound/core/init.c b/sound/core/init.c index 7bdfd19e24a8..074875d68c15 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -438,17 +438,6 @@ int snd_card_disconnect(struct snd_card *card) EXPORT_SYMBOL(snd_card_disconnect); -/** - * snd_card_free - frees given soundcard structure - * @card: soundcard structure - * - * This function releases the soundcard structure and the all assigned - * devices automatically. That is, you don't have to release the devices - * by yourself. - * - * Return: Zero. Frees all associated devices and frees the control - * interface associated to given soundcard. - */ static int snd_card_do_free(struct snd_card *card) { #if IS_ENABLED(CONFIG_SND_MIXER_OSS) @@ -469,6 +458,15 @@ static int snd_card_do_free(struct snd_card *card) return 0; } +/** + * snd_card_free_when_closed - Disconnect the card, free it later eventually + * @card: soundcard structure + * + * Unlike snd_card_free(), this function doesn't try to release the card + * resource immediately, but tries to disconnect at first. When the card + * is still in use, the function returns before freeing the resources. + * The card resources will be freed when the refcount gets to zero. + */ int snd_card_free_when_closed(struct snd_card *card) { int ret = snd_card_disconnect(card); @@ -479,6 +477,19 @@ int snd_card_free_when_closed(struct snd_card *card) } EXPORT_SYMBOL(snd_card_free_when_closed); +/** + * snd_card_free - frees given soundcard structure + * @card: soundcard structure + * + * This function releases the soundcard structure and the all assigned + * devices automatically. That is, you don't have to release the devices + * by yourself. + * + * This function waits until the all resources are properly released. + * + * Return: Zero. Frees all associated devices and frees the control + * interface associated to given soundcard. + */ int snd_card_free(struct snd_card *card) { struct completion released; diff --git a/sound/core/pcm.c b/sound/core/pcm.c index c6ff94ab1ad6..cfc56c806964 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -220,6 +220,10 @@ static char *snd_pcm_format_names[] = { FORMAT(DSD_U32_BE), }; +/** + * snd_pcm_format_name - Return a name string for the given PCM format + * @format: PCM format + */ const char *snd_pcm_format_name(snd_pcm_format_t format) { if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names)) @@ -481,6 +485,19 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, } #ifdef CONFIG_SND_PCM_XRUN_DEBUG +static void snd_pcm_xrun_injection_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_pcm_substream *substream = entry->private_data; + struct snd_pcm_runtime *runtime; + + snd_pcm_stream_lock_irq(substream); + runtime = substream->runtime; + if (runtime && runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irq(substream); +} + static void snd_pcm_xrun_debug_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -612,6 +629,22 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) } substream->proc_status_entry = entry; +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + entry = snd_info_create_card_entry(card, "xrun_injection", + substream->proc_root); + if (entry) { + entry->private_data = substream; + entry->c.text.read = NULL; + entry->c.text.write = snd_pcm_xrun_injection_write; + entry->mode = S_IFREG | S_IWUSR; + if (snd_info_register(entry) < 0) { + snd_info_free_entry(entry); + entry = NULL; + } + } + substream->proc_xrun_injection_entry = entry; +#endif /* CONFIG_SND_PCM_XRUN_DEBUG */ + return 0; } @@ -625,6 +658,10 @@ static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) substream->proc_sw_params_entry = NULL; snd_info_free_entry(substream->proc_status_entry); substream->proc_status_entry = NULL; +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + snd_info_free_entry(substream->proc_xrun_injection_entry); + substream->proc_xrun_injection_entry = NULL; +#endif snd_info_free_entry(substream->proc_root); substream->proc_root = NULL; return 0; @@ -709,7 +746,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) } return 0; } - EXPORT_SYMBOL(snd_pcm_new_stream); static int _snd_pcm_new(struct snd_card *card, const char *id, int device, @@ -1157,6 +1193,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) return 0; } +/** + * snd_pcm_notify - Add/remove the notify list + * @notify: PCM notify list + * @nfree: 0 = register, 1 = unregister + * + * This adds the given notifier to the global list so that the callback is + * called for each registered PCM devices. This exists only for PCM OSS + * emulation, so far. + */ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) { struct snd_pcm *pcm; @@ -1179,7 +1224,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) mutex_unlock(®ister_mutex); return 0; } - EXPORT_SYMBOL(snd_pcm_notify); #ifdef CONFIG_PROC_FS diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index dfc28542a007..ec9e7866177f 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -32,6 +32,15 @@ #include <sound/pcm_params.h> #include <sound/timer.h> +#ifdef CONFIG_SND_PCM_XRUN_DEBUG +#define CREATE_TRACE_POINTS +#include "pcm_trace.h" +#else +#define trace_hwptr(substream, pos, in_interrupt) +#define trace_xrun(substream) +#define trace_hw_ptr_error(substream, reason) +#endif + /* * fill ring buffer with silence * runtime->silence_start: starting pointer to silence area @@ -146,10 +155,6 @@ EXPORT_SYMBOL(snd_pcm_debug_name); #define XRUN_DEBUG_BASIC (1<<0) #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ -#define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ -#define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ -#define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ -#define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -168,6 +173,7 @@ static void xrun(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + trace_xrun(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); @@ -180,97 +186,19 @@ static void xrun(struct snd_pcm_substream *substream) } #ifdef CONFIG_SND_PCM_XRUN_DEBUG -#define hw_ptr_error(substream, fmt, args...) \ +#define hw_ptr_error(substream, in_interrupt, reason, fmt, args...) \ do { \ + trace_hw_ptr_error(substream, reason); \ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ - xrun_log_show(substream); \ - pr_err_ratelimited("ALSA: PCM: " fmt, ##args); \ + pr_err_ratelimited("ALSA: PCM: [%c] " reason ": " fmt, \ + (in_interrupt) ? 'Q' : 'P', ##args); \ dump_stack_on_xrun(substream); \ } \ } while (0) -#define XRUN_LOG_CNT 10 - -struct hwptr_log_entry { - unsigned int in_interrupt; - unsigned long jiffies; - snd_pcm_uframes_t pos; - snd_pcm_uframes_t period_size; - snd_pcm_uframes_t buffer_size; - snd_pcm_uframes_t old_hw_ptr; - snd_pcm_uframes_t hw_ptr_base; -}; - -struct snd_pcm_hwptr_log { - unsigned int idx; - unsigned int hit: 1; - struct hwptr_log_entry entries[XRUN_LOG_CNT]; -}; - -static void xrun_log(struct snd_pcm_substream *substream, - snd_pcm_uframes_t pos, int in_interrupt) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_hwptr_log *log = runtime->hwptr_log; - struct hwptr_log_entry *entry; - - if (log == NULL) { - log = kzalloc(sizeof(*log), GFP_ATOMIC); - if (log == NULL) - return; - runtime->hwptr_log = log; - } else { - if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) - return; - } - entry = &log->entries[log->idx]; - entry->in_interrupt = in_interrupt; - entry->jiffies = jiffies; - entry->pos = pos; - entry->period_size = runtime->period_size; - entry->buffer_size = runtime->buffer_size; - entry->old_hw_ptr = runtime->status->hw_ptr; - entry->hw_ptr_base = runtime->hw_ptr_base; - log->idx = (log->idx + 1) % XRUN_LOG_CNT; -} - -static void xrun_log_show(struct snd_pcm_substream *substream) -{ - struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; - struct hwptr_log_entry *entry; - char name[16]; - unsigned int idx; - int cnt; - - if (log == NULL) - return; - if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) - return; - snd_pcm_debug_name(substream, name, sizeof(name)); - for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { - entry = &log->entries[idx]; - if (entry->period_size == 0) - break; - pr_info("hwptr log: %s: %sj=%lu, pos=%ld/%ld/%ld, " - "hwptr=%ld/%ld\n", - name, entry->in_interrupt ? "[Q] " : "", - entry->jiffies, - (unsigned long)entry->pos, - (unsigned long)entry->period_size, - (unsigned long)entry->buffer_size, - (unsigned long)entry->old_hw_ptr, - (unsigned long)entry->hw_ptr_base); - idx++; - idx %= XRUN_LOG_CNT; - } - log->hit = 1; -} - #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ #define hw_ptr_error(substream, fmt, args...) do { } while (0) -#define xrun_log(substream, pos, in_interrupt) do { } while (0) -#define xrun_log_show(substream) do { } while (0) #endif @@ -343,17 +271,15 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, if (printk_ratelimit()) { char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); - xrun_log_show(substream); pcm_err(substream->pcm, - "XRUN: %s, pos = %ld, buffer size = %ld, period size = %ld\n", + "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n", name, pos, runtime->buffer_size, runtime->period_size); } pos = 0; } pos -= pos % runtime->min_align; - if (xrun_debug(substream, XRUN_DEBUG_LOG)) - xrun_log(substream, pos, in_interrupt); + trace_hwptr(substream, pos, in_interrupt); hw_base = runtime->hw_ptr_base; new_hw_ptr = hw_base + pos; if (in_interrupt) { @@ -388,22 +314,6 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, delta = new_hw_ptr - old_hw_ptr; if (delta < 0) delta += runtime->boundary; - if (xrun_debug(substream, in_interrupt ? - XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { - char name[16]; - snd_pcm_debug_name(substream, name, sizeof(name)); - pcm_dbg(substream->pcm, - "%s_update: %s: pos=%u/%u/%u, hwptr=%ld/%ld/%ld/%ld\n", - in_interrupt ? "period" : "hwptr", - name, - (unsigned int)pos, - (unsigned int)runtime->period_size, - (unsigned int)runtime->buffer_size, - (unsigned long)delta, - (unsigned long)old_hw_ptr, - (unsigned long)new_hw_ptr, - (unsigned long)runtime->hw_ptr_base); - } if (runtime->no_period_wakeup) { snd_pcm_sframes_t xrun_threshold; @@ -431,13 +341,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, /* something must be really wrong */ if (delta >= runtime->buffer_size + runtime->period_size) { - hw_ptr_error(substream, - "Unexpected hw_pointer value %s" - "(stream=%i, pos=%ld, new_hw_ptr=%ld, " - "old_hw_ptr=%ld)\n", - in_interrupt ? "[Q] " : "[P]", - substream->stream, (long)pos, - (long)new_hw_ptr, (long)old_hw_ptr); + hw_ptr_error(substream, in_interrupt, "Unexpected hw_ptr", + "(stream=%i, pos=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", + substream->stream, (long)pos, + (long)new_hw_ptr, (long)old_hw_ptr); return 0; } @@ -474,11 +381,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, delta--; } /* align hw_base to buffer_size */ - hw_ptr_error(substream, - "hw_ptr skipping! %s" - "(pos=%ld, delta=%ld, period=%ld, " - "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", - in_interrupt ? "[Q] " : "", + hw_ptr_error(substream, in_interrupt, "hw_ptr skipping", + "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", (long)pos, (long)hdelta, (long)runtime->period_size, jdelta, ((hdelta * HZ) / runtime->rate), hw_base, @@ -490,11 +394,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } no_jiffies_check: if (delta > runtime->period_size + runtime->period_size / 2) { - hw_ptr_error(substream, - "Lost interrupts? %s" - "(stream=%i, delta=%ld, new_hw_ptr=%ld, " - "old_hw_ptr=%ld)\n", - in_interrupt ? "[Q] " : "", + hw_ptr_error(substream, in_interrupt, + "Lost interrupts?", + "(stream=%i, delta=%ld, new_hw_ptr=%ld, old_hw_ptr=%ld)\n", substream->stream, (long)delta, (long)new_hw_ptr, (long)old_hw_ptr); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 166d59cdc86b..095d9572ad2b 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -35,9 +35,6 @@ #include <sound/timer.h> #include <sound/minors.h> #include <asm/io.h> -#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) -#include <dma-coherence.h> -#endif /* * Compatibility @@ -77,6 +74,14 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream); static DEFINE_RWLOCK(snd_pcm_link_rwlock); static DECLARE_RWSEM(snd_pcm_link_rwsem); +/** + * snd_pcm_stream_lock - Lock the PCM stream + * @substream: PCM substream + * + * This locks the PCM stream's spinlock or mutex depending on the nonatomic + * flag of the given substream. This also takes the global link rw lock + * (or rw sem), too, for avoiding the race with linked streams. + */ void snd_pcm_stream_lock(struct snd_pcm_substream *substream) { if (substream->pcm->nonatomic) { @@ -89,6 +94,12 @@ void snd_pcm_stream_lock(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock); +/** + * snd_pcm_stream_lock - Unlock the PCM stream + * @substream: PCM substream + * + * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock(). + */ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) { if (substream->pcm->nonatomic) { @@ -101,6 +112,14 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock); +/** + * snd_pcm_stream_lock_irq - Lock the PCM stream + * @substream: PCM substream + * + * This locks the PCM stream like snd_pcm_stream_lock() and disables the local + * IRQ (only when nonatomic is false). In nonatomic case, this is identical + * as snd_pcm_stream_lock(). + */ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) { if (!substream->pcm->nonatomic) @@ -109,6 +128,12 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); +/** + * snd_pcm_stream_unlock_irq - Unlock the PCM stream + * @substream: PCM substream + * + * This is a counter-part of snd_pcm_stream_lock_irq(). + */ void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream) { snd_pcm_stream_unlock(substream); @@ -127,6 +152,13 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave); +/** + * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream + * @substream: PCM substream + * @flags: irq flags + * + * This is a counter-part of snd_pcm_stream_lock_irqsave(). + */ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, unsigned long flags) { @@ -195,6 +227,21 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream, return err; } +static bool hw_support_mmap(struct snd_pcm_substream *substream) +{ + if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_MMAP)) + return false; + /* check architectures that return -EINVAL from dma_mmap_coherent() */ + /* FIXME: this should be some global flag */ +#if defined(CONFIG_C6X) || defined(CONFIG_FRV) || defined(CONFIG_MN10300) ||\ + defined(CONFIG_PARISC) || defined(CONFIG_XTENSA) + if (!substream->ops->mmap && + substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) + return false; +#endif + return true; +} + #undef RULES_DEBUG #ifdef RULES_DEBUG @@ -372,8 +419,12 @@ int snd_pcm_hw_refine(struct snd_pcm_substream *substream, } hw = &substream->runtime->hw; - if (!params->info) + if (!params->info) { params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES; + if (!hw_support_mmap(substream)) + params->info &= ~(SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID); + } if (!params->fifo_size) { m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); @@ -849,14 +900,19 @@ static int snd_pcm_action_single(struct action_ops *ops, return res; } -/* call in mutex-protected context */ -static int snd_pcm_action_mutex(struct action_ops *ops, - struct snd_pcm_substream *substream, - int state) +/* + * Note: call with stream lock + */ +static int snd_pcm_action(struct action_ops *ops, + struct snd_pcm_substream *substream, + int state) { int res; - if (snd_pcm_stream_linked(substream)) { + if (!snd_pcm_stream_linked(substream)) + return snd_pcm_action_single(ops, substream, state); + + if (substream->pcm->nonatomic) { if (!mutex_trylock(&substream->group->mutex)) { mutex_unlock(&substream->self_group.mutex); mutex_lock(&substream->group->mutex); @@ -865,24 +921,6 @@ static int snd_pcm_action_mutex(struct action_ops *ops, res = snd_pcm_action_group(ops, substream, state, 1); mutex_unlock(&substream->group->mutex); } else { - res = snd_pcm_action_single(ops, substream, state); - } - return res; -} - -/* - * Note: call with stream lock - */ -static int snd_pcm_action(struct action_ops *ops, - struct snd_pcm_substream *substream, - int state) -{ - int res; - - if (substream->pcm->nonatomic) - return snd_pcm_action_mutex(ops, substream, state); - - if (snd_pcm_stream_linked(substream)) { if (!spin_trylock(&substream->group->lock)) { spin_unlock(&substream->self_group.lock); spin_lock(&substream->group->lock); @@ -890,34 +928,10 @@ static int snd_pcm_action(struct action_ops *ops, } res = snd_pcm_action_group(ops, substream, state, 1); spin_unlock(&substream->group->lock); - } else { - res = snd_pcm_action_single(ops, substream, state); } return res; } -static int snd_pcm_action_lock_mutex(struct action_ops *ops, - struct snd_pcm_substream *substream, - int state) -{ - int res; - - down_read(&snd_pcm_link_rwsem); - if (snd_pcm_stream_linked(substream)) { - mutex_lock(&substream->group->mutex); - mutex_lock(&substream->self_group.mutex); - res = snd_pcm_action_group(ops, substream, state, 1); - mutex_unlock(&substream->self_group.mutex); - mutex_unlock(&substream->group->mutex); - } else { - mutex_lock(&substream->self_group.mutex); - res = snd_pcm_action_single(ops, substream, state); - mutex_unlock(&substream->self_group.mutex); - } - up_read(&snd_pcm_link_rwsem); - return res; -} - /* * Note: don't use any locks before */ @@ -927,22 +941,9 @@ static int snd_pcm_action_lock_irq(struct action_ops *ops, { int res; - if (substream->pcm->nonatomic) - return snd_pcm_action_lock_mutex(ops, substream, state); - - read_lock_irq(&snd_pcm_link_rwlock); - if (snd_pcm_stream_linked(substream)) { - spin_lock(&substream->group->lock); - spin_lock(&substream->self_group.lock); - res = snd_pcm_action_group(ops, substream, state, 1); - spin_unlock(&substream->self_group.lock); - spin_unlock(&substream->group->lock); - } else { - spin_lock(&substream->self_group.lock); - res = snd_pcm_action_single(ops, substream, state); - spin_unlock(&substream->self_group.lock); - } - read_unlock_irq(&snd_pcm_link_rwlock); + snd_pcm_stream_lock_irq(substream); + res = snd_pcm_action(ops, substream, state); + snd_pcm_stream_unlock_irq(substream); return res; } @@ -1051,10 +1052,10 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state) struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->status->state != state) { snd_pcm_trigger_tstamp(substream); + runtime->status->state = state; if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP, &runtime->trigger_tstamp); - runtime->status->state = state; } wake_up(&runtime->sleep); wake_up(&runtime->tsleep); @@ -1097,6 +1098,28 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream) SNDRV_PCM_STATE_SETUP); } +/** + * snd_pcm_stop_xrun - stop the running streams as XRUN + * @substream: the PCM substream instance + * + * This stops the given running substream (and all linked substreams) as XRUN. + * Unlike snd_pcm_stop(), this function takes the substream lock by itself. + * + * Return: Zero if successful, or a negative error code. + */ +int snd_pcm_stop_xrun(struct snd_pcm_substream *substream) +{ + unsigned long flags; + int ret = 0; + + snd_pcm_stream_lock_irqsave(substream, flags); + if (snd_pcm_running(substream)) + ret = snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + snd_pcm_stream_unlock_irqrestore(substream, flags); + return ret; +} +EXPORT_SYMBOL_GPL(snd_pcm_stop_xrun); + /* * pause callbacks */ @@ -1203,11 +1226,11 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); + runtime->status->suspended_state = runtime->status->state; + runtime->status->state = SNDRV_PCM_STATE_SUSPENDED; if (substream->timer) 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; wake_up(&runtime->sleep); wake_up(&runtime->tsleep); } @@ -1310,10 +1333,10 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_trigger_tstamp(substream); + runtime->status->state = runtime->status->suspended_state; if (substream->timer) snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME, &runtime->trigger_tstamp); - runtime->status->state = runtime->status->suspended_state; } static struct action_ops snd_pcm_action_resume = { @@ -2070,7 +2093,7 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream) mask |= 1 << SNDRV_PCM_ACCESS_RW_INTERLEAVED; if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) mask |= 1 << SNDRV_PCM_ACCESS_RW_NONINTERLEAVED; - if (hw->info & SNDRV_PCM_INFO_MMAP) { + if (hw_support_mmap(substream)) { if (hw->info & SNDRV_PCM_INFO_INTERLEAVED) mask |= 1 << SNDRV_PCM_ACCESS_MMAP_INTERLEAVED; if (hw->info & SNDRV_PCM_INFO_NONINTERLEAVED) @@ -3249,20 +3272,6 @@ static inline struct page * snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs) { void *vaddr = substream->runtime->dma_area + ofs; -#if defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) - if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) - return virt_to_page(CAC_ADDR(vaddr)); -#endif -#if defined(CONFIG_PPC32) && defined(CONFIG_NOT_COHERENT_CACHE) - if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) { - dma_addr_t addr = substream->runtime->dma_addr + ofs; - addr -= get_dma_offset(substream->dma_buffer.dev.dev); - /* assume dma_handle set via pfn_to_phys() in - * mm/dma-noncoherent.c - */ - return pfn_to_page(addr >> PAGE_SHIFT); - } -#endif return virt_to_page(vaddr); } @@ -3307,16 +3316,18 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = { .fault = snd_pcm_mmap_data_fault, }; -#ifndef ARCH_HAS_DMA_MMAP_COHERENT -/* This should be defined / handled globally! */ -#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -#define ARCH_HAS_DMA_MMAP_COHERENT -#endif -#endif - /* * mmap the DMA buffer on RAM */ + +/** + * snd_pcm_lib_default_mmap - Default PCM data mmap function + * @substream: PCM substream + * @area: VMA + * + * This is the default mmap handler for PCM data. When mmap pcm_ops is NULL, + * this function is invoked implicitly. + */ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { @@ -3329,7 +3340,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, area->vm_end - area->vm_start, area->vm_page_prot); } #endif /* CONFIG_GENERIC_ALLOCATOR */ -#ifdef ARCH_HAS_DMA_MMAP_COHERENT +#ifndef CONFIG_X86 /* for avoiding warnings arch/x86/mm/pat.c */ if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) return dma_mmap_coherent(substream->dma_buffer.dev.dev, @@ -3337,11 +3348,7 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, substream->runtime->dma_area, substream->runtime->dma_addr, area->vm_end - area->vm_start); -#elif defined(CONFIG_MIPS) && defined(CONFIG_DMA_NONCOHERENT) - if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV && - !plat_device_is_coherent(substream->dma_buffer.dev.dev)) - area->vm_page_prot = pgprot_noncached(area->vm_page_prot); -#endif /* ARCH_HAS_DMA_MMAP_COHERENT */ +#endif /* CONFIG_X86 */ /* mmap with fault handler */ area->vm_ops = &snd_pcm_vm_ops_data_fault; return 0; @@ -3352,6 +3359,15 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap); * mmap the DMA buffer on I/O memory area */ #if SNDRV_PCM_INFO_MMAP_IOMEM +/** + * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem + * @substream: PCM substream + * @area: VMA + * + * When your hardware uses the iomapped pages as the hardware buffer and + * wants to mmap it, pass this function as mmap pcm_ops. Note that this + * is supposed to work only on limited architectures. + */ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_struct *area) { diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h new file mode 100644 index 000000000000..b63b654da5ff --- /dev/null +++ b/sound/core/pcm_trace.h @@ -0,0 +1,110 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_pcm +#define TRACE_INCLUDE_FILE pcm_trace + +#if !defined(_PCM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _PCM_TRACE_H + +#include <linux/tracepoint.h> + +TRACE_EVENT(hwptr, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t pos, bool irq), + TP_ARGS(substream, pos, irq), + TP_STRUCT__entry( + __field( bool, in_interrupt ) + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( snd_pcm_uframes_t, pos ) + __field( snd_pcm_uframes_t, period_size ) + __field( snd_pcm_uframes_t, buffer_size ) + __field( snd_pcm_uframes_t, old_hw_ptr ) + __field( snd_pcm_uframes_t, hw_ptr_base ) + ), + TP_fast_assign( + __entry->in_interrupt = (irq); + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->pos = (pos); + __entry->period_size = (substream)->runtime->period_size; + __entry->buffer_size = (substream)->runtime->buffer_size; + __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; + __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; + ), + TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu", + __entry->card, __entry->device, + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->number, + __entry->in_interrupt ? "IRQ" : "POS", + (unsigned long)__entry->pos, + (unsigned long)__entry->old_hw_ptr, + (unsigned long)__entry->hw_ptr_base, + (unsigned long)__entry->period_size, + (unsigned long)__entry->buffer_size) +); + +TRACE_EVENT(xrun, + TP_PROTO(struct snd_pcm_substream *substream), + TP_ARGS(substream), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( snd_pcm_uframes_t, period_size ) + __field( snd_pcm_uframes_t, buffer_size ) + __field( snd_pcm_uframes_t, old_hw_ptr ) + __field( snd_pcm_uframes_t, hw_ptr_base ) + ), + TP_fast_assign( + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->period_size = (substream)->runtime->period_size; + __entry->buffer_size = (substream)->runtime->buffer_size; + __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; + __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; + ), + TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu", + __entry->card, __entry->device, + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->number, + (unsigned long)__entry->old_hw_ptr, + (unsigned long)__entry->hw_ptr_base, + (unsigned long)__entry->period_size, + (unsigned long)__entry->buffer_size) +); + +TRACE_EVENT(hw_ptr_error, + TP_PROTO(struct snd_pcm_substream *substream, const char *why), + TP_ARGS(substream, why), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( const char *, reason ) + ), + TP_fast_assign( + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->reason = (why); + ), + TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s", + __entry->card, __entry->device, + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->number, __entry->reason) +); + +#endif /* _PCM_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include <trace/define_trace.h> diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index b9184d20c39f..b0e32e161dd1 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -403,14 +403,11 @@ free_devinfo(void *private) { struct seq_oss_devinfo *dp = (struct seq_oss_devinfo *)private; - if (dp->timer) - snd_seq_oss_timer_delete(dp->timer); + snd_seq_oss_timer_delete(dp->timer); - if (dp->writeq) - snd_seq_oss_writeq_delete(dp->writeq); + snd_seq_oss_writeq_delete(dp->writeq); - if (dp->readq) - snd_seq_oss_readq_delete(dp->readq); + snd_seq_oss_readq_delete(dp->readq); kfree(dp); } diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 712110561082..7e0aabb808a6 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -86,7 +86,6 @@ static int __init alsa_seq_init(void) { int err; - snd_seq_autoload_lock(); if ((err = client_init_data()) < 0) goto error; @@ -110,8 +109,8 @@ static int __init alsa_seq_init(void) if ((err = snd_seq_system_client_init()) < 0) goto error; + snd_seq_autoload_init(); error: - snd_seq_autoload_unlock(); return err; } @@ -131,6 +130,8 @@ static void __exit alsa_seq_exit(void) /* release event memory */ snd_sequencer_memory_done(); + + snd_seq_autoload_exit(); } module_init(alsa_seq_init) diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 91a786a783e1..0631bdadd12b 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -56,6 +56,7 @@ MODULE_LICENSE("GPL"); #define DRIVER_LOADED (1<<0) #define DRIVER_REQUESTED (1<<1) #define DRIVER_LOCKED (1<<2) +#define DRIVER_REQUESTING (1<<3) struct ops_list { char id[ID_LEN]; /* driver id */ @@ -127,42 +128,82 @@ static void snd_seq_device_info(struct snd_info_entry *entry, #ifdef CONFIG_MODULES /* avoid auto-loading during module_init() */ -static int snd_seq_in_init; +static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ void snd_seq_autoload_lock(void) { - snd_seq_in_init++; + atomic_inc(&snd_seq_in_init); } void snd_seq_autoload_unlock(void) { - snd_seq_in_init--; + atomic_dec(&snd_seq_in_init); } -#endif -void snd_seq_device_load_drivers(void) +static void autoload_drivers(void) { -#ifdef CONFIG_MODULES - struct ops_list *ops; + /* avoid reentrance */ + if (atomic_inc_return(&snd_seq_in_init) == 1) { + struct ops_list *ops; + + mutex_lock(&ops_mutex); + list_for_each_entry(ops, &opslist, list) { + if ((ops->driver & DRIVER_REQUESTING) && + !(ops->driver & DRIVER_REQUESTED)) { + ops->used++; + mutex_unlock(&ops_mutex); + ops->driver |= DRIVER_REQUESTED; + request_module("snd-%s", ops->id); + mutex_lock(&ops_mutex); + ops->used--; + } + } + mutex_unlock(&ops_mutex); + } + atomic_dec(&snd_seq_in_init); +} - /* Calling request_module during module_init() - * may cause blocking. - */ - if (snd_seq_in_init) - return; +static void call_autoload(struct work_struct *work) +{ + autoload_drivers(); +} - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if (! (ops->driver & DRIVER_LOADED) && - ! (ops->driver & DRIVER_REQUESTED)) { - ops->used++; - mutex_unlock(&ops_mutex); - ops->driver |= DRIVER_REQUESTED; - request_module("snd-%s", ops->id); - mutex_lock(&ops_mutex); - ops->used--; - } +static DECLARE_WORK(autoload_work, call_autoload); + +static void try_autoload(struct ops_list *ops) +{ + if (!ops->driver) { + ops->driver |= DRIVER_REQUESTING; + schedule_work(&autoload_work); } +} + +static void queue_autoload_drivers(void) +{ + struct ops_list *ops; + + mutex_lock(&ops_mutex); + list_for_each_entry(ops, &opslist, list) + try_autoload(ops); mutex_unlock(&ops_mutex); +} + +void snd_seq_autoload_init(void) +{ + atomic_dec(&snd_seq_in_init); +#ifdef CONFIG_SND_SEQUENCER_MODULE + /* initial autoload only when snd-seq is a module */ + queue_autoload_drivers(); +#endif +} +#else +#define try_autoload(ops) /* NOP */ +#endif + +void snd_seq_device_load_drivers(void) +{ +#ifdef CONFIG_MODULES + queue_autoload_drivers(); + flush_work(&autoload_work); #endif } @@ -214,13 +255,14 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, ops->num_devices++; mutex_unlock(&ops->reg_mutex); - unlock_driver(ops); - if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { snd_seq_device_free(dev); return err; } + try_autoload(ops); + unlock_driver(ops); + if (result) *result = dev; @@ -318,16 +360,12 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, entry->init_device == NULL || entry->free_device == NULL) return -EINVAL; - snd_seq_autoload_lock(); ops = find_driver(id, 1); - if (ops == NULL) { - snd_seq_autoload_unlock(); + if (ops == NULL) return -ENOMEM; - } if (ops->driver & DRIVER_LOADED) { pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); unlock_driver(ops); - snd_seq_autoload_unlock(); return -EBUSY; } @@ -344,7 +382,6 @@ int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, mutex_unlock(&ops->reg_mutex); unlock_driver(ops); - snd_seq_autoload_unlock(); return 0; } @@ -554,6 +591,9 @@ static int __init alsa_seq_device_init(void) static void __exit alsa_seq_device_exit(void) { +#ifdef CONFIG_MODULES + cancel_work_sync(&autoload_work); +#endif remove_drivers(); #ifdef CONFIG_PROC_FS snd_info_free_entry(info_entry); @@ -570,6 +610,7 @@ EXPORT_SYMBOL(snd_seq_device_new); EXPORT_SYMBOL(snd_seq_device_register_driver); EXPORT_SYMBOL(snd_seq_device_unregister_driver); #ifdef CONFIG_MODULES +EXPORT_SYMBOL(snd_seq_autoload_init); EXPORT_SYMBOL(snd_seq_autoload_lock); EXPORT_SYMBOL(snd_seq_autoload_unlock); #endif diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index 0a418503ec41..84fffabdd129 100644 --- a/sound/core/sgbuf.c +++ b/sound/core/sgbuf.c @@ -39,8 +39,7 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) if (! sgbuf) return -EINVAL; - if (dmab->area) - vunmap(dmab->area); + vunmap(dmab->area); dmab->area = NULL; tmpb.dev.type = SNDRV_DMA_TYPE_DEV; diff --git a/sound/core/sound.c b/sound/core/sound.c index 38ad1a0dd3f7..f1333060bf1c 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -355,8 +355,13 @@ int snd_unregister_device(int type, struct snd_card *card, int dev) EXPORT_SYMBOL(snd_unregister_device); -/* get the assigned device to the given type and device number; - * the caller needs to release it via put_device() after using it +/** + * snd_get_device - get the assigned device to the given type and device number + * @type: the device type, SNDRV_DEVICE_TYPE_XXX + * @card:the card instance + * @dev: the device index + * + * The caller needs to release it via put_device() after using it. */ struct device *snd_get_device(int type, struct snd_card *card, int dev) { diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index f5fd448dbc57..0388fbbd2c06 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -604,21 +604,11 @@ static struct snd_kcontrol_new mts64_ctl_smpte_time_frames = { static int snd_mts64_ctl_smpte_fps_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo) { - static char *texts[5] = { "24", - "25", - "29.97", - "30D", - "30" }; + static const char * const texts[5] = { + "24", "25", "29.97", "30D", "30" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl, diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index b178724295f3..d28d8706443c 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -99,30 +99,33 @@ static int snd_virmidi_probe(struct platform_device *devptr) if (midi_devs[dev] > MAX_MIDI_DEVICES) { snd_printk(KERN_WARNING - "too much midi devices for virmidi %d: " - "force to use %d\n", dev, MAX_MIDI_DEVICES); + "too much midi devices for virmidi %d: force to use %d\n", + dev, MAX_MIDI_DEVICES); midi_devs[dev] = MAX_MIDI_DEVICES; } for (idx = 0; idx < midi_devs[dev]; idx++) { struct snd_rawmidi *rmidi; struct snd_virmidi_dev *rdev; - if ((err = snd_virmidi_new(card, idx, &rmidi)) < 0) + + err = snd_virmidi_new(card, idx, &rmidi); + if (err < 0) goto __nodev; rdev = rmidi->private_data; vmidi->midi[idx] = rmidi; strcpy(rmidi->name, "Virtual Raw MIDI"); rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH; } - + strcpy(card->driver, "VirMIDI"); strcpy(card->shortname, "VirMIDI"); sprintf(card->longname, "Virtual MIDI Card %i", dev + 1); - if ((err = snd_card_register(card)) == 0) { + err = snd_card_register(card); + if (!err) { platform_set_drvdata(devptr, card); return 0; } - __nodev: +__nodev: snd_card_free(card); return err; } @@ -157,13 +160,15 @@ static int __init alsa_card_virmidi_init(void) { int i, cards, err; - if ((err = platform_driver_register(&snd_virmidi_driver)) < 0) + err = platform_driver_register(&snd_virmidi_driver); + if (err < 0) return err; cards = 0; for (i = 0; i < SNDRV_CARDS; i++) { struct platform_device *device; - if (! enable[i]) + + if (!enable[i]) continue; device = platform_device_register_simple(SND_VIRMIDI_DRIVER, i, NULL, 0); diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index e8cc16993903..fc05a37fd017 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -416,6 +416,7 @@ int vx_send_rih(struct vx_core *chip, int cmd) /** * snd_vx_boot_xilinx - boot up the xilinx interface + * @chip: VX core instance * @boot: the boot record to load */ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot) @@ -538,6 +539,8 @@ EXPORT_SYMBOL(snd_vx_threaded_irq_handler); /** * snd_vx_irq_handler - interrupt handler + * @irq: irq number + * @dev: VX core instance */ irqreturn_t snd_vx_irq_handler(int irq, void *dev) { @@ -649,6 +652,8 @@ static void vx_proc_init(struct vx_core *chip) /** * snd_vx_dsp_boot - load the DSP boot + * @chip: VX core instance + * @boot: firmware data */ int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot) { @@ -669,6 +674,8 @@ EXPORT_SYMBOL(snd_vx_dsp_boot); /** * snd_vx_dsp_load - load the DSP image + * @chip: VX core instance + * @dsp: firmware data */ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp) { @@ -768,7 +775,10 @@ EXPORT_SYMBOL(snd_vx_resume); /** * snd_vx_create - constructor for struct vx_core + * @card: card instance * @hw: hardware specific record + * @ops: VX ops pointer + * @extra_size: extra byte size to allocate appending to chip * * this function allocates the instance and prepare for the hardware * initialization. diff --git a/sound/drivers/vx/vx_mixer.c b/sound/drivers/vx/vx_mixer.c index 3b6823fc0606..be9477e30739 100644 --- a/sound/drivers/vx/vx_mixer.c +++ b/sound/drivers/vx/vx_mixer.c @@ -471,30 +471,18 @@ static struct snd_kcontrol_new vx_control_output_level = { */ static int vx_audio_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts_mic[3] = { + static const char * const texts_mic[3] = { "Digital", "Line", "Mic" }; - static char *texts_vx2[2] = { + static const char * const texts_vx2[2] = { "Digital", "Analog" }; struct vx_core *chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (chip->type >= VX_TYPE_VXPOCKET) { - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, - texts_mic[uinfo->value.enumerated.item]); - } else { - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts_vx2[uinfo->value.enumerated.item]); - } - return 0; + if (chip->type >= VX_TYPE_VXPOCKET) + return snd_ctl_enum_info(uinfo, 1, 3, texts_mic); + else + return snd_ctl_enum_info(uinfo, 1, 2, texts_vx2); } static int vx_audio_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -539,18 +527,11 @@ static struct snd_kcontrol_new vx_control_audio_src = { */ static int vx_clock_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Auto", "Internal", "External" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int vx_clock_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 46dff64908c8..ecec547782b2 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -13,28 +13,34 @@ config SND_FIREWIRE_LIB select SND_RAWMIDI config SND_DICE - tristate "DICE-based DACs (EXPERIMENTAL)" + tristate "DICE-based DACs support" select SND_HWDEP select SND_FIREWIRE_LIB help Say Y here to include support for many DACs based on the DICE - chip family (DICE-II/Jr/Mini) from TC Applied Technologies. - - At the moment, this driver supports playback only. If you - want to use devices that support capturing, use FFADO instead. + chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces. To compile this driver as a module, choose M here: the module will be called snd-dice. -config SND_FIREWIRE_SPEAKERS - tristate "FireWire speakers" +config SND_OXFW + tristate "Oxford Semiconductor FW970/971 chipset support" select SND_FIREWIRE_LIB + select SND_HWDEP help - Say Y here to include support for the Griffin FireWave Surround - and the LaCie FireWire Speakers. + Say Y here to include support for FireWire devices based on + Oxford Semiconductor FW970/971 chipset. + * Griffin Firewave + * LaCie Firewire Speakers + * Behringer F-Control Audio 202 + * Mackie(Loud) Onyx-i series (former models) + * Mackie(Loud) Onyx Satellite + * Mackie(Loud) Tapco Link.Firewire + * Mackie(Loud) d.2 pro/d.4 pro + * Mackie(Loud) U.420/U.420d To compile this driver as a module, choose M here: the module - will be called snd-firewire-speakers. + will be called snd-oxfw. config SND_ISIGHT tristate "Apple iSight microphone" diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index fad8d49306ab..8b37f084b2ab 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,13 +1,12 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp.o -snd-dice-objs := dice.o -snd-firewire-speakers-objs := speakers.o +snd-oxfw-objs := oxfw.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o -obj-$(CONFIG_SND_DICE) += snd-dice.o -obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o +obj-$(CONFIG_SND_DICE) += dice/ +obj-$(CONFIG_SND_OXFW) += oxfw/ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o obj-$(CONFIG_SND_FIREWORKS) += fireworks/ diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 95fc2eaf11dc..3badc70124ab 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -1006,11 +1006,7 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s) struct snd_pcm_substream *pcm; pcm = ACCESS_ONCE(s->pcm); - if (pcm) { - snd_pcm_stream_lock_irq(pcm); - if (snd_pcm_running(pcm)) - snd_pcm_stop(pcm, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irq(pcm); - } + if (pcm) + snd_pcm_stop_xrun(pcm); } EXPORT_SYMBOL(amdtp_stream_pcm_abort); diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index 4823c08196ac..e6e8926275b0 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -23,7 +23,7 @@ * corresponds to the end of event in the packet. Out of IEC 61883. * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets. * The value of data_block_quadlets is used instead of reported value. - * @SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is + * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is * skipped for detecting discontinuity. * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first * packet is not continuous from an initial value. @@ -43,7 +43,27 @@ enum cip_flags { }; /** - * enum cip_sfc - a stream's sample rate + * enum cip_sfc - supported Sampling Frequency Codes (SFCs) + * @CIP_SFC_32000: 32,000 data blocks + * @CIP_SFC_44100: 44,100 data blocks + * @CIP_SFC_48000: 48,000 data blocks + * @CIP_SFC_88200: 88,200 data blocks + * @CIP_SFC_96000: 96,000 data blocks + * @CIP_SFC_176400: 176,400 data blocks + * @CIP_SFC_192000: 192,000 data blocks + * @CIP_SFC_COUNT: the number of supported SFCs + * + * These values are used to show nominal Sampling Frequency Code in + * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002, + * this code means the number of events per second. Actually the code + * represents the number of data blocks transferred per second in an AMDTP + * stream. + * + * In IEC 61883-6:2005, some extensions were added to support more types of + * data such as 'One Bit LInear Audio', therefore the meaning of SFC became + * different depending on the types. + * + * Currently our implementation is compatible with IEC 61883-6:2002. */ enum cip_sfc { CIP_SFC_32000 = 0, diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index e13eef99c27a..dfbcd233178c 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -52,7 +52,7 @@ extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES]; #define SND_BEBOB_CLOCK_INTERNAL "Internal" struct snd_bebob_clock_spec { unsigned int num; - char *const *labels; + const char *const *labels; int (*get)(struct snd_bebob *bebob, unsigned int *id); }; struct snd_bebob_rate_spec { @@ -61,7 +61,7 @@ struct snd_bebob_rate_spec { }; struct snd_bebob_meter_spec { unsigned int num; - char *const *labels; + const char *const *labels; int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size); }; struct snd_bebob_spec { diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c index 3b052ed0fbf5..fc67c1b7cb5b 100644 --- a/sound/firewire/bebob/bebob_focusrite.c +++ b/sound/firewire/bebob/bebob_focusrite.c @@ -103,10 +103,10 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value) &data, sizeof(__be32), 0); } -static char *const saffirepro_10_clk_src_labels[] = { +static const char *const saffirepro_10_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock" }; -static char *const saffirepro_26_clk_src_labels[] = { +static const char *const saffirepro_26_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock" }; /* Value maps between registers and labels for SaffirePro 10/26. */ @@ -195,7 +195,7 @@ end: } struct snd_bebob_spec saffire_le_spec; -static char *const saffire_both_clk_src_labels[] = { +static const char *const saffire_both_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "S/PDIF" }; static int @@ -210,12 +210,12 @@ saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id) return err; }; -static char *const saffire_le_meter_labels[] = { +static const char *const saffire_le_meter_labels[] = { ANA_IN, ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, STM_IN, STM_IN }; -static char *const saffire_meter_labels[] = { +static const char *const saffire_meter_labels[] = { ANA_IN, ANA_IN, STM_IN, STM_IN, STM_IN, STM_IN, STM_IN, }; diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c index 70faa3a32526..a422aaa3bb0c 100644 --- a/sound/firewire/bebob/bebob_maudio.c +++ b/sound/firewire/bebob/bebob_maudio.c @@ -340,7 +340,7 @@ end: } /* Clock source control for special firmware */ -static char *const special_clk_labels[] = { +static const char *const special_clk_labels[] = { SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital", "Word Clock", SND_BEBOB_CLOCK_INTERNAL}; static int special_clk_get(struct snd_bebob *bebob, unsigned int *id) @@ -352,17 +352,8 @@ static int special_clk_get(struct snd_bebob *bebob, unsigned int *id) static int special_clk_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *einf) { - einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - einf->count = 1; - einf->value.enumerated.items = ARRAY_SIZE(special_clk_labels); - - if (einf->value.enumerated.item >= einf->value.enumerated.items) - einf->value.enumerated.item = einf->value.enumerated.items - 1; - - strcpy(einf->value.enumerated.name, - special_clk_labels[einf->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_labels), + special_clk_labels); } static int special_clk_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uval) @@ -438,23 +429,15 @@ static struct snd_kcontrol_new special_sync_ctl = { }; /* Digital input interface control for special firmware */ -static char *const special_dig_in_iface_labels[] = { +static const char *const special_dig_in_iface_labels[] = { "S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical" }; static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *einf) { - einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - einf->count = 1; - einf->value.enumerated.items = ARRAY_SIZE(special_dig_in_iface_labels); - - if (einf->value.enumerated.item >= einf->value.enumerated.items) - einf->value.enumerated.item = einf->value.enumerated.items - 1; - - strcpy(einf->value.enumerated.name, - special_dig_in_iface_labels[einf->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(einf, 1, + ARRAY_SIZE(special_dig_in_iface_labels), + special_dig_in_iface_labels); } static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uval) @@ -539,23 +522,15 @@ static struct snd_kcontrol_new special_dig_in_iface_ctl = { }; /* Digital output interface control for special firmware */ -static char *const special_dig_out_iface_labels[] = { +static const char *const special_dig_out_iface_labels[] = { "S/PDIF Optical and Coaxial", "ADAT Optical" }; static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *einf) { - einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - einf->count = 1; - einf->value.enumerated.items = ARRAY_SIZE(special_dig_out_iface_labels); - - if (einf->value.enumerated.item >= einf->value.enumerated.items) - einf->value.enumerated.item = einf->value.enumerated.items - 1; - - strcpy(einf->value.enumerated.name, - special_dig_out_iface_labels[einf->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(einf, 1, + ARRAY_SIZE(special_dig_out_iface_labels), + special_dig_out_iface_labels); } static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *uval) @@ -631,7 +606,7 @@ end: } /* Hardware metering for special firmware */ -static char *const special_meter_labels[] = { +static const char *const special_meter_labels[] = { ANA_IN, ANA_IN, ANA_IN, ANA_IN, SPDIF_IN, ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN, @@ -671,30 +646,30 @@ end: } /* last 4 bytes are omitted because it's clock info. */ -static char *const fw410_meter_labels[] = { +static const char *const fw410_meter_labels[] = { ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT, HP_OUT }; -static char *const audiophile_meter_labels[] = { +static const char *const audiophile_meter_labels[] = { ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, DIG_OUT, HP_OUT, AUX_OUT, }; -static char *const solo_meter_labels[] = { +static const char *const solo_meter_labels[] = { ANA_IN, DIG_IN, STRM_IN, STRM_IN, ANA_OUT, DIG_OUT }; /* no clock info */ -static char *const ozonic_meter_labels[] = { +static const char *const ozonic_meter_labels[] = { ANA_IN, ANA_IN, STRM_IN, STRM_IN, ANA_OUT, ANA_OUT }; /* TODO: need testers. these positions are based on authour's assumption */ -static char *const nrv10_meter_labels[] = { +static const char *const nrv10_meter_labels[] = { ANA_IN, ANA_IN, ANA_IN, ANA_IN, DIG_IN, ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c index 9940611f2e1b..ad635004d699 100644 --- a/sound/firewire/bebob/bebob_terratec.c +++ b/sound/firewire/bebob/bebob_terratec.c @@ -8,7 +8,7 @@ #include "./bebob.h" -static char *const phase88_rack_clk_src_labels[] = { +static const char *const phase88_rack_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock" }; static int @@ -34,7 +34,7 @@ end: return err; } -static char *const phase24_series_clk_src_labels[] = { +static const char *const phase24_series_clk_src_labels[] = { SND_BEBOB_CLOCK_INTERNAL, "Digital In" }; static int diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c index 9b7e798180ff..ef1fe3823a9c 100644 --- a/sound/firewire/bebob/bebob_yamaha.c +++ b/sound/firewire/bebob/bebob_yamaha.c @@ -28,7 +28,7 @@ * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless. */ -static char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"}; +static const char *const clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"}; static int clk_src_get(struct snd_bebob *bebob, unsigned int *id) { diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index ba8df5a1be39..ae3bc1940efa 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -114,6 +114,7 @@ static int pcr_modify(struct cmp_connection *c, * cmp_connection_init - initializes a connection manager * @c: the connection manager to initialize * @unit: a unit of the target device + * @direction: input or output * @pcr_index: the index of the iPCR/oPCR on the target device */ int cmp_connection_init(struct cmp_connection *c, @@ -154,6 +155,7 @@ EXPORT_SYMBOL(cmp_connection_init); /** * cmp_connection_check_used - check connection is already esablished or not * @c: the connection manager to be checked + * @used: the pointer to store the result of checking the connection */ int cmp_connection_check_used(struct cmp_connection *c, bool *used) { diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c deleted file mode 100644 index e3a04d69c853..000000000000 --- a/sound/firewire/dice.c +++ /dev/null @@ -1,1511 +0,0 @@ -/* - * TC Applied Technologies Digital Interface Communications Engine driver - * - * Copyright (c) Clemens Ladisch <clemens@ladisch.de> - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include <linux/compat.h> -#include <linux/completion.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/firewire.h> -#include <linux/firewire-constants.h> -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <sound/control.h> -#include <sound/core.h> -#include <sound/firewire.h> -#include <sound/hwdep.h> -#include <sound/info.h> -#include <sound/initval.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include "amdtp.h" -#include "iso-resources.h" -#include "lib.h" -#include "dice-interface.h" - - -struct dice { - struct snd_card *card; - struct fw_unit *unit; - spinlock_t lock; - struct mutex mutex; - unsigned int global_offset; - unsigned int rx_offset; - unsigned int clock_caps; - unsigned int rx_channels[3]; - unsigned int rx_midi_ports[3]; - struct fw_address_handler notification_handler; - int owner_generation; - int dev_lock_count; /* > 0 driver, < 0 userspace */ - bool dev_lock_changed; - bool global_enabled; - struct completion clock_accepted; - wait_queue_head_t hwdep_wait; - u32 notification_bits; - struct fw_iso_resources resources; - struct amdtp_stream stream; -}; - -MODULE_DESCRIPTION("DICE driver"); -MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); -MODULE_LICENSE("GPL v2"); - -static const unsigned int dice_rates[] = { - /* mode 0 */ - [0] = 32000, - [1] = 44100, - [2] = 48000, - /* mode 1 */ - [3] = 88200, - [4] = 96000, - /* mode 2 */ - [5] = 176400, - [6] = 192000, -}; - -static unsigned int rate_to_index(unsigned int rate) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if (dice_rates[i] == rate) - return i; - - return 0; -} - -static unsigned int rate_index_to_mode(unsigned int rate_index) -{ - return ((int)rate_index - 1) / 2; -} - -static void dice_lock_changed(struct dice *dice) -{ - dice->dev_lock_changed = true; - wake_up(&dice->hwdep_wait); -} - -static int dice_try_lock(struct dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count < 0) { - err = -EBUSY; - goto out; - } - - if (dice->dev_lock_count++ == 0) - dice_lock_changed(dice); - err = 0; - -out: - spin_unlock_irq(&dice->lock); - - return err; -} - -static void dice_unlock(struct dice *dice) -{ - spin_lock_irq(&dice->lock); - - if (WARN_ON(dice->dev_lock_count <= 0)) - goto out; - - if (--dice->dev_lock_count == 0) - dice_lock_changed(dice); - -out: - spin_unlock_irq(&dice->lock); -} - -static inline u64 global_address(struct dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->global_offset + offset; -} - -// TODO: rx index -static inline u64 rx_address(struct dice *dice, unsigned int offset) -{ - return DICE_PRIVATE_SPACE + dice->rx_offset + offset; -} - -static int dice_owner_set(struct dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err, errors = 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - for (;;) { - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, - TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | - dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - break; - } - if (err != -EAGAIN || ++errors >= 3) - break; - - msleep(20); - } - - kfree(buffer); - - return err; -} - -static int dice_owner_update(struct dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - int err; - - if (dice->owner_generation == -1) - return 0; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = cpu_to_be64(OWNER_NO_OWNER); - buffer[1] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - - dice->owner_generation = device->generation; - smp_rmb(); /* node_id vs. generation */ - err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, - FW_FIXED_GENERATION | dice->owner_generation); - - if (err == 0) { - if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { - dev_err(&dice->unit->device, - "device is already in use\n"); - err = -EBUSY; - } - } else if (err == -EAGAIN) { - err = 0; /* try again later */ - } - - kfree(buffer); - - if (err < 0) - dice->owner_generation = -1; - - return err; -} - -static void dice_owner_clear(struct dice *dice) -{ - struct fw_device *device = fw_parent_device(dice->unit); - __be64 *buffer; - - buffer = kmalloc(2 * 8, GFP_KERNEL); - if (!buffer) - return; - - buffer[0] = cpu_to_be64( - ((u64)device->card->node_id << OWNER_NODE_SHIFT) | - dice->notification_handler.offset); - buffer[1] = cpu_to_be64(OWNER_NO_OWNER); - snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, - global_address(dice, GLOBAL_OWNER), - buffer, 2 * 8, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - kfree(buffer); - - dice->owner_generation = -1; -} - -static int dice_enable_set(struct dice *dice) -{ - __be32 value; - int err; - - value = cpu_to_be32(1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, - FW_FIXED_GENERATION | dice->owner_generation); - if (err < 0) - return err; - - dice->global_enabled = true; - - return 0; -} - -static void dice_enable_clear(struct dice *dice) -{ - __be32 value; - - if (!dice->global_enabled) - return; - - value = 0; - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_ENABLE), - &value, 4, FW_QUIET | - FW_FIXED_GENERATION | dice->owner_generation); - - dice->global_enabled = false; -} - -static void dice_notification(struct fw_card *card, struct fw_request *request, - int tcode, int destination, int source, - int generation, unsigned long long offset, - void *data, size_t length, void *callback_data) -{ - struct dice *dice = callback_data; - u32 bits; - unsigned long flags; - - if (tcode != TCODE_WRITE_QUADLET_REQUEST) { - fw_send_response(card, request, RCODE_TYPE_ERROR); - return; - } - if ((offset & 3) != 0) { - fw_send_response(card, request, RCODE_ADDRESS_ERROR); - return; - } - - bits = be32_to_cpup(data); - - spin_lock_irqsave(&dice->lock, flags); - dice->notification_bits |= bits; - spin_unlock_irqrestore(&dice->lock, flags); - - fw_send_response(card, request, RCODE_COMPLETE); - - if (bits & NOTIFY_CLOCK_ACCEPTED) - complete(&dice->clock_accepted); - wake_up(&dice->hwdep_wait); -} - -static int dice_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct dice *dice = rule->private; - const struct snd_interval *channels = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval allowed_rates = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, mode; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { - mode = rate_index_to_mode(i); - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(channels, dice->rx_channels[mode])) { - allowed_rates.min = min(allowed_rates.min, - dice_rates[i]); - allowed_rates.max = max(allowed_rates.max, - dice_rates[i]); - } - } - - return snd_interval_refine(rate, &allowed_rates); -} - -static int dice_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct dice *dice = rule->private; - const struct snd_interval *rate = - hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval allowed_channels = { - .min = UINT_MAX, .max = 0, .integer = 1 - }; - unsigned int i, mode; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if ((dice->clock_caps & (1 << i)) && - snd_interval_test(rate, dice_rates[i])) { - mode = rate_index_to_mode(i); - allowed_channels.min = min(allowed_channels.min, - dice->rx_channels[mode]); - allowed_channels.max = max(allowed_channels.max, - dice->rx_channels[mode]); - } - - return snd_interval_refine(channels, &allowed_channels); -} - -static int dice_open(struct snd_pcm_substream *substream) -{ - static const struct snd_pcm_hardware hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = UINT_MAX, - .channels_max = 0, - .buffer_bytes_max = 16 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct dice *dice = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int i; - int err; - - err = dice_try_lock(dice); - if (err < 0) - goto error; - - runtime->hw = hardware; - - for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) - if (dice->clock_caps & (1 << i)) - runtime->hw.rates |= - snd_pcm_rate_to_rate_bit(dice_rates[i]); - snd_pcm_limit_hw_rates(runtime); - - for (i = 0; i < 3; ++i) - if (dice->rx_channels[i]) { - runtime->hw.channels_min = min(runtime->hw.channels_min, - dice->rx_channels[i]); - runtime->hw.channels_max = max(runtime->hw.channels_max, - dice->rx_channels[i]); - } - - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - dice_rate_constraint, dice, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - goto err_lock; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - dice_channels_constraint, dice, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - goto err_lock; - - err = amdtp_stream_add_pcm_hw_constraints(&dice->stream, runtime); - if (err < 0) - goto err_lock; - - return 0; - -err_lock: - dice_unlock(dice); -error: - return err; -} - -static int dice_close(struct snd_pcm_substream *substream) -{ - struct dice *dice = substream->private_data; - - dice_unlock(dice); - - return 0; -} - -static int dice_stream_start_packets(struct dice *dice) -{ - int err; - - if (amdtp_stream_running(&dice->stream)) - return 0; - - err = amdtp_stream_start(&dice->stream, dice->resources.channel, - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - return err; - - err = dice_enable_set(dice); - if (err < 0) { - amdtp_stream_stop(&dice->stream); - return err; - } - - return 0; -} - -static int dice_stream_start(struct dice *dice) -{ - __be32 channel; - int err; - - if (!dice->resources.allocated) { - err = fw_iso_resources_allocate(&dice->resources, - amdtp_stream_get_max_payload(&dice->stream), - fw_parent_device(dice->unit)->max_speed); - if (err < 0) - goto error; - - channel = cpu_to_be32(dice->resources.channel); - err = snd_fw_transaction(dice->unit, - TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), - &channel, 4, 0); - if (err < 0) - goto err_resources; - } - - err = dice_stream_start_packets(dice); - if (err < 0) - goto err_rx_channel; - - return 0; - -err_rx_channel: - channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); -err_resources: - fw_iso_resources_free(&dice->resources); -error: - return err; -} - -static void dice_stream_stop_packets(struct dice *dice) -{ - if (amdtp_stream_running(&dice->stream)) { - dice_enable_clear(dice); - amdtp_stream_stop(&dice->stream); - } -} - -static void dice_stream_stop(struct dice *dice) -{ - __be32 channel; - - dice_stream_stop_packets(dice); - - if (!dice->resources.allocated) - return; - - channel = cpu_to_be32((u32)-1); - snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); - - fw_iso_resources_free(&dice->resources); -} - -static int dice_change_rate(struct dice *dice, unsigned int clock_rate) -{ - __be32 value; - int err; - - reinit_completion(&dice->clock_accepted); - - value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &value, 4, 0); - if (err < 0) - return err; - - if (!wait_for_completion_timeout(&dice->clock_accepted, - msecs_to_jiffies(100))) - dev_warn(&dice->unit->device, "clock change timed out\n"); - - return 0; -} - -static int dice_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct dice *dice = substream->private_data; - unsigned int rate_index, mode, rate, channels, i; - int err; - - mutex_lock(&dice->mutex); - dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - return err; - - rate = params_rate(hw_params); - rate_index = rate_to_index(rate); - err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); - if (err < 0) - return err; - - /* - * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in - * one data block of AMDTP packet. Thus sampling transfer frequency is - * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are - * transferred on AMDTP packets at 96 kHz. Two successive samples of a - * channel are stored consecutively in the packet. This quirk is called - * as 'Dual Wire'. - * For this quirk, blocking mode is required and PCM buffer size should - * be aligned to SYT_INTERVAL. - */ - channels = params_channels(hw_params); - if (rate_index > 4) { - if (channels > AMDTP_MAX_CHANNELS_FOR_PCM / 2) { - err = -ENOSYS; - return err; - } - - rate /= 2; - channels *= 2; - dice->stream.double_pcm_frames = true; - } else { - dice->stream.double_pcm_frames = false; - } - - mode = rate_index_to_mode(rate_index); - amdtp_stream_set_parameters(&dice->stream, rate, channels, - dice->rx_midi_ports[mode]); - if (rate_index > 4) { - channels /= 2; - - for (i = 0; i < channels; i++) { - dice->stream.pcm_positions[i] = i * 2; - dice->stream.pcm_positions[i + channels] = i * 2 + 1; - } - } - - amdtp_stream_set_pcm_format(&dice->stream, - params_format(hw_params)); - - return 0; -} - -static int dice_hw_free(struct snd_pcm_substream *substream) -{ - struct dice *dice = substream->private_data; - - mutex_lock(&dice->mutex); - dice_stream_stop(dice); - mutex_unlock(&dice->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int dice_prepare(struct snd_pcm_substream *substream) -{ - struct dice *dice = substream->private_data; - int err; - - mutex_lock(&dice->mutex); - - if (amdtp_streaming_error(&dice->stream)) - dice_stream_stop_packets(dice); - - err = dice_stream_start(dice); - if (err < 0) { - mutex_unlock(&dice->mutex); - return err; - } - - mutex_unlock(&dice->mutex); - - amdtp_stream_pcm_prepare(&dice->stream); - - return 0; -} - -static int dice_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct dice *dice = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&dice->stream, pcm); - - return 0; -} - -static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) -{ - struct dice *dice = substream->private_data; - - return amdtp_stream_pcm_pointer(&dice->stream); -} - -static int dice_create_pcm(struct dice *dice) -{ - static struct snd_pcm_ops ops = { - .open = dice_open, - .close = dice_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = dice_hw_params, - .hw_free = dice_hw_free, - .prepare = dice_prepare, - .trigger = dice_trigger, - .pointer = dice_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = dice; - strcpy(pcm->name, dice->card->shortname); - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops; - - return 0; -} - -static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, - long count, loff_t *offset) -{ - struct dice *dice = hwdep->private_data; - DEFINE_WAIT(wait); - union snd_firewire_event event; - - spin_lock_irq(&dice->lock); - - while (!dice->dev_lock_changed && dice->notification_bits == 0) { - prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); - spin_unlock_irq(&dice->lock); - schedule(); - finish_wait(&dice->hwdep_wait, &wait); - if (signal_pending(current)) - return -ERESTARTSYS; - spin_lock_irq(&dice->lock); - } - - memset(&event, 0, sizeof(event)); - if (dice->dev_lock_changed) { - event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; - event.lock_status.status = dice->dev_lock_count > 0; - dice->dev_lock_changed = false; - - count = min(count, (long)sizeof(event.lock_status)); - } else { - event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; - event.dice_notification.notification = dice->notification_bits; - dice->notification_bits = 0; - - count = min(count, (long)sizeof(event.dice_notification)); - } - - spin_unlock_irq(&dice->lock); - - if (copy_to_user(buf, &event, count)) - return -EFAULT; - - return count; -} - -static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, - poll_table *wait) -{ - struct dice *dice = hwdep->private_data; - unsigned int events; - - poll_wait(file, &dice->hwdep_wait, wait); - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_changed || dice->notification_bits != 0) - events = POLLIN | POLLRDNORM; - else - events = 0; - spin_unlock_irq(&dice->lock); - - return events; -} - -static int dice_hwdep_get_info(struct dice *dice, void __user *arg) -{ - struct fw_device *dev = fw_parent_device(dice->unit); - struct snd_firewire_get_info info; - - memset(&info, 0, sizeof(info)); - info.type = SNDRV_FIREWIRE_TYPE_DICE; - info.card = dev->card->index; - *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); - *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); - strlcpy(info.device_name, dev_name(&dev->device), - sizeof(info.device_name)); - - if (copy_to_user(arg, &info, sizeof(info))) - return -EFAULT; - - return 0; -} - -static int dice_hwdep_lock(struct dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == 0) { - dice->dev_lock_count = -1; - err = 0; - } else { - err = -EBUSY; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_unlock(struct dice *dice) -{ - int err; - - spin_lock_irq(&dice->lock); - - if (dice->dev_lock_count == -1) { - dice->dev_lock_count = 0; - err = 0; - } else { - err = -EBADFD; - } - - spin_unlock_irq(&dice->lock); - - return err; -} - -static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) -{ - struct dice *dice = hwdep->private_data; - - spin_lock_irq(&dice->lock); - if (dice->dev_lock_count == -1) - dice->dev_lock_count = 0; - spin_unlock_irq(&dice->lock); - - return 0; -} - -static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct dice *dice = hwdep->private_data; - - switch (cmd) { - case SNDRV_FIREWIRE_IOCTL_GET_INFO: - return dice_hwdep_get_info(dice, (void __user *)arg); - case SNDRV_FIREWIRE_IOCTL_LOCK: - return dice_hwdep_lock(dice); - case SNDRV_FIREWIRE_IOCTL_UNLOCK: - return dice_hwdep_unlock(dice); - default: - return -ENOIOCTLCMD; - } -} - -#ifdef CONFIG_COMPAT -static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return dice_hwdep_ioctl(hwdep, file, cmd, - (unsigned long)compat_ptr(arg)); -} -#else -#define dice_hwdep_compat_ioctl NULL -#endif - -static int dice_create_hwdep(struct dice *dice) -{ - static const struct snd_hwdep_ops ops = { - .read = dice_hwdep_read, - .release = dice_hwdep_release, - .poll = dice_hwdep_poll, - .ioctl = dice_hwdep_ioctl, - .ioctl_compat = dice_hwdep_compat_ioctl, - }; - struct snd_hwdep *hwdep; - int err; - - err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); - if (err < 0) - return err; - strcpy(hwdep->name, "DICE"); - hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; - hwdep->ops = ops; - hwdep->private_data = dice; - hwdep->exclusive = true; - - return 0; -} - -static int dice_proc_read_mem(struct dice *dice, void *buffer, - unsigned int offset_q, unsigned int quadlets) -{ - unsigned int i; - int err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE + 4 * offset_q, - buffer, 4 * quadlets, 0); - if (err < 0) - return err; - - for (i = 0; i < quadlets; ++i) - be32_to_cpus(&((u32 *)buffer)[i]); - - return 0; -} - -static const char *str_from_array(const char *const strs[], unsigned int count, - unsigned int i) -{ - if (i < count) - return strs[i]; - else - return "(unknown)"; -} - -static void dice_proc_fixup_string(char *s, unsigned int size) -{ - unsigned int i; - - for (i = 0; i < size; i += 4) - cpu_to_le32s((u32 *)(s + i)); - - for (i = 0; i < size - 2; ++i) { - if (s[i] == '\0') - return; - if (s[i] == '\\' && s[i + 1] == '\\') { - s[i + 2] = '\0'; - return; - } - } - s[size - 1] = '\0'; -} - -static void dice_proc_read(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) -{ - static const char *const section_names[5] = { - "global", "tx", "rx", "ext_sync", "unused2" - }; - static const char *const clock_sources[] = { - "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", - "wc", "arx1", "arx2", "arx3", "arx4", "internal" - }; - static const char *const rates[] = { - "32000", "44100", "48000", "88200", "96000", "176400", "192000", - "any low", "any mid", "any high", "none" - }; - struct dice *dice = entry->private_data; - u32 sections[ARRAY_SIZE(section_names) * 2]; - struct { - u32 number; - u32 size; - } tx_rx_header; - union { - struct { - u32 owner_hi, owner_lo; - u32 notification; - char nick_name[NICK_NAME_SIZE]; - u32 clock_select; - u32 enable; - u32 status; - u32 extended_status; - u32 sample_rate; - u32 version; - u32 clock_caps; - char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; - } global; - struct { - u32 iso; - u32 number_audio; - u32 number_midi; - u32 speed; - char names[TX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } tx; - struct { - u32 iso; - u32 seq_start; - u32 number_audio; - u32 number_midi; - char names[RX_NAMES_SIZE]; - u32 ac3_caps; - u32 ac3_enable; - } rx; - struct { - u32 clock_source; - u32 locked; - u32 rate; - u32 adat_user_data; - } ext_sync; - } buf; - unsigned int quadlets, stream, i; - - if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) - return; - snd_iprintf(buffer, "sections:\n"); - for (i = 0; i < ARRAY_SIZE(section_names); ++i) - snd_iprintf(buffer, " %s: offset %u, size %u\n", - section_names[i], - sections[i * 2], sections[i * 2 + 1]); - - quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); - if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) - return; - snd_iprintf(buffer, "global:\n"); - snd_iprintf(buffer, " owner: %04x:%04x%08x\n", - buf.global.owner_hi >> 16, - buf.global.owner_hi & 0xffff, buf.global.owner_lo); - snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); - dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); - snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); - snd_iprintf(buffer, " clock select: %s %s\n", - str_from_array(clock_sources, ARRAY_SIZE(clock_sources), - buf.global.clock_select & CLOCK_SOURCE_MASK), - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.clock_select & CLOCK_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " enable: %u\n", buf.global.enable); - snd_iprintf(buffer, " status: %slocked %s\n", - buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", - str_from_array(rates, ARRAY_SIZE(rates), - (buf.global.status & - STATUS_NOMINAL_RATE_MASK) - >> CLOCK_RATE_SHIFT)); - snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); - snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); - snd_iprintf(buffer, " version: %u.%u.%u.%u\n", - (buf.global.version >> 24) & 0xff, - (buf.global.version >> 16) & 0xff, - (buf.global.version >> 8) & 0xff, - (buf.global.version >> 0) & 0xff); - if (quadlets >= 90) { - snd_iprintf(buffer, " clock caps:"); - for (i = 0; i <= 6; ++i) - if (buf.global.clock_caps & (1 << i)) - snd_iprintf(buffer, " %s", rates[i]); - for (i = 0; i <= 12; ++i) - if (buf.global.clock_caps & (1 << (16 + i))) - snd_iprintf(buffer, " %s", clock_sources[i]); - snd_iprintf(buffer, "\n"); - dice_proc_fixup_string(buf.global.clock_source_names, - CLOCK_SOURCE_NAMES_SIZE); - snd_iprintf(buffer, " clock source names: %s\n", - buf.global.clock_source_names); - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "tx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); - snd_iprintf(buffer, " audio channels: %u\n", - buf.tx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); - snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.tx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.tx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.tx.ac3_enable); - } - } - - if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) - return; - quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); - for (stream = 0; stream < tx_rx_header.number; ++stream) { - if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + - stream * tx_rx_header.size, - quadlets) < 0) - break; - snd_iprintf(buffer, "rx %u:\n", stream); - snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); - snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); - snd_iprintf(buffer, " audio channels: %u\n", - buf.rx.number_audio); - snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); - if (quadlets >= 68) { - dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); - snd_iprintf(buffer, " names: %s\n", buf.rx.names); - } - if (quadlets >= 70) { - snd_iprintf(buffer, " ac3 caps: %08x\n", - buf.rx.ac3_caps); - snd_iprintf(buffer, " ac3 enable: %08x\n", - buf.rx.ac3_enable); - } - } - - quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); - if (quadlets >= 4) { - if (dice_proc_read_mem(dice, &buf.ext_sync, - sections[6], 4) < 0) - return; - snd_iprintf(buffer, "ext status:\n"); - snd_iprintf(buffer, " clock source: %s\n", - str_from_array(clock_sources, - ARRAY_SIZE(clock_sources), - buf.ext_sync.clock_source)); - snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); - snd_iprintf(buffer, " rate: %s\n", - str_from_array(rates, ARRAY_SIZE(rates), - buf.ext_sync.rate)); - snd_iprintf(buffer, " adat user data: "); - if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) - snd_iprintf(buffer, "-\n"); - else - snd_iprintf(buffer, "%x\n", - buf.ext_sync.adat_user_data); - } -} - -static void dice_create_proc(struct dice *dice) -{ - struct snd_info_entry *entry; - - if (!snd_card_proc_new(dice->card, "dice", &entry)) - snd_info_set_text_ops(entry, dice, dice_proc_read); -} - -static void dice_card_free(struct snd_card *card) -{ - struct dice *dice = card->private_data; - - amdtp_stream_destroy(&dice->stream); - fw_core_remove_address_handler(&dice->notification_handler); - mutex_destroy(&dice->mutex); -} - -#define OUI_WEISS 0x001c6a - -#define DICE_CATEGORY_ID 0x04 -#define WEISS_CATEGORY_ID 0x00 - -static int dice_interface_check(struct fw_unit *unit) -{ - static const int min_values[10] = { - 10, 0x64 / 4, - 10, 0x18 / 4, - 10, 0x18 / 4, - 0, 0, - 0, 0, - }; - struct fw_device *device = fw_parent_device(unit); - struct fw_csr_iterator it; - int key, value, vendor = -1, model = -1, err; - unsigned int category, i; - __be32 pointers[ARRAY_SIZE(min_values)]; - __be32 tx_data[4]; - __be32 version; - - /* - * Check that GUID and unit directory are constructed according to DICE - * rules, i.e., that the specifier ID is the GUID's OUI, and that the - * GUID chip ID consists of the 8-bit category ID, the 10-bit product - * ID, and a 22-bit serial number. - */ - fw_csr_iterator_init(&it, unit->directory); - while (fw_csr_iterator_next(&it, &key, &value)) { - switch (key) { - case CSR_SPECIFIER_ID: - vendor = value; - break; - case CSR_MODEL: - model = value; - break; - } - } - if (vendor == OUI_WEISS) - category = WEISS_CATEGORY_ID; - else - category = DICE_CATEGORY_ID; - if (device->config_rom[3] != ((vendor << 8) | category) || - device->config_rom[4] >> 22 != model) - return -ENODEV; - - /* - * Check that the sub address spaces exist and are located inside the - * private address space. The minimum values are chosen so that all - * minimally required registers are included. - */ - err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return -ENODEV; - for (i = 0; i < ARRAY_SIZE(pointers); ++i) { - value = be32_to_cpu(pointers[i]); - if (value < min_values[i] || value >= 0x40000) - return -ENODEV; - } - - /* We support playback only. Let capture devices be handled by FFADO. */ - err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE + - be32_to_cpu(pointers[2]) * 4, - tx_data, sizeof(tx_data), 0); - if (err < 0 || (tx_data[0] && tx_data[3])) - return -ENODEV; - - /* - * Check that the implemented DICE driver specification major version - * number matches. - */ - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - DICE_PRIVATE_SPACE + - be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, - &version, 4, 0); - if (err < 0) - return -ENODEV; - if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { - dev_err(&unit->device, - "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); - return -ENODEV; - } - - return 0; -} - -static int highest_supported_mode_rate(struct dice *dice, unsigned int mode) -{ - int i; - - for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i) - if ((dice->clock_caps & (1 << i)) && - rate_index_to_mode(i) == mode) - return i; - - return -1; -} - -static int dice_read_mode_params(struct dice *dice, unsigned int mode) -{ - __be32 values[2]; - int rate_index, err; - - rate_index = highest_supported_mode_rate(dice, mode); - if (rate_index < 0) { - dice->rx_channels[mode] = 0; - dice->rx_midi_ports[mode] = 0; - return 0; - } - - err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); - if (err < 0) - return err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - rx_address(dice, RX_NUMBER_AUDIO), - values, 2 * 4, 0); - if (err < 0) - return err; - - dice->rx_channels[mode] = be32_to_cpu(values[0]); - dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); - - return 0; -} - -static int dice_read_params(struct dice *dice) -{ - __be32 pointers[6]; - __be32 value; - int mode, err; - - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - DICE_PRIVATE_SPACE, - pointers, sizeof(pointers), 0); - if (err < 0) - return err; - - dice->global_offset = be32_to_cpu(pointers[0]) * 4; - dice->rx_offset = be32_to_cpu(pointers[4]) * 4; - - /* some very old firmwares don't tell about their clock support */ - if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) { - err = snd_fw_transaction( - dice->unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_CAPABILITIES), - &value, 4, 0); - if (err < 0) - return err; - dice->clock_caps = be32_to_cpu(value); - } else { - /* this should be supported by any device */ - dice->clock_caps = CLOCK_CAP_RATE_44100 | - CLOCK_CAP_RATE_48000 | - CLOCK_CAP_SOURCE_ARX1 | - CLOCK_CAP_SOURCE_INTERNAL; - } - - for (mode = 2; mode >= 0; --mode) { - err = dice_read_mode_params(dice, mode); - if (err < 0) - return err; - } - - return 0; -} - -static void dice_card_strings(struct dice *dice) -{ - struct snd_card *card = dice->card; - struct fw_device *dev = fw_parent_device(dice->unit); - char vendor[32], model[32]; - unsigned int i; - int err; - - strcpy(card->driver, "DICE"); - - strcpy(card->shortname, "DICE"); - BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); - err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, - global_address(dice, GLOBAL_NICK_NAME), - card->shortname, sizeof(card->shortname), 0); - if (err >= 0) { - /* DICE strings are returned in "always-wrong" endianness */ - BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); - for (i = 0; i < sizeof(card->shortname); i += 4) - swab32s((u32 *)&card->shortname[i]); - card->shortname[sizeof(card->shortname) - 1] = '\0'; - } - - strcpy(vendor, "?"); - fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); - strcpy(model, "?"); - fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); - snprintf(card->longname, sizeof(card->longname), - "%s %s (serial %u) at %s, S%d", - vendor, model, dev->config_rom[4] & 0x3fffff, - dev_name(&dice->unit->device), 100 << dev->max_speed); - - strcpy(card->mixername, "DICE"); -} - -static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) -{ - struct snd_card *card; - struct dice *dice; - __be32 clock_sel; - int err; - - err = dice_interface_check(unit); - if (err < 0) - return err; - - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(*dice), &card); - if (err < 0) - return err; - - dice = card->private_data; - dice->card = card; - spin_lock_init(&dice->lock); - mutex_init(&dice->mutex); - dice->unit = unit; - init_completion(&dice->clock_accepted); - init_waitqueue_head(&dice->hwdep_wait); - - dice->notification_handler.length = 4; - dice->notification_handler.address_callback = dice_notification; - dice->notification_handler.callback_data = dice; - err = fw_core_add_address_handler(&dice->notification_handler, - &fw_high_memory_region); - if (err < 0) - goto err_mutex; - - err = dice_owner_set(dice); - if (err < 0) - goto err_notification_handler; - - err = dice_read_params(dice); - if (err < 0) - goto err_owner; - - err = fw_iso_resources_init(&dice->resources, unit); - if (err < 0) - goto err_owner; - dice->resources.channels_mask = 0x00000000ffffffffuLL; - - err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM, - CIP_BLOCKING); - if (err < 0) - goto err_resources; - - card->private_free = dice_card_free; - - dice_card_strings(dice); - - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); - if (err < 0) - goto error; - clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); - clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); - err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, - global_address(dice, GLOBAL_CLOCK_SELECT), - &clock_sel, 4, 0); - if (err < 0) - goto error; - - err = dice_create_pcm(dice); - if (err < 0) - goto error; - - err = dice_create_hwdep(dice); - if (err < 0) - goto error; - - dice_create_proc(dice); - - err = snd_card_register(card); - if (err < 0) - goto error; - - dev_set_drvdata(&unit->device, dice); - - return 0; - -err_resources: - fw_iso_resources_destroy(&dice->resources); -err_owner: - dice_owner_clear(dice); -err_notification_handler: - fw_core_remove_address_handler(&dice->notification_handler); -err_mutex: - mutex_destroy(&dice->mutex); -error: - snd_card_free(card); - return err; -} - -static void dice_remove(struct fw_unit *unit) -{ - struct dice *dice = dev_get_drvdata(&unit->device); - - amdtp_stream_pcm_abort(&dice->stream); - - snd_card_disconnect(dice->card); - - mutex_lock(&dice->mutex); - - dice_stream_stop(dice); - dice_owner_clear(dice); - - mutex_unlock(&dice->mutex); - - snd_card_free_when_closed(dice->card); -} - -static void dice_bus_reset(struct fw_unit *unit) -{ - struct dice *dice = dev_get_drvdata(&unit->device); - - /* - * On a bus reset, the DICE firmware disables streaming and then goes - * off contemplating its own navel for hundreds of milliseconds before - * it can react to any of our attempts to reenable streaming. This - * means that we lose synchronization anyway, so we force our streams - * to stop so that the application can restart them in an orderly - * manner. - */ - amdtp_stream_pcm_abort(&dice->stream); - - mutex_lock(&dice->mutex); - - dice->global_enabled = false; - dice_stream_stop_packets(dice); - - dice_owner_update(dice); - - fw_iso_resources_update(&dice->resources); - - mutex_unlock(&dice->mutex); -} - -#define DICE_INTERFACE 0x000001 - -static const struct ieee1394_device_id dice_id_table[] = { - { - .match_flags = IEEE1394_MATCH_VERSION, - .version = DICE_INTERFACE, - }, - { } -}; -MODULE_DEVICE_TABLE(ieee1394, dice_id_table); - -static struct fw_driver dice_driver = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - .bus = &fw_bus_type, - }, - .probe = dice_probe, - .update = dice_bus_reset, - .remove = dice_remove, - .id_table = dice_id_table, -}; - -static int __init alsa_dice_init(void) -{ - return driver_register(&dice_driver.driver); -} - -static void __exit alsa_dice_exit(void) -{ - driver_unregister(&dice_driver.driver); -} - -module_init(alsa_dice_init); -module_exit(alsa_dice_exit); diff --git a/sound/firewire/dice/Makefile b/sound/firewire/dice/Makefile new file mode 100644 index 000000000000..9ef228ef7baf --- /dev/null +++ b/sound/firewire/dice/Makefile @@ -0,0 +1,3 @@ +snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \ + dice-pcm.o dice-hwdep.o dice.o +obj-m += snd-dice.o diff --git a/sound/firewire/dice/dice-hwdep.c b/sound/firewire/dice/dice-hwdep.c new file mode 100644 index 000000000000..a4dc02a86f12 --- /dev/null +++ b/sound/firewire/dice/dice-hwdep.c @@ -0,0 +1,190 @@ +/* + * dice_hwdep.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp> + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, + long count, loff_t *offset) +{ + struct snd_dice *dice = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&dice->lock); + + while (!dice->dev_lock_changed && dice->notification_bits == 0) { + prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&dice->lock); + schedule(); + finish_wait(&dice->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&dice->lock); + } + + memset(&event, 0, sizeof(event)); + if (dice->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = dice->dev_lock_count > 0; + dice->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } else { + event.dice_notification.type = + SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.notification = dice->notification_bits; + dice->notification_bits = 0; + + count = min_t(long, count, sizeof(event.dice_notification)); + } + + spin_unlock_irq(&dice->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct snd_dice *dice = hwdep->private_data; + unsigned int events; + + poll_wait(file, &dice->hwdep_wait, wait); + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_changed || dice->notification_bits != 0) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&dice->lock); + + return events; +} + +static int hwdep_get_info(struct snd_dice *dice, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(dice->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_DICE; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int hwdep_lock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == 0) { + dice->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int hwdep_unlock(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == -1) { + dice->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_dice *dice = hwdep->private_data; + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_count == -1) + dice->dev_lock_count = 0; + spin_unlock_irq(&dice->lock); + + return 0; +} + +static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_dice *dice = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(dice, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(dice); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(dice); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +int snd_dice_create_hwdep(struct snd_dice *dice) +{ + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); + if (err < 0) + return err; + strcpy(hwdep->name, "DICE"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; + hwdep->ops = ops; + hwdep->private_data = dice; + hwdep->exclusive = true; + + return 0; +} diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice/dice-interface.h index 27b044f84c81..27b044f84c81 100644 --- a/sound/firewire/dice-interface.h +++ b/sound/firewire/dice/dice-interface.h diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c new file mode 100644 index 000000000000..fe43ce791f84 --- /dev/null +++ b/sound/firewire/dice/dice-midi.c @@ -0,0 +1,157 @@ +/* + * dice_midi.c - a part of driver for Dice based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ +#include "dice.h" + +static int midi_open(struct snd_rawmidi_substream *substream) +{ + struct snd_dice *dice = substream->rmidi->private_data; + int err; + + err = snd_dice_stream_lock_try(dice); + if (err < 0) + return err; + + mutex_lock(&dice->mutex); + + dice->substreams_counter++; + err = snd_dice_stream_start_duplex(dice, 0); + + mutex_unlock(&dice->mutex); + + if (err < 0) + snd_dice_stream_lock_release(dice); + + return err; +} + +static int midi_close(struct snd_rawmidi_substream *substream) +{ + struct snd_dice *dice = substream->rmidi->private_data; + + mutex_lock(&dice->mutex); + + dice->substreams_counter--; + snd_dice_stream_stop_duplex(dice); + + mutex_unlock(&dice->mutex); + + snd_dice_stream_lock_release(dice); + return 0; +} + +static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dice *dice = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dice->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&dice->tx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&dice->tx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dice->lock, flags); +} + +static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_dice *dice = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&dice->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&dice->rx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&dice->rx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&dice->lock, flags); +} + +static struct snd_rawmidi_ops capture_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_capture_trigger, +}; + +static struct snd_rawmidi_ops playback_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_playback_trigger, +}; + +static void set_midi_substream_names(struct snd_dice *dice, + struct snd_rawmidi_str *str) +{ + struct snd_rawmidi_substream *subs; + + list_for_each_entry(subs, &str->substreams, list) { + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", dice->card->shortname, subs->number + 1); + } +} + +int snd_dice_create_midi(struct snd_dice *dice) +{ + struct snd_rawmidi *rmidi; + struct snd_rawmidi_str *str; + unsigned int i, midi_in_ports, midi_out_ports; + int err; + + midi_in_ports = midi_out_ports = 0; + for (i = 0; i < 3; i++) { + midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports); + midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports); + } + + if (midi_in_ports + midi_out_ports == 0) + return 0; + + /* create midi ports */ + err = snd_rawmidi_new(dice->card, dice->card->driver, 0, + midi_out_ports, midi_in_ports, + &rmidi); + if (err < 0) + return err; + + snprintf(rmidi->name, sizeof(rmidi->name), + "%s MIDI", dice->card->shortname); + rmidi->private_data = dice; + + if (midi_in_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &capture_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + + set_midi_substream_names(dice, str); + } + + if (midi_out_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &playback_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + + set_midi_substream_names(dice, str); + } + + if ((midi_out_ports > 0) && (midi_in_ports > 0)) + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c new file mode 100644 index 000000000000..f77714511f8b --- /dev/null +++ b/sound/firewire/dice/dice-pcm.c @@ -0,0 +1,422 @@ +/* + * dice_pcm.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp> + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; + + const struct snd_interval *c = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval rates = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, rate, mode, *pcm_channels; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_channels; + else + pcm_channels = dice->rx_channels; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(c, pcm_channels[mode])) + continue; + + rates.min = min(rates.min, rate); + rates.max = max(rates.max, rate); + } + + return snd_interval_refine(r, &rates); +} + +static int dice_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_substream *substream = rule->private; + struct snd_dice *dice = substream->private_data; + + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval channels = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, rate, mode, *pcm_channels; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + pcm_channels = dice->tx_channels; + else + pcm_channels = dice->rx_channels; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + + if (!snd_interval_test(r, rate)) + continue; + + channels.min = min(channels.min, pcm_channels[mode]); + channels.max = max(channels.max, pcm_channels[mode]); + } + + return snd_interval_refine(c, &channels); +} + +static void limit_channels_and_rates(struct snd_dice *dice, + struct snd_pcm_runtime *runtime, + unsigned int *pcm_channels) +{ + struct snd_pcm_hardware *hw = &runtime->hw; + unsigned int i, rate, mode; + + hw->channels_min = UINT_MAX; + hw->channels_max = 0; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) { + rate = snd_dice_rates[i]; + if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0) + continue; + hw->rates |= snd_pcm_rate_to_rate_bit(rate); + + if (pcm_channels[mode] == 0) + continue; + hw->channels_min = min(hw->channels_min, pcm_channels[mode]); + hw->channels_max = max(hw->channels_max, pcm_channels[mode]); + } + + snd_pcm_limit_hw_rates(runtime); +} + +static void limit_period_and_buffer(struct snd_pcm_hardware *hw) +{ + hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ + hw->periods_max = UINT_MAX; + + hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */ + + /* Just to prevent from allocating much pages. */ + hw->period_bytes_max = hw->period_bytes_min * 2048; + hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; +} + +static int init_hw_info(struct snd_dice *dice, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hardware *hw = &runtime->hw; + struct amdtp_stream *stream; + unsigned int *pcm_channels; + int err; + + hw->info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | + SNDRV_PCM_INFO_BLOCK_TRANSFER; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + hw->formats = AMDTP_IN_PCM_FORMAT_BITS; + stream = &dice->tx_stream; + pcm_channels = dice->tx_channels; + } else { + hw->formats = AMDTP_OUT_PCM_FORMAT_BITS; + stream = &dice->rx_stream; + pcm_channels = dice->rx_channels; + } + + limit_channels_and_rates(dice, runtime, pcm_channels); + limit_period_and_buffer(hw); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, substream, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto end; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, substream, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto end; + + err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); +end: + return err; +} + +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + unsigned int source, rate; + bool internal; + int err; + + err = snd_dice_stream_lock_try(dice); + if (err < 0) + goto end; + + err = init_hw_info(dice, substream); + if (err < 0) + goto err_locked; + + err = snd_dice_transaction_get_clock_source(dice, &source); + if (err < 0) + goto err_locked; + switch (source) { + case CLOCK_SOURCE_AES1: + case CLOCK_SOURCE_AES2: + case CLOCK_SOURCE_AES3: + case CLOCK_SOURCE_AES4: + case CLOCK_SOURCE_AES_ANY: + case CLOCK_SOURCE_ADAT: + case CLOCK_SOURCE_TDIF: + case CLOCK_SOURCE_WC: + internal = false; + break; + default: + internal = true; + break; + } + + /* + * When source of clock is not internal or any PCM streams are running, + * available sampling rate is limited at current sampling rate. + */ + if (!internal || + amdtp_stream_pcm_running(&dice->tx_stream) || + amdtp_stream_pcm_running(&dice->rx_stream)) { + err = snd_dice_transaction_get_rate(dice, &rate); + if (err < 0) + goto err_locked; + substream->runtime->hw.rate_min = rate; + substream->runtime->hw.rate_max = rate; + } + + snd_pcm_set_sync(substream); +end: + return err; +err_locked: + snd_dice_stream_lock_release(dice); + return err; +} + +static int pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + snd_dice_stream_lock_release(dice); + + return 0; +} + +static int capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&dice->mutex); + dice->substreams_counter++; + mutex_unlock(&dice->mutex); + } + + amdtp_stream_set_pcm_format(&dice->tx_stream, + params_format(hw_params)); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} +static int playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_dice *dice = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&dice->mutex); + dice->substreams_counter++; + mutex_unlock(&dice->mutex); + } + + amdtp_stream_set_pcm_format(&dice->rx_stream, + params_format(hw_params)); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static int capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + dice->substreams_counter--; + + snd_dice_stream_stop_duplex(dice); + + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + dice->substreams_counter--; + + snd_dice_stream_stop_duplex(dice); + + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); + mutex_unlock(&dice->mutex); + if (err >= 0) + amdtp_stream_pcm_prepare(&dice->tx_stream); + + return 0; +} +static int playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + err = snd_dice_stream_start_duplex(dice, substream->runtime->rate); + mutex_unlock(&dice->mutex); + if (err >= 0) + amdtp_stream_pcm_prepare(&dice->rx_stream); + + return err; +} + +static int capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dice->tx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dice->tx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} +static int playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_dice *dice = substream->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + amdtp_stream_pcm_trigger(&dice->rx_stream, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + amdtp_stream_pcm_trigger(&dice->rx_stream, NULL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->tx_stream); +} +static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) +{ + struct snd_dice *dice = substream->private_data; + + return amdtp_stream_pcm_pointer(&dice->rx_stream); +} + +int snd_dice_create_pcm(struct snd_dice *dice) +{ + static struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = capture_hw_params, + .hw_free = capture_hw_free, + .prepare = capture_prepare, + .trigger = capture_trigger, + .pointer = capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + static struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = playback_hw_params, + .hw_free = playback_hw_free, + .prepare = playback_prepare, + .trigger = playback_trigger, + .pointer = playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + unsigned int i, capture, playback; + int err; + + capture = playback = 0; + for (i = 0; i < 3; i++) { + if (dice->tx_channels[i] > 0) + capture = 1; + if (dice->rx_channels[i] > 0) + playback = 1; + } + + err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + + if (capture > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); + + if (playback > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + + return 0; +} diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c new file mode 100644 index 000000000000..f5c1d1bced59 --- /dev/null +++ b/sound/firewire/dice/dice-proc.c @@ -0,0 +1,252 @@ +/* + * dice_proc.c - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +static int dice_proc_read_mem(struct snd_dice *dice, void *buffer, + unsigned int offset_q, unsigned int quadlets) +{ + unsigned int i; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + 4 * offset_q, + buffer, 4 * quadlets, 0); + if (err < 0) + return err; + + for (i = 0; i < quadlets; ++i) + be32_to_cpus(&((u32 *)buffer)[i]); + + return 0; +} + +static const char *str_from_array(const char *const strs[], unsigned int count, + unsigned int i) +{ + if (i < count) + return strs[i]; + + return "(unknown)"; +} + +static void dice_proc_fixup_string(char *s, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i += 4) + cpu_to_le32s((u32 *)(s + i)); + + for (i = 0; i < size - 2; ++i) { + if (s[i] == '\0') + return; + if (s[i] == '\\' && s[i + 1] == '\\') { + s[i + 2] = '\0'; + return; + } + } + s[size - 1] = '\0'; +} + +static void dice_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const section_names[5] = { + "global", "tx", "rx", "ext_sync", "unused2" + }; + static const char *const clock_sources[] = { + "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", + "wc", "arx1", "arx2", "arx3", "arx4", "internal" + }; + static const char *const rates[] = { + "32000", "44100", "48000", "88200", "96000", "176400", "192000", + "any low", "any mid", "any high", "none" + }; + struct snd_dice *dice = entry->private_data; + u32 sections[ARRAY_SIZE(section_names) * 2]; + struct { + u32 number; + u32 size; + } tx_rx_header; + union { + struct { + u32 owner_hi, owner_lo; + u32 notification; + char nick_name[NICK_NAME_SIZE]; + u32 clock_select; + u32 enable; + u32 status; + u32 extended_status; + u32 sample_rate; + u32 version; + u32 clock_caps; + char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; + } global; + struct { + u32 iso; + u32 number_audio; + u32 number_midi; + u32 speed; + char names[TX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } tx; + struct { + u32 iso; + u32 seq_start; + u32 number_audio; + u32 number_midi; + char names[RX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } rx; + struct { + u32 clock_source; + u32 locked; + u32 rate; + u32 adat_user_data; + } ext_sync; + } buf; + unsigned int quadlets, stream, i; + + if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) + return; + snd_iprintf(buffer, "sections:\n"); + for (i = 0; i < ARRAY_SIZE(section_names); ++i) + snd_iprintf(buffer, " %s: offset %u, size %u\n", + section_names[i], + sections[i * 2], sections[i * 2 + 1]); + + quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); + if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) + return; + snd_iprintf(buffer, "global:\n"); + snd_iprintf(buffer, " owner: %04x:%04x%08x\n", + buf.global.owner_hi >> 16, + buf.global.owner_hi & 0xffff, buf.global.owner_lo); + snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); + dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); + snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); + snd_iprintf(buffer, " clock select: %s %s\n", + str_from_array(clock_sources, ARRAY_SIZE(clock_sources), + buf.global.clock_select & CLOCK_SOURCE_MASK), + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.clock_select & CLOCK_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " enable: %u\n", buf.global.enable); + snd_iprintf(buffer, " status: %slocked %s\n", + buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.status & + STATUS_NOMINAL_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); + snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); + snd_iprintf(buffer, " version: %u.%u.%u.%u\n", + (buf.global.version >> 24) & 0xff, + (buf.global.version >> 16) & 0xff, + (buf.global.version >> 8) & 0xff, + (buf.global.version >> 0) & 0xff); + if (quadlets >= 90) { + snd_iprintf(buffer, " clock caps:"); + for (i = 0; i <= 6; ++i) + if (buf.global.clock_caps & (1 << i)) + snd_iprintf(buffer, " %s", rates[i]); + for (i = 0; i <= 12; ++i) + if (buf.global.clock_caps & (1 << (16 + i))) + snd_iprintf(buffer, " %s", clock_sources[i]); + snd_iprintf(buffer, "\n"); + dice_proc_fixup_string(buf.global.clock_source_names, + CLOCK_SOURCE_NAMES_SIZE); + snd_iprintf(buffer, " clock source names: %s\n", + buf.global.clock_source_names); + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "tx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); + snd_iprintf(buffer, " audio channels: %u\n", + buf.tx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); + snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.tx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.tx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.tx.ac3_enable); + } + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "rx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); + snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); + snd_iprintf(buffer, " audio channels: %u\n", + buf.rx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.rx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.rx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.rx.ac3_enable); + } + } + + quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); + if (quadlets >= 4) { + if (dice_proc_read_mem(dice, &buf.ext_sync, + sections[6], 4) < 0) + return; + snd_iprintf(buffer, "ext status:\n"); + snd_iprintf(buffer, " clock source: %s\n", + str_from_array(clock_sources, + ARRAY_SIZE(clock_sources), + buf.ext_sync.clock_source)); + snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); + snd_iprintf(buffer, " rate: %s\n", + str_from_array(rates, ARRAY_SIZE(rates), + buf.ext_sync.rate)); + snd_iprintf(buffer, " adat user data: "); + if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) + snd_iprintf(buffer, "-\n"); + else + snd_iprintf(buffer, "%x\n", + buf.ext_sync.adat_user_data); + } +} + +void snd_dice_create_proc(struct snd_dice *dice) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(dice->card, "dice", &entry)) + snd_info_set_text_ops(entry, dice, dice_proc_read); +} diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c new file mode 100644 index 000000000000..fa9cf761b610 --- /dev/null +++ b/sound/firewire/dice/dice-stream.c @@ -0,0 +1,407 @@ +/* + * dice_stream.c - a part of driver for DICE based devices + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Copyright (c) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp> + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +#define CALLBACK_TIMEOUT 200 + +const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = { + /* mode 0 */ + [0] = 32000, + [1] = 44100, + [2] = 48000, + /* mode 1 */ + [3] = 88200, + [4] = 96000, + /* mode 2 */ + [5] = 176400, + [6] = 192000, +}; + +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate, + unsigned int *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { + if (!(dice->clock_caps & BIT(i))) + continue; + if (snd_dice_rates[i] != rate) + continue; + + *mode = (i - 1) / 2; + return 0; + } + return -EINVAL; +} + +static void release_resources(struct snd_dice *dice, + struct fw_iso_resources *resources) +{ + unsigned int channel; + + /* Reset channel number */ + channel = cpu_to_be32((u32)-1); + if (resources == &dice->tx_resources) + snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); + + fw_iso_resources_free(resources); +} + +static int keep_resources(struct snd_dice *dice, + struct fw_iso_resources *resources, + unsigned int max_payload_bytes) +{ + unsigned int channel; + int err; + + err = fw_iso_resources_allocate(resources, max_payload_bytes, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + goto end; + + /* Set channel number */ + channel = cpu_to_be32(resources->channel); + if (resources == &dice->tx_resources) + err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS, + &channel, 4); + else + err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS, + &channel, 4); + if (err < 0) + release_resources(dice, resources); +end: + return err; +} + +static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream) +{ + amdtp_stream_pcm_abort(stream); + amdtp_stream_stop(stream); + + if (stream == &dice->tx_stream) + release_resources(dice, &dice->tx_resources); + else + release_resources(dice, &dice->rx_resources); +} + +static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream, + unsigned int rate) +{ + struct fw_iso_resources *resources; + unsigned int i, mode, pcm_chs, midi_ports; + int err; + + err = snd_dice_stream_get_rate_mode(dice, rate, &mode); + if (err < 0) + goto end; + if (stream == &dice->tx_stream) { + resources = &dice->tx_resources; + pcm_chs = dice->tx_channels[mode]; + midi_ports = dice->tx_midi_ports[mode]; + } else { + resources = &dice->rx_resources; + pcm_chs = dice->rx_channels[mode]; + midi_ports = dice->rx_midi_ports[mode]; + } + + /* + * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in + * one data block of AMDTP packet. Thus sampling transfer frequency is + * a half of PCM sampling frequency, i.e. PCM frames at 192.0 kHz are + * transferred on AMDTP packets at 96 kHz. Two successive samples of a + * channel are stored consecutively in the packet. This quirk is called + * as 'Dual Wire'. + * For this quirk, blocking mode is required and PCM buffer size should + * be aligned to SYT_INTERVAL. + */ + if (mode > 1) { + rate /= 2; + pcm_chs *= 2; + stream->double_pcm_frames = true; + } else { + stream->double_pcm_frames = false; + } + + amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports); + if (mode > 1) { + pcm_chs /= 2; + + for (i = 0; i < pcm_chs; i++) { + stream->pcm_positions[i] = i * 2; + stream->pcm_positions[i + pcm_chs] = i * 2 + 1; + } + } + + err = keep_resources(dice, resources, + amdtp_stream_get_max_payload(stream)); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to keep isochronous resources\n"); + goto end; + } + + err = amdtp_stream_start(stream, resources->channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + release_resources(dice, resources); +end: + return err; +} + +static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode) +{ + u32 source; + int err; + + err = snd_dice_transaction_get_clock_source(dice, &source); + if (err < 0) + goto end; + + switch (source) { + /* So-called 'SYT Match' modes, sync_to_syt value of packets received */ + case CLOCK_SOURCE_ARX4: /* in 4th stream */ + case CLOCK_SOURCE_ARX3: /* in 3rd stream */ + case CLOCK_SOURCE_ARX2: /* in 2nd stream */ + err = -ENOSYS; + break; + case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */ + *sync_mode = 0; + break; + default: + *sync_mode = CIP_SYNC_TO_DEVICE; + break; + } +end: + return err; +} + +int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate) +{ + struct amdtp_stream *master, *slave; + unsigned int curr_rate; + enum cip_flags sync_mode; + int err = 0; + + if (dice->substreams_counter == 0) + goto end; + + err = get_sync_mode(dice, &sync_mode); + if (err < 0) + goto end; + if (sync_mode == CIP_SYNC_TO_DEVICE) { + master = &dice->tx_stream; + slave = &dice->rx_stream; + } else { + master = &dice->rx_stream; + slave = &dice->tx_stream; + } + + /* Some packet queueing errors. */ + if (amdtp_streaming_error(master) || amdtp_streaming_error(slave)) + stop_stream(dice, master); + + /* Stop stream if rate is different. */ + err = snd_dice_transaction_get_rate(dice, &curr_rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to get sampling rate\n"); + goto end; + } + if (rate == 0) + rate = curr_rate; + if (rate != curr_rate) + stop_stream(dice, master); + + if (!amdtp_stream_running(master)) { + stop_stream(dice, slave); + snd_dice_transaction_clear_enable(dice); + + amdtp_stream_set_sync(sync_mode, master, slave); + + err = snd_dice_transaction_set_rate(dice, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to set sampling rate\n"); + goto end; + } + + /* Start both streams. */ + err = start_stream(dice, master, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to start AMDTP master stream\n"); + goto end; + } + err = start_stream(dice, slave, rate); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to start AMDTP slave stream\n"); + stop_stream(dice, master); + goto end; + } + err = snd_dice_transaction_set_enable(dice); + if (err < 0) { + dev_err(&dice->unit->device, + "fail to enable interface\n"); + stop_stream(dice, master); + stop_stream(dice, slave); + goto end; + } + + /* Wait first callbacks */ + if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) || + !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) { + snd_dice_transaction_clear_enable(dice); + stop_stream(dice, master); + stop_stream(dice, slave); + err = -ETIMEDOUT; + } + } +end: + return err; +} + +void snd_dice_stream_stop_duplex(struct snd_dice *dice) +{ + if (dice->substreams_counter > 0) + return; + + snd_dice_transaction_clear_enable(dice); + + stop_stream(dice, &dice->tx_stream); + stop_stream(dice, &dice->rx_stream); +} + +static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream) +{ + int err; + struct fw_iso_resources *resources; + enum amdtp_stream_direction dir; + + if (stream == &dice->tx_stream) { + resources = &dice->tx_resources; + dir = AMDTP_IN_STREAM; + } else { + resources = &dice->rx_resources; + dir = AMDTP_OUT_STREAM; + } + + err = fw_iso_resources_init(resources, dice->unit); + if (err < 0) + goto end; + resources->channels_mask = 0x00000000ffffffffuLL; + + err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING); + if (err < 0) { + amdtp_stream_destroy(stream); + fw_iso_resources_destroy(resources); + } +end: + return err; +} + +static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream) +{ + amdtp_stream_destroy(stream); + + if (stream == &dice->tx_stream) + fw_iso_resources_destroy(&dice->tx_resources); + else + fw_iso_resources_destroy(&dice->rx_resources); +} + +int snd_dice_stream_init_duplex(struct snd_dice *dice) +{ + int err; + + dice->substreams_counter = 0; + + err = init_stream(dice, &dice->tx_stream); + if (err < 0) + goto end; + + err = init_stream(dice, &dice->rx_stream); +end: + return err; +} + +void snd_dice_stream_destroy_duplex(struct snd_dice *dice) +{ + snd_dice_transaction_clear_enable(dice); + + stop_stream(dice, &dice->tx_stream); + destroy_stream(dice, &dice->tx_stream); + + stop_stream(dice, &dice->rx_stream); + destroy_stream(dice, &dice->rx_stream); + + dice->substreams_counter = 0; +} + +void snd_dice_stream_update_duplex(struct snd_dice *dice) +{ + /* + * On a bus reset, the DICE firmware disables streaming and then goes + * off contemplating its own navel for hundreds of milliseconds before + * it can react to any of our attempts to reenable streaming. This + * means that we lose synchronization anyway, so we force our streams + * to stop so that the application can restart them in an orderly + * manner. + */ + dice->global_enabled = false; + + stop_stream(dice, &dice->rx_stream); + stop_stream(dice, &dice->tx_stream); + + fw_iso_resources_update(&dice->rx_resources); + fw_iso_resources_update(&dice->tx_resources); +} + +static void dice_lock_changed(struct snd_dice *dice) +{ + dice->dev_lock_changed = true; + wake_up(&dice->hwdep_wait); +} + +int snd_dice_stream_lock_try(struct snd_dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count < 0) { + err = -EBUSY; + goto out; + } + + if (dice->dev_lock_count++ == 0) + dice_lock_changed(dice); + err = 0; +out: + spin_unlock_irq(&dice->lock); + return err; +} + +void snd_dice_stream_lock_release(struct snd_dice *dice) +{ + spin_lock_irq(&dice->lock); + + if (WARN_ON(dice->dev_lock_count <= 0)) + goto out; + + if (--dice->dev_lock_count == 0) + dice_lock_changed(dice); +out: + spin_unlock_irq(&dice->lock); +} diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c new file mode 100644 index 000000000000..aee746187665 --- /dev/null +++ b/sound/firewire/dice/dice-transaction.c @@ -0,0 +1,382 @@ +/* + * dice_transaction.c - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +#define NOTIFICATION_TIMEOUT_MS 100 + +static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type, + u64 offset) +{ + switch (type) { + case SND_DICE_ADDR_TYPE_TX: + offset += dice->tx_offset; + break; + case SND_DICE_ADDR_TYPE_RX: + offset += dice->rx_offset; + break; + case SND_DICE_ADDR_TYPE_SYNC: + offset += dice->sync_offset; + break; + case SND_DICE_ADDR_TYPE_RSRV: + offset += dice->rsrv_offset; + break; + case SND_DICE_ADDR_TYPE_GLOBAL: + default: + offset += dice->global_offset; + break; + } + offset += DICE_PRIVATE_SPACE; + return offset; +} + +int snd_dice_transaction_write(struct snd_dice *dice, + enum snd_dice_addr_type type, + unsigned int offset, void *buf, unsigned int len) +{ + return snd_fw_transaction(dice->unit, + (len == 4) ? TCODE_WRITE_QUADLET_REQUEST : + TCODE_WRITE_BLOCK_REQUEST, + get_subaddr(dice, type, offset), buf, len, 0); +} + +int snd_dice_transaction_read(struct snd_dice *dice, + enum snd_dice_addr_type type, unsigned int offset, + void *buf, unsigned int len) +{ + return snd_fw_transaction(dice->unit, + (len == 4) ? TCODE_READ_QUADLET_REQUEST : + TCODE_READ_BLOCK_REQUEST, + get_subaddr(dice, type, offset), buf, len, 0); +} + +static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info) +{ + return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT, + info, 4); +} + +static int set_clock_info(struct snd_dice *dice, + unsigned int rate, unsigned int source) +{ + unsigned int retries = 3; + unsigned int i; + __be32 info; + u32 mask; + u32 clock; + int err; +retry: + err = get_clock_info(dice, &info); + if (err < 0) + goto end; + + clock = be32_to_cpu(info); + if (source != UINT_MAX) { + mask = CLOCK_SOURCE_MASK; + clock &= ~mask; + clock |= source; + } + if (rate != UINT_MAX) { + for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { + if (snd_dice_rates[i] == rate) + break; + } + if (i == ARRAY_SIZE(snd_dice_rates)) { + err = -EINVAL; + goto end; + } + + mask = CLOCK_RATE_MASK; + clock &= ~mask; + clock |= i << CLOCK_RATE_SHIFT; + } + info = cpu_to_be32(clock); + + if (completion_done(&dice->clock_accepted)) + reinit_completion(&dice->clock_accepted); + + err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, + &info, 4); + if (err < 0) + goto end; + + /* Timeout means it's invalid request, probably bus reset occurred. */ + if (wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) { + if (retries-- == 0) { + err = -ETIMEDOUT; + goto end; + } + + err = snd_dice_transaction_reinit(dice); + if (err < 0) + goto end; + + msleep(500); /* arbitrary */ + goto retry; + } +end: + return err; +} + +int snd_dice_transaction_get_clock_source(struct snd_dice *dice, + unsigned int *source) +{ + __be32 info; + int err; + + err = get_clock_info(dice, &info); + if (err >= 0) + *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK; + + return err; +} + +int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) +{ + __be32 info; + unsigned int index; + int err; + + err = get_clock_info(dice, &info); + if (err < 0) + goto end; + + index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; + if (index >= SND_DICE_RATES_COUNT) { + err = -ENOSYS; + goto end; + } + + *rate = snd_dice_rates[index]; +end: + return err; +} +int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate) +{ + return set_clock_info(dice, rate, UINT_MAX); +} + +int snd_dice_transaction_set_enable(struct snd_dice *dice) +{ + __be32 value; + int err = 0; + + if (dice->global_enabled) + goto end; + + value = cpu_to_be32(1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_ENABLE), + &value, 4, + FW_FIXED_GENERATION | dice->owner_generation); + if (err < 0) + goto end; + + dice->global_enabled = true; +end: + return err; +} + +void snd_dice_transaction_clear_enable(struct snd_dice *dice) +{ + __be32 value; + + value = 0; + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_ENABLE), + &value, 4, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + dice->global_enabled = false; +} + +static void dice_notification(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct snd_dice *dice = callback_data; + u32 bits; + unsigned long flags; + + if (tcode != TCODE_WRITE_QUADLET_REQUEST) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + if ((offset & 3) != 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + bits = be32_to_cpup(data); + + spin_lock_irqsave(&dice->lock, flags); + dice->notification_bits |= bits; + spin_unlock_irqrestore(&dice->lock, flags); + + fw_send_response(card, request, RCODE_COMPLETE); + + if (bits & NOTIFY_CLOCK_ACCEPTED) + complete(&dice->clock_accepted); + wake_up(&dice->hwdep_wait); +} + +static int register_notification_address(struct snd_dice *dice, bool retry) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + unsigned int retries; + int err; + + retries = (retry) ? 3 : 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + get_subaddr(dice, + SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | + dice->owner_generation); + if (err == 0) { + /* success */ + if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) + break; + /* The address seems to be already registered. */ + if (buffer[0] == buffer[1]) + break; + + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + if (err != -EAGAIN || retries-- > 0) + break; + + msleep(20); + } + + kfree(buffer); + + if (err < 0) + dice->owner_generation = -1; + + return err; +} + +static void unregister_notification_address(struct snd_dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (buffer == NULL) + return; + + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, + GLOBAL_OWNER), + buffer, 2 * 8, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + kfree(buffer); + + dice->owner_generation = -1; +} + +void snd_dice_transaction_destroy(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + + if (handler->callback_data == NULL) + return; + + unregister_notification_address(dice); + + fw_core_remove_address_handler(handler); + handler->callback_data = NULL; +} + +int snd_dice_transaction_reinit(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + + if (handler->callback_data == NULL) + return -EINVAL; + + return register_notification_address(dice, false); +} + +int snd_dice_transaction_init(struct snd_dice *dice) +{ + struct fw_address_handler *handler = &dice->notification_handler; + __be32 *pointers; + int err; + + /* Use the same way which dice_interface_check() does. */ + pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + + /* Get offsets for sub-addresses */ + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(__be32) * 10, 0); + if (err < 0) + goto end; + + /* Allocation callback in address space over host controller */ + handler->length = 4; + handler->address_callback = dice_notification; + handler->callback_data = dice; + err = fw_core_add_address_handler(handler, &fw_high_memory_region); + if (err < 0) { + handler->callback_data = NULL; + goto end; + } + + /* Register the address space */ + err = register_notification_address(dice, true); + if (err < 0) { + fw_core_remove_address_handler(handler); + handler->callback_data = NULL; + goto end; + } + + dice->global_offset = be32_to_cpu(pointers[0]) * 4; + dice->tx_offset = be32_to_cpu(pointers[2]) * 4; + dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + dice->sync_offset = be32_to_cpu(pointers[6]) * 4; + dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4; + + /* Set up later. */ + if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) + dice->clock_caps = 1; +end: + kfree(pointers); + return err; +} diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c new file mode 100644 index 000000000000..90d8f40ff727 --- /dev/null +++ b/sound/firewire/dice/dice.c @@ -0,0 +1,361 @@ +/* + * TC Applied Technologies Digital Interface Communications Engine driver + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "dice.h" + +MODULE_DESCRIPTION("DICE driver"); +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_LICENSE("GPL v2"); + +#define OUI_WEISS 0x001c6a + +#define DICE_CATEGORY_ID 0x04 +#define WEISS_CATEGORY_ID 0x00 + +static int dice_interface_check(struct fw_unit *unit) +{ + static const int min_values[10] = { + 10, 0x64 / 4, + 10, 0x18 / 4, + 10, 0x18 / 4, + 0, 0, + 0, 0, + }; + struct fw_device *device = fw_parent_device(unit); + struct fw_csr_iterator it; + int key, val, vendor = -1, model = -1, err; + unsigned int category, i; + __be32 *pointers, value; + __be32 version; + + pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32), + GFP_KERNEL); + if (pointers == NULL) + return -ENOMEM; + + /* + * Check that GUID and unit directory are constructed according to DICE + * rules, i.e., that the specifier ID is the GUID's OUI, and that the + * GUID chip ID consists of the 8-bit category ID, the 10-bit product + * ID, and a 22-bit serial number. + */ + fw_csr_iterator_init(&it, unit->directory); + while (fw_csr_iterator_next(&it, &key, &val)) { + switch (key) { + case CSR_SPECIFIER_ID: + vendor = val; + break; + case CSR_MODEL: + model = val; + break; + } + } + if (vendor == OUI_WEISS) + category = WEISS_CATEGORY_ID; + else + category = DICE_CATEGORY_ID; + if (device->config_rom[3] != ((vendor << 8) | category) || + device->config_rom[4] >> 22 != model) { + err = -ENODEV; + goto end; + } + + /* + * Check that the sub address spaces exist and are located inside the + * private address space. The minimum values are chosen so that all + * minimally required registers are included. + */ + err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, pointers, + sizeof(__be32) * ARRAY_SIZE(min_values), 0); + if (err < 0) { + err = -ENODEV; + goto end; + } + for (i = 0; i < ARRAY_SIZE(min_values); ++i) { + value = be32_to_cpu(pointers[i]); + if (value < min_values[i] || value >= 0x40000) { + err = -ENODEV; + goto end; + } + } + + /* + * Check that the implemented DICE driver specification major version + * number matches. + */ + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, + &version, 4, 0); + if (err < 0) { + err = -ENODEV; + goto end; + } + if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { + dev_err(&unit->device, + "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); + err = -ENODEV; + goto end; + } +end: + return err; +} + +static int highest_supported_mode_rate(struct snd_dice *dice, + unsigned int mode, unsigned int *rate) +{ + unsigned int i, m; + + for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) { + *rate = snd_dice_rates[i - 1]; + if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0) + continue; + if (mode == m) + break; + } + if (i == 0) + return -EINVAL; + + return 0; +} + +static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode) +{ + __be32 values[2]; + unsigned int rate; + int err; + + if (highest_supported_mode_rate(dice, mode, &rate) < 0) { + dice->tx_channels[mode] = 0; + dice->tx_midi_ports[mode] = 0; + dice->rx_channels[mode] = 0; + dice->rx_midi_ports[mode] = 0; + return 0; + } + + err = snd_dice_transaction_set_rate(dice, rate); + if (err < 0) + return err; + + err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO, + values, sizeof(values)); + if (err < 0) + return err; + + dice->tx_channels[mode] = be32_to_cpu(values[0]); + dice->tx_midi_ports[mode] = be32_to_cpu(values[1]); + + err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO, + values, sizeof(values)); + if (err < 0) + return err; + + dice->rx_channels[mode] = be32_to_cpu(values[0]); + dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); + + return 0; +} + +static int dice_read_params(struct snd_dice *dice) +{ + __be32 value; + int mode, err; + + /* some very old firmwares don't tell about their clock support */ + if (dice->clock_caps > 0) { + err = snd_dice_transaction_read_global(dice, + GLOBAL_CLOCK_CAPABILITIES, + &value, 4); + if (err < 0) + return err; + dice->clock_caps = be32_to_cpu(value); + } else { + /* this should be supported by any device */ + dice->clock_caps = CLOCK_CAP_RATE_44100 | + CLOCK_CAP_RATE_48000 | + CLOCK_CAP_SOURCE_ARX1 | + CLOCK_CAP_SOURCE_INTERNAL; + } + + for (mode = 2; mode >= 0; --mode) { + err = dice_read_mode_params(dice, mode); + if (err < 0) + return err; + } + + return 0; +} + +static void dice_card_strings(struct snd_dice *dice) +{ + struct snd_card *card = dice->card; + struct fw_device *dev = fw_parent_device(dice->unit); + char vendor[32], model[32]; + unsigned int i; + int err; + + strcpy(card->driver, "DICE"); + + strcpy(card->shortname, "DICE"); + BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); + err = snd_dice_transaction_read_global(dice, GLOBAL_NICK_NAME, + card->shortname, + sizeof(card->shortname)); + if (err >= 0) { + /* DICE strings are returned in "always-wrong" endianness */ + BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); + for (i = 0; i < sizeof(card->shortname); i += 4) + swab32s((u32 *)&card->shortname[i]); + card->shortname[sizeof(card->shortname) - 1] = '\0'; + } + + strcpy(vendor, "?"); + fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); + strcpy(model, "?"); + fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); + snprintf(card->longname, sizeof(card->longname), + "%s %s (serial %u) at %s, S%d", + vendor, model, dev->config_rom[4] & 0x3fffff, + dev_name(&dice->unit->device), 100 << dev->max_speed); + + strcpy(card->mixername, "DICE"); +} + +static void dice_card_free(struct snd_card *card) +{ + struct snd_dice *dice = card->private_data; + + snd_dice_transaction_destroy(dice); + mutex_destroy(&dice->mutex); +} + +static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) +{ + struct snd_card *card; + struct snd_dice *dice; + int err; + + err = dice_interface_check(unit); + if (err < 0) + goto end; + + err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, + sizeof(*dice), &card); + if (err < 0) + goto end; + + dice = card->private_data; + dice->card = card; + dice->unit = unit; + card->private_free = dice_card_free; + + spin_lock_init(&dice->lock); + mutex_init(&dice->mutex); + init_completion(&dice->clock_accepted); + init_waitqueue_head(&dice->hwdep_wait); + + err = snd_dice_transaction_init(dice); + if (err < 0) + goto error; + + err = dice_read_params(dice); + if (err < 0) + goto error; + + dice_card_strings(dice); + + err = snd_dice_create_pcm(dice); + if (err < 0) + goto error; + + err = snd_dice_create_hwdep(dice); + if (err < 0) + goto error; + + snd_dice_create_proc(dice); + + err = snd_dice_create_midi(dice); + if (err < 0) + goto error; + + err = snd_dice_stream_init_duplex(dice); + if (err < 0) + goto error; + + err = snd_card_register(card); + if (err < 0) { + snd_dice_stream_destroy_duplex(dice); + goto error; + } + + dev_set_drvdata(&unit->device, dice); +end: + return err; +error: + snd_card_free(card); + return err; +} + +static void dice_remove(struct fw_unit *unit) +{ + struct snd_dice *dice = dev_get_drvdata(&unit->device); + + snd_card_disconnect(dice->card); + + snd_dice_stream_destroy_duplex(dice); + + snd_card_free_when_closed(dice->card); +} + +static void dice_bus_reset(struct fw_unit *unit) +{ + struct snd_dice *dice = dev_get_drvdata(&unit->device); + + /* The handler address register becomes initialized. */ + snd_dice_transaction_reinit(dice); + + mutex_lock(&dice->mutex); + snd_dice_stream_update_duplex(dice); + mutex_unlock(&dice->mutex); +} + +#define DICE_INTERFACE 0x000001 + +static const struct ieee1394_device_id dice_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VERSION, + .version = DICE_INTERFACE, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, dice_id_table); + +static struct fw_driver dice_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = dice_probe, + .update = dice_bus_reset, + .remove = dice_remove, + .id_table = dice_id_table, +}; + +static int __init alsa_dice_init(void) +{ + return driver_register(&dice_driver.driver); +} + +static void __exit alsa_dice_exit(void) +{ + driver_unregister(&dice_driver.driver); +} + +module_init(alsa_dice_init); +module_exit(alsa_dice_exit); diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h new file mode 100644 index 000000000000..ecf5dc862235 --- /dev/null +++ b/sound/firewire/dice/dice.h @@ -0,0 +1,189 @@ +/* + * dice.h - a part of driver for Dice based devices + * + * Copyright (c) Clemens Ladisch + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef SOUND_DICE_H_INCLUDED +#define SOUND_DICE_H_INCLUDED + +#include <linux/compat.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +#include <sound/control.h> +#include <sound/core.h> +#include <sound/firewire.h> +#include <sound/hwdep.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/rawmidi.h> + +#include "../amdtp.h" +#include "../iso-resources.h" +#include "../lib.h" +#include "dice-interface.h" + +struct snd_dice { + struct snd_card *card; + struct fw_unit *unit; + spinlock_t lock; + struct mutex mutex; + + /* Offsets for sub-addresses */ + unsigned int global_offset; + unsigned int rx_offset; + unsigned int tx_offset; + unsigned int sync_offset; + unsigned int rsrv_offset; + + unsigned int clock_caps; + unsigned int tx_channels[3]; + unsigned int rx_channels[3]; + unsigned int tx_midi_ports[3]; + unsigned int rx_midi_ports[3]; + + struct fw_address_handler notification_handler; + int owner_generation; + u32 notification_bits; + + /* For uapi */ + int dev_lock_count; /* > 0 driver, < 0 userspace */ + bool dev_lock_changed; + wait_queue_head_t hwdep_wait; + + /* For streaming */ + struct fw_iso_resources tx_resources; + struct fw_iso_resources rx_resources; + struct amdtp_stream tx_stream; + struct amdtp_stream rx_stream; + bool global_enabled; + struct completion clock_accepted; + unsigned int substreams_counter; +}; + +enum snd_dice_addr_type { + SND_DICE_ADDR_TYPE_PRIVATE, + SND_DICE_ADDR_TYPE_GLOBAL, + SND_DICE_ADDR_TYPE_TX, + SND_DICE_ADDR_TYPE_RX, + SND_DICE_ADDR_TYPE_SYNC, + SND_DICE_ADDR_TYPE_RSRV, +}; + +int snd_dice_transaction_write(struct snd_dice *dice, + enum snd_dice_addr_type type, + unsigned int offset, + void *buf, unsigned int len); +int snd_dice_transaction_read(struct snd_dice *dice, + enum snd_dice_addr_type type, unsigned int offset, + void *buf, unsigned int len); + +static inline int snd_dice_transaction_write_global(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, + SND_DICE_ADDR_TYPE_GLOBAL, offset, + buf, len); +} +static inline int snd_dice_transaction_read_global(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, + SND_DICE_ADDR_TYPE_GLOBAL, offset, + buf, len); +} +static inline int snd_dice_transaction_write_tx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_TX, offset, + buf, len); +} +static inline int snd_dice_transaction_read_tx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_TX, offset, + buf, len); +} +static inline int snd_dice_transaction_write_rx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_RX, offset, + buf, len); +} +static inline int snd_dice_transaction_read_rx(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_RX, offset, + buf, len); +} +static inline int snd_dice_transaction_write_sync(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_write(dice, SND_DICE_ADDR_TYPE_SYNC, offset, + buf, len); +} +static inline int snd_dice_transaction_read_sync(struct snd_dice *dice, + unsigned int offset, + void *buf, unsigned int len) +{ + return snd_dice_transaction_read(dice, SND_DICE_ADDR_TYPE_SYNC, offset, + buf, len); +} + +int snd_dice_transaction_get_clock_source(struct snd_dice *dice, + unsigned int *source); +int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate); +int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate); +int snd_dice_transaction_set_enable(struct snd_dice *dice); +void snd_dice_transaction_clear_enable(struct snd_dice *dice); +int snd_dice_transaction_init(struct snd_dice *dice); +int snd_dice_transaction_reinit(struct snd_dice *dice); +void snd_dice_transaction_destroy(struct snd_dice *dice); + +#define SND_DICE_RATES_COUNT 7 +extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT]; + +int snd_dice_stream_get_rate_mode(struct snd_dice *dice, + unsigned int rate, unsigned int *mode); + +int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate); +void snd_dice_stream_stop_duplex(struct snd_dice *dice); +int snd_dice_stream_init_duplex(struct snd_dice *dice); +void snd_dice_stream_destroy_duplex(struct snd_dice *dice); +void snd_dice_stream_update_duplex(struct snd_dice *dice); + +int snd_dice_stream_lock_try(struct snd_dice *dice); +void snd_dice_stream_lock_release(struct snd_dice *dice); + +int snd_dice_create_pcm(struct snd_dice *dice); + +int snd_dice_create_hwdep(struct snd_dice *dice); + +void snd_dice_create_proc(struct snd_dice *dice); + +int snd_dice_create_midi(struct snd_dice *dice); + +#endif diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 7ac94439e758..48d6dca471c6 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -131,14 +131,8 @@ static void isight_samples(struct isight *isight, static void isight_pcm_abort(struct isight *isight) { - unsigned long flags; - - if (ACCESS_ONCE(isight->pcm_active)) { - snd_pcm_stream_lock_irqsave(isight->pcm, flags); - if (snd_pcm_running(isight->pcm)) - snd_pcm_stop(isight->pcm, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(isight->pcm, flags); - } + if (ACCESS_ONCE(isight->pcm_active)) + snd_pcm_stop_xrun(isight->pcm); } static void isight_dropped_samples(struct isight *isight, unsigned int total) diff --git a/sound/firewire/oxfw/Makefile b/sound/firewire/oxfw/Makefile new file mode 100644 index 000000000000..a926850864f6 --- /dev/null +++ b/sound/firewire/oxfw/Makefile @@ -0,0 +1,3 @@ +snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \ + oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o +obj-m += snd-oxfw.o diff --git a/sound/firewire/oxfw/oxfw-command.c b/sound/firewire/oxfw/oxfw-command.c new file mode 100644 index 000000000000..12ef3253bc89 --- /dev/null +++ b/sound/firewire/oxfw/oxfw-command.c @@ -0,0 +1,153 @@ +/* + * oxfw_command.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir, + unsigned int pid, u8 *format, unsigned int len) +{ + u8 *buf; + int err; + + buf = kmalloc(len + 10, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = 0x00; /* CONTROL */ + buf[1] = 0xff; /* UNIT */ + buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */ + buf[3] = 0xc0; /* SINGLE subfunction */ + buf[4] = dir; /* Plug Direction */ + buf[5] = 0x00; /* UNIT */ + buf[6] = 0x00; /* PCR (Isochronous Plug) */ + buf[7] = 0xff & pid; /* Plug ID */ + buf[8] = 0xff; /* Padding */ + buf[9] = 0xff; /* Support status in response */ + memcpy(buf + 10, format, len); + + /* do transaction and check buf[1-8] are the same against command */ + err = fcp_avc_transaction(unit, buf, len + 10, buf, len + 10, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | + BIT(6) | BIT(7) | BIT(8)); + if ((err > 0) && (err < len + 10)) + err = -EIO; + else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ + err = -ENOSYS; + else if (buf[0] == 0x0a) /* REJECTED */ + err = -EINVAL; + else + err = 0; + + kfree(buf); + + return err; +} + +int avc_stream_get_format(struct fw_unit *unit, + enum avc_general_plug_dir dir, unsigned int pid, + u8 *buf, unsigned int *len, unsigned int eid) +{ + unsigned int subfunc; + int err; + + if (eid == 0xff) + subfunc = 0xc0; /* SINGLE */ + else + subfunc = 0xc1; /* LIST */ + + buf[0] = 0x01; /* STATUS */ + buf[1] = 0xff; /* UNIT */ + buf[2] = 0xbf; /* EXTENDED STREAM FORMAT INFORMATION */ + buf[3] = subfunc; /* SINGLE or LIST */ + buf[4] = dir; /* Plug Direction */ + buf[5] = 0x00; /* Unit */ + buf[6] = 0x00; /* PCR (Isochronous Plug) */ + buf[7] = 0xff & pid; /* Plug ID */ + buf[8] = 0xff; /* Padding */ + buf[9] = 0xff; /* support status in response */ + buf[10] = 0xff & eid; /* entry ID for LIST subfunction */ + buf[11] = 0xff; /* padding */ + + /* do transaction and check buf[1-7] are the same against command */ + err = fcp_avc_transaction(unit, buf, 12, buf, *len, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) | + BIT(6) | BIT(7)); + if ((err > 0) && (err < 10)) + err = -EIO; + else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ + err = -ENOSYS; + else if (buf[0] == 0x0a) /* REJECTED */ + err = -EINVAL; + else if (buf[0] == 0x0b) /* IN TRANSITION */ + err = -EAGAIN; + /* LIST subfunction has entry ID */ + else if ((subfunc == 0xc1) && (buf[10] != eid)) + err = -EIO; + if (err < 0) + goto end; + + /* keep just stream format information */ + if (subfunc == 0xc0) { + memmove(buf, buf + 10, err - 10); + *len = err - 10; + } else { + memmove(buf, buf + 11, err - 11); + *len = err - 11; + } + + err = 0; +end: + return err; +} + +int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate, + enum avc_general_plug_dir dir, + unsigned short pid) +{ + unsigned int sfc; + u8 *buf; + int err; + + for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) { + if (amdtp_rate_table[sfc] == rate) + break; + } + if (sfc == CIP_SFC_COUNT) + return -EINVAL; + + buf = kzalloc(8, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = 0x02; /* SPECIFIC INQUIRY */ + buf[1] = 0xff; /* UNIT */ + if (dir == AVC_GENERAL_PLUG_DIR_IN) + buf[2] = 0x19; /* INPUT PLUG SIGNAL FORMAT */ + else + buf[2] = 0x18; /* OUTPUT PLUG SIGNAL FORMAT */ + buf[3] = 0xff & pid; /* plug id */ + buf[4] = 0x90; /* EOH_1, Form_1, FMT. AM824 */ + buf[5] = 0x07 & sfc; /* FDF-hi. AM824, frequency */ + buf[6] = 0xff; /* FDF-mid. AM824, SYT hi (not used) */ + buf[7] = 0xff; /* FDF-low. AM824, SYT lo (not used) */ + + /* do transaction and check buf[1-5] are the same against command */ + err = fcp_avc_transaction(unit, buf, 8, buf, 8, + BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5)); + if ((err > 0) && (err < 8)) + err = -EIO; + else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ + err = -ENOSYS; + if (err < 0) + goto end; + + err = 0; +end: + kfree(buf); + return err; +} diff --git a/sound/firewire/oxfw/oxfw-control.c b/sound/firewire/oxfw/oxfw-control.c new file mode 100644 index 000000000000..02a1cb90f20d --- /dev/null +++ b/sound/firewire/oxfw/oxfw-control.c @@ -0,0 +1,283 @@ +/* + * oxfw_stream.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +enum control_action { CTL_READ, CTL_WRITE }; +enum control_attribute { + CTL_MIN = 0x02, + CTL_MAX = 0x03, + CTL_CURRENT = 0x10, +}; + +static int oxfw_mute_command(struct snd_oxfw *oxfw, bool *value, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(11, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->mute_fb_id; /* function block ID */ + buf[5] = 0x10; /* control attribute: current */ + buf[6] = 0x02; /* selector length */ + buf[7] = 0x00; /* audio channel number */ + buf[8] = 0x01; /* control selector: mute */ + buf[9] = 0x01; /* control data length */ + if (action == CTL_READ) + buf[10] = 0xff; + else + buf[10] = *value ? 0x70 : 0x60; + + err = fcp_avc_transaction(oxfw->unit, buf, 11, buf, 11, 0x3fe); + if (err < 0) + goto error; + if (err < 11) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "mute command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = buf[10] == 0x70; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_volume_command(struct snd_oxfw *oxfw, s16 *value, + unsigned int channel, + enum control_attribute attribute, + enum control_action action) +{ + u8 *buf; + u8 response_ok; + int err; + + buf = kmalloc(12, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (action == CTL_READ) { + buf[0] = 0x01; /* AV/C, STATUS */ + response_ok = 0x0c; /* STABLE */ + } else { + buf[0] = 0x00; /* AV/C, CONTROL */ + response_ok = 0x09; /* ACCEPTED */ + } + buf[1] = 0x08; /* audio unit 0 */ + buf[2] = 0xb8; /* FUNCTION BLOCK */ + buf[3] = 0x81; /* function block type: feature */ + buf[4] = oxfw->device_info->volume_fb_id; /* function block ID */ + buf[5] = attribute; /* control attribute */ + buf[6] = 0x02; /* selector length */ + buf[7] = channel; /* audio channel number */ + buf[8] = 0x02; /* control selector: volume */ + buf[9] = 0x02; /* control data length */ + if (action == CTL_READ) { + buf[10] = 0xff; + buf[11] = 0xff; + } else { + buf[10] = *value >> 8; + buf[11] = *value; + } + + err = fcp_avc_transaction(oxfw->unit, buf, 12, buf, 12, 0x3fe); + if (err < 0) + goto error; + if (err < 12) { + dev_err(&oxfw->unit->device, "short FCP response\n"); + err = -EIO; + goto error; + } + if (buf[0] != response_ok) { + dev_err(&oxfw->unit->device, "volume command failed\n"); + err = -EIO; + goto error; + } + if (action == CTL_READ) + *value = (buf[10] << 8) | buf[11]; + + err = 0; + +error: + kfree(buf); + + return err; +} + +static int oxfw_mute_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + + value->value.integer.value[0] = !oxfw->mute; + + return 0; +} + +static int oxfw_mute_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + bool mute; + int err; + + mute = !value->value.integer.value[0]; + + if (mute == oxfw->mute) + return 0; + + err = oxfw_mute_command(oxfw, &mute, CTL_WRITE); + if (err < 0) + return err; + oxfw->mute = mute; + + return 1; +} + +static int oxfw_volume_info(struct snd_kcontrol *control, + struct snd_ctl_elem_info *info) +{ + struct snd_oxfw *oxfw = control->private_data; + + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = oxfw->device_info->mixer_channels; + info->value.integer.min = oxfw->volume_min; + info->value.integer.max = oxfw->volume_max; + + return 0; +} + +static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; + +static int oxfw_volume_get(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + value->value.integer.value[channel_map[i]] = oxfw->volume[i]; + + return 0; +} + +static int oxfw_volume_put(struct snd_kcontrol *control, + struct snd_ctl_elem_value *value) +{ + struct snd_oxfw *oxfw = control->private_data; + unsigned int i, changed_channels; + bool equal_values = true; + s16 volume; + int err; + + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + if (value->value.integer.value[i] < oxfw->volume_min || + value->value.integer.value[i] > oxfw->volume_max) + return -EINVAL; + if (value->value.integer.value[i] != + value->value.integer.value[0]) + equal_values = false; + } + + changed_channels = 0; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) + if (value->value.integer.value[channel_map[i]] != + oxfw->volume[i]) + changed_channels |= 1 << (i + 1); + + if (equal_values && changed_channels != 0) + changed_channels = 1 << 0; + + for (i = 0; i <= oxfw->device_info->mixer_channels; ++i) { + volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; + if (changed_channels & (1 << i)) { + err = oxfw_volume_command(oxfw, &volume, i, + CTL_CURRENT, CTL_WRITE); + if (err < 0) + return err; + } + if (i > 0) + oxfw->volume[i - 1] = volume; + } + + return changed_channels != 0; +} + +int snd_oxfw_create_mixer(struct snd_oxfw *oxfw) +{ + static const struct snd_kcontrol_new controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = oxfw_mute_get, + .put = oxfw_mute_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .info = oxfw_volume_info, + .get = oxfw_volume_get, + .put = oxfw_volume_put, + }, + }; + unsigned int i, first_ch; + int err; + + err = oxfw_volume_command(oxfw, &oxfw->volume_min, + 0, CTL_MIN, CTL_READ); + if (err < 0) + return err; + err = oxfw_volume_command(oxfw, &oxfw->volume_max, + 0, CTL_MAX, CTL_READ); + if (err < 0) + return err; + + err = oxfw_mute_command(oxfw, &oxfw->mute, CTL_READ); + if (err < 0) + return err; + + first_ch = oxfw->device_info->mixer_channels == 1 ? 0 : 1; + for (i = 0; i < oxfw->device_info->mixer_channels; ++i) { + err = oxfw_volume_command(oxfw, &oxfw->volume[i], + first_ch + i, CTL_CURRENT, CTL_READ); + if (err < 0) + return err; + } + + for (i = 0; i < ARRAY_SIZE(controls); ++i) { + err = snd_ctl_add(oxfw->card, + snd_ctl_new1(&controls[i], oxfw)); + if (err < 0) + return err; + } + + return 0; +} diff --git a/sound/firewire/oxfw/oxfw-hwdep.c b/sound/firewire/oxfw/oxfw-hwdep.c new file mode 100644 index 000000000000..ff2687ad0460 --- /dev/null +++ b/sound/firewire/oxfw/oxfw-hwdep.c @@ -0,0 +1,190 @@ +/* + * oxfw_hwdep.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +/* + * This codes give three functionality. + * + * 1.get firewire node information + * 2.get notification about starting/stopping stream + * 3.lock/unlock stream + */ + +#include "oxfw.h" + +static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, + loff_t *offset) +{ + struct snd_oxfw *oxfw = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&oxfw->lock); + + while (!oxfw->dev_lock_changed) { + prepare_to_wait(&oxfw->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&oxfw->lock); + schedule(); + finish_wait(&oxfw->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&oxfw->lock); + } + + memset(&event, 0, sizeof(event)); + if (oxfw->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = (oxfw->dev_lock_count > 0); + oxfw->dev_lock_changed = false; + + count = min_t(long, count, sizeof(event.lock_status)); + } + + spin_unlock_irq(&oxfw->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct snd_oxfw *oxfw = hwdep->private_data; + unsigned int events; + + poll_wait(file, &oxfw->hwdep_wait, wait); + + spin_lock_irq(&oxfw->lock); + if (oxfw->dev_lock_changed) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&oxfw->lock); + + return events; +} + +static int hwdep_get_info(struct snd_oxfw *oxfw, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(oxfw->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_OXFW; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int hwdep_lock(struct snd_oxfw *oxfw) +{ + int err; + + spin_lock_irq(&oxfw->lock); + + if (oxfw->dev_lock_count == 0) { + oxfw->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&oxfw->lock); + + return err; +} + +static int hwdep_unlock(struct snd_oxfw *oxfw) +{ + int err; + + spin_lock_irq(&oxfw->lock); + + if (oxfw->dev_lock_count == -1) { + oxfw->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&oxfw->lock); + + return err; +} + +static int hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct snd_oxfw *oxfw = hwdep->private_data; + + spin_lock_irq(&oxfw->lock); + if (oxfw->dev_lock_count == -1) + oxfw->dev_lock_count = 0; + spin_unlock_irq(&oxfw->lock); + + return 0; +} + +static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct snd_oxfw *oxfw = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return hwdep_get_info(oxfw, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return hwdep_lock(oxfw); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return hwdep_unlock(oxfw); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define hwdep_compat_ioctl NULL +#endif + +int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw) +{ + static const struct snd_hwdep_ops hwdep_ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(oxfw->card, oxfw->card->driver, 0, &hwdep); + if (err < 0) + goto end; + strcpy(hwdep->name, oxfw->card->driver); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_OXFW; + hwdep->ops = hwdep_ops; + hwdep->private_data = oxfw; + hwdep->exclusive = true; +end: + return err; +} diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c new file mode 100644 index 000000000000..540a30338516 --- /dev/null +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -0,0 +1,207 @@ +/* + * oxfw_midi.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +static int midi_capture_open(struct snd_rawmidi_substream *substream) +{ + struct snd_oxfw *oxfw = substream->rmidi->private_data; + int err; + + err = snd_oxfw_stream_lock_try(oxfw); + if (err < 0) + return err; + + mutex_lock(&oxfw->mutex); + + oxfw->capture_substreams++; + err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, 0, 0); + + mutex_unlock(&oxfw->mutex); + + if (err < 0) + snd_oxfw_stream_lock_release(oxfw); + + return err; +} + +static int midi_playback_open(struct snd_rawmidi_substream *substream) +{ + struct snd_oxfw *oxfw = substream->rmidi->private_data; + int err; + + err = snd_oxfw_stream_lock_try(oxfw); + if (err < 0) + return err; + + mutex_lock(&oxfw->mutex); + + oxfw->playback_substreams++; + err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, 0, 0); + + mutex_unlock(&oxfw->mutex); + + if (err < 0) + snd_oxfw_stream_lock_release(oxfw); + + return err; +} + +static int midi_capture_close(struct snd_rawmidi_substream *substream) +{ + struct snd_oxfw *oxfw = substream->rmidi->private_data; + + mutex_lock(&oxfw->mutex); + + oxfw->capture_substreams--; + snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream); + + mutex_unlock(&oxfw->mutex); + + snd_oxfw_stream_lock_release(oxfw); + return 0; +} + +static int midi_playback_close(struct snd_rawmidi_substream *substream) +{ + struct snd_oxfw *oxfw = substream->rmidi->private_data; + + mutex_lock(&oxfw->mutex); + + oxfw->playback_substreams--; + snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); + + mutex_unlock(&oxfw->mutex); + + snd_oxfw_stream_lock_release(oxfw); + return 0; +} + +static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_oxfw *oxfw = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&oxfw->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&oxfw->tx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&oxfw->tx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&oxfw->lock, flags); +} + +static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) +{ + struct snd_oxfw *oxfw = substrm->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&oxfw->lock, flags); + + if (up) + amdtp_stream_midi_trigger(&oxfw->rx_stream, + substrm->number, substrm); + else + amdtp_stream_midi_trigger(&oxfw->rx_stream, + substrm->number, NULL); + + spin_unlock_irqrestore(&oxfw->lock, flags); +} + +static struct snd_rawmidi_ops midi_capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, +}; + +static struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, +}; + +static void set_midi_substream_names(struct snd_oxfw *oxfw, + struct snd_rawmidi_str *str) +{ + struct snd_rawmidi_substream *subs; + + list_for_each_entry(subs, &str->substreams, list) { + snprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + oxfw->card->shortname, subs->number + 1); + } +} + +int snd_oxfw_create_midi(struct snd_oxfw *oxfw) +{ + struct snd_oxfw_stream_formation formation; + struct snd_rawmidi *rmidi; + struct snd_rawmidi_str *str; + u8 *format; + int i, err; + + /* If its stream has MIDI conformant data channel, add one MIDI port */ + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + format = oxfw->tx_stream_formats[i]; + if (format != NULL) { + err = snd_oxfw_stream_parse_format(format, &formation); + if (err >= 0 && formation.midi > 0) + oxfw->midi_input_ports = 1; + } + + format = oxfw->rx_stream_formats[i]; + if (format != NULL) { + err = snd_oxfw_stream_parse_format(format, &formation); + if (err >= 0 && formation.midi > 0) + oxfw->midi_output_ports = 1; + } + } + if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0)) + return 0; + + /* create midi ports */ + err = snd_rawmidi_new(oxfw->card, oxfw->card->driver, 0, + oxfw->midi_output_ports, oxfw->midi_input_ports, + &rmidi); + if (err < 0) + return err; + + snprintf(rmidi->name, sizeof(rmidi->name), + "%s MIDI", oxfw->card->shortname); + rmidi->private_data = oxfw; + + if (oxfw->midi_input_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &midi_capture_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; + + set_midi_substream_names(oxfw, str); + } + + if (oxfw->midi_output_ports > 0) { + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &midi_playback_ops); + + str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; + + set_midi_substream_names(oxfw, str); + } + + if ((oxfw->midi_output_ports > 0) && (oxfw->midi_input_ports > 0)) + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + + return 0; +} diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c new file mode 100644 index 000000000000..67ade0775a5b --- /dev/null +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -0,0 +1,424 @@ +/* + * oxfw_pcm.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +static int hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + u8 **formats = rule->private; + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + const struct snd_interval *c = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval t = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + struct snd_oxfw_stream_formation formation; + int i, err; + + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + if (formats[i] == NULL) + continue; + + err = snd_oxfw_stream_parse_format(formats[i], &formation); + if (err < 0) + continue; + if (!snd_interval_test(c, formation.pcm)) + continue; + + t.min = min(t.min, formation.rate); + t.max = max(t.max, formation.rate); + + } + return snd_interval_refine(r, &t); +} + +static int hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + u8 **formats = rule->private; + struct snd_interval *c = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_oxfw_stream_formation formation; + int i, j, err; + unsigned int count, list[SND_OXFW_STREAM_FORMAT_ENTRIES] = {0}; + + count = 0; + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + if (formats[i] == NULL) + break; + + err = snd_oxfw_stream_parse_format(formats[i], &formation); + if (err < 0) + continue; + if (!snd_interval_test(r, formation.rate)) + continue; + if (list[count] == formation.pcm) + continue; + + for (j = 0; j < ARRAY_SIZE(list); j++) { + if (list[j] == formation.pcm) + break; + } + if (j == ARRAY_SIZE(list)) { + list[count] = formation.pcm; + if (++count == ARRAY_SIZE(list)) + break; + } + } + + return snd_interval_list(c, count, list, 0); +} + +static void limit_channels_and_rates(struct snd_pcm_hardware *hw, u8 **formats) +{ + struct snd_oxfw_stream_formation formation; + int i, err; + + hw->channels_min = UINT_MAX; + hw->channels_max = 0; + + hw->rate_min = UINT_MAX; + hw->rate_max = 0; + hw->rates = 0; + + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + if (formats[i] == NULL) + break; + + err = snd_oxfw_stream_parse_format(formats[i], &formation); + if (err < 0) + continue; + + hw->channels_min = min(hw->channels_min, formation.pcm); + hw->channels_max = max(hw->channels_max, formation.pcm); + + hw->rate_min = min(hw->rate_min, formation.rate); + hw->rate_max = max(hw->rate_max, formation.rate); + hw->rates |= snd_pcm_rate_to_rate_bit(formation.rate); + } +} + +static void limit_period_and_buffer(struct snd_pcm_hardware *hw) +{ + hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */ + hw->periods_max = UINT_MAX; + + hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */ + + /* Just to prevent from allocating much pages. */ + hw->period_bytes_max = hw->period_bytes_min * 2048; + hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; +} + +static int init_hw_params(struct snd_oxfw *oxfw, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + u8 **formats; + struct amdtp_stream *stream; + int err; + + runtime->hw.info = SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS; + stream = &oxfw->tx_stream; + formats = oxfw->tx_stream_formats; + } else { + runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS; + stream = &oxfw->rx_stream; + formats = oxfw->rx_stream_formats; + } + + limit_channels_and_rates(&runtime->hw, formats); + limit_period_and_buffer(&runtime->hw); + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, formats, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto end; + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, formats, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto end; + + err = amdtp_stream_add_pcm_hw_constraints(stream, runtime); +end: + return err; +} + +static int limit_to_current_params(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_oxfw_stream_formation formation; + enum avc_general_plug_dir dir; + int err; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = AVC_GENERAL_PLUG_DIR_OUT; + else + dir = AVC_GENERAL_PLUG_DIR_IN; + + err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); + if (err < 0) + goto end; + + substream->runtime->hw.channels_min = formation.pcm; + substream->runtime->hw.channels_max = formation.pcm; + substream->runtime->hw.rate_min = formation.rate; + substream->runtime->hw.rate_max = formation.rate; +end: + return err; +} + +static int pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + int err; + + err = snd_oxfw_stream_lock_try(oxfw); + if (err < 0) + goto end; + + err = init_hw_params(oxfw, substream); + if (err < 0) + goto err_locked; + + /* + * When any PCM streams are already running, the available sampling + * rate is limited at current value. + */ + if (amdtp_stream_pcm_running(&oxfw->tx_stream) || + amdtp_stream_pcm_running(&oxfw->rx_stream)) { + err = limit_to_current_params(substream); + if (err < 0) + goto end; + } + + snd_pcm_set_sync(substream); +end: + return err; +err_locked: + snd_oxfw_stream_lock_release(oxfw); + return err; +} + +static int pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + snd_oxfw_stream_lock_release(oxfw); + return 0; +} + +static int pcm_capture_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_oxfw *oxfw = substream->private_data; + + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&oxfw->mutex); + oxfw->capture_substreams++; + mutex_unlock(&oxfw->mutex); + } + + amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params)); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} +static int pcm_playback_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_oxfw *oxfw = substream->private_data; + + if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { + mutex_lock(&oxfw->mutex); + oxfw->playback_substreams++; + mutex_unlock(&oxfw->mutex); + } + + amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params)); + + return snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); +} + +static int pcm_capture_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + mutex_lock(&oxfw->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + oxfw->capture_substreams--; + + snd_oxfw_stream_stop_simplex(oxfw, &oxfw->tx_stream); + + mutex_unlock(&oxfw->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} +static int pcm_playback_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + + mutex_lock(&oxfw->mutex); + + if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) + oxfw->playback_substreams--; + + snd_oxfw_stream_stop_simplex(oxfw, &oxfw->rx_stream); + + mutex_unlock(&oxfw->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int pcm_capture_prepare(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + mutex_lock(&oxfw->mutex); + err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->tx_stream, + runtime->rate, runtime->channels); + mutex_unlock(&oxfw->mutex); + if (err < 0) + goto end; + + amdtp_stream_pcm_prepare(&oxfw->tx_stream); +end: + return err; +} +static int pcm_playback_prepare(struct snd_pcm_substream *substream) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + mutex_lock(&oxfw->mutex); + err = snd_oxfw_stream_start_simplex(oxfw, &oxfw->rx_stream, + runtime->rate, runtime->channels); + mutex_unlock(&oxfw->mutex); + if (err < 0) + goto end; + + amdtp_stream_pcm_prepare(&oxfw->rx_stream); +end: + return err; +} + +static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_stream_pcm_trigger(&oxfw->tx_stream, pcm); + return 0; +} +static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_oxfw *oxfw = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_stream_pcm_trigger(&oxfw->rx_stream, pcm); + return 0; +} + +static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm) +{ + struct snd_oxfw *oxfw = sbstm->private_data; + + return amdtp_stream_pcm_pointer(&oxfw->tx_stream); +} +static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) +{ + struct snd_oxfw *oxfw = sbstm->private_data; + + return amdtp_stream_pcm_pointer(&oxfw->rx_stream); +} + +int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) +{ + static struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + static struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + unsigned int cap = 0; + int err; + + if (oxfw->has_output) + cap = 1; + + err = snd_pcm_new(oxfw->card, oxfw->card->driver, 0, 1, cap, &pcm); + if (err < 0) + return err; + + pcm->private_data = oxfw; + strcpy(pcm->name, oxfw->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + if (cap > 0) + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); + + return 0; +} diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c new file mode 100644 index 000000000000..8ba4f9f262b8 --- /dev/null +++ b/sound/firewire/oxfw/oxfw-proc.c @@ -0,0 +1,113 @@ +/* + * oxfw_proc.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "./oxfw.h" + +static void proc_read_formation(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct snd_oxfw *oxfw = entry->private_data; + struct snd_oxfw_stream_formation formation, curr; + u8 *format; + char flag; + int i, err; + + /* Show input. */ + err = snd_oxfw_stream_get_current_formation(oxfw, + AVC_GENERAL_PLUG_DIR_IN, + &curr); + if (err < 0) + return; + + snd_iprintf(buffer, "Input Stream to device:\n"); + snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n"); + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + format = oxfw->rx_stream_formats[i]; + if (format == NULL) + continue; + + err = snd_oxfw_stream_parse_format(format, &formation); + if (err < 0) + continue; + + if (memcmp(&formation, &curr, sizeof(curr)) == 0) + flag = '*'; + else + flag = ' '; + + snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag, + formation.rate, formation.pcm, formation.midi); + } + + if (!oxfw->has_output) + return; + + /* Show output. */ + err = snd_oxfw_stream_get_current_formation(oxfw, + AVC_GENERAL_PLUG_DIR_OUT, + &curr); + if (err < 0) + return; + + snd_iprintf(buffer, "Output Stream from device:\n"); + snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n"); + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + format = oxfw->tx_stream_formats[i]; + if (format == NULL) + continue; + + err = snd_oxfw_stream_parse_format(format, &formation); + if (err < 0) + continue; + + if (memcmp(&formation, &curr, sizeof(curr)) == 0) + flag = '*'; + else + flag = ' '; + + snd_iprintf(buffer, "%c\t%d\t%d\t%d\n", flag, + formation.rate, formation.pcm, formation.midi); + } +} + +static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root, + const char *name, + void (*op)(struct snd_info_entry *e, + struct snd_info_buffer *b)) +{ + struct snd_info_entry *entry; + + entry = snd_info_create_card_entry(oxfw->card, name, root); + if (entry == NULL) + return; + + snd_info_set_text_ops(entry, oxfw, op); + if (snd_info_register(entry) < 0) + snd_info_free_entry(entry); +} + +void snd_oxfw_proc_init(struct snd_oxfw *oxfw) +{ + struct snd_info_entry *root; + + /* + * All nodes are automatically removed at snd_card_disconnect(), + * by following to link list. + */ + root = snd_info_create_card_entry(oxfw->card, "firewire", + oxfw->card->proc_root); + if (root == NULL) + return; + root->mode = S_IFDIR | S_IRUGO | S_IXUGO; + if (snd_info_register(root) < 0) { + snd_info_free_entry(root); + return; + } + + add_node(oxfw, root, "formation", proc_read_formation); +} diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c new file mode 100644 index 000000000000..bda845afb470 --- /dev/null +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -0,0 +1,686 @@ +/* + * oxfw_stream.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) 2014 Takashi Sakamoto + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" +#include <linux/delay.h> + +#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512 +#define CALLBACK_TIMEOUT 200 + +/* + * According to datasheet of Oxford Semiconductor: + * OXFW970: 32.0/44.1/48.0/96.0 Khz, 8 audio channels I/O + * OXFW971: 32.0/44.1/48.0/88.2/96.0/192.0 kHz, 16 audio channels I/O, MIDI I/O + */ +static const unsigned int oxfw_rate_table[] = { + [0] = 32000, + [1] = 44100, + [2] = 48000, + [3] = 88200, + [4] = 96000, + [5] = 192000, +}; + +/* + * See Table 5.7 – Sampling frequency for Multi-bit Audio + * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA) + */ +static const unsigned int avc_stream_rate_table[] = { + [0] = 0x02, + [1] = 0x03, + [2] = 0x04, + [3] = 0x0a, + [4] = 0x05, + [5] = 0x07, +}; + +static int set_rate(struct snd_oxfw *oxfw, unsigned int rate) +{ + int err; + + err = avc_general_set_sig_fmt(oxfw->unit, rate, + AVC_GENERAL_PLUG_DIR_IN, 0); + if (err < 0) + goto end; + + if (oxfw->has_output) + err = avc_general_set_sig_fmt(oxfw->unit, rate, + AVC_GENERAL_PLUG_DIR_OUT, 0); +end: + return err; +} + +static int set_stream_format(struct snd_oxfw *oxfw, struct amdtp_stream *s, + unsigned int rate, unsigned int pcm_channels) +{ + u8 **formats; + struct snd_oxfw_stream_formation formation; + enum avc_general_plug_dir dir; + unsigned int len; + int i, err; + + if (s == &oxfw->tx_stream) { + formats = oxfw->tx_stream_formats; + dir = AVC_GENERAL_PLUG_DIR_OUT; + } else { + formats = oxfw->rx_stream_formats; + dir = AVC_GENERAL_PLUG_DIR_IN; + } + + /* Seek stream format for requirements. */ + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + err = snd_oxfw_stream_parse_format(formats[i], &formation); + if (err < 0) + return err; + + if ((formation.rate == rate) && (formation.pcm == pcm_channels)) + break; + } + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) + return -EINVAL; + + /* If assumed, just change rate. */ + if (oxfw->assumed) + return set_rate(oxfw, rate); + + /* Calculate format length. */ + len = 5 + formats[i][4] * 2; + + err = avc_stream_set_format(oxfw->unit, dir, 0, formats[i], len); + if (err < 0) + return err; + + /* Some requests just after changing format causes freezing. */ + msleep(100); + + return 0; +} + +static void stop_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream) +{ + amdtp_stream_pcm_abort(stream); + amdtp_stream_stop(stream); + + if (stream == &oxfw->tx_stream) + cmp_connection_break(&oxfw->out_conn); + else + cmp_connection_break(&oxfw->in_conn); +} + +static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream, + unsigned int rate, unsigned int pcm_channels) +{ + u8 **formats; + struct cmp_connection *conn; + struct snd_oxfw_stream_formation formation; + unsigned int i, midi_ports; + int err; + + if (stream == &oxfw->rx_stream) { + formats = oxfw->rx_stream_formats; + conn = &oxfw->in_conn; + } else { + formats = oxfw->tx_stream_formats; + conn = &oxfw->out_conn; + } + + /* Get stream format */ + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + if (formats[i] == NULL) + break; + + err = snd_oxfw_stream_parse_format(formats[i], &formation); + if (err < 0) + goto end; + if (rate != formation.rate) + continue; + if (pcm_channels == 0 || pcm_channels == formation.pcm) + break; + } + if (i == SND_OXFW_STREAM_FORMAT_ENTRIES) { + err = -EINVAL; + goto end; + } + + pcm_channels = formation.pcm; + midi_ports = DIV_ROUND_UP(formation.midi, 8); + + /* The stream should have one pcm channels at least */ + if (pcm_channels == 0) { + err = -EINVAL; + goto end; + } + amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports); + + err = cmp_connection_establish(conn, + amdtp_stream_get_max_payload(stream)); + if (err < 0) + goto end; + + err = amdtp_stream_start(stream, + conn->resources.channel, + conn->speed); + if (err < 0) { + cmp_connection_break(conn); + goto end; + } + + /* Wait first packet */ + err = amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT); + if (err < 0) + stop_stream(oxfw, stream); +end: + return err; +} + +static int check_connection_used_by_others(struct snd_oxfw *oxfw, + struct amdtp_stream *stream) +{ + struct cmp_connection *conn; + bool used; + int err; + + if (stream == &oxfw->tx_stream) + conn = &oxfw->out_conn; + else + conn = &oxfw->in_conn; + + err = cmp_connection_check_used(conn, &used); + if ((err >= 0) && used && !amdtp_stream_running(stream)) { + dev_err(&oxfw->unit->device, + "Connection established by others: %cPCR[%d]\n", + (conn->direction == CMP_OUTPUT) ? 'o' : 'i', + conn->pcr_index); + err = -EBUSY; + } + + return err; +} + +int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream) +{ + struct cmp_connection *conn; + enum cmp_direction c_dir; + enum amdtp_stream_direction s_dir; + int err; + + if (stream == &oxfw->tx_stream) { + conn = &oxfw->out_conn; + c_dir = CMP_OUTPUT; + s_dir = AMDTP_IN_STREAM; + } else { + conn = &oxfw->in_conn; + c_dir = CMP_INPUT; + s_dir = AMDTP_OUT_STREAM; + } + + err = cmp_connection_init(conn, oxfw->unit, c_dir, 0); + if (err < 0) + goto end; + + err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING); + if (err < 0) { + amdtp_stream_destroy(stream); + cmp_connection_destroy(conn); + goto end; + } + + /* OXFW starts to transmit packets with non-zero dbc. */ + if (stream == &oxfw->tx_stream) + oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK; +end: + return err; +} + +int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream, + unsigned int rate, unsigned int pcm_channels) +{ + struct amdtp_stream *opposite; + struct snd_oxfw_stream_formation formation; + enum avc_general_plug_dir dir; + unsigned int substreams, opposite_substreams; + int err = 0; + + if (stream == &oxfw->tx_stream) { + substreams = oxfw->capture_substreams; + opposite = &oxfw->rx_stream; + opposite_substreams = oxfw->playback_substreams; + dir = AVC_GENERAL_PLUG_DIR_OUT; + } else { + substreams = oxfw->playback_substreams; + opposite_substreams = oxfw->capture_substreams; + + if (oxfw->has_output) + opposite = &oxfw->rx_stream; + else + opposite = NULL; + + dir = AVC_GENERAL_PLUG_DIR_IN; + } + + if (substreams == 0) + goto end; + + /* + * Considering JACK/FFADO streaming: + * TODO: This can be removed hwdep functionality becomes popular. + */ + err = check_connection_used_by_others(oxfw, stream); + if (err < 0) + goto end; + + /* packet queueing error */ + if (amdtp_streaming_error(stream)) + stop_stream(oxfw, stream); + + err = snd_oxfw_stream_get_current_formation(oxfw, dir, &formation); + if (err < 0) + goto end; + if (rate == 0) + rate = formation.rate; + if (pcm_channels == 0) + pcm_channels = formation.pcm; + + if ((formation.rate != rate) || (formation.pcm != pcm_channels)) { + if (opposite != NULL) { + err = check_connection_used_by_others(oxfw, opposite); + if (err < 0) + goto end; + stop_stream(oxfw, opposite); + } + stop_stream(oxfw, stream); + + err = set_stream_format(oxfw, stream, rate, pcm_channels); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to set stream format: %d\n", err); + goto end; + } + + /* Start opposite stream if needed. */ + if (opposite && !amdtp_stream_running(opposite) && + (opposite_substreams > 0)) { + err = start_stream(oxfw, opposite, rate, 0); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to restart stream: %d\n", err); + goto end; + } + } + } + + /* Start requested stream. */ + if (!amdtp_stream_running(stream)) { + err = start_stream(oxfw, stream, rate, pcm_channels); + if (err < 0) + dev_err(&oxfw->unit->device, + "fail to start stream: %d\n", err); + } +end: + return err; +} + +void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream) +{ + if (((stream == &oxfw->tx_stream) && (oxfw->capture_substreams > 0)) || + ((stream == &oxfw->rx_stream) && (oxfw->playback_substreams > 0))) + return; + + stop_stream(oxfw, stream); +} + +void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream) +{ + struct cmp_connection *conn; + + if (stream == &oxfw->tx_stream) + conn = &oxfw->out_conn; + else + conn = &oxfw->in_conn; + + stop_stream(oxfw, stream); + + amdtp_stream_destroy(stream); + cmp_connection_destroy(conn); +} + +void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream) +{ + struct cmp_connection *conn; + + if (stream == &oxfw->tx_stream) + conn = &oxfw->out_conn; + else + conn = &oxfw->in_conn; + + if (cmp_connection_update(conn) < 0) + stop_stream(oxfw, stream); + else + amdtp_stream_update(stream); +} + +int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, + enum avc_general_plug_dir dir, + struct snd_oxfw_stream_formation *formation) +{ + u8 *format; + unsigned int len; + int err; + + len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; + format = kmalloc(len, GFP_KERNEL); + if (format == NULL) + return -ENOMEM; + + err = avc_stream_get_format_single(oxfw->unit, dir, 0, format, &len); + if (err < 0) + goto end; + if (len < 3) { + err = -EIO; + goto end; + } + + err = snd_oxfw_stream_parse_format(format, formation); +end: + kfree(format); + return err; +} + +/* + * See Table 6.16 - AM824 Stream Format + * Figure 6.19 - format_information field for AM824 Compound + * in AV/C Stream Format Information Specification 1.1 (Apr 2005, 1394TA) + * Also 'Clause 12 AM824 sequence adaption layers' in IEC 61883-6:2005 + */ +int snd_oxfw_stream_parse_format(u8 *format, + struct snd_oxfw_stream_formation *formation) +{ + unsigned int i, e, channels, type; + + memset(formation, 0, sizeof(struct snd_oxfw_stream_formation)); + + /* + * this module can support a hierarchy combination that: + * Root: Audio and Music (0x90) + * Level 1: AM824 Compound (0x40) + */ + if ((format[0] != 0x90) || (format[1] != 0x40)) + return -ENOSYS; + + /* check the sampling rate */ + for (i = 0; i < ARRAY_SIZE(avc_stream_rate_table); i++) { + if (format[2] == avc_stream_rate_table[i]) + break; + } + if (i == ARRAY_SIZE(avc_stream_rate_table)) + return -ENOSYS; + + formation->rate = oxfw_rate_table[i]; + + for (e = 0; e < format[4]; e++) { + channels = format[5 + e * 2]; + type = format[6 + e * 2]; + + switch (type) { + /* IEC 60958 Conformant, currently handled as MBLA */ + case 0x00: + /* Multi Bit Linear Audio (Raw) */ + case 0x06: + formation->pcm += channels; + break; + /* MIDI Conformant */ + case 0x0d: + formation->midi = channels; + break; + /* IEC 61937-3 to 7 */ + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + /* Multi Bit Linear Audio */ + case 0x07: /* DVD-Audio */ + case 0x0c: /* High Precision */ + /* One Bit Audio */ + case 0x08: /* (Plain) Raw */ + case 0x09: /* (Plain) SACD */ + case 0x0a: /* (Encoded) Raw */ + case 0x0b: /* (Encoded) SACD */ + /* SMPTE Time-Code conformant */ + case 0x0e: + /* Sample Count */ + case 0x0f: + /* Anciliary Data */ + case 0x10: + /* Synchronization Stream (Stereo Raw audio) */ + case 0x40: + /* Don't care */ + case 0xff: + default: + return -ENOSYS; /* not supported */ + } + } + + if (formation->pcm > AMDTP_MAX_CHANNELS_FOR_PCM || + formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI) + return -ENOSYS; + + return 0; +} + +static int +assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, + unsigned int pid, u8 *buf, unsigned int *len, + u8 **formats) +{ + struct snd_oxfw_stream_formation formation; + unsigned int i, eid; + int err; + + /* get format at current sampling rate */ + err = avc_stream_get_format_single(oxfw->unit, dir, pid, buf, len); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to get current stream format for isoc %s plug %d:%d\n", + (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", + pid, err); + goto end; + } + + /* parse and set stream format */ + eid = 0; + err = snd_oxfw_stream_parse_format(buf, &formation); + if (err < 0) + goto end; + + formats[eid] = kmalloc(*len, GFP_KERNEL); + if (formats[eid] == NULL) { + err = -ENOMEM; + goto end; + } + memcpy(formats[eid], buf, *len); + + /* apply the format for each available sampling rate */ + for (i = 0; i < ARRAY_SIZE(oxfw_rate_table); i++) { + if (formation.rate == oxfw_rate_table[i]) + continue; + + err = avc_general_inquiry_sig_fmt(oxfw->unit, + oxfw_rate_table[i], + dir, pid); + if (err < 0) + continue; + + eid++; + formats[eid] = kmalloc(*len, GFP_KERNEL); + if (formats[eid] == NULL) { + err = -ENOMEM; + goto end; + } + memcpy(formats[eid], buf, *len); + formats[eid][2] = avc_stream_rate_table[i]; + } + + err = 0; + oxfw->assumed = true; +end: + return err; +} + +static int fill_stream_formats(struct snd_oxfw *oxfw, + enum avc_general_plug_dir dir, + unsigned short pid) +{ + u8 *buf, **formats; + unsigned int len, eid = 0; + struct snd_oxfw_stream_formation dummy; + int err; + + buf = kmalloc(AVC_GENERIC_FRAME_MAXIMUM_BYTES, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (dir == AVC_GENERAL_PLUG_DIR_OUT) + formats = oxfw->tx_stream_formats; + else + formats = oxfw->rx_stream_formats; + + /* get first entry */ + len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; + err = avc_stream_get_format_list(oxfw->unit, dir, 0, buf, &len, 0); + if (err == -ENOSYS) { + /* LIST subfunction is not implemented */ + len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; + err = assume_stream_formats(oxfw, dir, pid, buf, &len, + formats); + goto end; + } else if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to get stream format %d for isoc %s plug %d:%d\n", + eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : "out", + pid, err); + goto end; + } + + /* LIST subfunction is implemented */ + while (eid < SND_OXFW_STREAM_FORMAT_ENTRIES) { + /* The format is too short. */ + if (len < 3) { + err = -EIO; + break; + } + + /* parse and set stream format */ + err = snd_oxfw_stream_parse_format(buf, &dummy); + if (err < 0) + break; + + formats[eid] = kmalloc(len, GFP_KERNEL); + if (formats[eid] == NULL) { + err = -ENOMEM; + break; + } + memcpy(formats[eid], buf, len); + + /* get next entry */ + len = AVC_GENERIC_FRAME_MAXIMUM_BYTES; + err = avc_stream_get_format_list(oxfw->unit, dir, 0, + buf, &len, ++eid); + /* No entries remained. */ + if (err == -EINVAL) { + err = 0; + break; + } else if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to get stream format %d for isoc %s plug %d:%d\n", + eid, (dir == AVC_GENERAL_PLUG_DIR_IN) ? "in" : + "out", + pid, err); + break; + } + } +end: + kfree(buf); + return err; +} + +int snd_oxfw_stream_discover(struct snd_oxfw *oxfw) +{ + u8 plugs[AVC_PLUG_INFO_BUF_BYTES]; + int err; + + /* the number of plugs for isoc in/out, ext in/out */ + err = avc_general_get_plug_info(oxfw->unit, 0x1f, 0x07, 0x00, plugs); + if (err < 0) { + dev_err(&oxfw->unit->device, + "fail to get info for isoc/external in/out plugs: %d\n", + err); + goto end; + } else if ((plugs[0] == 0) && (plugs[1] == 0)) { + err = -ENOSYS; + goto end; + } + + /* use oPCR[0] if exists */ + if (plugs[1] > 0) { + err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0); + if (err < 0) + goto end; + oxfw->has_output = true; + } + + /* use iPCR[0] if exists */ + if (plugs[0] > 0) + err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0); +end: + return err; +} + +void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw) +{ + oxfw->dev_lock_changed = true; + wake_up(&oxfw->hwdep_wait); +} + +int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw) +{ + int err; + + spin_lock_irq(&oxfw->lock); + + /* user land lock this */ + if (oxfw->dev_lock_count < 0) { + err = -EBUSY; + goto end; + } + + /* this is the first time */ + if (oxfw->dev_lock_count++ == 0) + snd_oxfw_stream_lock_changed(oxfw); + err = 0; +end: + spin_unlock_irq(&oxfw->lock); + return err; +} + +void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw) +{ + spin_lock_irq(&oxfw->lock); + + if (WARN_ON(oxfw->dev_lock_count <= 0)) + goto end; + if (--oxfw->dev_lock_count == 0) + snd_oxfw_stream_lock_changed(oxfw); +end: + spin_unlock_irq(&oxfw->lock); +} diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c new file mode 100644 index 000000000000..60e5cad0531a --- /dev/null +++ b/sound/firewire/oxfw/oxfw.c @@ -0,0 +1,317 @@ +/* + * oxfw.c - a part of driver for OXFW970/971 based devices + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include "oxfw.h" + +#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) +/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ + +#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) +#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 +#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 + +#define VENDOR_LOUD 0x000ff2 +#define VENDOR_GRIFFIN 0x001292 +#define VENDOR_BEHRINGER 0x001564 +#define VENDOR_LACIE 0x00d04b + +#define SPECIFIER_1394TA 0x00a02d +#define VERSION_AVC 0x010001 + +MODULE_DESCRIPTION("Oxford Semiconductor FW970/971 driver"); +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("snd-firewire-speakers"); + +static bool detect_loud_models(struct fw_unit *unit) +{ + const char *const models[] = { + "Onyxi", + "Onyx-i", + "d.Pro", + "Mackie Onyx Satellite", + "Tapco LINK.firewire 4x6", + "U.420"}; + char model[32]; + unsigned int i; + int err; + + err = fw_csr_string(unit->directory, CSR_MODEL, + model, sizeof(model)); + if (err < 0) + return false; + + for (i = 0; i < ARRAY_SIZE(models); i++) { + if (strcmp(models[i], model) == 0) + break; + } + + return (i < ARRAY_SIZE(models)); +} + +static int name_card(struct snd_oxfw *oxfw) +{ + struct fw_device *fw_dev = fw_parent_device(oxfw->unit); + char vendor[24]; + char model[32]; + const char *d, *v, *m; + u32 firmware; + int err; + + /* get vendor name from root directory */ + err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR, + vendor, sizeof(vendor)); + if (err < 0) + goto end; + + /* get model name from unit directory */ + err = fw_csr_string(oxfw->unit->directory, CSR_MODEL, + model, sizeof(model)); + if (err < 0) + goto end; + + err = snd_fw_transaction(oxfw->unit, TCODE_READ_QUADLET_REQUEST, + OXFORD_FIRMWARE_ID_ADDRESS, &firmware, 4, 0); + if (err < 0) + goto end; + be32_to_cpus(&firmware); + + /* to apply card definitions */ + if (oxfw->device_info) { + d = oxfw->device_info->driver_name; + v = oxfw->device_info->vendor_name; + m = oxfw->device_info->model_name; + } else { + d = "OXFW"; + v = vendor; + m = model; + } + + strcpy(oxfw->card->driver, d); + strcpy(oxfw->card->mixername, m); + strcpy(oxfw->card->shortname, m); + + snprintf(oxfw->card->longname, sizeof(oxfw->card->longname), + "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d", + v, m, firmware >> 20, firmware & 0xffff, + fw_dev->config_rom[3], fw_dev->config_rom[4], + dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed); +end: + return err; +} + +static void oxfw_card_free(struct snd_card *card) +{ + struct snd_oxfw *oxfw = card->private_data; + unsigned int i; + + for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { + kfree(oxfw->tx_stream_formats[i]); + kfree(oxfw->rx_stream_formats[i]); + } + + mutex_destroy(&oxfw->mutex); +} + +static int oxfw_probe(struct fw_unit *unit, + const struct ieee1394_device_id *id) +{ + struct snd_card *card; + struct snd_oxfw *oxfw; + int err; + + if ((id->vendor_id == VENDOR_LOUD) && !detect_loud_models(unit)) + return -ENODEV; + + err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, + sizeof(*oxfw), &card); + if (err < 0) + return err; + + card->private_free = oxfw_card_free; + oxfw = card->private_data; + oxfw->card = card; + mutex_init(&oxfw->mutex); + oxfw->unit = unit; + oxfw->device_info = (const struct device_info *)id->driver_data; + spin_lock_init(&oxfw->lock); + init_waitqueue_head(&oxfw->hwdep_wait); + + err = snd_oxfw_stream_discover(oxfw); + if (err < 0) + goto error; + + err = name_card(oxfw); + if (err < 0) + goto error; + + err = snd_oxfw_create_pcm(oxfw); + if (err < 0) + goto error; + + if (oxfw->device_info) { + err = snd_oxfw_create_mixer(oxfw); + if (err < 0) + goto error; + } + + snd_oxfw_proc_init(oxfw); + + err = snd_oxfw_create_midi(oxfw); + if (err < 0) + goto error; + + err = snd_oxfw_create_hwdep(oxfw); + if (err < 0) + goto error; + + err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream); + if (err < 0) + goto error; + if (oxfw->has_output) { + err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream); + if (err < 0) + goto error; + } + + err = snd_card_register(card); + if (err < 0) { + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); + goto error; + } + dev_set_drvdata(&unit->device, oxfw); + + return 0; +error: + snd_card_free(card); + return err; +} + +static void oxfw_bus_reset(struct fw_unit *unit) +{ + struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); + + fcp_bus_reset(oxfw->unit); + + mutex_lock(&oxfw->mutex); + + snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream); + + mutex_unlock(&oxfw->mutex); +} + +static void oxfw_remove(struct fw_unit *unit) +{ + struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device); + + snd_card_disconnect(oxfw->card); + + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); + if (oxfw->has_output) + snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); + + snd_card_free_when_closed(oxfw->card); +} + +static const struct device_info griffin_firewave = { + .driver_name = "FireWave", + .vendor_name = "Griffin", + .model_name = "FireWave", + .mixer_channels = 6, + .mute_fb_id = 0x01, + .volume_fb_id = 0x02, +}; + +static const struct device_info lacie_speakers = { + .driver_name = "FWSpeakers", + .vendor_name = "LaCie", + .model_name = "FireWire Speakers", + .mixer_channels = 1, + .mute_fb_id = 0x01, + .volume_fb_id = 0x01, +}; + +static const struct ieee1394_device_id oxfw_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_GRIFFIN, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&griffin_firewave, + }, + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_LACIE, + .model_id = 0x00f970, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + .driver_data = (kernel_ulong_t)&lacie_speakers, + }, + /* Behringer,F-Control Audio 202 */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = VENDOR_BEHRINGER, + .model_id = 0x00fc22, + }, + /* + * Any Mackie(Loud) models (name string/model id): + * Onyx-i series (former models): 0x081216 + * Mackie Onyx Satellite: 0x00200f + * Tapco LINK.firewire 4x6: 0x000460 + * d.2 pro: Unknown + * d.4 pro: Unknown + * U.420: Unknown + * U.420d: Unknown + */ + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .vendor_id = VENDOR_LOUD, + .specifier_id = SPECIFIER_1394TA, + .version = VERSION_AVC, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table); + +static struct fw_driver oxfw_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = oxfw_probe, + .update = oxfw_bus_reset, + .remove = oxfw_remove, + .id_table = oxfw_id_table, +}; + +static int __init snd_oxfw_init(void) +{ + return driver_register(&oxfw_driver.driver); +} + +static void __exit snd_oxfw_exit(void) +{ + driver_unregister(&oxfw_driver.driver); +} + +module_init(snd_oxfw_init); +module_exit(snd_oxfw_exit); diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h new file mode 100644 index 000000000000..cace5ad4fe76 --- /dev/null +++ b/sound/firewire/oxfw/oxfw.h @@ -0,0 +1,146 @@ +/* + * oxfw.h - a part of driver for OXFW970/971 based devices + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/compat.h> + +#include <sound/control.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/rawmidi.h> +#include <sound/firewire.h> +#include <sound/hwdep.h> + +#include "../lib.h" +#include "../fcp.h" +#include "../packets-buffer.h" +#include "../iso-resources.h" +#include "../amdtp.h" +#include "../cmp.h" + +struct device_info { + const char *driver_name; + const char *vendor_name; + const char *model_name; + unsigned int mixer_channels; + u8 mute_fb_id; + u8 volume_fb_id; +}; + +/* This is an arbitrary number for convinience. */ +#define SND_OXFW_STREAM_FORMAT_ENTRIES 10 +struct snd_oxfw { + struct snd_card *card; + struct fw_unit *unit; + const struct device_info *device_info; + struct mutex mutex; + spinlock_t lock; + + bool has_output; + u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES]; + u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES]; + bool assumed; + struct cmp_connection out_conn; + struct cmp_connection in_conn; + struct amdtp_stream tx_stream; + struct amdtp_stream rx_stream; + unsigned int capture_substreams; + unsigned int playback_substreams; + + unsigned int midi_input_ports; + unsigned int midi_output_ports; + + bool mute; + s16 volume[6]; + s16 volume_min; + s16 volume_max; + + int dev_lock_count; + bool dev_lock_changed; + wait_queue_head_t hwdep_wait; +}; + +/* + * AV/C Stream Format Information Specification 1.1 Working Draft + * (Apr 2005, 1394TA) + */ +int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir, + unsigned int pid, u8 *format, unsigned int len); +int avc_stream_get_format(struct fw_unit *unit, + enum avc_general_plug_dir dir, unsigned int pid, + u8 *buf, unsigned int *len, unsigned int eid); +static inline int +avc_stream_get_format_single(struct fw_unit *unit, + enum avc_general_plug_dir dir, unsigned int pid, + u8 *buf, unsigned int *len) +{ + return avc_stream_get_format(unit, dir, pid, buf, len, 0xff); +} +static inline int +avc_stream_get_format_list(struct fw_unit *unit, + enum avc_general_plug_dir dir, unsigned int pid, + u8 *buf, unsigned int *len, + unsigned int eid) +{ + return avc_stream_get_format(unit, dir, pid, buf, len, eid); +} + +/* + * AV/C Digital Interface Command Set General Specification 4.2 + * (Sep 2004, 1394TA) + */ +int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate, + enum avc_general_plug_dir dir, + unsigned short pid); + +int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream); +int snd_oxfw_stream_start_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream, + unsigned int rate, unsigned int pcm_channels); +void snd_oxfw_stream_stop_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream); +void snd_oxfw_stream_destroy_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream); +void snd_oxfw_stream_update_simplex(struct snd_oxfw *oxfw, + struct amdtp_stream *stream); + +struct snd_oxfw_stream_formation { + unsigned int rate; + unsigned int pcm; + unsigned int midi; +}; +int snd_oxfw_stream_parse_format(u8 *format, + struct snd_oxfw_stream_formation *formation); +int snd_oxfw_stream_get_current_formation(struct snd_oxfw *oxfw, + enum avc_general_plug_dir dir, + struct snd_oxfw_stream_formation *formation); + +int snd_oxfw_stream_discover(struct snd_oxfw *oxfw); + +void snd_oxfw_stream_lock_changed(struct snd_oxfw *oxfw); +int snd_oxfw_stream_lock_try(struct snd_oxfw *oxfw); +void snd_oxfw_stream_lock_release(struct snd_oxfw *oxfw); + +int snd_oxfw_create_pcm(struct snd_oxfw *oxfw); + +int snd_oxfw_create_mixer(struct snd_oxfw *oxfw); + +void snd_oxfw_proc_init(struct snd_oxfw *oxfw); + +int snd_oxfw_create_midi(struct snd_oxfw *oxfw); + +int snd_oxfw_create_hwdep(struct snd_oxfw *oxfw); diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c deleted file mode 100644 index 768d40ddfebb..000000000000 --- a/sound/firewire/speakers.c +++ /dev/null @@ -1,792 +0,0 @@ -/* - * OXFW970-based speakers driver - * - * Copyright (c) Clemens Ladisch <clemens@ladisch.de> - * Licensed under the terms of the GNU General Public License, version 2. - */ - -#include <linux/device.h> -#include <linux/firewire.h> -#include <linux/firewire-constants.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <sound/control.h> -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include "cmp.h" -#include "fcp.h" -#include "amdtp.h" -#include "lib.h" - -#define OXFORD_FIRMWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x50000) -/* 0x970?vvvv or 0x971?vvvv, where vvvv = firmware version */ - -#define OXFORD_HARDWARE_ID_ADDRESS (CSR_REGISTER_BASE + 0x90020) -#define OXFORD_HARDWARE_ID_OXFW970 0x39443841 -#define OXFORD_HARDWARE_ID_OXFW971 0x39373100 - -#define VENDOR_GRIFFIN 0x001292 -#define VENDOR_LACIE 0x00d04b - -#define SPECIFIER_1394TA 0x00a02d -#define VERSION_AVC 0x010001 - -struct device_info { - const char *driver_name; - const char *short_name; - const char *long_name; - int (*pcm_constraints)(struct snd_pcm_runtime *runtime); - unsigned int mixer_channels; - u8 mute_fb_id; - u8 volume_fb_id; -}; - -struct fwspk { - struct snd_card *card; - struct fw_unit *unit; - const struct device_info *device_info; - struct mutex mutex; - struct cmp_connection connection; - struct amdtp_stream stream; - bool mute; - s16 volume[6]; - s16 volume_min; - s16 volume_max; -}; - -MODULE_DESCRIPTION("FireWire speakers driver"); -MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); -MODULE_LICENSE("GPL v2"); - -static int firewave_rate_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static unsigned int stereo_rates[] = { 48000, 96000 }; - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - - /* two channels work only at 48/96 kHz */ - if (snd_interval_max(channels) < 6) - return snd_interval_list(rate, 2, stereo_rates, 0); - return 0; -} - -static int firewave_channels_constraint(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - static const struct snd_interval all_channels = { .min = 6, .max = 6 }; - struct snd_interval *rate = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - struct snd_interval *channels = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - - /* 32/44.1 kHz work only with all six channels */ - if (snd_interval_max(rate) < 48000) - return snd_interval_refine(channels, &all_channels); - return 0; -} - -static int firewave_constraints(struct snd_pcm_runtime *runtime) -{ - static unsigned int channels_list[] = { 2, 6 }; - static struct snd_pcm_hw_constraint_list channels_list_constraint = { - .count = 2, - .list = channels_list, - }; - int err; - - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_96000; - runtime->hw.channels_max = 6; - - err = snd_pcm_hw_constraint_list(runtime, 0, - SNDRV_PCM_HW_PARAM_CHANNELS, - &channels_list_constraint); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - firewave_rate_constraint, NULL, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - if (err < 0) - return err; - err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - firewave_channels_constraint, NULL, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (err < 0) - return err; - - return 0; -} - -static int lacie_speakers_constraints(struct snd_pcm_runtime *runtime) -{ - runtime->hw.rates = SNDRV_PCM_RATE_32000 | - SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000; - - return 0; -} - -static int fwspk_open(struct snd_pcm_substream *substream) -{ - static const struct snd_pcm_hardware hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER, - .formats = AMDTP_OUT_PCM_FORMAT_BITS, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 4 * 1024 * 1024, - .period_bytes_min = 1, - .period_bytes_max = UINT_MAX, - .periods_min = 1, - .periods_max = UINT_MAX, - }; - struct fwspk *fwspk = substream->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - - runtime->hw = hardware; - - err = fwspk->device_info->pcm_constraints(runtime); - if (err < 0) - return err; - err = snd_pcm_limit_hw_rates(runtime); - if (err < 0) - return err; - - err = amdtp_stream_add_pcm_hw_constraints(&fwspk->stream, runtime); - if (err < 0) - return err; - - return 0; -} - -static int fwspk_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -static void fwspk_stop_stream(struct fwspk *fwspk) -{ - if (amdtp_stream_running(&fwspk->stream)) { - amdtp_stream_stop(&fwspk->stream); - cmp_connection_break(&fwspk->connection); - } -} - -static int fwspk_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct fwspk *fwspk = substream->private_data; - int err; - - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - - err = snd_pcm_lib_alloc_vmalloc_buffer(substream, - params_buffer_bytes(hw_params)); - if (err < 0) - goto error; - - amdtp_stream_set_parameters(&fwspk->stream, - params_rate(hw_params), - params_channels(hw_params), - 0); - - amdtp_stream_set_pcm_format(&fwspk->stream, - params_format(hw_params)); - - err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params), - AVC_GENERAL_PLUG_DIR_IN, 0); - if (err < 0) { - dev_err(&fwspk->unit->device, "failed to set sample rate\n"); - goto err_buffer; - } - - return 0; - -err_buffer: - snd_pcm_lib_free_vmalloc_buffer(substream); -error: - return err; -} - -static int fwspk_hw_free(struct snd_pcm_substream *substream) -{ - struct fwspk *fwspk = substream->private_data; - - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - - return snd_pcm_lib_free_vmalloc_buffer(substream); -} - -static int fwspk_prepare(struct snd_pcm_substream *substream) -{ - struct fwspk *fwspk = substream->private_data; - int err; - - mutex_lock(&fwspk->mutex); - - if (amdtp_streaming_error(&fwspk->stream)) - fwspk_stop_stream(fwspk); - - if (!amdtp_stream_running(&fwspk->stream)) { - err = cmp_connection_establish(&fwspk->connection, - amdtp_stream_get_max_payload(&fwspk->stream)); - if (err < 0) - goto err_mutex; - - err = amdtp_stream_start(&fwspk->stream, - fwspk->connection.resources.channel, - fwspk->connection.speed); - if (err < 0) - goto err_connection; - } - - mutex_unlock(&fwspk->mutex); - - amdtp_stream_pcm_prepare(&fwspk->stream); - - return 0; - -err_connection: - cmp_connection_break(&fwspk->connection); -err_mutex: - mutex_unlock(&fwspk->mutex); - - return err; -} - -static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct fwspk *fwspk = substream->private_data; - struct snd_pcm_substream *pcm; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm = substream; - break; - case SNDRV_PCM_TRIGGER_STOP: - pcm = NULL; - break; - default: - return -EINVAL; - } - amdtp_stream_pcm_trigger(&fwspk->stream, pcm); - return 0; -} - -static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream) -{ - struct fwspk *fwspk = substream->private_data; - - return amdtp_stream_pcm_pointer(&fwspk->stream); -} - -static int fwspk_create_pcm(struct fwspk *fwspk) -{ - static struct snd_pcm_ops ops = { - .open = fwspk_open, - .close = fwspk_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = fwspk_hw_params, - .hw_free = fwspk_hw_free, - .prepare = fwspk_prepare, - .trigger = fwspk_trigger, - .pointer = fwspk_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, - }; - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(fwspk->card, "OXFW970", 0, 1, 0, &pcm); - if (err < 0) - return err; - pcm->private_data = fwspk; - strcpy(pcm->name, fwspk->device_info->short_name); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops); - return 0; -} - -enum control_action { CTL_READ, CTL_WRITE }; -enum control_attribute { - CTL_MIN = 0x02, - CTL_MAX = 0x03, - CTL_CURRENT = 0x10, -}; - -static int fwspk_mute_command(struct fwspk *fwspk, bool *value, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(11, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = fwspk->device_info->mute_fb_id; /* function block ID */ - buf[5] = 0x10; /* control attribute: current */ - buf[6] = 0x02; /* selector length */ - buf[7] = 0x00; /* audio channel number */ - buf[8] = 0x01; /* control selector: mute */ - buf[9] = 0x01; /* control data length */ - if (action == CTL_READ) - buf[10] = 0xff; - else - buf[10] = *value ? 0x70 : 0x60; - - err = fcp_avc_transaction(fwspk->unit, buf, 11, buf, 11, 0x3fe); - if (err < 0) - goto error; - if (err < 11) { - dev_err(&fwspk->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&fwspk->unit->device, "mute command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = buf[10] == 0x70; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int fwspk_volume_command(struct fwspk *fwspk, s16 *value, - unsigned int channel, - enum control_attribute attribute, - enum control_action action) -{ - u8 *buf; - u8 response_ok; - int err; - - buf = kmalloc(12, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (action == CTL_READ) { - buf[0] = 0x01; /* AV/C, STATUS */ - response_ok = 0x0c; /* STABLE */ - } else { - buf[0] = 0x00; /* AV/C, CONTROL */ - response_ok = 0x09; /* ACCEPTED */ - } - buf[1] = 0x08; /* audio unit 0 */ - buf[2] = 0xb8; /* FUNCTION BLOCK */ - buf[3] = 0x81; /* function block type: feature */ - buf[4] = fwspk->device_info->volume_fb_id; /* function block ID */ - buf[5] = attribute; /* control attribute */ - buf[6] = 0x02; /* selector length */ - buf[7] = channel; /* audio channel number */ - buf[8] = 0x02; /* control selector: volume */ - buf[9] = 0x02; /* control data length */ - if (action == CTL_READ) { - buf[10] = 0xff; - buf[11] = 0xff; - } else { - buf[10] = *value >> 8; - buf[11] = *value; - } - - err = fcp_avc_transaction(fwspk->unit, buf, 12, buf, 12, 0x3fe); - if (err < 0) - goto error; - if (err < 12) { - dev_err(&fwspk->unit->device, "short FCP response\n"); - err = -EIO; - goto error; - } - if (buf[0] != response_ok) { - dev_err(&fwspk->unit->device, "volume command failed\n"); - err = -EIO; - goto error; - } - if (action == CTL_READ) - *value = (buf[10] << 8) | buf[11]; - - err = 0; - -error: - kfree(buf); - - return err; -} - -static int fwspk_mute_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - - value->value.integer.value[0] = !fwspk->mute; - - return 0; -} - -static int fwspk_mute_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - bool mute; - int err; - - mute = !value->value.integer.value[0]; - - if (mute == fwspk->mute) - return 0; - - err = fwspk_mute_command(fwspk, &mute, CTL_WRITE); - if (err < 0) - return err; - fwspk->mute = mute; - - return 1; -} - -static int fwspk_volume_info(struct snd_kcontrol *control, - struct snd_ctl_elem_info *info) -{ - struct fwspk *fwspk = control->private_data; - - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = fwspk->device_info->mixer_channels; - info->value.integer.min = fwspk->volume_min; - info->value.integer.max = fwspk->volume_max; - - return 0; -} - -static const u8 channel_map[6] = { 0, 1, 4, 5, 2, 3 }; - -static int fwspk_volume_get(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - unsigned int i; - - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) - value->value.integer.value[channel_map[i]] = fwspk->volume[i]; - - return 0; -} - -static int fwspk_volume_put(struct snd_kcontrol *control, - struct snd_ctl_elem_value *value) -{ - struct fwspk *fwspk = control->private_data; - unsigned int i, changed_channels; - bool equal_values = true; - s16 volume; - int err; - - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { - if (value->value.integer.value[i] < fwspk->volume_min || - value->value.integer.value[i] > fwspk->volume_max) - return -EINVAL; - if (value->value.integer.value[i] != - value->value.integer.value[0]) - equal_values = false; - } - - changed_channels = 0; - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) - if (value->value.integer.value[channel_map[i]] != - fwspk->volume[i]) - changed_channels |= 1 << (i + 1); - - if (equal_values && changed_channels != 0) - changed_channels = 1 << 0; - - for (i = 0; i <= fwspk->device_info->mixer_channels; ++i) { - volume = value->value.integer.value[channel_map[i ? i - 1 : 0]]; - if (changed_channels & (1 << i)) { - err = fwspk_volume_command(fwspk, &volume, i, - CTL_CURRENT, CTL_WRITE); - if (err < 0) - return err; - } - if (i > 0) - fwspk->volume[i - 1] = volume; - } - - return changed_channels != 0; -} - -static int fwspk_create_mixer(struct fwspk *fwspk) -{ - static const struct snd_kcontrol_new controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Switch", - .info = snd_ctl_boolean_mono_info, - .get = fwspk_mute_get, - .put = fwspk_mute_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .info = fwspk_volume_info, - .get = fwspk_volume_get, - .put = fwspk_volume_put, - }, - }; - unsigned int i, first_ch; - int err; - - err = fwspk_volume_command(fwspk, &fwspk->volume_min, - 0, CTL_MIN, CTL_READ); - if (err < 0) - return err; - err = fwspk_volume_command(fwspk, &fwspk->volume_max, - 0, CTL_MAX, CTL_READ); - if (err < 0) - return err; - - err = fwspk_mute_command(fwspk, &fwspk->mute, CTL_READ); - if (err < 0) - return err; - - first_ch = fwspk->device_info->mixer_channels == 1 ? 0 : 1; - for (i = 0; i < fwspk->device_info->mixer_channels; ++i) { - err = fwspk_volume_command(fwspk, &fwspk->volume[i], - first_ch + i, CTL_CURRENT, CTL_READ); - if (err < 0) - return err; - } - - for (i = 0; i < ARRAY_SIZE(controls); ++i) { - err = snd_ctl_add(fwspk->card, - snd_ctl_new1(&controls[i], fwspk)); - if (err < 0) - return err; - } - - return 0; -} - -static u32 fwspk_read_firmware_version(struct fw_unit *unit) -{ - __be32 data; - int err; - - err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0); - return err >= 0 ? be32_to_cpu(data) : 0; -} - -static void fwspk_card_free(struct snd_card *card) -{ - struct fwspk *fwspk = card->private_data; - - amdtp_stream_destroy(&fwspk->stream); - cmp_connection_destroy(&fwspk->connection); - fw_unit_put(fwspk->unit); - mutex_destroy(&fwspk->mutex); -} - -static int fwspk_probe(struct fw_unit *unit, - const struct ieee1394_device_id *id) -{ - struct fw_device *fw_dev = fw_parent_device(unit); - struct snd_card *card; - struct fwspk *fwspk; - u32 firmware; - int err; - - err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, - sizeof(*fwspk), &card); - if (err < 0) - return err; - - fwspk = card->private_data; - fwspk->card = card; - mutex_init(&fwspk->mutex); - fwspk->unit = fw_unit_get(unit); - fwspk->device_info = (const struct device_info *)id->driver_data; - - err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0); - if (err < 0) - goto err_unit; - - err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM, - CIP_NONBLOCKING); - if (err < 0) - goto err_connection; - - card->private_free = fwspk_card_free; - - strcpy(card->driver, fwspk->device_info->driver_name); - strcpy(card->shortname, fwspk->device_info->short_name); - firmware = fwspk_read_firmware_version(unit); - snprintf(card->longname, sizeof(card->longname), - "%s (OXFW%x %04x), GUID %08x%08x at %s, S%d", - fwspk->device_info->long_name, - firmware >> 20, firmware & 0xffff, - fw_dev->config_rom[3], fw_dev->config_rom[4], - dev_name(&unit->device), 100 << fw_dev->max_speed); - strcpy(card->mixername, "OXFW970"); - - err = fwspk_create_pcm(fwspk); - if (err < 0) - goto error; - - err = fwspk_create_mixer(fwspk); - if (err < 0) - goto error; - - err = snd_card_register(card); - if (err < 0) - goto error; - - dev_set_drvdata(&unit->device, fwspk); - - return 0; - -err_connection: - cmp_connection_destroy(&fwspk->connection); -err_unit: - fw_unit_put(fwspk->unit); - mutex_destroy(&fwspk->mutex); -error: - snd_card_free(card); - return err; -} - -static void fwspk_bus_reset(struct fw_unit *unit) -{ - struct fwspk *fwspk = dev_get_drvdata(&unit->device); - - fcp_bus_reset(fwspk->unit); - - if (cmp_connection_update(&fwspk->connection) < 0) { - amdtp_stream_pcm_abort(&fwspk->stream); - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - return; - } - - amdtp_stream_update(&fwspk->stream); -} - -static void fwspk_remove(struct fw_unit *unit) -{ - struct fwspk *fwspk = dev_get_drvdata(&unit->device); - - amdtp_stream_pcm_abort(&fwspk->stream); - snd_card_disconnect(fwspk->card); - - mutex_lock(&fwspk->mutex); - fwspk_stop_stream(fwspk); - mutex_unlock(&fwspk->mutex); - - snd_card_free_when_closed(fwspk->card); -} - -static const struct device_info griffin_firewave = { - .driver_name = "FireWave", - .short_name = "FireWave", - .long_name = "Griffin FireWave Surround", - .pcm_constraints = firewave_constraints, - .mixer_channels = 6, - .mute_fb_id = 0x01, - .volume_fb_id = 0x02, -}; - -static const struct device_info lacie_speakers = { - .driver_name = "FWSpeakers", - .short_name = "FireWire Speakers", - .long_name = "LaCie FireWire Speakers", - .pcm_constraints = lacie_speakers_constraints, - .mixer_channels = 1, - .mute_fb_id = 0x01, - .volume_fb_id = 0x01, -}; - -static const struct ieee1394_device_id fwspk_id_table[] = { - { - .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_MODEL_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .vendor_id = VENDOR_GRIFFIN, - .model_id = 0x00f970, - .specifier_id = SPECIFIER_1394TA, - .version = VERSION_AVC, - .driver_data = (kernel_ulong_t)&griffin_firewave, - }, - { - .match_flags = IEEE1394_MATCH_VENDOR_ID | - IEEE1394_MATCH_MODEL_ID | - IEEE1394_MATCH_SPECIFIER_ID | - IEEE1394_MATCH_VERSION, - .vendor_id = VENDOR_LACIE, - .model_id = 0x00f970, - .specifier_id = SPECIFIER_1394TA, - .version = VERSION_AVC, - .driver_data = (kernel_ulong_t)&lacie_speakers, - }, - { } -}; -MODULE_DEVICE_TABLE(ieee1394, fwspk_id_table); - -static struct fw_driver fwspk_driver = { - .driver = { - .owner = THIS_MODULE, - .name = KBUILD_MODNAME, - .bus = &fw_bus_type, - }, - .probe = fwspk_probe, - .update = fwspk_bus_reset, - .remove = fwspk_remove, - .id_table = fwspk_id_table, -}; - -static int __init alsa_fwspk_init(void) -{ - return driver_register(&fwspk_driver.driver); -} - -static void __exit alsa_fwspk_exit(void) -{ - driver_unregister(&fwspk_driver.driver); -} - -module_init(alsa_fwspk_init); -module_exit(alsa_fwspk_exit); diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index f3735e64791c..67dbfde837ab 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -465,17 +465,10 @@ static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol, static int snd_akm4xxx_deemphasis_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "44.1kHz", "Off", "48kHz", "32kHz", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_akm4xxx_deemphasis_get(struct snd_kcontrol *kcontrol, @@ -570,22 +563,13 @@ static int ak4xxx_capture_source_info(struct snd_kcontrol *kcontrol, { struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); int mixer_ch = AK_GET_SHIFT(kcontrol->private_value); - const char **input_names; - unsigned int num_names, idx; + unsigned int num_names; num_names = ak4xxx_capture_num_inputs(ak, mixer_ch); if (!num_names) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_names; - idx = uinfo->value.enumerated.item; - if (idx >= num_names) - return -EINVAL; - input_names = ak->adc_info[mixer_ch].input_names; - strlcpy(uinfo->value.enumerated.name, input_names[idx], - sizeof(uinfo->value.enumerated.name)); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_names, + ak->adc_info[mixer_ch].input_names); } static int ak4xxx_capture_source_get(struct snd_kcontrol *kcontrol, diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index f0fd98e695e3..01a07986f4a3 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -731,18 +731,12 @@ int snd_ad1816a_timer(struct snd_ad1816a *chip, int device, static int snd_ad1816a_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[8] = { + static const char * const texts[8] = { "Line", "Mix", "CD", "Synth", "Video", "Mic", "Phone", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item > 6) - uinfo->value.enumerated.item = 6; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 2, 7, texts); } static int snd_ad1816a_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index b3b4f15e45ba..b5450143407b 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -614,8 +614,7 @@ static int snd_es1688_free(struct snd_es1688 *chip) { if (chip->hardware != ES1688_HW_UNDEF) snd_es1688_init(chip, 0); - if (chip->res_port) - release_and_free_resource(chip->res_port); + release_and_free_resource(chip->res_port); if (chip->irq >= 0) free_irq(chip->irq, (void *) chip); if (chip->dma8 >= 0) { @@ -762,18 +761,12 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, static int snd_es1688_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[9] = { + static const char * const texts[8] = { "Mic", "Mic Master", "CD", "AOUT", "Mic1", "Mix", "Line", "Master" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_es1688_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index 6faaac60161a..b481bb8c31bc 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -156,6 +156,7 @@ struct snd_es18xx { #define ES18XX_I2S 0x0200 /* I2S mixer control */ #define ES18XX_MUTEREC 0x0400 /* Record source can be muted */ #define ES18XX_CONTROL 0x0800 /* Has control ports */ +#define ES18XX_GPO_2BIT 0x1000 /* GPO0,1 controlled by PM port */ /* Power Management */ #define ES18XX_PM 0x07 @@ -964,44 +965,28 @@ static int snd_es18xx_capture_close(struct snd_pcm_substream *substream) static int snd_es18xx_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts5Source[5] = { + static const char * const texts5Source[5] = { "Mic", "CD", "Line", "Master", "Mix" }; - static char *texts8Source[8] = { + static const char * const texts8Source[8] = { "Mic", "Mic Master", "CD", "AOUT", "Mic1", "Mix", "Line", "Master" }; struct snd_es18xx *chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; switch (chip->version) { case 0x1868: case 0x1878: - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts5Source[uinfo->value.enumerated.item]); - break; + return snd_ctl_enum_info(uinfo, 1, 4, texts5Source); case 0x1887: case 0x1888: - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts5Source[uinfo->value.enumerated.item]); - break; + return snd_ctl_enum_info(uinfo, 1, 5, texts5Source); case 0x1869: /* DS somewhat contradictory for 1869: could be be 5 or 8 */ case 0x1879: - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts8Source[uinfo->value.enumerated.item]); - break; + return snd_ctl_enum_info(uinfo, 1, 8, texts8Source); default: return -EINVAL; } - return 0; } static int snd_es18xx_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1136,11 +1121,14 @@ static int snd_es18xx_reg_read(struct snd_es18xx *chip, unsigned char reg) return snd_es18xx_read(chip, reg); } -#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \ +#define ES18XX_SINGLE(xname, xindex, reg, shift, mask, flags) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_es18xx_info_single, \ .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \ - .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + .private_value = reg | (shift << 8) | (mask << 16) | (flags << 24) } + +#define ES18XX_FL_INVERT (1 << 0) +#define ES18XX_FL_PMPORT (1 << 1) static int snd_es18xx_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -1159,10 +1147,14 @@ static int snd_es18xx_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; + int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; int val; - - val = snd_es18xx_reg_read(chip, reg); + + if (pm_port) + val = inb(chip->port + ES18XX_PM); + else + val = snd_es18xx_reg_read(chip, reg); ucontrol->value.integer.value[0] = (val >> shift) & mask; if (invert) ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; @@ -1175,7 +1167,8 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; - int invert = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 24) & ES18XX_FL_INVERT; + int pm_port = (kcontrol->private_value >> 24) & ES18XX_FL_PMPORT; unsigned char val; val = (ucontrol->value.integer.value[0] & mask); @@ -1183,6 +1176,15 @@ static int snd_es18xx_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e val = mask - val; mask <<= shift; val <<= shift; + if (pm_port) { + unsigned char cur = inb(chip->port + ES18XX_PM); + + if ((cur & mask) == val) + return 0; + outb((cur & ~mask) | val, chip->port + ES18XX_PM); + return 1; + } + return snd_es18xx_reg_bits(chip, reg, mask, val) != val; } @@ -1304,7 +1306,7 @@ static struct snd_kcontrol_new snd_es18xx_opt_speaker = ES18XX_SINGLE("Beep Playback Volume", 0, 0x3c, 0, 7, 0); static struct snd_kcontrol_new snd_es18xx_opt_1869[] = { -ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1), +ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, ES18XX_FL_INVERT), ES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0), ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0), ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0) @@ -1363,6 +1365,11 @@ static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = { ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0), }; +static struct snd_kcontrol_new snd_es18xx_opt_gpo_2bit[] = { +ES18XX_SINGLE("GPO0 Switch", 0, ES18XX_PM, 0, 1, ES18XX_FL_PMPORT), +ES18XX_SINGLE("GPO1 Switch", 0, ES18XX_PM, 1, 1, ES18XX_FL_PMPORT), +}; + static int snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg) { int data; @@ -1629,10 +1636,10 @@ static int snd_es18xx_probe(struct snd_es18xx *chip, switch (chip->version) { case 0x1868: - chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL; + chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL | ES18XX_GPO_2BIT; break; case 0x1869: - chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV; + chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV | ES18XX_GPO_2BIT; break; case 0x1878: chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL; @@ -1642,7 +1649,7 @@ static int snd_es18xx_probe(struct snd_es18xx *chip, break; case 0x1887: case 0x1888: - chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME; + chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME | ES18XX_GPO_2BIT; break; default: snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n", @@ -1944,6 +1951,15 @@ static int snd_es18xx_mixer(struct snd_card *card) return err; } } + if (chip->caps & ES18XX_GPO_2BIT) { + for (idx = 0; idx < ARRAY_SIZE(snd_es18xx_opt_gpo_2bit); idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_es18xx_opt_gpo_2bit[idx], + chip)); + if (err < 0) + return err; + } + } return 0; } diff --git a/sound/isa/msnd/msnd_pinnacle_mixer.c b/sound/isa/msnd/msnd_pinnacle_mixer.c index 031dc69b7470..17e49a071af4 100644 --- a/sound/isa/msnd/msnd_pinnacle_mixer.c +++ b/sound/isa/msnd/msnd_pinnacle_mixer.c @@ -55,20 +55,13 @@ static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Analog", "MASS", "SPDIF", }; struct snd_msnd *chip = snd_kcontrol_chip(kcontrol); unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = items; - if (uinfo->value.enumerated.item >= items) - uinfo->value.enumerated.item = items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, items, texts); } static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol, diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 4e3fcfb15ad4..95b39beb61c1 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -105,8 +105,7 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev) snd_device_free(dev->card, hw->pcm); if (hw->emu) snd_emux_free(hw->emu); - if (hw->memhdr) - snd_util_memhdr_free(hw->memhdr); + snd_util_memhdr_free(hw->memhdr); hw->emu = NULL; hw->memhdr = NULL; return 0; diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c index 0bbcd4714d28..72b10f4f3e70 100644 --- a/sound/isa/sb/sb16_main.c +++ b/sound/isa/sb/sb16_main.c @@ -702,17 +702,11 @@ static int snd_sb16_get_dma_mode(struct snd_sb *chip) static int snd_sb16_dma_control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Auto", "Playback", "Capture" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_sb16_dma_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index 3ef990602cdd..f22b4480828e 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -184,8 +184,7 @@ static int snd_sbdsp_probe(struct snd_sb * chip) static int snd_sbdsp_free(struct snd_sb *chip) { - if (chip->res_port) - release_and_free_resource(chip->res_port); + release_and_free_resource(chip->res_port); if (chip->irq >= 0) free_irq(chip->irq, (void *) chip); #ifdef CONFIG_ISA diff --git a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c index 1ff78ec9f0ac..e403334a19ad 100644 --- a/sound/isa/sb/sb_mixer.c +++ b/sound/isa/sb/sb_mixer.c @@ -182,17 +182,11 @@ static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_ static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[5] = { + static const char * const texts[5] = { "CD", "Mic", "Line", "Synth", "Master" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -275,18 +269,11 @@ static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[3] = { + static const char * const texts[3] = { "L chan only", "R chan only", "L ch/2 + R ch/2" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol, @@ -335,17 +322,11 @@ static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol, static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[3] = { + static const char * const texts[3] = { "Mic", "CD", "Line" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c index 360b08b03e1d..347bb1bda110 100644 --- a/sound/isa/wss/wss_lib.c +++ b/sound/isa/wss/wss_lib.c @@ -1993,25 +1993,20 @@ EXPORT_SYMBOL(snd_wss_timer); static int snd_wss_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "Line", "Aux", "Mic", "Mix" }; - static char *opl3sa_texts[4] = { + static const char * const opl3sa_texts[4] = { "Line", "CD", "Mic", "Mix" }; - static char *gusmax_texts[4] = { + static const char * const gusmax_texts[4] = { "Line", "Synth", "Mic", "Mix" }; - char **ptexts = texts; + const char * const *ptexts = texts; struct snd_wss *chip = snd_kcontrol_chip(kcontrol); if (snd_BUG_ON(!chip->card)) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; if (!strcmp(chip->card->driver, "GUS MAX")) ptexts = gusmax_texts; switch (chip->hardware) { @@ -2023,8 +2018,7 @@ static int snd_wss_info_mux(struct snd_kcontrol *kcontrol, ptexts = opl3sa_texts; break; } - strcpy(uinfo->value.enumerated.name, ptexts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 2, 4, ptexts); } static int snd_wss_get_mux(struct snd_kcontrol *kcontrol, diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c index 04bb06c03ec8..33b08fcc27a9 100644 --- a/sound/mips/sgio2audio.c +++ b/sound/mips/sgio2audio.c @@ -201,17 +201,10 @@ static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol, static int sgio2audio_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[3] = { + static const char * const texts[3] = { "Cam Mic", "Mic", "Line" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= 3) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int sgio2audio_source_get(struct snd_kcontrol *kcontrol, diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c index 279bc565ac7e..dae4d4344407 100644 --- a/sound/oss/uart401.c +++ b/sound/oss/uart401.c @@ -412,13 +412,10 @@ void unload_uart401(struct address_info *hw_config) if (!devc->share_irq) free_irq(devc->irq, devc); - if (devc) - { - kfree(midi_devs[devc->my_dev]->converter); - kfree(midi_devs[devc->my_dev]); - kfree(devc); - devc = NULL; - } + kfree(midi_devs[devc->my_dev]->converter); + kfree(midi_devs[devc->my_dev]); + kfree(devc); + /* This kills midi_devs[x] */ sound_unload_mididev(hw_config->slots[4]); } diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c index 4b20be79c1dd..29604a239c44 100644 --- a/sound/parisc/harmony.c +++ b/sound/parisc/harmony.c @@ -776,15 +776,9 @@ static int snd_harmony_captureroute_info(struct snd_kcontrol *kc, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "Line", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + static const char * const texts[2] = { "Line", "Mic" }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 14ad54b7928c..5ee2f17c287c 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -463,14 +463,8 @@ static int snd_ac97_info_enum_double(struct snd_kcontrol *kcontrol, { struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = e->shift_l == e->shift_r ? 1 : 2; - uinfo->value.enumerated.items = e->mask; - - if (uinfo->value.enumerated.item > e->mask - 1) - uinfo->value.enumerated.item = e->mask - 1; - strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2, + e->mask, e->texts); } static int snd_ac97_get_enum_double(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 991762215417..ceaac1c41906 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -33,7 +33,8 @@ static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97, const char *name); static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, - const unsigned int *tlv, const char **slaves); + const unsigned int *tlv, + const char * const *slaves); /* * Chip specific initialization @@ -81,22 +82,11 @@ static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsi /* * shared line-in/mic controls */ -static int ac97_enum_text_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo, - const char **texts, unsigned int nums) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = nums; - if (uinfo->value.enumerated.item > nums - 1) - uinfo->value.enumerated.item = nums - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; -} - static int ac97_surround_jack_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[] = { "Shared", "Independent" }; - return ac97_enum_text_info(kcontrol, uinfo, texts, 2); + static const char * const texts[] = { "Shared", "Independent" }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int ac97_surround_jack_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -123,9 +113,9 @@ static int ac97_surround_jack_mode_put(struct snd_kcontrol *kcontrol, struct snd static int ac97_channel_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[] = { "2ch", "4ch", "6ch", "8ch" }; - return ac97_enum_text_info(kcontrol, uinfo, texts, - kcontrol->private_value); + static const char * const texts[] = { "2ch", "4ch", "6ch", "8ch" }; + + return snd_ctl_enum_info(uinfo, 1, kcontrol->private_value, texts); } static int ac97_channel_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -240,17 +230,11 @@ static inline int alc850_is_aux_back_surround(struct snd_ac97 *ac97) static int snd_ac97_ymf7x3_info_speaker(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { + static const char * const texts[3] = { "Standard", "Small", "Smaller" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_ymf7x3_get_speaker(struct snd_kcontrol *kcontrol, @@ -293,15 +277,9 @@ static const struct snd_kcontrol_new snd_ac97_ymf7x3_controls_speaker = static int snd_ac97_ymf7x3_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "AC-Link", "A/D Converter" }; + static const char * const texts[2] = { "AC-Link", "A/D Converter" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ac97_ymf7x3_spdif_source_get(struct snd_kcontrol *kcontrol, @@ -401,15 +379,9 @@ static int patch_yamaha_ymf743(struct snd_ac97 *ac97) There is also a bit to mute S/PDIF output in a vendor-specific register. */ static int snd_ac97_ymf753_spdif_output_pin_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { "Disabled", "Pin 43", "Pin 48" }; + static const char * const texts[3] = { "Disabled", "Pin 43", "Pin 48" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_ymf753_spdif_output_pin_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1103,16 +1075,11 @@ static int patch_sigmatel_stac9756(struct snd_ac97 * ac97) static int snd_ac97_stac9758_output_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[5] = { "Input/Disabled", "Front Output", + static const char * const texts[5] = { + "Input/Disabled", "Front Output", "Rear Output", "Center/LFE Output", "Mixer Output" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_ac97_stac9758_output_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1147,16 +1114,11 @@ static int snd_ac97_stac9758_output_jack_put(struct snd_kcontrol *kcontrol, stru static int snd_ac97_stac9758_input_jack_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[7] = { "Mic2 Jack", "Mic1 Jack", "Line In Jack", + static const char * const texts[7] = { + "Mic2 Jack", "Mic1 Jack", "Line In Jack", "Front Jack", "Rear Jack", "Center/LFE Jack", "Mute" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item > 6) - uinfo->value.enumerated.item = 6; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 7, texts); } static int snd_ac97_stac9758_input_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1181,15 +1143,11 @@ static int snd_ac97_stac9758_input_jack_put(struct snd_kcontrol *kcontrol, struc static int snd_ac97_stac9758_phonesel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { "None", "Front Jack", "Rear Jack" }; + static const char * const texts[3] = { + "None", "Front Jack", "Rear Jack" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_stac9758_phonesel_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1804,15 +1762,9 @@ static int patch_ad1886(struct snd_ac97 * ac97) static int snd_ac97_ad198x_spdif_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "AC-Link", "A/D Converter" }; + static const char * const texts[2] = { "AC-Link", "A/D Converter" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ac97_ad198x_spdif_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1994,15 +1946,9 @@ static int snd_ac97_ad1888_lohpsel_put(struct snd_kcontrol *kcontrol, struct snd static int snd_ac97_ad1888_downmix_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"}; + static const char * const texts[3] = {"Off", "6 -> 4", "6 -> 2"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_ac97_ad1888_downmix_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2153,16 +2099,11 @@ static int patch_ad1980(struct snd_ac97 * ac97) static int snd_ac97_ad1985_vrefout_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"High-Z", "3.7 V", "2.25 V", "0 V"}; + static const char * const texts[4] = { + "High-Z", "3.7 V", "2.25 V", "0 V" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_ac97_ad1985_vrefout_get(struct snd_kcontrol *kcontrol, @@ -2756,20 +2697,18 @@ static const struct snd_kcontrol_new snd_ac97_controls_alc655[] = { static int alc655_iec958_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts_655[3] = { "PCM", "Analog In", "IEC958 In" }; - static char *texts_658[4] = { "PCM", "Analog1 In", "Analog2 In", "IEC958 In" }; + static const char * const texts_655[3] = { + "PCM", "Analog In", "IEC958 In" + }; + static const char * const texts_658[4] = { + "PCM", "Analog1 In", "Analog2 In", "IEC958 In" + }; struct snd_ac97 *ac97 = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ac97->spec.dev_flags ? 4 : 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - ac97->spec.dev_flags ? - texts_658[uinfo->value.enumerated.item] : - texts_655[uinfo->value.enumerated.item]); - return 0; + if (ac97->spec.dev_flags) + return snd_ctl_enum_info(uinfo, 1, 4, texts_658); + else + return snd_ctl_enum_info(uinfo, 1, 3, texts_655); } static int alc655_iec958_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -3055,15 +2994,9 @@ static int patch_cm9738(struct snd_ac97 * ac97) static int snd_ac97_cmedia_spdif_playback_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "Analog", "Digital" }; + static const char * const texts[] = { "Analog", "Digital" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ac97_cmedia_spdif_playback_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -3235,15 +3168,9 @@ static const struct snd_kcontrol_new snd_ac97_cm9761_controls[] = { static int cm9761_spdif_out_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" }; + static const char * const texts[] = { "AC-Link", "ADC", "SPDIF-In" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int cm9761_spdif_out_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -3270,7 +3197,9 @@ static int cm9761_spdif_out_source_put(struct snd_kcontrol *kcontrol, struct snd ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0); } -static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" }; +static const char * const cm9761_dac_clock[] = { + "AC-Link", "SPDIF-In", "Both" +}; static const struct ac97_enum cm9761_dac_clock_enum = AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock); @@ -3384,7 +3313,9 @@ static int patch_cm9761(struct snd_ac97 *ac97) #define AC97_CM9780_MULTI_CHAN 0x66 #define AC97_CM9780_SPDIF 0x6c -static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" }; +static const char * const cm9780_ch_select[] = { + "Front", "Side", "Center/LFE", "Rear" +}; static const struct ac97_enum cm9780_ch_select_enum = AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select); static const struct snd_kcontrol_new cm9780_controls[] = { @@ -3430,7 +3361,7 @@ AC97_SINGLE("Downmix LFE and Center to Front", 0x5a, 12, 1, 0), AC97_SINGLE("Downmix Surround to Front", 0x5a, 11, 1, 0), }; -static const char *slave_vols_vt1616[] = { +static const char * const slave_vols_vt1616[] = { "Front Playback Volume", "Surround Playback Volume", "Center Playback Volume", @@ -3438,7 +3369,7 @@ static const char *slave_vols_vt1616[] = { NULL }; -static const char *slave_sws_vt1616[] = { +static const char * const slave_sws_vt1616[] = { "Front Playback Switch", "Surround Playback Switch", "Center Playback Switch", @@ -3459,10 +3390,11 @@ static struct snd_kcontrol *snd_ac97_find_mixer_ctl(struct snd_ac97 *ac97, /* create a virtual master control and add slaves */ static int snd_ac97_add_vmaster(struct snd_ac97 *ac97, char *name, - const unsigned int *tlv, const char **slaves) + const unsigned int *tlv, + const char * const *slaves) { struct snd_kcontrol *kctl; - const char **s; + const char * const *s; int err; kctl = snd_ctl_make_virtual_master(name, tlv); @@ -3552,11 +3484,12 @@ static int snd_ac97_vt1617a_smart51_info(struct snd_kcontrol *kcontrol, * is SM51EN *AND* it's Bit14, not Bit15 so the table is very * counter-intuitive */ - static const char* texts[] = { "LineIn Mic1", "LineIn Mic1 Mic3", + static const char * const texts[] = {"LineIn Mic1", "LineIn Mic1 Mic3", "Surr LFE/C Mic3", "LineIn LFE/C Mic3", "LineIn Mic2", "LineIn Mic2 Mic1", "Surr LFE Mic1", "Surr LFE Mic1 Mic2"}; - return ac97_enum_text_info(kcontrol, uinfo, texts, 8); + + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_ac97_vt1617a_smart51_get(struct snd_kcontrol *kcontrol, @@ -3685,7 +3618,7 @@ static int patch_vt1617a(struct snd_ac97 * ac97) struct vt1618_uaj_item { unsigned short mask; unsigned short shift; - const char *items[4]; + const char * const items[4]; }; /* This list reflects the vt1618 docs for Vendor Defined Register 0x60. */ @@ -3720,9 +3653,8 @@ static struct vt1618_uaj_item vt1618_uaj[3] = { static int snd_ac97_vt1618_UAJ_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - return ac97_enum_text_info(kcontrol, uinfo, - vt1618_uaj[kcontrol->private_value].items, - 4); + return snd_ctl_enum_info(uinfo, 1, 4, + vt1618_uaj[kcontrol->private_value].items); } /* All of the vt1618 Universal Audio Jack twiddlers are on @@ -3767,9 +3699,9 @@ static int snd_ac97_vt1618_UAJ_put(struct snd_kcontrol *kcontrol, static int snd_ac97_vt1618_aux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *txt_aux[] = {"Aux In", "Back Surr Out"}; + static const char * const txt_aux[] = {"Aux In", "Back Surr Out"}; - return ac97_enum_text_info(kcontrol, uinfo, txt_aux, 2); + return snd_ctl_enum_info(uinfo, 1, 2, txt_aux); } static int snd_ac97_vt1618_aux_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h index 47bf8dfe8276..d1ce151fe722 100644 --- a/sound/pci/ac97/ac97_patch.h +++ b/sound/pci/ac97/ac97_patch.h @@ -49,7 +49,7 @@ struct ac97_enum { unsigned char shift_l; unsigned char shift_r; unsigned short mask; - const char **texts; + const char * const *texts; }; #define AC97_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \ diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 5017176bfaa1..e9273fb2a505 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1,6 +1,6 @@ /* * Asihpi soundcard - * Copyright (c) by AudioScience Inc <alsa@audioscience.com> + * Copyright (c) by AudioScience Inc <support@audioscience.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -28,7 +28,6 @@ #include "hpioctl.h" #include "hpicmn.h" - #include <linux/pci.h> #include <linux/init.h> #include <linux/jiffies.h> @@ -47,7 +46,7 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("AudioScience inc. <support@audioscience.com>"); -MODULE_DESCRIPTION("AudioScience ALSA ASI5000 ASI6000 ASI87xx ASI89xx " +MODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx " HPI_VER_STRING); #if defined CONFIG_SND_DEBUG_VERBOSE @@ -87,11 +86,11 @@ MODULE_PARM_DESC(enable_hpi_hwdep, #ifdef KERNEL_ALSA_BUILD static char *build_info = "Built using headers from kernel source"; module_param(build_info, charp, S_IRUGO); -MODULE_PARM_DESC(build_info, "built using headers from kernel source"); +MODULE_PARM_DESC(build_info, "Built using headers from kernel source"); #else static char *build_info = "Built within ALSA source"; module_param(build_info, charp, S_IRUGO); -MODULE_PARM_DESC(build_info, "built within ALSA source"); +MODULE_PARM_DESC(build_info, "Built within ALSA source"); #endif /* set to 1 to dump every control from adapter to log */ @@ -110,7 +109,7 @@ static int adapter_fs = DEFAULT_SAMPLERATE; struct clk_source { int source; int index; - char *name; + const char *name; }; struct clk_cache { @@ -125,6 +124,16 @@ struct snd_card_asihpi { struct pci_dev *pci; struct hpi_adapter *hpi; + /* In low latency mode there is only one stream, a pointer to its + * private data is stored here on trigger and cleared on stop. + * The interrupt handler uses it as a parameter when calling + * snd_card_asihpi_timer_function(). + */ + struct snd_card_asihpi_pcm *llmode_streampriv; + struct tasklet_struct t; + void (*pcm_start)(struct snd_pcm_substream *substream); + void (*pcm_stop)(struct snd_pcm_substream *substream); + u32 h_mixer; struct clk_cache cc; @@ -289,21 +298,17 @@ static void print_hwparams(struct snd_pcm_substream *substream, { char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printd("%s HWPARAMS\n", name); - snd_printd(" samplerate %d Hz\n", params_rate(p)); - snd_printd(" channels %d\n", params_channels(p)); - snd_printd(" format %d\n", params_format(p)); - snd_printd(" subformat %d\n", params_subformat(p)); - snd_printd(" buffer %d B\n", params_buffer_bytes(p)); - snd_printd(" period %d B\n", params_period_bytes(p)); - snd_printd(" access %d\n", params_access(p)); - snd_printd(" period_size %d\n", params_period_size(p)); - snd_printd(" periods %d\n", params_periods(p)); - snd_printd(" buffer_size %d\n", params_buffer_size(p)); - snd_printd(" %d B/s\n", params_rate(p) * - params_channels(p) * + snd_printdd("%s HWPARAMS\n", name); + snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n", + params_rate(p), params_channels(p), + params_format(p), params_subformat(p)); + snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n", + params_buffer_bytes(p), params_period_bytes(p), + params_period_size(p), params_periods(p)); + snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n", + params_buffer_size(p), params_access(p), + params_rate(p) * params_channels(p) * snd_pcm_format_width(params_format(p)) / 8); - } static snd_pcm_format_t hpi_to_alsa_formats[] = { @@ -375,7 +380,7 @@ static void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, HPI_CONTROL_SAMPLECLOCK, &h_control); if (err) { - snd_printk(KERN_ERR + dev_err(&asihpi->pci->dev, "No local sampleclock, err %d\n", err); } @@ -481,7 +486,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, params_buffer_bytes(params), runtime->dma_addr); if (err == 0) { snd_printdd( - "stream_host_buffer_attach succeeded %u %lu\n", + "stream_host_buffer_attach success %u %lu\n", params_buffer_bytes(params), (unsigned long)runtime->dma_addr); } else { @@ -491,12 +496,7 @@ static int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, } err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, - &dpcm->hpi_buffer_attached, - NULL, NULL, NULL); - - snd_printdd("stream_host_buffer_attach status 0x%x\n", - dpcm->hpi_buffer_attached); - + &dpcm->hpi_buffer_attached, NULL, NULL, NULL); } bytes_per_sec = params_rate(params) * params_channels(params); width = snd_pcm_format_width(params_format(params)); @@ -538,7 +538,7 @@ static void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * int expiry; expiry = HZ / 200; - /*? (dpcm->period_bytes * HZ / dpcm->bytes_per_sec); */ + expiry = max(expiry, 1); /* don't let it be zero! */ dpcm->timer.expires = jiffies + expiry; dpcm->respawn_timer = 1; @@ -554,6 +554,48 @@ static void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream) del_timer(&dpcm->timer); } +static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) +{ + struct snd_card_asihpi_pcm *dpcm; + struct snd_card_asihpi *card; + + BUG_ON(!substream); + + dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; + card = snd_pcm_substream_chip(substream); + + BUG_ON(in_interrupt()); + tasklet_disable(&card->t); + card->llmode_streampriv = dpcm; + tasklet_enable(&card->t); + + hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, + HPI_ADAPTER_PROPERTY_IRQ_RATE, + card->update_interval_frames, 0)); +} + +static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) +{ + struct snd_card_asihpi_pcm *dpcm; + struct snd_card_asihpi *card; + + BUG_ON(!substream); + + dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; + card = snd_pcm_substream_chip(substream); + + hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, + HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); + + if (in_interrupt()) + card->llmode_streampriv = NULL; + else { + tasklet_disable(&card->t); + card->llmode_streampriv = NULL; + tasklet_enable(&card->t); + } +} + static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -564,10 +606,10 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, char name[16]; snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printdd("%s trigger\n", name); switch (cmd) { case SNDRV_PCM_TRIGGER_START: + snd_printdd("%s trigger start\n", name); snd_pcm_group_for_each_entry(s, substream) { struct snd_pcm_runtime *runtime = s->runtime; struct snd_card_asihpi_pcm *ds = runtime->private_data; @@ -588,7 +630,7 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, * data?? */ unsigned int preload = ds->period_bytes * 1; - snd_printddd("%d preload x%x\n", s->number, preload); + snd_printddd("%d preload %d\n", s->number, preload); hpi_handle_error(hpi_outstream_write_buf( ds->h_stream, &runtime->dma_area[0], @@ -611,16 +653,16 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } else break; } - snd_printdd("start\n"); /* start the master stream */ - snd_card_asihpi_pcm_timer_start(substream); + card->pcm_start(substream); if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || !card->can_dma) hpi_handle_error(hpi_stream_start(dpcm->h_stream)); break; case SNDRV_PCM_TRIGGER_STOP: - snd_card_asihpi_pcm_timer_stop(substream); + snd_printdd("%s trigger stop\n", name); + card->pcm_stop(substream); snd_pcm_group_for_each_entry(s, substream) { if (snd_pcm_substream_chip(s) != card) continue; @@ -638,7 +680,6 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, } else break; } - snd_printdd("stop\n"); /* _prepare and _hwparams reset the stream */ hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); @@ -651,13 +692,13 @@ static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - snd_printdd("pause release\n"); + snd_printdd("%s trigger pause release\n", name); + card->pcm_start(substream); hpi_handle_error(hpi_stream_start(dpcm->h_stream)); - snd_card_asihpi_pcm_timer_start(substream); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - snd_printdd("pause\n"); - snd_card_asihpi_pcm_timer_stop(substream); + snd_printdd("%s trigger pause push\n", name); + card->pcm_stop(substream); hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); break; default: @@ -729,9 +770,8 @@ static void snd_card_asihpi_timer_function(unsigned long data) u32 buffer_size, bytes_avail, samples_played, on_card_bytes; char name[16]; - snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printdd("%s snd_card_asihpi_timer_function\n", name); + snd_pcm_debug_name(substream, name, sizeof(name)); /* find minimum newdata and buffer pos in group */ snd_pcm_group_for_each_entry(s, substream) { @@ -769,10 +809,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) s->number); ds->drained_count++; if (ds->drained_count > 20) { - unsigned long flags; - snd_pcm_stream_lock_irqsave(s, flags); - snd_pcm_stop(s, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(s, flags); + snd_pcm_stop_xrun(s); continue; } } else { @@ -794,19 +831,21 @@ static void snd_card_asihpi_timer_function(unsigned long data) newdata); } - snd_printdd("hw_ptr 0x%04lX, appl_ptr 0x%04lX\n", + snd_printddd( + "timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n", + name, s->number, state, + ds->pcm_buf_elapsed_dma_ofs, + ds->pcm_buf_host_rw_ofs, + pcm_buf_dma_ofs, + (int)bytes_avail, + + (int)on_card_bytes, + buffer_size-bytes_avail, (unsigned long)frames_to_bytes(runtime, runtime->status->hw_ptr), (unsigned long)frames_to_bytes(runtime, - runtime->control->appl_ptr)); - - snd_printdd("%d S=%d, " - "rw=0x%04X, dma=0x%04X, left=0x%04X, " - "aux=0x%04X space=0x%04X\n", - s->number, state, - ds->pcm_buf_host_rw_ofs, pcm_buf_dma_ofs, - (int)bytes_avail, - (int)on_card_bytes, buffer_size-bytes_avail); + runtime->control->appl_ptr) + ); loops++; } pcm_buf_dma_ofs = min_buf_pos; @@ -824,16 +863,18 @@ static void snd_card_asihpi_timer_function(unsigned long data) next_jiffies = max(next_jiffies, 1U); dpcm->timer.expires = jiffies + next_jiffies; - snd_printdd("jif %d buf pos 0x%04X newdata 0x%04X xfer 0x%04X\n", + snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n", next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); snd_pcm_group_for_each_entry(s, substream) { struct snd_card_asihpi_pcm *ds = s->runtime->private_data; + runtime = s->runtime; /* don't link Cap and Play */ if (substream->stream != s->stream) continue; + /* Store dma offset for use by pointer callback */ ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs; if (xfercount && @@ -856,7 +897,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) } if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { - snd_printddd("P%d write1 0x%04X 0x%04X\n", + snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n", s->number, xfer1, buf_ofs); hpi_handle_error( hpi_outstream_write_buf( @@ -866,7 +907,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) if (xfer2) { pd = s->runtime->dma_area; - snd_printddd("P%d write2 0x%04X 0x%04X\n", + snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n", s->number, xfercount - xfer1, buf_ofs); hpi_handle_error( @@ -876,7 +917,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) &ds->format)); } } else { - snd_printddd("C%d read1 0x%04x\n", + snd_printddd("read1, C=%d, xfer=%d\n", s->number, xfer1); hpi_handle_error( hpi_instream_read_buf( @@ -884,7 +925,7 @@ static void snd_card_asihpi_timer_function(unsigned long data) pd, xfer1)); if (xfer2) { pd = s->runtime->dma_area; - snd_printddd("C%d read2 0x%04x\n", + snd_printddd("read2, C=%d, xfer=%d\n", s->number, xfer2); hpi_handle_error( hpi_instream_read_buf( @@ -892,16 +933,38 @@ static void snd_card_asihpi_timer_function(unsigned long data) pd, xfer2)); } } + /* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */ ds->pcm_buf_host_rw_ofs += xfercount; ds->pcm_buf_elapsed_dma_ofs += xfercount; snd_pcm_period_elapsed(s); } } - if (dpcm->respawn_timer) + if (!card->hpi->interrupt_mode && dpcm->respawn_timer) add_timer(&dpcm->timer); } +static void snd_card_asihpi_int_task(unsigned long data) +{ + struct hpi_adapter *a = (struct hpi_adapter *)data; + struct snd_card_asihpi *asihpi; + + WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); + asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; + if (asihpi->llmode_streampriv) + snd_card_asihpi_timer_function( + (unsigned long)asihpi->llmode_streampriv); +} + +static void snd_card_asihpi_isr(struct hpi_adapter *a) +{ + struct snd_card_asihpi *asihpi; + + WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); + asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; + tasklet_schedule(&asihpi->t); +} + /***************************** PLAYBACK OPS ****************/ static int snd_card_asihpi_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) @@ -937,7 +1000,7 @@ snd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) snd_pcm_debug_name(substream, name, sizeof(name)); ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); - snd_printddd("%s pointer = 0x%04lx\n", name, (unsigned long)ptr); + snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr); return ptr; } @@ -1009,13 +1072,22 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) runtime->private_free = snd_card_asihpi_runtime_free; memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback)); - snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; - snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN; - /*?snd_card_asihpi_playback.period_bytes_min = - card->out_max_chans * 4096; */ - snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; - snd_card_asihpi_playback.periods_min = PERIODS_MIN; - snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + if (!card->hpi->interrupt_mode) { + snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN; + snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_playback.periods_min = PERIODS_MIN; + snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + } else { + size_t pbmin = card->update_interval_frames * + card->out_max_chans; + snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_playback.period_bytes_min = pbmin; + snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_playback.periods_min = PERIODS_MIN; + snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin; + } + /* snd_card_asihpi_playback.fifo_size = 0; */ snd_card_asihpi_playback.channels_max = card->out_max_chans; snd_card_asihpi_playback.channels_min = card->out_min_chans; @@ -1050,7 +1122,7 @@ static int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) card->update_interval_frames); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - card->update_interval_frames * 2, UINT_MAX); + card->update_interval_frames, UINT_MAX); snd_printdd("playback open\n"); @@ -1085,9 +1157,10 @@ snd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_card_asihpi_pcm *dpcm = runtime->private_data; + char name[16]; + snd_pcm_debug_name(substream, name, sizeof(name)); - snd_printddd("capture pointer %d=%d\n", - substream->number, dpcm->pcm_buf_dma_ofs); + snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs); /* NOTE Unlike playback can't use actual samples_played for the capture position, because those samples aren't yet in the local buffer available for reading. @@ -1115,8 +1188,6 @@ static int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) return 0; } - - static u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi, u32 h_stream) { @@ -1183,11 +1254,21 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) runtime->private_free = snd_card_asihpi_runtime_free; memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture)); - snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; - snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN; - snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; - snd_card_asihpi_capture.periods_min = PERIODS_MIN; - snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + if (!card->hpi->interrupt_mode) { + snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN; + snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_capture.periods_min = PERIODS_MIN; + snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; + } else { + size_t pbmin = card->update_interval_frames * + card->out_max_chans; + snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; + snd_card_asihpi_capture.period_bytes_min = pbmin; + snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; + snd_card_asihpi_capture.periods_min = PERIODS_MIN; + snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin; + } /* snd_card_asihpi_capture.fifo_size = 0; */ snd_card_asihpi_capture.channels_max = card->in_max_chans; snd_card_asihpi_capture.channels_min = card->in_min_chans; @@ -1212,7 +1293,7 @@ static int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, card->update_interval_frames); snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - card->update_interval_frames * 2, UINT_MAX); + card->update_interval_frames, UINT_MAX); snd_pcm_set_sync(substream); @@ -1296,8 +1377,9 @@ static const char * const asihpi_tuner_band_names[] = { "TV PAL I", "TV PAL DK", "TV SECAM", + "TV DAB", }; - +/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */ compile_time_assert( (ARRAY_SIZE(asihpi_tuner_band_names) == (HPI_TUNER_BAND_LAST+1)), @@ -1317,9 +1399,11 @@ static const char * const asihpi_src_names[] = { "Analog", "Adapter", "RTP", - "Internal" + "Internal", + "AVB", + "BLU-Link" }; - +/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */ compile_time_assert( (ARRAY_SIZE(asihpi_src_names) == (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)), @@ -1335,8 +1419,11 @@ static const char * const asihpi_dst_names[] = { "Net", "Analog", "RTP", + "AVB", + "Internal", + "BLU-Link" }; - +/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */ compile_time_assert( (ARRAY_SIZE(asihpi_dst_names) == (HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)), @@ -1351,7 +1438,7 @@ static inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl, if (err < 0) return err; else if (mixer_dump) - snd_printk(KERN_INFO "added %s(%d)\n", ctl->name, ctl->index); + dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index); return 0; } @@ -1625,18 +1712,7 @@ static const char * const asihpi_aesebu_format_names[] = { static int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - asihpi_aesebu_format_names[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names); } static int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, @@ -1863,22 +1939,7 @@ static int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol, if (num_bands < 0) return num_bands; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_bands; - - if (num_bands > 0) { - if (uinfo->value.enumerated.item >= - uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - asihpi_tuner_band_names[ - tuner_bands[uinfo->value.enumerated.item]]); - - } - return 0; + return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names); } static int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol, @@ -2253,7 +2314,7 @@ static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, u32 h_control = kcontrol->private_value; u16 mode; int i; - u16 mode_map[6]; + const char *mapped_names[6]; int valid_modes = 0; /* HPI channel mode values can be from 1 to 6 @@ -2262,24 +2323,14 @@ static int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++) if (!hpi_channel_mode_query_mode( h_control, i, &mode)) { - mode_map[valid_modes] = mode; + mapped_names[valid_modes] = mode_names[mode]; valid_modes++; } if (!valid_modes) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = valid_modes; - - if (uinfo->value.enumerated.item >= valid_modes) - uinfo->value.enumerated.item = valid_modes - 1; - - strcpy(uinfo->value.enumerated.name, - mode_names[mode_map[uinfo->value.enumerated.item]]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names); } static int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol, @@ -2328,13 +2379,18 @@ static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, /*------------------------------------------------------------ Sampleclock source controls ------------------------------------------------------------*/ -static char *sampleclock_sources[MAX_CLOCKSOURCES] = { +static const char const *sampleclock_sources[] = { "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", "SMPTE", "Digital1", "Auto", "Network", "Invalid", - "Prev Module", + "Prev Module", "BLU-Link", "Digital2", "Digital3", "Digital4", "Digital5", "Digital6", "Digital7", "Digital8"}; + /* Number of strings must match expected enumerated values */ + compile_time_assert( + (ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES), + assert_sampleclock_sources_size); + static int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2482,15 +2538,19 @@ static int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol, static int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, struct hpi_control *hpi_ctl) { - struct snd_card *card = asihpi->card; + struct snd_card *card; struct snd_kcontrol_new snd_control; - struct clk_cache *clkcache = &asihpi->cc; + struct clk_cache *clkcache; u32 hSC = hpi_ctl->h_control; int has_aes_in = 0; int i, j; u16 source; + if (snd_BUG_ON(!asihpi)) + return -EINVAL; + card = asihpi->card; + clkcache = &asihpi->cc; snd_control.private_value = hpi_ctl->h_control; clkcache->has_local = 0; @@ -2592,7 +2652,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (err) { if (err == HPI_ERROR_CONTROL_DISABLED) { if (mixer_dump) - snd_printk(KERN_INFO + dev_info(&asihpi->pci->dev, "Disabled HPI Control(%d)\n", idx); continue; @@ -2657,9 +2717,8 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) case HPI_CONTROL_COMPANDER: default: if (mixer_dump) - snd_printk(KERN_INFO - "Untranslated HPI Control" - "(%d) %d %d %d %d %d\n", + dev_info(&asihpi->pci->dev, + "Untranslated HPI Control (%d) %d %d %d %d %d\n", idx, hpi_ctl.control_type, hpi_ctl.src_node_type, @@ -2674,7 +2733,7 @@ static int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) if (HPI_ERROR_INVALID_OBJ_INDEX != err) hpi_handle_error(err); - snd_printk(KERN_INFO "%d mixer controls found\n", idx); + dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx); return 0; } @@ -2837,8 +2896,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, &card); if (err < 0) return err; - snd_printk(KERN_WARNING - "**** WARNING **** Adapter index %d->ALSA index %d\n", + dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n", adapter_index, card->number); } @@ -2846,9 +2904,7 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->card = card; asihpi->pci = pci_dev; asihpi->hpi = hpi; - - snd_printk(KERN_INFO "adapter ID=%4X index=%d\n", - asihpi->hpi->adapter->type, adapter_index); + hpi->snd_card = card; err = hpi_adapter_get_property(adapter_index, HPI_ADAPTER_PROPERTY_CAPS1, @@ -2868,8 +2924,16 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, if (err) asihpi->update_interval_frames = 512; - if (!asihpi->can_dma) - asihpi->update_interval_frames *= 2; + if (hpi->interrupt_mode) { + asihpi->pcm_start = snd_card_asihpi_pcm_int_start; + asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop; + tasklet_init(&asihpi->t, snd_card_asihpi_int_task, + (unsigned long)hpi); + hpi->interrupt_callback = snd_card_asihpi_isr; + } else { + asihpi->pcm_start = snd_card_asihpi_pcm_timer_start; + asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop; + } hpi_handle_error(hpi_instream_open(adapter_index, 0, &h_stream)); @@ -2879,6 +2943,9 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, hpi_handle_error(hpi_instream_close(h_stream)); + if (!asihpi->can_dma) + asihpi->update_interval_frames *= 2; + err = hpi_adapter_get_property(adapter_index, HPI_ADAPTER_PROPERTY_CURCHANNELS, &asihpi->in_max_chans, &asihpi->out_max_chans); @@ -2896,20 +2963,21 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, asihpi->in_min_chans = 1; } - snd_printk(KERN_INFO "Has dma:%d, grouping:%d, mrx:%d\n", + dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n", asihpi->can_dma, asihpi->support_grouping, - asihpi->support_mrx + asihpi->support_mrx, + asihpi->update_interval_frames ); err = snd_card_asihpi_pcm_new(asihpi, 0); if (err < 0) { - snd_printk(KERN_ERR "pcm_new failed\n"); + dev_err(&pci_dev->dev, "pcm_new failed\n"); goto __nodev; } err = snd_card_asihpi_mixer_new(asihpi); if (err < 0) { - snd_printk(KERN_ERR "mixer_new failed\n"); + dev_err(&pci_dev->dev, "mixer_new failed\n"); goto __nodev; } @@ -2936,13 +3004,12 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, err = snd_card_register(card); if (!err) { - hpi->snd_card = card; dev++; return 0; } __nodev: snd_card_free(card); - snd_printk(KERN_ERR "snd_asihpi_probe error %d\n", err); + dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err); return err; } @@ -2950,6 +3017,16 @@ __nodev: static void snd_asihpi_remove(struct pci_dev *pci_dev) { struct hpi_adapter *hpi = pci_get_drvdata(pci_dev); + struct snd_card_asihpi *asihpi = hpi->snd_card->private_data; + + /* Stop interrupts */ + if (hpi->interrupt_mode) { + hpi->interrupt_callback = NULL; + hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index, + HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); + tasklet_kill(&asihpi->t); + } + snd_card_free(hpi->snd_card); hpi->snd_card = NULL; asihpi_adapter_remove(pci_dev); @@ -2971,10 +3048,6 @@ static struct pci_driver driver = { .id_table = asihpi_pci_tbl, .probe = snd_asihpi_probe, .remove = snd_asihpi_remove, -#ifdef CONFIG_PM_SLEEP -/* .suspend = snd_asihpi_suspend, - .resume = snd_asihpi_resume, */ -#endif }; static int __init snd_asihpi_init(void) diff --git a/sound/pci/asihpi/hpi.h b/sound/pci/asihpi/hpi.h index 20887241a3ae..4466bd2c5272 100644 --- a/sound/pci/asihpi/hpi.h +++ b/sound/pci/asihpi/hpi.h @@ -196,8 +196,10 @@ enum HPI_SOURCENODES { packets of RTP audio samples from other devices. */ HPI_SOURCENODE_RTP_DESTINATION = 112, HPI_SOURCENODE_INTERNAL = 113, /**< node internal to the device. */ + HPI_SOURCENODE_AVB = 114, /**< AVB input stream */ + HPI_SOURCENODE_BLULINK = 115, /**< BLU-link input channel */ /* !!!Update this AND hpidebug.h if you add a new sourcenode type!!! */ - HPI_SOURCENODE_LAST_INDEX = 113 /**< largest ID */ + HPI_SOURCENODE_LAST_INDEX = 115 /**< largest ID */ /* AX6 max sourcenode types = 15 */ }; @@ -224,8 +226,11 @@ enum HPI_DESTNODES { /** RTP stream output node - This node is a source for packets of RTP audio samples that are sent to other devices. */ HPI_DESTNODE_RTP_SOURCE = 208, + HPI_DESTNODE_AVB = 209, /**< AVB output stream */ + HPI_DESTNODE_INTERNAL = 210, /**< node internal to the device. */ + HPI_DESTNODE_BLULINK = 211, /**< BLU-link output channel. */ /* !!!Update this AND hpidebug.h if you add a new destnode type!!! */ - HPI_DESTNODE_LAST_INDEX = 208 /**< largest ID */ + HPI_DESTNODE_LAST_INDEX = 211 /**< largest ID */ /* AX6 max destnode types = 15 */ }; @@ -752,7 +757,8 @@ enum HPI_TUNER_BAND { HPI_TUNER_BAND_TV_PAL_I = 7, /**< PAL-I TV band*/ HPI_TUNER_BAND_TV_PAL_DK = 8, /**< PAL-D/K TV band*/ HPI_TUNER_BAND_TV_SECAM_L = 9, /**< SECAM-L TV band*/ - HPI_TUNER_BAND_LAST = 9 /**< the index of the last tuner band. */ + HPI_TUNER_BAND_DAB = 10, + HPI_TUNER_BAND_LAST = 10 /**< the index of the last tuner band. */ }; /** Tuner mode attributes @@ -842,8 +848,10 @@ enum HPI_SAMPLECLOCK_SOURCES { HPI_SAMPLECLOCK_SOURCE_NETWORK = 8, /** From previous adjacent module (ASI2416 only)*/ HPI_SAMPLECLOCK_SOURCE_PREV_MODULE = 10, +/** Blu link sample clock*/ + HPI_SAMPLECLOCK_SOURCE_BLULINK = 11, /*! Update this if you add a new clock source.*/ - HPI_SAMPLECLOCK_SOURCE_LAST = 10 + HPI_SAMPLECLOCK_SOURCE_LAST = 11 }; /** Equalizer filter types. Used by HPI_ParametricEq_SetBand() diff --git a/sound/pci/asihpi/hpi6205.c b/sound/pci/asihpi/hpi6205.c index 4f2873880b16..8d5abfa4e24b 100644 --- a/sound/pci/asihpi/hpi6205.c +++ b/sound/pci/asihpi/hpi6205.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -163,6 +163,9 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, static void delete_adapter_obj(struct hpi_adapter_obj *pao); +static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao, + u32 message); + static void outstream_host_buffer_allocate(struct hpi_adapter_obj *pao, struct hpi_message *phm, struct hpi_response *phr); @@ -283,7 +286,6 @@ static void adapter_message(struct hpi_adapter_obj *pao, case HPI_ADAPTER_DELETE: adapter_delete(pao, phm, phr); break; - default: hw_message(pao, phm, phr); break; @@ -673,6 +675,12 @@ static u16 create_adapter_obj(struct hpi_adapter_obj *pao, HPI_DEBUG_LOG(INFO, "bootload DSP OK\n"); + pao->irq_query_and_clear = adapter_irq_query_and_clear; + pao->instream_host_buffer_status = + phw->p_interface_buffer->instream_host_buffer_status; + pao->outstream_host_buffer_status = + phw->p_interface_buffer->outstream_host_buffer_status; + return hpi_add_adapter(pao); } @@ -713,6 +721,21 @@ static void delete_adapter_obj(struct hpi_adapter_obj *pao) /*****************************************************************************/ /* Adapter functions */ +static int adapter_irq_query_and_clear(struct hpi_adapter_obj *pao, + u32 message) +{ + struct hpi_hw_obj *phw = pao->priv; + u32 hsr = 0; + + hsr = ioread32(phw->prHSR); + if (hsr & C6205_HSR_INTSRC) { + /* reset the interrupt from the DSP */ + iowrite32(C6205_HSR_INTSRC, phw->prHSR); + return HPI_IRQ_MIXER; + } + + return HPI_IRQ_NONE; +} /*****************************************************************************/ /* OutStream Host buffer functions */ @@ -1331,17 +1354,21 @@ static u16 adapter_boot_load_dsp(struct hpi_adapter_obj *pao, if (boot_code_id[1] != 0) { /* DSP 1 is a C6713 */ /* CLKX0 <- '1' release the C6205 bootmode pulldowns */ - boot_loader_write_mem32(pao, 0, (0x018C0024L), 0x00002202); + boot_loader_write_mem32(pao, 0, 0x018C0024, 0x00002202); hpios_delay_micro_seconds(100); /* Reset the 6713 #1 - revB */ boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 0); - - /* dummy read every 4 words for 6205 advisory 1.4.4 */ - boot_loader_read_mem32(pao, 0, 0); - + /* value of bit 3 is unknown after DSP reset, other bits shoudl be 0 */ + if (0 != (boot_loader_read_mem32(pao, 0, + (C6205_BAR0_TIMER1_CTL)) & ~8)) + return HPI6205_ERROR_6205_REG; hpios_delay_micro_seconds(100); + /* Release C6713 from reset - revB */ boot_loader_write_mem32(pao, 0, C6205_BAR0_TIMER1_CTL, 4); + if (4 != (boot_loader_read_mem32(pao, 0, + (C6205_BAR0_TIMER1_CTL)) & ~8)) + return HPI6205_ERROR_6205_REG; hpios_delay_micro_seconds(100); } @@ -2089,7 +2116,7 @@ static u16 message_response_sequence(struct hpi_adapter_obj *pao, return 0; } - /* Assume buffer of type struct bus_master_interface + /* Assume buffer of type struct bus_master_interface_62 is allocated "noncacheable" */ if (!wait_dsp_ack(phw, H620_HIF_IDLE, HPI6205_TIMEOUT)) { diff --git a/sound/pci/asihpi/hpi_internal.h b/sound/pci/asihpi/hpi_internal.h index bc86cb726d79..48380ce2c81b 100644 --- a/sound/pci/asihpi/hpi_internal.h +++ b/sound/pci/asihpi/hpi_internal.h @@ -554,17 +554,21 @@ struct hpi_pci { struct pci_dev *pci_dev; }; +/** Adapter specification resource */ +struct hpi_adapter_specification { + u32 type; + u8 modules[4]; +}; + struct hpi_resource { union { const struct hpi_pci *pci; const char *net_if; + struct hpi_adapter_specification adapter_spec; + const void *sw_if; } r; -#ifndef HPI64BIT /* keep structure size constant */ - u32 pad_to64; -#endif u16 bus_type; /* HPI_BUS_PNPISA, _PCI, _USB etc */ u16 padding; - }; /** Format info used inside struct hpi_message @@ -582,7 +586,7 @@ struct hpi_msg_format { struct hpi_msg_data { struct hpi_msg_format format; u8 *pb_data; -#ifndef HPI64BIT +#ifndef CONFIG_64BIT u32 padding; #endif u32 data_size; @@ -595,7 +599,7 @@ struct hpi_data_legacy32 { u32 data_size; }; -#ifdef HPI64BIT +#ifdef CONFIG_64BIT /* Compatibility version of struct hpi_data*/ struct hpi_data_compat32 { struct hpi_msg_format format; @@ -682,8 +686,8 @@ union hpi_adapterx_msg { u16 value; } test_assert; struct { - u32 yes; - } irq_query; + u32 message; + } irq; u32 pad[3]; }; diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c index 7ed5c26c3737..c7751243dc42 100644 --- a/sound/pci/asihpi/hpicmn.c +++ b/sound/pci/asihpi/hpicmn.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -206,6 +206,14 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) struct hpi_control_cache_info *info = (struct hpi_control_cache_info *) &p_master_cache[byte_count]; + u16 control_index = info->control_index; + + if (control_index >= pC->control_count) { + HPI_DEBUG_LOG(INFO, + "adap %d control index %d out of range, cache not ready?\n", + pC->adap_idx, control_index); + return 0; + } if (!info->size_in32bit_words) { if (!i) { @@ -225,10 +233,10 @@ static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) } if (info->control_type) { - pC->p_info[info->control_index] = info; + pC->p_info[control_index] = info; cached++; } else { /* dummy cache entry */ - pC->p_info[info->control_index] = NULL; + pC->p_info[control_index] = NULL; } byte_count += info->size_in32bit_words * 4; @@ -309,35 +317,18 @@ static const struct pad_ofs_size pad_desc[] = { /** CheckControlCache checks the cache and fills the struct hpi_response * accordingly. It returns one if a cache hit occurred, zero otherwise. */ -short hpi_check_control_cache(struct hpi_control_cache *p_cache, +short hpi_check_control_cache_single(struct hpi_control_cache_single *pC, struct hpi_message *phm, struct hpi_response *phr) { - short found = 1; - struct hpi_control_cache_info *pI; - struct hpi_control_cache_single *pC; size_t response_size; - if (!find_control(phm->obj_index, p_cache, &pI)) { - HPI_DEBUG_LOG(VERBOSE, - "HPICMN find_control() failed for adap %d\n", - phm->adapter_index); - return 0; - } - - phr->error = 0; - phr->specific_error = 0; - phr->version = 0; + short found = 1; /* set the default response size */ response_size = sizeof(struct hpi_response_header) + sizeof(struct hpi_control_res); - /* pC is the default cached control strucure. May be cast to - something else in the following switch statement. - */ - pC = (struct hpi_control_cache_single *)pI; - - switch (pI->control_type) { + switch (pC->u.i.control_type) { case HPI_CONTROL_METER: if (phm->u.c.attribute == HPI_METER_PEAK) { @@ -467,7 +458,7 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, break; case HPI_CONTROL_PAD:{ struct hpi_control_cache_pad *p_pad; - p_pad = (struct hpi_control_cache_pad *)pI; + p_pad = (struct hpi_control_cache_pad *)pC; if (!(p_pad->field_valid_flags & (1 << HPI_CTL_ATTR_INDEX(phm->u.c. @@ -531,7 +522,8 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n", found ? "Cached" : "Uncached", phm->adapter_index, - pI->control_index, pI->control_type, phm->u.c.attribute); + pC->u.i.control_index, pC->u.i.control_type, + phm->u.c.attribute); if (found) { phr->size = (u16)response_size; @@ -543,34 +535,36 @@ short hpi_check_control_cache(struct hpi_control_cache *p_cache, return found; } -/** Updates the cache with Set values. - -Only update if no error. -Volume and Level return the limited values in the response, so use these -Multiplexer does so use sent values -*/ -void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, +short hpi_check_control_cache(struct hpi_control_cache *p_cache, struct hpi_message *phm, struct hpi_response *phr) { - struct hpi_control_cache_single *pC; struct hpi_control_cache_info *pI; - if (phr->error) - return; - if (!find_control(phm->obj_index, p_cache, &pI)) { HPI_DEBUG_LOG(VERBOSE, "HPICMN find_control() failed for adap %d\n", phm->adapter_index); - return; + return 0; } - /* pC is the default cached control strucure. - May be cast to something else in the following switch statement. - */ - pC = (struct hpi_control_cache_single *)pI; + phr->error = 0; + phr->specific_error = 0; + phr->version = 0; + + return hpi_check_control_cache_single((struct hpi_control_cache_single + *)pI, phm, phr); +} + +/** Updates the cache with Set values. - switch (pI->control_type) { +Only update if no error. +Volume and Level return the limited values in the response, so use these +Multiplexer does so use sent values +*/ +void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single + *pC, struct hpi_message *phm, struct hpi_response *phr) +{ + switch (pC->u.i.control_type) { case HPI_CONTROL_VOLUME: if (phm->u.c.attribute == HPI_VOLUME_GAIN) { pC->u.vol.an_log[0] = phr->u.c.an_log_value[0]; @@ -625,6 +619,30 @@ void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, } } +void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache, + struct hpi_message *phm, struct hpi_response *phr) +{ + struct hpi_control_cache_single *pC; + struct hpi_control_cache_info *pI; + + if (phr->error) + return; + + if (!find_control(phm->obj_index, p_cache, &pI)) { + HPI_DEBUG_LOG(VERBOSE, + "HPICMN find_control() failed for adap %d\n", + phm->adapter_index); + return; + } + + /* pC is the default cached control strucure. + May be cast to something else in the following switch statement. + */ + pC = (struct hpi_control_cache_single *)pI; + + hpi_cmn_control_cache_sync_to_msg_single(pC, phm, phr); +} + /** Allocate control cache. \return Cache pointer, or NULL if allocation fails. @@ -637,12 +655,13 @@ struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count, if (!p_cache) return NULL; - p_cache->p_info = kcalloc(control_count, sizeof(*p_cache->p_info), - GFP_KERNEL); + p_cache->p_info = + kcalloc(control_count, sizeof(*p_cache->p_info), GFP_KERNEL); if (!p_cache->p_info) { kfree(p_cache); return NULL; } + p_cache->cache_size_in_bytes = size_in_bytes; p_cache->control_count = control_count; p_cache->p_cache = p_dsp_control_buffer; diff --git a/sound/pci/asihpi/hpicmn.h b/sound/pci/asihpi/hpicmn.h index e44121283047..46629c2d101b 100644 --- a/sound/pci/asihpi/hpicmn.h +++ b/sound/pci/asihpi/hpicmn.h @@ -1,7 +1,7 @@ /** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -21,7 +21,11 @@ struct hpi_adapter_obj; /* a function that takes an adapter obj and returns an int */ -typedef int adapter_int_func(struct hpi_adapter_obj *pao); +typedef int adapter_int_func(struct hpi_adapter_obj *pao, u32 message); + +#define HPI_IRQ_NONE (0) +#define HPI_IRQ_MESSAGE (1) +#define HPI_IRQ_MIXER (2) struct hpi_adapter_obj { struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */ @@ -33,6 +37,9 @@ struct hpi_adapter_obj { u16 dsp_crashed; u16 has_control_cache; void *priv; + adapter_int_func *irq_query_and_clear; + struct hpi_hostbuffer_status *instream_host_buffer_status; + struct hpi_hostbuffer_status *outstream_host_buffer_status; }; struct hpi_control_cache { @@ -55,13 +62,21 @@ void hpi_delete_adapter(struct hpi_adapter_obj *pao); short hpi_check_control_cache(struct hpi_control_cache *pC, struct hpi_message *phm, struct hpi_response *phr); + +short hpi_check_control_cache_single(struct hpi_control_cache_single *pC, + struct hpi_message *phm, struct hpi_response *phr); + struct hpi_control_cache *hpi_alloc_control_cache(const u32 number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer); + void hpi_free_control_cache(struct hpi_control_cache *p_cache); void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC, struct hpi_message *phm, struct hpi_response *phr); +void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single + *pC, struct hpi_message *phm, struct hpi_response *phr); + u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr); hpi_handler_func HPI_COMMON; diff --git a/sound/pci/asihpi/hpimsginit.c b/sound/pci/asihpi/hpimsginit.c index 032d563e3708..7eb617175fde 100644 --- a/sound/pci/asihpi/hpimsginit.c +++ b/sound/pci/asihpi/hpimsginit.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -37,11 +37,15 @@ static u16 gwSSX2_bypass; static void hpi_init_message(struct hpi_message *phm, u16 object, u16 function) { - memset(phm, 0, sizeof(*phm)); + u16 size; + if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) - phm->size = msg_size[object]; + size = msg_size[object]; else - phm->size = sizeof(*phm); + size = sizeof(*phm); + + memset(phm, 0, size); + phm->size = size; if (gwSSX2_bypass) phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE; @@ -60,12 +64,16 @@ static void hpi_init_message(struct hpi_message *phm, u16 object, void hpi_init_response(struct hpi_response *phr, u16 object, u16 function, u16 error) { - memset(phr, 0, sizeof(*phr)); - phr->type = HPI_TYPE_RESPONSE; + u16 size; + if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) - phr->size = res_size[object]; + size = res_size[object]; else - phr->size = sizeof(*phr); + size = sizeof(*phr); + + memset(phr, 0, sizeof(*phr)); + phr->size = size; + phr->type = HPI_TYPE_RESPONSE; phr->object = object; phr->function = function; phr->error = error; @@ -86,7 +94,7 @@ void hpi_init_message_response(struct hpi_message *phm, static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size, u16 object, u16 function) { - memset(phm, 0, sizeof(*phm)); + memset(phm, 0, size); if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) { phm->size = size; phm->type = HPI_TYPE_REQUEST; @@ -100,7 +108,9 @@ static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size, void hpi_init_responseV1(struct hpi_response_header *phr, u16 size, u16 object, u16 function) { - memset(phr, 0, sizeof(*phr)); + (void)object; + (void)function; + memset(phr, 0, size); phr->size = size; phr->version = 1; phr->type = HPI_TYPE_RESPONSE; diff --git a/sound/pci/asihpi/hpimsgx.c b/sound/pci/asihpi/hpimsgx.c index d4790ddc225c..736f45337fc7 100644 --- a/sound/pci/asihpi/hpimsgx.c +++ b/sound/pci/asihpi/hpimsgx.c @@ -1,7 +1,7 @@ /****************************************************************************** AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> + Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -35,6 +35,7 @@ static struct pci_device_id asihpi_pci_tbl[] = { static struct hpios_spinlock msgx_lock; static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS]; +static int logging_enabled = 1; static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci *pci_info) @@ -312,7 +313,9 @@ static void instream_message(struct hpi_message *phm, void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, void *h_owner) { - HPI_DEBUG_MESSAGE(DEBUG, phm); + + if (logging_enabled) + HPI_DEBUG_MESSAGE(DEBUG, phm); if (phm->type != HPI_TYPE_REQUEST) { hpi_init_response(phr, phm->object, phm->function, @@ -352,8 +355,14 @@ void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr, hw_entry_point(phm, phr); break; } - HPI_DEBUG_RESPONSE(phr); + if (logging_enabled) + HPI_DEBUG_RESPONSE(phr); + + if (phr->error >= HPI_ERROR_DSP_COMMUNICATION) { + hpi_debug_level_set(HPI_DEBUG_LEVEL_ERROR); + logging_enabled = 0; + } } static void adapter_open(struct hpi_message *phm, struct hpi_response *phr) diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 7f0272032fbb..6aa677e60555 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -1,7 +1,8 @@ /******************************************************************************* - AudioScience HPI driver - Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com> + Common Linux HPI ioctl and module probe/remove functions + + Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as @@ -12,11 +13,6 @@ 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 - -Common Linux HPI ioctl and module probe/remove functions *******************************************************************************/ #define SOURCEFILE_NAME "hpioctl.c" @@ -29,6 +25,7 @@ Common Linux HPI ioctl and module probe/remove functions #include "hpicmn.h" #include <linux/fs.h> +#include <linux/interrupt.h> #include <linux/slab.h> #include <linux/moduleparam.h> #include <asm/uaccess.h> @@ -307,10 +304,38 @@ out: return err; } +static int asihpi_irq_count; + +static irqreturn_t asihpi_isr(int irq, void *dev_id) +{ + struct hpi_adapter *a = dev_id; + int handled; + + if (!a->adapter->irq_query_and_clear) { + pr_err("asihpi_isr ASI%04X:%d no handler\n", a->adapter->type, + a->adapter->index); + return IRQ_NONE; + } + + handled = a->adapter->irq_query_and_clear(a->adapter, 0); + + if (!handled) + return IRQ_NONE; + + asihpi_irq_count++; + /* printk(KERN_INFO "asihpi_isr %d ASI%04X:%d irq handled\n", + asihpi_irq_count, a->adapter->type, a->adapter->index); */ + + if (a->interrupt_callback) + a->interrupt_callback(a); + + return IRQ_HANDLED; +} + int asihpi_adapter_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { - int idx, nm; + int idx, nm, low_latency_mode = 0, irq_supported = 0; int adapter_index; unsigned int memlen; struct hpi_message hm; @@ -388,8 +413,38 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, hm.adapter_index = adapter.adapter->index; hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); - if (hr.error) + if (hr.error) { + HPI_DEBUG_LOG(ERROR, "HPI_ADAPTER_OPEN failed, aborting\n"); goto err; + } + + /* Check if current mode == Low Latency mode */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_MODE); + hm.adapter_index = adapter.adapter->index; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + + if (!hr.error + && hr.u.ax.mode.adapter_mode == HPI_ADAPTER_MODE_LOW_LATENCY) + low_latency_mode = 1; + else + dev_info(&pci_dev->dev, + "Adapter at index %d is not in low latency mode\n", + adapter.adapter->index); + + /* Check if IRQs are supported */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_GET_PROPERTY); + hm.adapter_index = adapter.adapter->index; + hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_SUPPORTS_IRQ; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + if (hr.error || !hr.u.ax.property_get.parameter1) { + dev_info(&pci_dev->dev, + "IRQs not supported by adapter at index %d\n", + adapter.adapter->index); + } else { + irq_supported = 1; + } /* WARNING can't init mutex in 'adapter' * and then copy it to adapters[] ?!?! @@ -398,6 +453,44 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, mutex_init(&adapters[adapter_index].mutex); pci_set_drvdata(pci_dev, &adapters[adapter_index]); + if (low_latency_mode && irq_supported) { + if (!adapter.adapter->irq_query_and_clear) { + dev_err(&pci_dev->dev, + "no IRQ handler for adapter %d, aborting\n", + adapter.adapter->index); + goto err; + } + + /* Disable IRQ generation on DSP side by setting the rate to 0 */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_SET_PROPERTY); + hm.adapter_index = adapter.adapter->index; + hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; + hm.u.ax.property_set.parameter1 = 0; + hm.u.ax.property_set.parameter2 = 0; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + if (hr.error) { + HPI_DEBUG_LOG(ERROR, + "HPI_ADAPTER_GET_MODE failed, aborting\n"); + goto err; + } + + /* Note: request_irq calls asihpi_isr here */ + if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED, + "asihpi", &adapters[adapter_index])) { + dev_err(&pci_dev->dev, "request_irq(%d) failed\n", + pci_dev->irq); + goto err; + } + + adapters[adapter_index].interrupt_mode = 1; + + dev_info(&pci_dev->dev, "using irq %d\n", pci_dev->irq); + adapters[adapter_index].irq = pci_dev->irq; + } else { + dev_info(&pci_dev->dev, "using polled mode\n"); + } + dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n", adapter.adapter->type, adapter_index); @@ -431,6 +524,15 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) pa = pci_get_drvdata(pci_dev); pci = pa->adapter->pci; + /* Disable IRQ generation on DSP side */ + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, + HPI_ADAPTER_SET_PROPERTY); + hm.adapter_index = pa->adapter->index; + hm.u.ax.property_set.property = HPI_ADAPTER_PROPERTY_IRQ_RATE; + hm.u.ax.property_set.parameter1 = 0; + hm.u.ax.property_set.parameter2 = 0; + hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL); + hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_DELETE); hm.adapter_index = pa->adapter->index; @@ -442,8 +544,10 @@ void asihpi_adapter_remove(struct pci_dev *pci_dev) iounmap(pci.ap_mem_base[idx]); } - if (pa->p_buffer) - vfree(pa->p_buffer); + if (pa->irq) + free_irq(pa->irq, pa); + + vfree(pa->p_buffer); if (1) dev_info(&pci_dev->dev, diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h index d3fbd0d76c37..4e383601b9cf 100644 --- a/sound/pci/asihpi/hpios.h +++ b/sound/pci/asihpi/hpios.h @@ -41,10 +41,6 @@ HPI Operating System Specific macros for Linux Kernel driver #define HPI_NO_OS_FILE_OPS -#ifdef CONFIG_64BIT -#define HPI64BIT -#endif - /** Details of a memory area allocated with pci_alloc_consistent Need all info for parameters to pci_free_consistent */ @@ -155,6 +151,10 @@ struct hpi_adapter { struct hpi_adapter_obj *adapter; struct snd_card *snd_card; + int irq; + int interrupt_mode; + void (*interrupt_callback) (struct hpi_adapter *); + /* mutex prevents contention for one card between multiple user programs (via ioctl) */ struct mutex mutex; diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index 7895c5d300c7..9c1c4452a8ee 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -688,9 +688,7 @@ static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma) if (! dma->substream || ! dma->running) return; dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type); - snd_pcm_stream_lock(dma->substream); - snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(dma->substream); + snd_pcm_stop_xrun(dma->substream); } /* diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 3c3241309a30..b2f63e0727de 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -638,9 +638,7 @@ static void snd_atiixp_xrun_dma(struct atiixp_modem *chip, if (! dma->substream || ! dma->running) return; dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type); - snd_pcm_stream_lock(dma->substream); - snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(dma->substream); + snd_pcm_stop_xrun(dma->substream); } /* diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 21ce31f636bc..996369134ea8 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -48,11 +48,10 @@ static void vortex_fix_latency(struct pci_dev *vortex) { int rc; if (!(rc = pci_write_config_byte(vortex, 0x40, 0xff))) { - pr_info( CARD_NAME - ": vortex latency is 0xff\n"); + dev_info(&vortex->dev, "vortex latency is 0xff\n"); } else { - pr_warn( CARD_NAME - ": could not set vortex latency: pci error 0x%x\n", rc); + dev_warn(&vortex->dev, + "could not set vortex latency: pci error 0x%x\n", rc); } } @@ -70,11 +69,10 @@ static void vortex_fix_agp_bridge(struct pci_dev *via) if (!(rc = pci_read_config_byte(via, 0x42, &value)) && ((value & 0x10) || !(rc = pci_write_config_byte(via, 0x42, value | 0x10)))) { - pr_info( CARD_NAME - ": bridge config is 0x%x\n", value | 0x10); + dev_info(&via->dev, "bridge config is 0x%x\n", value | 0x10); } else { - pr_warn( CARD_NAME - ": could not set vortex latency: pci error 0x%x\n", rc); + dev_warn(&via->dev, + "could not set vortex latency: pci error 0x%x\n", rc); } } @@ -97,7 +95,8 @@ static void snd_vortex_workaround(struct pci_dev *vortex, int fix) PCI_DEVICE_ID_AMD_FE_GATE_7007, NULL); } if (via) { - pr_info( CARD_NAME ": Activating latency workaround...\n"); + dev_info(&vortex->dev, + "Activating latency workaround...\n"); vortex_fix_latency(vortex); vortex_fix_agp_bridge(via); } @@ -153,7 +152,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) return err; if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0 || pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) < 0) { - pr_err( "error to set DMA mask\n"); + dev_err(card->dev, "error to set DMA mask\n"); pci_disable_device(pci); return -ENXIO; } @@ -182,7 +181,7 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) chip->mmio = pci_ioremap_bar(pci, 0); if (!chip->mmio) { - pr_err( "MMIO area remap failed.\n"); + dev_err(card->dev, "MMIO area remap failed.\n"); err = -ENOMEM; goto ioremap_out; } @@ -191,14 +190,14 @@ snd_vortex_create(struct snd_card *card, struct pci_dev *pci, vortex_t ** rchip) * This must be done before we do request_irq otherwise we can get spurious * interrupts that we do not handle properly and make a mess of things */ if ((err = vortex_core_init(chip)) != 0) { - pr_err( "hw core init failed\n"); + dev_err(card->dev, "hw core init failed\n"); goto core_out; } if ((err = request_irq(pci->irq, vortex_interrupt, IRQF_SHARED, KBUILD_MODNAME, chip)) != 0) { - pr_err( "cannot grab irq\n"); + dev_err(card->dev, "cannot grab irq\n"); goto irq_out; } chip->irq = pci->irq; @@ -315,7 +314,7 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_VORTEX_SYNTH, sizeof(snd_vortex_synth_arg_t), &wave) < 0 || wave == NULL) { - snd_printk(KERN_ERR "Can't initialize Aureal wavetable synth\n"); + dev_err(card->dev, "Can't initialize Aureal wavetable synth\n"); } else { snd_vortex_synth_arg_t *arg; @@ -342,11 +341,11 @@ snd_vortex_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) chip->rev = pci->revision; #ifdef CHIP_AU8830 if ((chip->rev) != 0xfe && (chip->rev) != 0xfa) { - pr_alert( - "vortex: The revision (%x) of your card has not been seen before.\n", + dev_alert(card->dev, + "The revision (%x) of your card has not been seen before.\n", chip->rev); - pr_alert( - "vortex: Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n"); + dev_alert(card->dev, + "Please email the results of 'lspci -vv' to openvortex-dev@nongnu.org.\n"); snd_card_free(card); err = -ENODEV; return err; diff --git a/sound/pci/au88x0/au88x0.h b/sound/pci/au88x0/au88x0.h index 466a5c8e8354..3a8fefefea77 100644 --- a/sound/pci/au88x0/au88x0.h +++ b/sound/pci/au88x0/au88x0.h @@ -243,7 +243,7 @@ static int vortex_core_init(vortex_t * card); static int vortex_core_shutdown(vortex_t * card); static void vortex_enable_int(vortex_t * card); static irqreturn_t vortex_interrupt(int irq, void *dev_id); -static int vortex_alsafmt_aspfmt(int alsafmt); +static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v); /* Connection stuff. */ static void vortex_connect_default(vortex_t * vortex, int en); @@ -278,7 +278,7 @@ static void vortex_mix_setvolumebyte(vortex_t * vortex, unsigned char mix, static void vortex_Vort3D_enable(vortex_t * v); static void vortex_Vort3D_disable(vortex_t * v); static void vortex_Vort3D_connect(vortex_t * vortex, int en); -static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en); +static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v); #endif /* Driver stuff. */ diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c index 30f760e3d2c0..ab0f87312911 100644 --- a/sound/pci/au88x0/au88x0_a3d.c +++ b/sound/pci/au88x0/au88x0_a3d.c @@ -484,12 +484,13 @@ static void a3dsrc_ZeroState(a3dsrc_t * a) } /* Reset entire A3D engine */ -static void a3dsrc_ZeroStateA3D(a3dsrc_t * a) +static void a3dsrc_ZeroStateA3D(a3dsrc_t *a, vortex_t *v) { int i, var, var2; if ((a->vortex) == NULL) { - pr_err( "vortex: ZeroStateA3D: ERROR: a->vortex is NULL\n"); + dev_err(v->card->dev, + "ZeroStateA3D: ERROR: a->vortex is NULL\n"); return; } @@ -601,7 +602,7 @@ static void vortex_Vort3D_enable(vortex_t *v) Vort3DRend_Initialize(v, XT_HEADPHONE); for (i = 0; i < NR_A3D; i++) { vortex_A3dSourceHw_Initialize(v, i % 4, i >> 2); - a3dsrc_ZeroStateA3D(&(v->a3d[0])); + a3dsrc_ZeroStateA3D(&v->a3d[0], v); } /* Register ALSA controls */ vortex_a3d_register_controls(v); @@ -628,15 +629,15 @@ static void vortex_Vort3D_connect(vortex_t * v, int en) v->mixxtlk[0] = vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN); if (v->mixxtlk[0] < 0) { - pr_warn - ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n"); + dev_warn(v->card->dev, + "vortex_Vort3D: ERROR: not enough free mixer resources.\n"); return; } v->mixxtlk[1] = vortex_adb_checkinout(v, v->fixed_res, en, VORTEX_RESOURCE_MIXIN); if (v->mixxtlk[1] < 0) { - pr_warn - ("vortex: vortex_Vort3D: ERROR: not enough free mixer resources.\n"); + dev_warn(v->card->dev, + "vortex_Vort3D: ERROR: not enough free mixer resources.\n"); return; } #endif @@ -676,11 +677,11 @@ static void vortex_Vort3D_connect(vortex_t * v, int en) } /* Initialize one single A3D source. */ -static void vortex_Vort3D_InitializeSource(a3dsrc_t * a, int en) +static void vortex_Vort3D_InitializeSource(a3dsrc_t *a, int en, vortex_t *v) { if (a->vortex == NULL) { - pr_warn - ("vortex: Vort3D_InitializeSource: A3D source not initialized\n"); + dev_warn(v->card->dev, + "Vort3D_InitializeSource: A3D source not initialized\n"); return; } if (en) { diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 72e81286b70e..4667c3232b7f 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -285,8 +285,8 @@ vortex_mixer_addWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) temp = hwread(vortex->mmio, prev); //printk(KERN_INFO "vortex: mixAddWTD: while addr=%x, val=%x\n", prev, temp); if ((++lifeboat) > 0xf) { - pr_err( - "vortex_mixer_addWTD: lifeboat overflow\n"); + dev_err(vortex->card->dev, + "vortex_mixer_addWTD: lifeboat overflow\n"); return 0; } } @@ -303,7 +303,7 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) eax = hwread(vortex->mmio, VORTEX_MIXER_SR); if (((1 << ch) & eax) == 0) { - pr_err( "mix ALARM %x\n", eax); + dev_err(vortex->card->dev, "mix ALARM %x\n", eax); return 0; } ebp = VORTEX_MIXER_CHNBASE + (ch << 2); @@ -324,8 +324,8 @@ vortex_mixer_delWTD(vortex_t * vortex, unsigned char mix, unsigned char ch) //printk(KERN_INFO "vortex: mixdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src); while ((edx & 0xf) != mix) { if ((esi) > 0xf) { - pr_err( - "vortex: mixdelWTD: error lifeboat overflow\n"); + dev_err(vortex->card->dev, + "mixdelWTD: error lifeboat overflow\n"); return 0; } esp14 = ebx; @@ -492,7 +492,7 @@ vortex_src_persist_convratio(vortex_t * vortex, unsigned char src, int ratio) hwwrite(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2), ratio); temp = hwread(vortex->mmio, VORTEX_SRC_CONVRATIO + (src << 2)); if ((++lifeboat) > 0x9) { - pr_err( "Vortex: Src cvr fail\n"); + dev_err(vortex->card->dev, "Src cvr fail\n"); break; } } @@ -684,8 +684,8 @@ vortex_src_addWTD(vortex_t * vortex, unsigned char src, unsigned char ch) temp = hwread(vortex->mmio, prev); //printk(KERN_INFO "vortex: srcAddWTD: while addr=%x, val=%x\n", prev, temp); if ((++lifeboat) > 0xf) { - pr_err( - "vortex_src_addWTD: lifeboat overflow\n"); + dev_err(vortex->card->dev, + "vortex_src_addWTD: lifeboat overflow\n"); return 0; } } @@ -703,7 +703,7 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch) eax = hwread(vortex->mmio, VORTEX_SRCBLOCK_SR); if (((1 << ch) & eax) == 0) { - pr_err( "src alarm\n"); + dev_err(vortex->card->dev, "src alarm\n"); return 0; } ebp = VORTEX_SRC_CHNBASE + (ch << 2); @@ -724,8 +724,8 @@ vortex_src_delWTD(vortex_t * vortex, unsigned char src, unsigned char ch) //printk(KERN_INFO "vortex: srcdelWTD: 1 addr=%x, val=%x, src=%x\n", ebx, edx, src); while ((edx & 0xf) != src) { if ((esi) > 0xf) { - pr_warn - ("vortex: srcdelWTD: error, lifeboat overflow\n"); + dev_warn(vortex->card->dev, + "srcdelWTD: error, lifeboat overflow\n"); return 0; } esp14 = ebx; @@ -819,8 +819,8 @@ vortex_fifo_setadbctrl(vortex_t * vortex, int fifo, int stereo, int priority, do { temp = hwread(vortex->mmio, VORTEX_FIFO_ADBCTRL + (fifo << 2)); if (lifeboat++ > 0xbb8) { - pr_err( - "Vortex: vortex_fifo_setadbctrl fail\n"); + dev_err(vortex->card->dev, + "vortex_fifo_setadbctrl fail\n"); break; } } @@ -915,7 +915,8 @@ vortex_fifo_setwtctrl(vortex_t * vortex, int fifo, int ctrl, int priority, do { temp = hwread(vortex->mmio, VORTEX_FIFO_WTCTRL + (fifo << 2)); if (lifeboat++ > 0xbb8) { - pr_err( "Vortex: vortex_fifo_setwtctrl fail\n"); + dev_err(vortex->card->dev, + "vortex_fifo_setwtctrl fail\n"); break; } } @@ -1042,7 +1043,7 @@ static void vortex_fifo_init(vortex_t * vortex) for (x = NR_ADB - 1; x >= 0; x--) { hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1)); if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1)) - pr_err( "bad adb fifo reset!"); + dev_err(vortex->card->dev, "bad adb fifo reset!"); vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE); addr -= 4; } @@ -1053,9 +1054,9 @@ static void vortex_fifo_init(vortex_t * vortex) for (x = NR_WT - 1; x >= 0; x--) { hwwrite(vortex->mmio, addr, FIFO_U0); if (hwread(vortex->mmio, addr) != FIFO_U0) - pr_err( - "bad wt fifo reset (0x%08x, 0x%08x)!\n", - addr, hwread(vortex->mmio, addr)); + dev_err(vortex->card->dev, + "bad wt fifo reset (0x%08x, 0x%08x)!\n", + addr, hwread(vortex->mmio, addr)); vortex_fifo_clearwtdata(vortex, x, FIFO_SIZE); addr -= 4; } @@ -1213,8 +1214,9 @@ static int vortex_adbdma_bufshift(vortex_t * vortex, int adbdma) if (dma->period_virt >= dma->nr_periods) dma->period_virt -= dma->nr_periods; if (delta != 1) - pr_info( "vortex: %d virt=%d, real=%d, delta=%d\n", - adbdma, dma->period_virt, dma->period_real, delta); + dev_info(vortex->card->dev, + "%d virt=%d, real=%d, delta=%d\n", + adbdma, dma->period_virt, dma->period_real, delta); return delta; } @@ -1482,8 +1484,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma) dma->period_real = page; if (delta != 1) - pr_warn( "vortex: wt virt = %d, delta = %d\n", - dma->period_virt, delta); + dev_warn(vortex->card->dev, "wt virt = %d, delta = %d\n", + dma->period_virt, delta); return delta; } @@ -1667,9 +1669,9 @@ vortex_adb_addroutes(vortex_t * vortex, unsigned char channel, hwread(vortex->mmio, VORTEX_ADB_RTBASE + (temp << 2)) & ADB_MASK; if ((lifeboat++) > ADB_MASK) { - pr_err( - "vortex_adb_addroutes: unending route! 0x%x\n", - *route); + dev_err(vortex->card->dev, + "vortex_adb_addroutes: unending route! 0x%x\n", + *route); return; } } @@ -1703,9 +1705,9 @@ vortex_adb_delroutes(vortex_t * vortex, unsigned char channel, hwread(vortex->mmio, VORTEX_ADB_RTBASE + (prev << 2)) & ADB_MASK; if (((lifeboat++) > ADB_MASK) || (temp == ADB_MASK)) { - pr_err( - "vortex_adb_delroutes: route not found! 0x%x\n", - route0); + dev_err(vortex->card->dev, + "vortex_adb_delroutes: route not found! 0x%x\n", + route0); return; } } @@ -2045,7 +2047,9 @@ vortex_adb_checkinout(vortex_t * vortex, int resmap[], int out, int restype) } } } - pr_err( "vortex: FATAL: ResManager: resource type %d exhausted.\n", restype); + dev_err(vortex->card->dev, + "FATAL: ResManager: resource type %d exhausted.\n", + restype); return -ENOMEM; } @@ -2173,11 +2177,13 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir, memset(stream->resources, 0, sizeof(unsigned char) * VORTEX_RESOURCE_LAST); - pr_err( "vortex: out of A3D sources. Sorry\n"); + dev_err(vortex->card->dev, + "out of A3D sources. Sorry\n"); return -EBUSY; } /* (De)Initialize A3D hardware source. */ - vortex_Vort3D_InitializeSource(&(vortex->a3d[a3d]), en); + vortex_Vort3D_InitializeSource(&vortex->a3d[a3d], en, + vortex); } /* Make SPDIF out exclusive to "spdif" device when in use. */ if ((stream->type == VORTEX_PCM_SPDIF) && (en)) { @@ -2421,7 +2427,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) hwread(vortex->mmio, VORTEX_IRQ_SOURCE); // Is at least one IRQ flag set? if (source == 0) { - pr_err( "vortex: missing irq source\n"); + dev_err(vortex->card->dev, "missing irq source\n"); return IRQ_NONE; } @@ -2429,19 +2435,19 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) // Attend every interrupt source. if (unlikely(source & IRQ_ERR_MASK)) { if (source & IRQ_FATAL) { - pr_err( "vortex: IRQ fatal error\n"); + dev_err(vortex->card->dev, "IRQ fatal error\n"); } if (source & IRQ_PARITY) { - pr_err( "vortex: IRQ parity error\n"); + dev_err(vortex->card->dev, "IRQ parity error\n"); } if (source & IRQ_REG) { - pr_err( "vortex: IRQ reg error\n"); + dev_err(vortex->card->dev, "IRQ reg error\n"); } if (source & IRQ_FIFO) { - pr_err( "vortex: IRQ fifo error\n"); + dev_err(vortex->card->dev, "IRQ fifo error\n"); } if (source & IRQ_DMA) { - pr_err( "vortex: IRQ dma error\n"); + dev_err(vortex->card->dev, "IRQ dma error\n"); } handled = 1; } @@ -2489,7 +2495,7 @@ static irqreturn_t vortex_interrupt(int irq, void *dev_id) } if (!handled) { - pr_err( "vortex: unknown irq source %x\n", source); + dev_err(vortex->card->dev, "unknown irq source %x\n", source); } return IRQ_RETVAL(handled); } @@ -2546,7 +2552,7 @@ vortex_codec_write(struct snd_ac97 * codec, unsigned short addr, unsigned short while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) { udelay(100); if (lifeboat++ > POLL_COUNT) { - pr_err( "vortex: ac97 codec stuck busy\n"); + dev_err(card->card->dev, "ac97 codec stuck busy\n"); return; } } @@ -2572,7 +2578,7 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short while (!(hwread(card->mmio, VORTEX_CODEC_CTRL) & 0x100)) { udelay(100); if (lifeboat++ > POLL_COUNT) { - pr_err( "vortex: ac97 codec stuck busy\n"); + dev_err(card->card->dev, "ac97 codec stuck busy\n"); return 0xffff; } } @@ -2586,7 +2592,8 @@ static unsigned short vortex_codec_read(struct snd_ac97 * codec, unsigned short udelay(100); data = hwread(card->mmio, VORTEX_CODEC_IO); if (lifeboat++ > POLL_COUNT) { - pr_err( "vortex: ac97 address never arrived\n"); + dev_err(card->card->dev, + "ac97 address never arrived\n"); return 0xffff; } } while ((data & VORTEX_CODEC_ADDMASK) != @@ -2683,7 +2690,7 @@ static void vortex_spdif_init(vortex_t * vortex, int spdif_sr, int spdif_mode) static int vortex_core_init(vortex_t *vortex) { - pr_info( "Vortex: init.... "); + dev_info(vortex->card->dev, "init started\n"); /* Hardware Init. */ hwwrite(vortex->mmio, VORTEX_CTRL, 0xffffffff); msleep(5); @@ -2728,7 +2735,7 @@ static int vortex_core_init(vortex_t *vortex) //vortex_enable_timer_int(vortex); //vortex_disable_timer_int(vortex); - pr_info( "done.\n"); + dev_info(vortex->card->dev, "init.... done.\n"); spin_lock_init(&vortex->lock); return 0; @@ -2737,7 +2744,7 @@ static int vortex_core_init(vortex_t *vortex) static int vortex_core_shutdown(vortex_t * vortex) { - pr_info( "Vortex: shutdown..."); + dev_info(vortex->card->dev, "shutdown started\n"); #ifndef CHIP_AU8820 vortex_eq_free(vortex); vortex_Vort3D_disable(vortex); @@ -2759,13 +2766,13 @@ static int vortex_core_shutdown(vortex_t * vortex) msleep(5); hwwrite(vortex->mmio, VORTEX_IRQ_SOURCE, 0xffff); - pr_info( "done.\n"); + dev_info(vortex->card->dev, "shutdown.... done.\n"); return 0; } /* Alsa support. */ -static int vortex_alsafmt_aspfmt(int alsafmt) +static int vortex_alsafmt_aspfmt(int alsafmt, vortex_t *v) { int fmt; @@ -2793,7 +2800,8 @@ static int vortex_alsafmt_aspfmt(int alsafmt) break; default: fmt = 0x8; - pr_err( "vortex: format unsupported %d\n", alsafmt); + dev_err(v->card->dev, + "format unsupported %d\n", alsafmt); break; } return fmt; diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c index 9404ba73eaf6..9585c5c63b96 100644 --- a/sound/pci/au88x0/au88x0_eq.c +++ b/sound/pci/au88x0/au88x0_eq.c @@ -845,7 +845,8 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u vortex_Eqlzr_GetAllPeaks(vortex, peaks, &count); if (count != 20) { - pr_err( "vortex: peak count error 20 != %d \n", count); + dev_err(vortex->card->dev, + "peak count error 20 != %d\n", count); return -1; } for (i = 0; i < 20; i++) diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index 72daf6cf8169..151815b857a0 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -98,7 +98,8 @@ static int vortex_gameport_register(vortex_t *vortex) vortex->gameport = gp = gameport_allocate_port(); if (!gp) { - pr_err( "vortex: cannot allocate memory for gameport\n"); + dev_err(vortex->card->dev, + "cannot allocate memory for gameport\n"); return -ENOMEM; } diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c index 328c1943c0c3..1025e55ca854 100644 --- a/sound/pci/au88x0/au88x0_mpu401.c +++ b/sound/pci/au88x0/au88x0_mpu401.c @@ -73,7 +73,7 @@ static int snd_vortex_midi(vortex_t *vortex) /* Check if anything is OK. */ temp = hwread(vortex->mmio, VORTEX_MIDI_DATA); if (temp != MPU401_ACK /*0xfe */ ) { - pr_err( "midi port doesn't acknowledge!\n"); + dev_err(vortex->card->dev, "midi port doesn't acknowledge!\n"); return -ENODEV; } /* Enable MPU401 interrupts. */ diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index 5adc6b92ffab..a6d6d8d0867a 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -227,7 +227,7 @@ snd_vortex_pcm_hw_params(struct snd_pcm_substream *substream, err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) { - pr_err( "Vortex: pcm page alloc failed!\n"); + dev_err(chip->card->dev, "Vortex: pcm page alloc failed!\n"); return err; } /* @@ -332,7 +332,7 @@ static int snd_vortex_pcm_prepare(struct snd_pcm_substream *substream) dir = 1; else dir = 0; - fmt = vortex_alsafmt_aspfmt(runtime->format); + fmt = vortex_alsafmt_aspfmt(runtime->format, chip); spin_lock_irq(&chip->lock); if (VORTEX_PCM_TYPE(substream->pcm) != VORTEX_PCM_WT) { vortex_adbdma_setmode(chip, dma, 1, dir, fmt, @@ -371,7 +371,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } #ifndef CHIP_AU8810 else { - pr_info( "vortex: wt start %d\n", dma); + dev_info(chip->card->dev, "wt start %d\n", dma); vortex_wtdma_startfifo(chip, dma); } #endif @@ -384,7 +384,7 @@ static int snd_vortex_pcm_trigger(struct snd_pcm_substream *substream, int cmd) vortex_adbdma_stopfifo(chip, dma); #ifndef CHIP_AU8810 else { - pr_info( "vortex: wt stop %d\n", dma); + dev_info(chip->card->dev, "wt stop %d\n", dma); vortex_wtdma_stopfifo(chip, dma); } #endif diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c index f094bac24291..78e12f4796f3 100644 --- a/sound/pci/au88x0/au88x0_synth.c +++ b/sound/pci/au88x0/au88x0_synth.c @@ -90,7 +90,7 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) hwwrite(vortex->mmio, WT_PARM(wt, 2), 0); temp = hwread(vortex->mmio, WT_PARM(wt, 3)); - pr_debug( "vortex: WT PARM3: %x\n", temp); + dev_dbg(vortex->card->dev, "WT PARM3: %x\n", temp); //hwwrite(vortex->mmio, WT_PARM(wt, 3), temp); hwwrite(vortex->mmio, WT_DELAY(wt, 0), 0); @@ -98,7 +98,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) hwwrite(vortex->mmio, WT_DELAY(wt, 2), 0); hwwrite(vortex->mmio, WT_DELAY(wt, 3), 0); - pr_debug( "vortex: WT GMODE: %x\n", hwread(vortex->mmio, WT_GMODE(wt))); + dev_dbg(vortex->card->dev, "WT GMODE: %x\n", + hwread(vortex->mmio, WT_GMODE(wt))); hwwrite(vortex->mmio, WT_PARM(wt, 2), 0xffffffff); hwwrite(vortex->mmio, WT_PARM(wt, 3), 0xcff1c810); @@ -106,7 +107,8 @@ static int vortex_wt_allocroute(vortex_t * vortex, int wt, int nr_ch) voice->parm0 = voice->parm1 = 0xcfb23e2f; hwwrite(vortex->mmio, WT_PARM(wt, 0), voice->parm0); hwwrite(vortex->mmio, WT_PARM(wt, 1), voice->parm1); - pr_debug( "vortex: WT GMODE 2 : %x\n", hwread(vortex->mmio, WT_GMODE(wt))); + dev_dbg(vortex->card->dev, "WT GMODE 2 : %x\n", + hwread(vortex->mmio, WT_GMODE(wt))); return 0; } @@ -196,14 +198,15 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, if ((reg == 5) || ((reg >= 7) && (reg <= 10)) || (reg == 0xc)) { if (wt >= (NR_WT / NR_WT_PB)) { - pr_warn - ("vortex: WT SetReg: bank out of range. reg=0x%x, wt=%d\n", - reg, wt); + dev_warn(vortex->card->dev, + "WT SetReg: bank out of range. reg=0x%x, wt=%d\n", + reg, wt); return 0; } } else { if (wt >= NR_WT) { - pr_err( "vortex: WT SetReg: voice out of range\n"); + dev_err(vortex->card->dev, + "WT SetReg: voice out of range\n"); return 0; } } diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c index 3878cf5de9a4..e1cf01949fda 100644 --- a/sound/pci/aw2/aw2-alsa.c +++ b/sound/pci/aw2/aw2-alsa.c @@ -725,19 +725,10 @@ static int snd_aw2_new_pcm(struct aw2 *chip) static int snd_aw2_control_switch_capture_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { + static const char * const texts[2] = { "Analog", "Digital" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) { - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - } - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_aw2_control_switch_capture_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 5a69e26cb2fb..fdbb9c05c77b 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1034,11 +1034,6 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, const char * const *p = NULL; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1; - uinfo->value.enumerated.items = reg.enum_c; - if (uinfo->value.enumerated.item > reg.enum_c - 1U) - uinfo->value.enumerated.item = reg.enum_c - 1U; if (reg.reg == IDX_MIXER_ADVCTL2) { switch(reg.lchan_shift) { case 8: /* modem out sel */ @@ -1051,12 +1046,12 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, p = texts4; break; } - } else - if (reg.reg == IDX_MIXER_REC_SELECT) + } else if (reg.reg == IDX_MIXER_REC_SELECT) p = texts3; - strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, + (reg.reg == IDX_MIXER_REC_SELECT) ? 2 : 1, + reg.enum_c, p); } static int diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 27de0de90018..68c0eb0a2807 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -185,17 +185,11 @@ static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol, static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[6] = { + static const char * const texts[6] = { "IEC958 out", "i2s mixer out", "IEC958 in", "i2s in", "AC97 in", "SRC out" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 6; - if (uinfo->value.enumerated.item > 5) - uinfo->value.enumerated.item = 5; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 6, texts); } static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol, @@ -228,17 +222,11 @@ static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol, static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[6] = { + static const char * const texts[4] = { "Phone", "Mic", "Line in", "Aux" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, @@ -273,29 +261,17 @@ static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "Side out", "Line in" }; + static const char * const texts[2] = { "Side out", "Line in" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { "Line in", "Mic in" }; + static const char * const texts[2] = { "Line in", "Mic in" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c index 454659074390..977a59855fa6 100644 --- a/sound/pci/ctxfi/ctatc.c +++ b/sound/pci/ctxfi/ctatc.c @@ -438,7 +438,9 @@ atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm) position = src->ops->get_ca(src); if (position < apcm->vm_block->addr) { - snd_printdd("ctxfi: bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n", position, apcm->vm_block->addr, apcm->vm_block->size); + dev_dbg(atc->card->dev, + "bad ca - ca=0x%08x, vba=0x%08x, vbs=0x%08x\n", + position, apcm->vm_block->addr, apcm->vm_block->size); position = apcm->vm_block->addr; } @@ -1145,7 +1147,6 @@ static int atc_release_resources(struct ct_atc *atc) int i; struct daio_mgr *daio_mgr = NULL; struct dao *dao = NULL; - struct dai *dai = NULL; struct daio *daio = NULL; struct sum_mgr *sum_mgr = NULL; struct src_mgr *src_mgr = NULL; @@ -1172,9 +1173,6 @@ static int atc_release_resources(struct ct_atc *atc) dao = container_of(daio, struct dao, daio); dao->ops->clear_left_input(dao); dao->ops->clear_right_input(dao); - } else { - dai = container_of(daio, struct dai, daio); - /* some thing to do for dai ... */ } daio_mgr->put_daio(daio_mgr, daio); } @@ -1299,7 +1297,7 @@ static int atc_identify_card(struct ct_atc *atc, unsigned int ssid) atc->model = CT20K2_UNKNOWN; } atc->model_name = ct_subsys_name[atc->model]; - snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n", + dev_info(atc->card->dev, "chip %s model %s (%04x:%04x) is found\n", atc->chip_name, atc->model_name, vendor_id, device_id); return 0; diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index c1c3f8816fff..9b87dd28de83 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -528,8 +528,6 @@ static int get_daio_rsc(struct daio_mgr *mgr, struct daio **rdaio) { int err; - struct dai *dai = NULL; - struct dao *dao = NULL; unsigned long flags; *rdaio = NULL; @@ -544,27 +542,30 @@ static int get_daio_rsc(struct daio_mgr *mgr, return err; } + err = -ENOMEM; /* Allocate mem for daio resource */ if (desc->type <= DAIO_OUT_MAX) { - dao = kzalloc(sizeof(*dao), GFP_KERNEL); - if (!dao) { - err = -ENOMEM; + struct dao *dao = kzalloc(sizeof(*dao), GFP_KERNEL); + if (!dao) goto error; - } + err = dao_rsc_init(dao, desc, mgr); - if (err) + if (err) { + kfree(dao); goto error; + } *rdaio = &dao->daio; } else { - dai = kzalloc(sizeof(*dai), GFP_KERNEL); - if (!dai) { - err = -ENOMEM; + struct dai *dai = kzalloc(sizeof(*dai), GFP_KERNEL); + if (!dai) goto error; - } + err = dai_rsc_init(dai, desc, mgr); - if (err) + if (err) { + kfree(dai); goto error; + } *rdaio = &dai->daio; } @@ -575,11 +576,6 @@ static int get_daio_rsc(struct daio_mgr *mgr, return 0; error: - if (dao) - kfree(dao); - else if (dai) - kfree(dai); - spin_lock_irqsave(&mgr->mgr_lock, flags); daio_mgr_put_rsc(&mgr->mgr, desc->type); spin_unlock_irqrestore(&mgr->mgr_lock, flags); diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c index 03fb909085af..a5d460453d7b 100644 --- a/sound/pci/ctxfi/cttimer.c +++ b/sound/pci/ctxfi/cttimer.c @@ -421,12 +421,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc) atimer->atc = atc; hw = atc->hw; if (!use_system_timer && hw->set_timer_irq) { - snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n"); + dev_info(atc->card->dev, "Use xfi-native timer\n"); atimer->ops = &ct_xfitimer_ops; hw->irq_callback_data = atimer; hw->irq_callback = ct_timer_interrupt; } else { - snd_printd(KERN_INFO "ctxfi: Use system timer\n"); + dev_info(atc->card->dev, "Use system timer\n"); atimer->ops = &ct_systimer_ops; } return atimer; diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c index 20c7cbc89bb3..febee5bda877 100644 --- a/sound/pci/echoaudio/darla20_dsp.c +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -33,12 +33,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Darla20\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA20)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw: could not initialize DSP comm page\n"); return err; } @@ -57,7 +57,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c index 6da6663e9176..7b4f6fd51b09 100644 --- a/sound/pci/echoaudio/darla24_dsp.c +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -33,12 +33,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Darla24\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != DARLA24)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw: could not initialize DSP comm page\n"); return err; } @@ -56,7 +56,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -128,15 +127,17 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) clock = GD24_8000; break; default: - DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n", - rate)); + dev_err(chip->card->dev, + "set_sample_rate: Error, invalid sample rate %d\n", + rate); return -EINVAL; } if (wait_handshake(chip)) return -EIO; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, clock); chip->sample_rate = rate; /* Override the sample rate if this card is set to Echo sync. */ diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c index 3cdc2ee2d1dd..ae11ce11b1c2 100644 --- a/sound/pci/echoaudio/echo3g_dsp.c +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -46,12 +46,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) int err; local_irq_enable(); - DE_INIT(("init_hw() - Echo3G\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != ECHO3G)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -98,7 +98,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | ECHOCAPS_HAS_DIGITAL_MODE_ADAT; - DE_INIT(("init_hw done\n")); return err; } diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index 631aaa4046ad..21228adaa70c 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -48,13 +48,16 @@ static int get_firmware(const struct firmware **fw_entry, #ifdef CONFIG_PM_SLEEP if (chip->fw_cache[fw_index]) { - DE_ACT(("firmware requested: %s is cached\n", card_fw[fw_index].data)); + dev_dbg(chip->card->dev, + "firmware requested: %s is cached\n", + card_fw[fw_index].data); *fw_entry = chip->fw_cache[fw_index]; return 0; } #endif - DE_ACT(("firmware requested: %s\n", card_fw[fw_index].data)); + dev_dbg(chip->card->dev, + "firmware requested: %s\n", card_fw[fw_index].data); snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data); err = request_firmware(fw_entry, name, pci_device(chip)); if (err < 0) @@ -69,13 +72,13 @@ static int get_firmware(const struct firmware **fw_entry, -static void free_firmware(const struct firmware *fw_entry) +static void free_firmware(const struct firmware *fw_entry, + struct echoaudio *chip) { #ifdef CONFIG_PM_SLEEP - DE_ACT(("firmware not released (kept in cache)\n")); + dev_dbg(chip->card->dev, "firmware not released (kept in cache)\n"); #else release_firmware(fw_entry); - DE_ACT(("firmware released\n")); #endif } @@ -89,10 +92,9 @@ static void free_firmware_cache(struct echoaudio *chip) for (i = 0; i < 8 ; i++) if (chip->fw_cache[i]) { release_firmware(chip->fw_cache[i]); - DE_ACT(("release_firmware(%d)\n", i)); + dev_dbg(chip->card->dev, "release_firmware(%d)\n", i); } - DE_ACT(("firmware_cache released\n")); #endif } @@ -286,7 +288,7 @@ static int pcm_open(struct snd_pcm_substream *substream, /* Set up hw capabilities and contraints */ memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware)); - DE_HWP(("max_channels=%d\n", max_channels)); + dev_dbg(chip->card->dev, "max_channels=%d\n", max_channels); pipe->constr.list = channels_list; pipe->constr.mask = 0; for (i = 0; channels_list[i] <= max_channels; i++); @@ -336,7 +338,7 @@ static int pcm_open(struct snd_pcm_substream *substream, if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), PAGE_SIZE, &pipe->sgpage)) < 0) { - DE_HWP(("s-g list allocation failed\n")); + dev_err(chip->card->dev, "s-g list allocation failed\n"); return err; } @@ -350,7 +352,6 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err; - DE_ACT(("pcm_analog_in_open\n")); if ((err = pcm_open(substream, num_analog_busses_in(chip) - substream->number)) < 0) return err; @@ -367,9 +368,9 @@ static int pcm_analog_in_open(struct snd_pcm_substream *substream) atomic_inc(&chip->opencount); if (atomic_read(&chip->opencount) > 1 && chip->rate_set) chip->can_set_rate=0; - DE_HWP(("pcm_analog_in_open cs=%d oc=%d r=%d\n", + dev_dbg(chip->card->dev, "pcm_analog_in_open cs=%d oc=%d r=%d\n", chip->can_set_rate, atomic_read(&chip->opencount), - chip->sample_rate)); + chip->sample_rate); return 0; } @@ -385,7 +386,6 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream) #else max_channels = num_analog_busses_out(chip); #endif - DE_ACT(("pcm_analog_out_open\n")); if ((err = pcm_open(substream, max_channels - substream->number)) < 0) return err; if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, @@ -403,9 +403,9 @@ static int pcm_analog_out_open(struct snd_pcm_substream *substream) atomic_inc(&chip->opencount); if (atomic_read(&chip->opencount) > 1 && chip->rate_set) chip->can_set_rate=0; - DE_HWP(("pcm_analog_out_open cs=%d oc=%d r=%d\n", + dev_dbg(chip->card->dev, "pcm_analog_out_open cs=%d oc=%d r=%d\n", chip->can_set_rate, atomic_read(&chip->opencount), - chip->sample_rate)); + chip->sample_rate); return 0; } @@ -418,7 +418,6 @@ static int pcm_digital_in_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err, max_channels; - DE_ACT(("pcm_digital_in_open\n")); max_channels = num_digital_busses_in(chip) - substream->number; mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) @@ -460,7 +459,6 @@ static int pcm_digital_out_open(struct snd_pcm_substream *substream) struct echoaudio *chip = snd_pcm_substream_chip(substream); int err, max_channels; - DE_ACT(("pcm_digital_out_open\n")); max_channels = num_digital_busses_out(chip) - substream->number; mutex_lock(&chip->mode_mutex); if (chip->digital_mode == DIGITAL_MODE_ADAT) @@ -507,18 +505,17 @@ static int pcm_close(struct snd_pcm_substream *substream) /* Nothing to do here. Audio is already off and pipe will be * freed by its callback */ - DE_ACT(("pcm_close\n")); atomic_dec(&chip->opencount); oc = atomic_read(&chip->opencount); - DE_ACT(("pcm_close oc=%d cs=%d rs=%d\n", oc, - chip->can_set_rate, chip->rate_set)); + dev_dbg(chip->card->dev, "pcm_close oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate, chip->rate_set); if (oc < 2) chip->can_set_rate = 1; if (oc == 0) chip->rate_set = 0; - DE_ACT(("pcm_close2 oc=%d cs=%d rs=%d\n", oc, - chip->can_set_rate,chip->rate_set)); + dev_dbg(chip->card->dev, "pcm_close2 oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate, chip->rate_set); return 0; } @@ -542,7 +539,7 @@ static int init_engine(struct snd_pcm_substream *substream, */ spin_lock_irq(&chip->lock); if (pipe->index >= 0) { - DE_HWP(("hwp_ie free(%d)\n", pipe->index)); + dev_dbg(chip->card->dev, "hwp_ie free(%d)\n", pipe->index); err = free_pipes(chip, pipe); snd_BUG_ON(err); chip->substream[pipe->index] = NULL; @@ -551,16 +548,17 @@ static int init_engine(struct snd_pcm_substream *substream, err = allocate_pipes(chip, pipe, pipe_index, interleave); if (err < 0) { spin_unlock_irq(&chip->lock); - DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n", - pipe_index, err)); + dev_err(chip->card->dev, "allocate_pipes(%d) err=%d\n", + pipe_index, err); return err; } spin_unlock_irq(&chip->lock); - DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index)); + dev_dbg(chip->card->dev, "allocate_pipes()=%d\n", pipe_index); - DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n", + dev_dbg(chip->card->dev, + "pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n", params_buffer_bytes(hw_params), params_periods(hw_params), - params_period_bytes(hw_params))); + params_period_bytes(hw_params)); err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) { @@ -615,7 +613,6 @@ static int init_engine(struct snd_pcm_substream *substream, spin_lock_irq(&chip->lock); set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den); spin_unlock_irq(&chip->lock); - DE_HWP(("pcm_hw_params ok\n")); return 0; } @@ -679,14 +676,13 @@ static int pcm_hw_free(struct snd_pcm_substream *substream) spin_lock_irq(&chip->lock); if (pipe->index >= 0) { - DE_HWP(("pcm_hw_free(%d)\n", pipe->index)); + dev_dbg(chip->card->dev, "pcm_hw_free(%d)\n", pipe->index); free_pipes(chip, pipe); chip->substream[pipe->index] = NULL; pipe->index = -1; } spin_unlock_irq(&chip->lock); - DE_HWP(("pcm_hw_freed\n")); snd_pcm_lib_free_pages(substream); return 0; } @@ -700,8 +696,8 @@ static int pcm_prepare(struct snd_pcm_substream *substream) struct audioformat format; int pipe_index = ((struct audiopipe *)runtime->private_data)->index; - DE_HWP(("Prepare rate=%d format=%d channels=%d\n", - runtime->rate, runtime->format, runtime->channels)); + dev_dbg(chip->card->dev, "Prepare rate=%d format=%d channels=%d\n", + runtime->rate, runtime->format, runtime->channels); format.interleave = runtime->channels; format.data_are_bigendian = 0; format.mono_to_stereo = 0; @@ -721,8 +717,9 @@ static int pcm_prepare(struct snd_pcm_substream *substream) format.bits_per_sample = 32; break; default: - DE_HWP(("Prepare error: unsupported format %d\n", - runtime->format)); + dev_err(chip->card->dev, + "Prepare error: unsupported format %d\n", + runtime->format); return -EINVAL; } @@ -757,10 +754,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) spin_lock(&chip->lock); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: - DE_ACT(("pcm_trigger resume\n")); case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - DE_ACT(("pcm_trigger start\n")); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -782,9 +777,7 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) chip->pipe_cyclic_mask); break; case SNDRV_PCM_TRIGGER_SUSPEND: - DE_ACT(("pcm_trigger suspend\n")); case SNDRV_PCM_TRIGGER_STOP: - DE_ACT(("pcm_trigger stop\n")); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -794,7 +787,6 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) err = stop_transport(chip, channelmask); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - DE_ACT(("pcm_trigger pause\n")); for (i = 0; i < DSP_MAXPIPES; i++) { if (channelmask & (1 << i)) { pipe = chip->substream[i]->runtime->private_data; @@ -931,7 +923,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Analog PCM ok\n")); #ifdef ECHOCARD_HAS_DIGITAL_IO /* PCM#1 Digital inputs, no outputs */ @@ -944,7 +935,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Digital PCM ok\n")); #endif /* ECHOCARD_HAS_DIGITAL_IO */ #else /* ECHOCARD_HAS_VMIXER */ @@ -966,7 +956,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Analog PCM ok\n")); #ifdef ECHOCARD_HAS_DIGITAL_IO /* PCM#1 Digital i/o */ @@ -981,7 +970,6 @@ static int snd_echo_new_pcm(struct echoaudio *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) return err; - DE_INIT(("Digital PCM ok\n")); #endif /* ECHOCARD_HAS_DIGITAL_IO */ #endif /* ECHOCARD_HAS_VMIXER */ @@ -1416,21 +1404,14 @@ static struct snd_kcontrol_new snd_echo_vmixer = { static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *names[4] = { + static const char * const names[4] = { "S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical", "S/PDIF Cdrom" }; struct echoaudio *chip; chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = chip->num_digital_modes; - uinfo->count = 1; - if (uinfo->value.enumerated.item >= chip->num_digital_modes) - uinfo->value.enumerated.item = chip->num_digital_modes - 1; - strcpy(uinfo->value.enumerated.name, names[ - chip->digital_mode_list[uinfo->value.enumerated.item]]); - return 0; + return snd_ctl_enum_info(uinfo, 1, chip->num_digital_modes, names); } static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol, @@ -1481,7 +1462,8 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->clock_src_ctl->id); - DE_ACT(("SDM() =%d\n", changed)); + dev_dbg(chip->card->dev, + "SDM() =%d\n", changed); } if (changed >= 0) changed = 1; /* No errors */ @@ -1509,16 +1491,9 @@ static struct snd_kcontrol_new snd_echo_digital_mode_switch = { static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *names[2] = {"Consumer", "Professional"}; + static const char * const names[2] = {"Consumer", "Professional"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = 2; - uinfo->count = 1; - if (uinfo->value.enumerated.item) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - names[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, names); } static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol, @@ -1566,21 +1541,14 @@ static struct snd_kcontrol_new snd_echo_spdif_mode_switch = { static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *names[8] = { + static const char * const names[8] = { "Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync", "ESync96", "MTC" }; struct echoaudio *chip; chip = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->value.enumerated.items = chip->num_clock_sources; - uinfo->count = 1; - if (uinfo->value.enumerated.item >= chip->num_clock_sources) - uinfo->value.enumerated.item = chip->num_clock_sources - 1; - strcpy(uinfo->value.enumerated.name, names[ - chip->clock_source_list[uinfo->value.enumerated.item]]); - return 0; + return snd_ctl_enum_info(uinfo, 1, chip->num_clock_sources, names); } static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol, @@ -1622,7 +1590,8 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol, } if (changed < 0) - DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed)); + dev_dbg(chip->card->dev, + "seticlk val%d err 0x%x\n", dclock, changed); return changed; } @@ -1879,7 +1848,7 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) #ifdef ECHOCARD_HAS_MIDI if (st > 0 && chip->midi_in) { snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st); - DE_MID(("rawmidi_iread=%d\n", st)); + dev_dbg(chip->card->dev, "rawmidi_iread=%d\n", st); } #endif return IRQ_HANDLED; @@ -1894,10 +1863,8 @@ static irqreturn_t snd_echo_interrupt(int irq, void *dev_id) static int snd_echo_free(struct echoaudio *chip) { - DE_INIT(("Stop DSP...\n")); if (chip->comm_page) rest_in_peace(chip); - DE_INIT(("Stopped.\n")); if (chip->irq >= 0) free_irq(chip->irq, chip); @@ -1908,17 +1875,14 @@ static int snd_echo_free(struct echoaudio *chip) if (chip->dsp_registers) iounmap(chip->dsp_registers); - if (chip->iores) - release_and_free_resource(chip->iores); + release_and_free_resource(chip->iores); - DE_INIT(("MMIO freed.\n")); pci_disable_device(chip->pci); /* release chip data */ free_firmware_cache(chip); kfree(chip); - DE_INIT(("Chip freed.\n")); return 0; } @@ -1928,7 +1892,6 @@ static int snd_echo_dev_free(struct snd_device *device) { struct echoaudio *chip = device->device_data; - DE_INIT(("snd_echo_dev_free()...\n")); return snd_echo_free(chip); } @@ -1961,7 +1924,7 @@ static int snd_echo_create(struct snd_card *card, pci_disable_device(pci); return -ENOMEM; } - DE_INIT(("chip=%p\n", chip)); + dev_dbg(card->dev, "chip=%p\n", chip); spin_lock_init(&chip->lock); chip->card = card; chip->pci = pci; @@ -1998,8 +1961,8 @@ static int snd_echo_create(struct snd_card *card, return -EBUSY; } chip->irq = pci->irq; - DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n", - chip->pci, chip->irq, chip->pci->subsystem_device)); + dev_dbg(card->dev, "pci=%p irq=%d subdev=%04x Init hardware...\n", + chip->pci, chip->irq, chip->pci->subsystem_device); /* Create the DSP comm page - this is the area of memory used for most of the communication with the DSP, which accesses it via bus mastering */ @@ -2017,11 +1980,10 @@ static int snd_echo_create(struct snd_card *card, if (err >= 0) err = set_mixer_defaults(chip); if (err < 0) { - DE_INIT(("init_hw err=%d\n", err)); + dev_err(card->dev, "init_hw err=%d\n", err); snd_echo_free(chip); return err; } - DE_INIT(("Card init OK\n")); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_echo_free(chip); @@ -2051,7 +2013,6 @@ static int snd_echo_probe(struct pci_dev *pci, return -ENOENT; } - DE_INIT(("Echoaudio driver starting...\n")); i = 0; err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); @@ -2204,7 +2165,6 @@ static int snd_echo_suspend(struct device *dev) struct pci_dev *pci = to_pci_dev(dev); struct echoaudio *chip = dev_get_drvdata(dev); - DE_INIT(("suspend start\n")); snd_pcm_suspend_all(chip->analog_pcm); snd_pcm_suspend_all(chip->digital_pcm); @@ -2231,7 +2191,6 @@ static int snd_echo_suspend(struct device *dev) pci_save_state(pci); pci_disable_device(pci); - DE_INIT(("suspend done\n")); return 0; } @@ -2245,7 +2204,6 @@ static int snd_echo_resume(struct device *dev) u32 pipe_alloc_mask; int err; - DE_INIT(("resume start\n")); pci_restore_state(pci); commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL); if (commpage_bak == NULL) @@ -2256,11 +2214,10 @@ static int snd_echo_resume(struct device *dev) err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); if (err < 0) { kfree(commpage_bak); - DE_INIT(("resume init_hw err=%d\n", err)); + dev_err(dev, "resume init_hw err=%d\n", err); snd_echo_free(chip); return err; } - DE_INIT(("resume init OK\n")); /* Temporarily set chip->pipe_alloc_mask=0 otherwise * restore_dsp_settings() fails. @@ -2273,7 +2230,6 @@ static int snd_echo_resume(struct device *dev) kfree(commpage_bak); return err; } - DE_INIT(("resume restore OK\n")); memcpy(&commpage->audio_format, &commpage_bak->audio_format, sizeof(commpage->audio_format)); @@ -2290,7 +2246,7 @@ static int snd_echo_resume(struct device *dev) return -EBUSY; } chip->irq = pci->irq; - DE_INIT(("resume irq=%d\n", chip->irq)); + dev_dbg(dev, "resume irq=%d\n", chip->irq); #ifdef ECHOCARD_HAS_MIDI if (chip->midi_input_enabled) @@ -2299,7 +2255,6 @@ static int snd_echo_resume(struct device *dev) snd_echo_midi_output_trigger(chip->midi_out, 1); #endif - DE_INIT(("resume done\n")); return 0; } diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h index b86b88da81cd..32515227a692 100644 --- a/sound/pci/echoaudio/echoaudio.h +++ b/sound/pci/echoaudio/echoaudio.h @@ -295,34 +295,6 @@ #define PIPE_STATE_PENDING 3 /* Pipe has pending start */ -/* Debug initialization */ -#ifdef CONFIG_SND_DEBUG -#define DE_INIT(x) snd_printk x -#else -#define DE_INIT(x) -#endif - -/* Debug hw_params callbacks */ -#ifdef CONFIG_SND_DEBUG -#define DE_HWP(x) snd_printk x -#else -#define DE_HWP(x) -#endif - -/* Debug normal activity (open, start, stop...) */ -#ifdef CONFIG_SND_DEBUG -#define DE_ACT(x) snd_printk x -#else -#define DE_ACT(x) -#endif - -/* Debug midi activity */ -#ifdef CONFIG_SND_DEBUG -#define DE_MID(x) snd_printk x -#else -#define DE_MID(x) -#endif - struct audiopipe { volatile u32 *dma_counter; /* Commpage register that contains @@ -468,7 +440,8 @@ static int wait_handshake(struct echoaudio *chip); static int send_vector(struct echoaudio *chip, u32 command); static int get_firmware(const struct firmware **fw_entry, struct echoaudio *chip, const short fw_index); -static void free_firmware(const struct firmware *fw_entry); +static void free_firmware(const struct firmware *fw_entry, + struct echoaudio *chip); #ifdef ECHOCARD_HAS_MIDI static int enable_midi_input(struct echoaudio *chip, char enable); diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c index 658db44ef746..2fa66dc3e675 100644 --- a/sound/pci/echoaudio/echoaudio_3g.c +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -51,7 +51,7 @@ static int check_asic_status(struct echoaudio *chip) } box_status = le32_to_cpu(chip->comm_page->ext_box_status); - DE_INIT(("box_status=%x\n", box_status)); + dev_dbg(chip->card->dev, "box_status=%x\n", box_status); if (box_status == E3G_ASIC_NOT_LOADED) return -ENODEV; @@ -76,7 +76,8 @@ static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, if (wait_handshake(chip)) return -EIO; - DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq)); + dev_dbg(chip->card->dev, + "WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq); ctl = cpu_to_le32(ctl); frq = cpu_to_le32(frq); @@ -89,7 +90,7 @@ static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, return send_vector(chip, DSP_VC_WRITE_CONTROL_REG); } - DE_ACT(("WriteControlReg: not written, no change\n")); + dev_dbg(chip->card->dev, "WriteControlReg: not written, no change\n"); return 0; } @@ -258,8 +259,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -313,7 +314,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ chip->sample_rate = rate; - DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg)); + dev_dbg(chip->card->dev, + "SetSampleRate: %d clock %x\n", rate, control_reg); /* Tell the DSP about it - DSP reads both control reg & freq reg */ return write_control_reg(chip, control_reg, frq_reg, 0); @@ -326,7 +328,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) { u32 control_reg, clocks_from_dsp; - DE_ACT(("set_input_clock:\n")); /* Mask off the clock select bits */ control_reg = le32_to_cpu(chip->comm_page->control_register) & @@ -335,13 +336,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Echo3G clock to INTERNAL\n")); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: if (chip->digital_mode == DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Echo3G clock to SPDIF\n")); control_reg |= E3G_SPDIF_CLOCK; if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96) control_reg |= E3G_DOUBLE_SPEED_MODE; @@ -351,12 +350,10 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Echo3G clock to ADAT\n")); control_reg |= E3G_ADAT_CLOCK; control_reg &= ~E3G_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_WORD: - DE_ACT(("Set Echo3G clock to WORD\n")); control_reg |= E3G_WORD_CLOCK; if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96) control_reg |= E3G_DOUBLE_SPEED_MODE; @@ -364,7 +361,8 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg &= ~E3G_DOUBLE_SPEED_MODE; break; default: - DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Echo3G\n", clock); return -EINVAL; } @@ -392,7 +390,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) incompatible_clock = TRUE; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -427,6 +426,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode)); + dev_dbg(chip->card->dev, "set_digital_mode(%d)\n", chip->digital_mode); return incompatible_clock; } diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c index 5a6a217b82e0..1a9427aabe1d 100644 --- a/sound/pci/echoaudio/echoaudio_dsp.c +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -80,7 +80,7 @@ static int send_vector(struct echoaudio *chip, u32 command) udelay(1); } - DE_ACT((KERN_ERR "timeout on send_vector\n")); + dev_err(chip->card->dev, "timeout on send_vector\n"); return -EBUSY; } @@ -104,7 +104,7 @@ static int write_dsp(struct echoaudio *chip, u32 data) } chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ - DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n")); + dev_dbg(chip->card->dev, "write_dsp: Set bad_board to TRUE\n"); return -EIO; } @@ -127,7 +127,7 @@ static int read_dsp(struct echoaudio *chip, u32 *data) } chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ - DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n")); + dev_err(chip->card->dev, "read_dsp: Set bad_board to TRUE\n"); return -EIO; } @@ -154,8 +154,9 @@ static int read_sn(struct echoaudio *chip) return -EIO; } } - DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n", - sn[0], sn[1], sn[2], sn[3], sn[4])); + dev_dbg(chip->card->dev, + "Read serial number %08x %08x %08x %08x %08x\n", + sn[0], sn[1], sn[2], sn[3], sn[4]); return 0; } @@ -205,13 +206,12 @@ static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic) goto la_error; } - DE_INIT(("ASIC loaded\n")); - free_firmware(fw); + free_firmware(fw, chip); return 0; la_error: - DE_INIT(("failed on write_dsp\n")); - free_firmware(fw); + dev_err(chip->card->dev, "failed on write_dsp\n"); + free_firmware(fw, chip); return -EIO; } @@ -241,8 +241,9 @@ static int install_resident_loader(struct echoaudio *chip) loader is already installed, host flag 5 will be on. */ status = get_dsp_register(chip, CHI32_STATUS_REG); if (status & CHI32_STATUS_REG_HF5) { - DE_INIT(("Resident loader already installed; status is 0x%x\n", - status)); + dev_dbg(chip->card->dev, + "Resident loader already installed; status is 0x%x\n", + status); return 0; } @@ -283,12 +284,14 @@ static int install_resident_loader(struct echoaudio *chip) /* Write the count to the DSP */ if (write_dsp(chip, words)) { - DE_INIT(("install_resident_loader: Failed to write word count!\n")); + dev_err(chip->card->dev, + "install_resident_loader: Failed to write word count!\n"); goto irl_error; } /* Write the DSP address */ if (write_dsp(chip, address)) { - DE_INIT(("install_resident_loader: Failed to write DSP address!\n")); + dev_err(chip->card->dev, + "install_resident_loader: Failed to write DSP address!\n"); goto irl_error; } /* Write out this block of code to the DSP */ @@ -297,7 +300,8 @@ static int install_resident_loader(struct echoaudio *chip) data = ((u32)code[index] << 16) + code[index + 1]; if (write_dsp(chip, data)) { - DE_INIT(("install_resident_loader: Failed to write DSP code\n")); + dev_err(chip->card->dev, + "install_resident_loader: Failed to write DSP code\n"); goto irl_error; } index += 2; @@ -312,16 +316,16 @@ static int install_resident_loader(struct echoaudio *chip) } if (i == 200) { - DE_INIT(("Resident loader failed to set HF5\n")); + dev_err(chip->card->dev, "Resident loader failed to set HF5\n"); goto irl_error; } - DE_INIT(("Resident loader successfully installed\n")); - free_firmware(fw); + dev_dbg(chip->card->dev, "Resident loader successfully installed\n"); + free_firmware(fw, chip); return 0; irl_error: - free_firmware(fw); + free_firmware(fw, chip); return -EIO; } @@ -334,14 +338,14 @@ static int load_dsp(struct echoaudio *chip, u16 *code) int index, words, i; if (chip->dsp_code == code) { - DE_INIT(("DSP is already loaded!\n")); + dev_warn(chip->card->dev, "DSP is already loaded!\n"); return 0; } chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ chip->dsp_code = NULL; /* Current DSP code not loaded */ chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */ - DE_INIT(("load_dsp: Set bad_board to TRUE\n")); + dev_dbg(chip->card->dev, "load_dsp: Set bad_board to TRUE\n"); /* If this board requires a resident loader, install it. */ #ifdef DSP_56361 @@ -351,7 +355,8 @@ static int load_dsp(struct echoaudio *chip, u16 *code) /* Send software reset command */ if (send_vector(chip, DSP_VC_RESET) < 0) { - DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n")); + dev_err(chip->card->dev, + "LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"); return -EIO; } /* Delay 10us */ @@ -366,7 +371,8 @@ static int load_dsp(struct echoaudio *chip, u16 *code) } if (i == 1000) { - DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n")); + dev_err(chip->card->dev, + "load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"); return -EIO; } @@ -403,29 +409,34 @@ static int load_dsp(struct echoaudio *chip, u16 *code) index += 2; if (write_dsp(chip, words) < 0) { - DE_INIT(("load_dsp: failed to write number of DSP words\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write number of DSP words\n"); return -EIO; } if (write_dsp(chip, address) < 0) { - DE_INIT(("load_dsp: failed to write DSP address\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write DSP address\n"); return -EIO; } if (write_dsp(chip, mem_type) < 0) { - DE_INIT(("load_dsp: failed to write DSP memory type\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write DSP memory type\n"); return -EIO; } /* Code */ for (i = 0; i < words; i++, index+=2) { data = ((u32)code[index] << 16) + code[index + 1]; if (write_dsp(chip, data) < 0) { - DE_INIT(("load_dsp: failed to write DSP data\n")); + dev_err(chip->card->dev, + "load_dsp: failed to write DSP data\n"); return -EIO; } } } if (write_dsp(chip, 0) < 0) { /* We're done!!! */ - DE_INIT(("load_dsp: Failed to write final zero\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to write final zero\n"); return -EIO; } udelay(10); @@ -438,12 +449,14 @@ static int load_dsp(struct echoaudio *chip, u16 *code) get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00); if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) { - DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"); return -EIO; } if (write_dsp(chip, chip->comm_page_phys) < 0) { - DE_INIT(("load_dsp: Failed to write comm page address\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to write comm page address\n"); return -EIO; } @@ -452,19 +465,20 @@ static int load_dsp(struct echoaudio *chip, u16 *code) We don't actually use the serial number but we have to get it as part of the DSP init voodoo. */ if (read_sn(chip) < 0) { - DE_INIT(("load_dsp: Failed to read serial number\n")); + dev_err(chip->card->dev, + "load_dsp: Failed to read serial number\n"); return -EIO; } chip->dsp_code = code; /* Show which DSP code loaded */ chip->bad_board = FALSE; /* DSP OK */ - DE_INIT(("load_dsp: OK!\n")); return 0; } udelay(100); } - DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n")); + dev_err(chip->card->dev, + "load_dsp: DSP load timed out waiting for HF4\n"); return -EIO; } @@ -491,7 +505,7 @@ static int load_firmware(struct echoaudio *chip) if (err < 0) return err; err = load_dsp(chip, (u16 *)fw->data); - free_firmware(fw); + free_firmware(fw, chip); if (err < 0) return err; @@ -658,7 +672,6 @@ static void get_audio_meters(struct echoaudio *chip, long *meters) static int restore_dsp_rettings(struct echoaudio *chip) { int i, o, err; - DE_INIT(("restore_dsp_settings\n")); if ((err = check_asic_status(chip)) < 0) return err; @@ -755,7 +768,6 @@ static int restore_dsp_rettings(struct echoaudio *chip) if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0) return -EIO; - DE_INIT(("restore_dsp_rettings done\n")); return 0; } @@ -835,7 +847,8 @@ static void set_audio_format(struct echoaudio *chip, u16 pipe_index, break; } } - DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format)); + dev_dbg(chip->card->dev, + "set_audio_format[%d] = %x\n", pipe_index, dsp_format); chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format); } @@ -848,7 +861,6 @@ Same thing for pause_ and stop_ -trasport below. */ static int start_transport(struct echoaudio *chip, u32 channel_mask, u32 cyclic_mask) { - DE_ACT(("start_transport %x\n", channel_mask)); if (wait_handshake(chip)) return -EIO; @@ -866,7 +878,7 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask, return 0; } - DE_ACT(("start_transport: No pipes to start!\n")); + dev_err(chip->card->dev, "start_transport: No pipes to start!\n"); return -EINVAL; } @@ -874,7 +886,6 @@ static int start_transport(struct echoaudio *chip, u32 channel_mask, static int pause_transport(struct echoaudio *chip, u32 channel_mask) { - DE_ACT(("pause_transport %x\n", channel_mask)); if (wait_handshake(chip)) return -EIO; @@ -893,7 +904,7 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask) return 0; } - DE_ACT(("pause_transport: No pipes to stop!\n")); + dev_warn(chip->card->dev, "pause_transport: No pipes to stop!\n"); return 0; } @@ -901,7 +912,6 @@ static int pause_transport(struct echoaudio *chip, u32 channel_mask) static int stop_transport(struct echoaudio *chip, u32 channel_mask) { - DE_ACT(("stop_transport %x\n", channel_mask)); if (wait_handshake(chip)) return -EIO; @@ -920,7 +930,7 @@ static int stop_transport(struct echoaudio *chip, u32 channel_mask) return 0; } - DE_ACT(("stop_transport: No pipes to stop!\n")); + dev_warn(chip->card->dev, "stop_transport: No pipes to stop!\n"); return 0; } @@ -937,7 +947,6 @@ static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index) stopped and unallocated. */ static int rest_in_peace(struct echoaudio *chip) { - DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask)); /* Stops all active pipes (just to be sure) */ stop_transport(chip, chip->active_mask); @@ -965,7 +974,8 @@ static int init_dsp_comm_page(struct echoaudio *chip) { /* Check if the compiler added extra padding inside the structure */ if (offsetof(struct comm_page, midi_output) != 0xbe0) { - DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n")); + dev_err(chip->card->dev, + "init_dsp_comm_page() - Invalid struct comm_page structure\n"); return -EPERM; } @@ -999,7 +1009,6 @@ static int init_dsp_comm_page(struct echoaudio *chip) */ static int init_line_levels(struct echoaudio *chip) { - DE_INIT(("init_line_levels\n")); memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain)); memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain)); memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain)); @@ -1051,7 +1060,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, u32 channel_mask; char is_cyclic; - DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave)); + dev_dbg(chip->card->dev, + "allocate_pipes: ch=%d int=%d\n", pipe_index, interleave); if (chip->bad_board) return -EIO; @@ -1061,7 +1071,8 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, for (channel_mask = i = 0; i < interleave; i++) channel_mask |= 1 << (pipe_index + i); if (chip->pipe_alloc_mask & channel_mask) { - DE_ACT(("allocate_pipes: channel already open\n")); + dev_err(chip->card->dev, + "allocate_pipes: channel already open\n"); return -EAGAIN; } @@ -1078,7 +1089,6 @@ static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, it moves data. The DMA counter is in units of bytes, not samples. */ pipe->dma_counter = &chip->comm_page->position[pipe_index]; *pipe->dma_counter = 0; - DE_ACT(("allocate_pipes: ok\n")); return pipe_index; } @@ -1089,7 +1099,6 @@ static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe) u32 channel_mask; int i; - DE_ACT(("free_pipes: Pipe %d\n", pipe->index)); if (snd_BUG_ON(!is_pipe_allocated(chip, pipe->index))) return -EINVAL; if (snd_BUG_ON(pipe->state != PIPE_STATE_STOPPED)) @@ -1131,7 +1140,7 @@ static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe, list[head].size = cpu_to_le32(length); pipe->sglist_head++; } else { - DE_ACT(("SGlist: too many fragments\n")); + dev_err(chip->card->dev, "SGlist: too many fragments\n"); return -ENOMEM; } return 0; diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c index afa273330e8a..23a099425834 100644 --- a/sound/pci/echoaudio/echoaudio_gml.c +++ b/sound/pci/echoaudio/echoaudio_gml.c @@ -46,7 +46,8 @@ static int check_asic_status(struct echoaudio *chip) /* The DSP will return a value to indicate whether or not the ASIC is currently loaded */ if (read_dsp(chip, &asic_status) < 0) { - DE_INIT(("check_asic_status: failed on read_dsp\n")); + dev_err(chip->card->dev, + "check_asic_status: failed on read_dsp\n"); chip->asic_loaded = FALSE; return -EIO; } @@ -68,7 +69,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force) else value &= ~GML_DIGITAL_IN_AUTO_MUTE; - DE_ACT(("write_control_reg: 0x%x\n", value)); + dev_dbg(chip->card->dev, "write_control_reg: 0x%x\n", value); /* Write the control register */ value = cpu_to_le32(value); @@ -91,7 +92,7 @@ If the auto-mute is disabled, the digital inputs are enabled regardless of what the input clock is set or what is connected. */ static int set_input_auto_mute(struct echoaudio *chip, int automute) { - DE_ACT(("set_input_auto_mute %d\n", automute)); + dev_dbg(chip->card->dev, "set_input_auto_mute %d\n", automute); chip->digital_in_automute = automute; @@ -194,7 +195,7 @@ static int set_professional_spdif(struct echoaudio *chip, char prof) if ((err = write_control_reg(chip, control_reg, FALSE))) return err; chip->professional_spdif = prof; - DE_ACT(("set_professional_spdif to %s\n", - prof ? "Professional" : "Consumer")); + dev_dbg(chip->card->dev, "set_professional_spdif to %s\n", + prof ? "Professional" : "Consumer"); return 0; } diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c index d1615a0579d1..5dafe9260cb4 100644 --- a/sound/pci/echoaudio/gina20_dsp.c +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -37,12 +37,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Gina20\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA20)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -62,7 +62,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -149,7 +148,6 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) static int set_input_clock(struct echoaudio *chip, u16 clock) { - DE_ACT(("set_input_clock:\n")); switch (clock) { case ECHO_CLOCK_INTERNAL: @@ -158,7 +156,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) chip->spdif_status = GD_SPDIF_STATUS_UNDEF; set_sample_rate(chip, chip->sample_rate); chip->input_clock = clock; - DE_ACT(("Set Gina clock to INTERNAL\n")); break; case ECHO_CLOCK_SPDIF: chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN; @@ -166,7 +163,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) clear_handshake(chip); send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); chip->clock_state = GD_CLOCK_SPDIFIN; - DE_ACT(("Set Gina20 clock to SPDIF\n")); chip->input_clock = clock; break; default: @@ -208,7 +204,6 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - DE_ACT(("set_professional_spdif %d\n", prof)); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c index 98f7cfa81b5f..6971766938bf 100644 --- a/sound/pci/echoaudio/gina24_dsp.c +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -41,12 +41,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Gina24\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != GINA24)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -78,7 +78,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -155,7 +154,6 @@ static int load_asic(struct echoaudio *chip) control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; err = write_control_reg(chip, control_reg, TRUE); } - DE_INIT(("load_asic() done\n")); return err; } @@ -171,8 +169,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -217,7 +215,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) clock = GML_8KHZ; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -225,7 +224,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ chip->sample_rate = rate; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, "set_sample_rate: %d clock %d\n", rate, clock); return write_control_reg(chip, control_reg, FALSE); } @@ -236,7 +235,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) { u32 control_reg, clocks_from_dsp; - DE_ACT(("set_input_clock:\n")); /* Mask off the clock select bits */ control_reg = le32_to_cpu(chip->comm_page->control_register) & @@ -245,13 +243,11 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Gina24 clock to INTERNAL\n")); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: if (chip->digital_mode == DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Gina24 clock to SPDIF\n")); control_reg |= GML_SPDIF_CLOCK; if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) control_reg |= GML_DOUBLE_SPEED_MODE; @@ -261,21 +257,19 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; - DE_ACT(("Set Gina24 clock to ADAT\n")); control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ESYNC: - DE_ACT(("Set Gina24 clock to ESYNC\n")); control_reg |= GML_ESYNC_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ESYNC96: - DE_ACT(("Set Gina24 clock to ESYNC96\n")); control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE; break; default: - DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Gina24\n", clock); return -EINVAL; } @@ -304,7 +298,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) incompatible_clock = TRUE; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -344,6 +339,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode to %d\n", chip->digital_mode)); + dev_dbg(chip->card->dev, + "set_digital_mode to %d\n", chip->digital_mode); return incompatible_clock; } diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c index 5e85f14fe5a8..54edd67edff7 100644 --- a/sound/pci/echoaudio/indigo_dsp.c +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -38,12 +38,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -60,7 +60,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -109,7 +108,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg = MIA_32000; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -147,7 +147,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigo_express_dsp.c b/sound/pci/echoaudio/indigo_express_dsp.c index 2e4ab3e34a74..ceda2d7046ac 100644 --- a/sound/pci/echoaudio/indigo_express_dsp.c +++ b/sound/pci/echoaudio/indigo_express_dsp.c @@ -61,7 +61,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg |= clock; if (control_reg != old_control_reg) { - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, clock); chip->comm_page->control_register = cpu_to_le32(control_reg); chip->sample_rate = rate; clear_handshake(chip); @@ -89,7 +90,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c index 68f3c8ccc1bf..2cf5cc092d7a 100644 --- a/sound/pci/echoaudio/indigodj_dsp.c +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -38,12 +38,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo DJ\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJ)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -60,7 +60,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -109,7 +108,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg = MIA_32000; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -147,7 +147,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigodjx_dsp.c b/sound/pci/echoaudio/indigodjx_dsp.c index bb9632c752a9..5252863f1681 100644 --- a/sound/pci/echoaudio/indigodjx_dsp.c +++ b/sound/pci/echoaudio/indigodjx_dsp.c @@ -35,13 +35,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo DJx\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_DJX)) return -ENODEV; err = init_dsp_comm_page(chip); if (err < 0) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -59,7 +59,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c index beb9a5b69892..4e81787627e0 100644 --- a/sound/pci/echoaudio/indigoio_dsp.c +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -38,12 +38,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo IO\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IO)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -60,7 +60,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -118,7 +117,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } diff --git a/sound/pci/echoaudio/indigoiox_dsp.c b/sound/pci/echoaudio/indigoiox_dsp.c index 394c6e76bcbc..6de3f9bc6d26 100644 --- a/sound/pci/echoaudio/indigoiox_dsp.c +++ b/sound/pci/echoaudio/indigoiox_dsp.c @@ -35,13 +35,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Indigo IOx\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != INDIGO_IOX)) return -ENODEV; err = init_dsp_comm_page(chip); if (err < 0) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -59,7 +59,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c index 53ce94605044..f2024a3565af 100644 --- a/sound/pci/echoaudio/layla20_dsp.c +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -40,12 +40,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Layla20\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA20)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -64,7 +64,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -121,7 +120,8 @@ static int check_asic_status(struct echoaudio *chip) /* The DSP will return a value to indicate whether or not the ASIC is currently loaded */ if (read_dsp(chip, &asic_status) < 0) { - DE_ACT(("check_asic_status: failed on read_dsp\n")); + dev_err(chip->card->dev, + "check_asic_status: failed on read_dsp\n"); return -EIO; } @@ -164,8 +164,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. Do not return failure, simply treat it as a non-event. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; return 0; @@ -174,7 +174,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) if (wait_handshake(chip)) return -EIO; - DE_ACT(("set_sample_rate(%d)\n", rate)); + dev_dbg(chip->card->dev, "set_sample_rate(%d)\n", rate); chip->sample_rate = rate; chip->comm_page->sample_rate = cpu_to_le32(rate); clear_handshake(chip); @@ -188,29 +188,25 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source) u16 clock; u32 rate; - DE_ACT(("set_input_clock:\n")); rate = 0; switch (clock_source) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Layla20 clock to INTERNAL\n")); rate = chip->sample_rate; clock = LAYLA20_CLOCK_INTERNAL; break; case ECHO_CLOCK_SPDIF: - DE_ACT(("Set Layla20 clock to SPDIF\n")); clock = LAYLA20_CLOCK_SPDIF; break; case ECHO_CLOCK_WORD: - DE_ACT(("Set Layla20 clock to WORD\n")); clock = LAYLA20_CLOCK_WORD; break; case ECHO_CLOCK_SUPER: - DE_ACT(("Set Layla20 clock to SUPER\n")); clock = LAYLA20_CLOCK_SUPER; break; default: - DE_ACT(("Input clock 0x%x not supported for Layla24\n", - clock_source)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Layla24\n", + clock_source); return -EINVAL; } chip->input_clock = clock_source; @@ -229,7 +225,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock_source) static int set_output_clock(struct echoaudio *chip, u16 clock) { - DE_ACT(("set_output_clock: %d\n", clock)); switch (clock) { case ECHO_CLOCK_SUPER: clock = LAYLA20_OUTPUT_CLOCK_SUPER; @@ -238,7 +233,7 @@ static int set_output_clock(struct echoaudio *chip, u16 clock) clock = LAYLA20_OUTPUT_CLOCK_WORD; break; default: - DE_ACT(("set_output_clock wrong clock\n")); + dev_err(chip->card->dev, "set_output_clock wrong clock\n"); return -EINVAL; } @@ -283,7 +278,6 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - DE_ACT(("set_professional_spdif %d\n", prof)); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c index 8c041647f285..4f11e81f6c0e 100644 --- a/sound/pci/echoaudio/layla24_dsp.c +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -40,12 +40,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Layla24\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != LAYLA24)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -69,7 +69,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) if ((err = init_line_levels(chip)) < 0) return err; - DE_INIT(("init_hw done\n")); return err; } @@ -117,7 +116,6 @@ static int load_asic(struct echoaudio *chip) if (chip->asic_loaded) return 1; - DE_INIT(("load_asic\n")); /* Give the DSP a few milliseconds to settle down */ mdelay(10); @@ -151,7 +149,6 @@ static int load_asic(struct echoaudio *chip) err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ, TRUE); - DE_INIT(("load_asic() done\n")); return err; } @@ -167,8 +164,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_warn(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -241,7 +238,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */ chip->sample_rate = rate; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, control_reg); return write_control_reg(chip, control_reg, FALSE); } @@ -260,7 +258,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) /* Pick the new clock */ switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Layla24 clock to INTERNAL\n")); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: @@ -269,7 +266,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg |= GML_SPDIF_CLOCK; /* Layla24 doesn't support 96KHz S/PDIF */ control_reg &= ~GML_DOUBLE_SPEED_MODE; - DE_ACT(("Set Layla24 clock to SPDIF\n")); break; case ECHO_CLOCK_WORD: control_reg |= GML_WORD_CLOCK; @@ -277,17 +273,16 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg |= GML_DOUBLE_SPEED_MODE; else control_reg &= ~GML_DOUBLE_SPEED_MODE; - DE_ACT(("Set Layla24 clock to WORD\n")); break; case ECHO_CLOCK_ADAT: if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; - DE_ACT(("Set Layla24 clock to ADAT\n")); break; default: - DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Layla24\n", clock); return -EINVAL; } @@ -353,7 +348,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) asic = FW_LAYLA24_2A_ASIC; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -393,6 +389,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode to %d\n", mode)); + dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode); return incompatible_clock; } diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c index 6ebfa6e7ab9e..fdad079ad9a1 100644 --- a/sound/pci/echoaudio/mia_dsp.c +++ b/sound/pci/echoaudio/mia_dsp.c @@ -41,12 +41,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Mia\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != MIA)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -66,7 +66,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -126,7 +125,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) control_reg = MIA_32000; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -153,7 +153,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) static int set_input_clock(struct echoaudio *chip, u16 clock) { - DE_ACT(("set_input_clock(%d)\n", clock)); + dev_dbg(chip->card->dev, "set_input_clock(%d)\n", clock); if (snd_BUG_ON(clock != ECHO_CLOCK_INTERNAL && clock != ECHO_CLOCK_SPDIF)) return -EINVAL; @@ -181,7 +181,8 @@ static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, index = output * num_pipes_out(chip) + pipe; chip->comm_page->vmixer[index] = gain; - DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + dev_dbg(chip->card->dev, + "set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain); return 0; } @@ -211,7 +212,7 @@ static int update_flags(struct echoaudio *chip) static int set_professional_spdif(struct echoaudio *chip, char prof) { - DE_ACT(("set_professional_spdif %d\n", prof)); + dev_dbg(chip->card->dev, "set_professional_spdif %d\n", prof); if (prof) chip->comm_page->flags |= cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index 7f4dfae0323a..d913749d154a 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -36,7 +36,7 @@ /* Start and stop Midi input */ static int enable_midi_input(struct echoaudio *chip, char enable) { - DE_MID(("enable_midi_input(%d)\n", enable)); + dev_dbg(chip->card->dev, "enable_midi_input(%d)\n", enable); if (wait_handshake(chip)) return -EIO; @@ -74,7 +74,7 @@ static int write_midi(struct echoaudio *chip, u8 *data, int bytes) chip->comm_page->midi_out_free_count = 0; clear_handshake(chip); send_vector(chip, DSP_VC_MIDI_WRITE); - DE_MID(("write_midi: %d\n", bytes)); + dev_dbg(chip->card->dev, "write_midi: %d\n", bytes); return bytes; } @@ -157,7 +157,6 @@ static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_in = substream; - DE_MID(("rawmidi_iopen\n")); return 0; } @@ -183,7 +182,6 @@ static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_in = NULL; - DE_MID(("rawmidi_iclose\n")); return 0; } @@ -196,7 +194,6 @@ static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream) chip->tinuse = 0; chip->midi_full = 0; chip->midi_out = substream; - DE_MID(("rawmidi_oopen\n")); return 0; } @@ -209,7 +206,6 @@ static void snd_echo_midi_output_write(unsigned long data) int bytes, sent, time; unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1]; - DE_MID(("snd_echo_midi_output_write\n")); /* No interrupts are involved: we have to check at regular intervals if the card's output buffer has room for new data. */ sent = bytes = 0; @@ -218,7 +214,7 @@ static void snd_echo_midi_output_write(unsigned long data) if (!snd_rawmidi_transmit_empty(chip->midi_out)) { bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf, MIDI_OUT_BUFFER_SIZE - 1); - DE_MID(("Try to send %d bytes...\n", bytes)); + dev_dbg(chip->card->dev, "Try to send %d bytes...\n", bytes); sent = write_midi(chip, buf, bytes); if (sent < 0) { dev_err(chip->card->dev, @@ -227,12 +223,12 @@ static void snd_echo_midi_output_write(unsigned long data) sent = 9000; chip->midi_full = 1; } else if (sent > 0) { - DE_MID(("%d bytes sent\n", sent)); + dev_dbg(chip->card->dev, "%d bytes sent\n", sent); snd_rawmidi_transmit_ack(chip->midi_out, sent); } else { /* Buffer is full. DSP's internal buffer is 64 (128 ?) bytes long. Let's wait until half of them are sent */ - DE_MID(("Full\n")); + dev_dbg(chip->card->dev, "Full\n"); sent = 32; chip->midi_full = 1; } @@ -244,7 +240,8 @@ static void snd_echo_midi_output_write(unsigned long data) sent */ time = (sent << 3) / 25 + 1; /* 8/25=0.32ms to send a byte */ mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000); - DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000))); + dev_dbg(chip->card->dev, + "Timer armed(%d)\n", ((time * HZ + 999) / 1000)); } spin_unlock_irqrestore(&chip->lock, flags); } @@ -256,7 +253,7 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream { struct echoaudio *chip = substream->rmidi->private_data; - DE_MID(("snd_echo_midi_output_trigger(%d)\n", up)); + dev_dbg(chip->card->dev, "snd_echo_midi_output_trigger(%d)\n", up); spin_lock_irq(&chip->lock); if (up) { if (!chip->tinuse) { @@ -270,7 +267,7 @@ static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream chip->tinuse = 0; spin_unlock_irq(&chip->lock); del_timer_sync(&chip->timer); - DE_MID(("Timer removed\n")); + dev_dbg(chip->card->dev, "Timer removed\n"); return; } } @@ -287,7 +284,6 @@ static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream) struct echoaudio *chip = substream->rmidi->private_data; chip->midi_out = NULL; - DE_MID(("rawmidi_oclose\n")); return 0; } @@ -327,6 +323,5 @@ static int snd_echo_midi_create(struct snd_card *card, chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; - DE_INIT(("MIDI ok\n")); return 0; } diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c index 6e6a7eb555b8..843c7a5e5105 100644 --- a/sound/pci/echoaudio/mona_dsp.c +++ b/sound/pci/echoaudio/mona_dsp.c @@ -41,12 +41,12 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) { int err; - DE_INIT(("init_hw() - Mona\n")); if (snd_BUG_ON((subdevice_id & 0xfff0) != MONA)) return -ENODEV; if ((err = init_dsp_comm_page(chip))) { - DE_INIT(("init_hw - could not initialize DSP comm page\n")); + dev_err(chip->card->dev, + "init_hw - could not initialize DSP comm page\n"); return err; } @@ -71,7 +71,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) return err; chip->bad_board = FALSE; - DE_INIT(("init_hw done\n")); return err; } @@ -202,8 +201,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) /* Only set the clock for internal mode. */ if (chip->input_clock != ECHO_CLOCK_INTERNAL) { - DE_ACT(("set_sample_rate: Cannot set sample rate - " - "clock not set to CLK_CLOCKININTERNAL\n")); + dev_dbg(chip->card->dev, + "Cannot set sample rate - clock not set to CLK_CLOCKININTERNAL\n"); /* Save the rate anyhow */ chip->comm_page->sample_rate = cpu_to_le32(rate); chip->sample_rate = rate; @@ -279,7 +278,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) clock = GML_8KHZ; break; default: - DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + dev_err(chip->card->dev, + "set_sample_rate: %d invalid!\n", rate); return -EINVAL; } @@ -287,7 +287,8 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate) chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ chip->sample_rate = rate; - DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + dev_dbg(chip->card->dev, + "set_sample_rate: %d clock %d\n", rate, clock); return write_control_reg(chip, control_reg, force_write); } @@ -299,7 +300,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) u32 control_reg, clocks_from_dsp; int err; - DE_ACT(("set_input_clock:\n")); /* Prevent two simultaneous calls to switch_asic() */ if (atomic_read(&chip->opencount)) @@ -312,7 +312,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) switch (clock) { case ECHO_CLOCK_INTERNAL: - DE_ACT(("Set Mona clock to INTERNAL\n")); chip->input_clock = ECHO_CLOCK_INTERNAL; return set_sample_rate(chip, chip->sample_rate); case ECHO_CLOCK_SPDIF: @@ -324,7 +323,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) spin_lock_irq(&chip->lock); if (err < 0) return err; - DE_ACT(("Set Mona clock to SPDIF\n")); control_reg |= GML_SPDIF_CLOCK; if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) control_reg |= GML_DOUBLE_SPEED_MODE; @@ -332,7 +330,6 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_WORD: - DE_ACT(("Set Mona clock to WORD\n")); spin_unlock_irq(&chip->lock); err = switch_asic(chip, clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96); @@ -346,14 +343,15 @@ static int set_input_clock(struct echoaudio *chip, u16 clock) control_reg &= ~GML_DOUBLE_SPEED_MODE; break; case ECHO_CLOCK_ADAT: - DE_ACT(("Set Mona clock to ADAT\n")); + dev_dbg(chip->card->dev, "Set Mona clock to ADAT\n"); if (chip->digital_mode != DIGITAL_MODE_ADAT) return -EAGAIN; control_reg |= GML_ADAT_CLOCK; control_reg &= ~GML_DOUBLE_SPEED_MODE; break; default: - DE_ACT(("Input clock 0x%x not supported for Mona\n", clock)); + dev_err(chip->card->dev, + "Input clock 0x%x not supported for Mona\n", clock); return -EINVAL; } @@ -381,7 +379,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) incompatible_clock = TRUE; break; default: - DE_ACT(("Digital mode not supported: %d\n", mode)); + dev_err(chip->card->dev, + "Digital mode not supported: %d\n", mode); return -EINVAL; } @@ -422,6 +421,6 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) return err; chip->digital_mode = mode; - DE_ACT(("set_digital_mode to %d\n", mode)); + dev_dbg(chip->card->dev, "set_digital_mode to %d\n", mode); return incompatible_clock; } diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 229269788023..b4458a630a7c 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1289,10 +1289,8 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) } if (emu->emu1010.firmware_thread) kthread_stop(emu->emu1010.firmware_thread); - if (emu->firmware) - release_firmware(emu->firmware); - if (emu->dock_fw) - release_firmware(emu->dock_fw); + release_firmware(emu->firmware); + release_firmware(emu->dock_fw); if (emu->irq >= 0) free_irq(emu->irq, emu); /* remove reserved page */ @@ -1301,8 +1299,7 @@ static int snd_emu10k1_free(struct snd_emu10k1 *emu) (struct snd_util_memblk *)emu->reserved_page); emu->reserved_page = NULL; } - if (emu->memhdr) - snd_util_memhdr_free(emu->memhdr); + snd_util_memhdr_free(emu->memhdr); if (emu->silent_page.area) snd_dma_free_pages(&emu->silent_page); if (emu->ptb_pages.area) diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index e223de1408c0..15933f92f63a 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -180,7 +180,7 @@ MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard."); /* From 0x50 - 0x5f, last samples captured */ -/** +/* * The hardware has 3 channels for playback and 1 for capture. * - channel 0 is the front channel * - channel 1 is the rear channel diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 745f0627c634..eb5c0aba41c1 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -777,8 +777,7 @@ static void snd_emu10k1_ctl_private_free(struct snd_kcontrol *kctl) kctl->private_value = 0; list_del(&ctl->list); kfree(ctl); - if (kctl->tlv.p) - kfree(kctl->tlv.p); + kfree(kctl->tlv.p); } static int snd_emu10k1_add_controls(struct snd_emu10k1 *emu, diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index c5ae2a24d8a5..1de33025669a 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -83,7 +83,7 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol, * Items labels in enum mixer controls assigning source data to * each destination */ -static char *emu1010_src_texts[] = { +static const char * const emu1010_src_texts[] = { "Silence", "Dock Mic A", "Dock Mic B", @@ -141,7 +141,7 @@ static char *emu1010_src_texts[] = { /* 1616(m) cardbus */ -static char *emu1616_src_texts[] = { +static const char * const emu1616_src_texts[] = { "Silence", "Dock Mic A", "Dock Mic B", @@ -393,23 +393,11 @@ static int snd_emu1010_input_output_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); - char **items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) { - uinfo->value.enumerated.items = 49; - items = emu1616_src_texts; - } else { - uinfo->value.enumerated.items = 53; - items = emu1010_src_texts; - } - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - items[uinfo->value.enumerated.item]); - return 0; + if (emu->card_capabilities->emu_model == EMU_MODEL_EMU1616) + return snd_ctl_enum_info(uinfo, 1, 49, emu1616_src_texts); + else + return snd_ctl_enum_info(uinfo, 1, 53, emu1010_src_texts); } static int snd_emu1010_output_source_get(struct snd_kcontrol *kcontrol, @@ -699,19 +687,11 @@ static struct snd_kcontrol_new snd_emu1010_dac_pads[] = { static int snd_emu1010_internal_clock_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "44100", "48000", "SPDIF", "ADAT" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; - - + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_emu1010_internal_clock_get(struct snd_kcontrol *kcontrol, @@ -830,21 +810,15 @@ static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { #if 0 - static char *texts[4] = { + static const char * const texts[4] = { "Unknown1", "Unknown2", "Mic", "Line" }; #endif - static char *texts[2] = { + static const char * const texts[2] = { "Mic", "Line" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_audigy_i2c_capture_source_get(struct snd_kcontrol *kcontrol, @@ -997,15 +971,9 @@ static struct snd_kcontrol_new snd_audigy_i2c_volume_ctls[] = { #if 0 static int snd_audigy_spdif_output_rate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"44100", "48000", "96000"}; + static const char * const texts[] = {"44100", "48000", "96000"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_audigy_spdif_output_rate_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c index a4fe7f0c9458..7ef3898a7806 100644 --- a/sound/pci/emu10k1/p16v.c +++ b/sound/pci/emu10k1/p16v.c @@ -757,18 +757,12 @@ static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol, static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[8] = { + static const char * const texts[8] = { "SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S", "CDIF", "FX", "AC97" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol, @@ -805,15 +799,9 @@ static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol, static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "0", "1", "2", "3", }; + static const char * const texts[4] = { "0", "1", "2", "3", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 639962443ccc..0fc46eb4e251 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1045,18 +1045,12 @@ static int snd_es1938_new_pcm(struct es1938 *chip, int device) static int snd_es1938_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[8] = { + static const char * const texts[8] = { "Mic", "Mic Master", "CD", "AOUT", "Mic1", "Mix", "Line", "Master" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 8, texts); } static int snd_es1938_get_mux(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index a9956a7c5677..6039700f8579 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -1710,7 +1710,8 @@ static void es1968_measure_clock(struct es1968 *chip) int i, apu; unsigned int pa, offset, t; struct esm_memory *memory; - struct timeval start_time, stop_time; + ktime_t start_time, stop_time; + ktime_t diff; if (chip->clock == 0) chip->clock = 48000; /* default clock value */ @@ -1761,12 +1762,12 @@ static void es1968_measure_clock(struct es1968 *chip) snd_es1968_bob_inc(chip, ESM_BOB_FREQ); __apu_set_register(chip, apu, 5, pa & 0xffff); snd_es1968_trigger_apu(chip, apu, ESM_APU_16BITLINEAR); - do_gettimeofday(&start_time); + start_time = ktime_get(); spin_unlock_irq(&chip->reg_lock); msleep(50); spin_lock_irq(&chip->reg_lock); offset = __apu_get_register(chip, apu, 5); - do_gettimeofday(&stop_time); + stop_time = ktime_get(); snd_es1968_trigger_apu(chip, apu, 0); /* stop */ snd_es1968_bob_dec(chip); chip->in_measurement = 0; @@ -1777,12 +1778,8 @@ static void es1968_measure_clock(struct es1968 *chip) offset &= 0xfffe; offset += chip->measure_count * (CLOCK_MEASURE_BUFSIZE/2); - t = stop_time.tv_sec - start_time.tv_sec; - t *= 1000000; - if (stop_time.tv_usec < start_time.tv_usec) - t -= start_time.tv_usec - stop_time.tv_usec; - else - t += stop_time.tv_usec - start_time.tv_usec; + diff = ktime_sub(stop_time, start_time); + t = ktime_to_us(diff); if (t == 0) { dev_err(chip->card->dev, "?? calculation error..\n"); } else { diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index c5038303a126..d167afffce5f 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -958,17 +958,11 @@ static int snd_fm801_put_double(struct snd_kcontrol *kcontrol, static int snd_fm801_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[5] = { + static const char * const texts[5] = { "AC97 Primary", "FM", "I2S", "PCM", "AC97 Secondary" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item > 4) - uinfo->value.enumerated.item = 4; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static int snd_fm801_get_mux(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index fcc5e478c9a1..1ede82200ee5 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -441,6 +441,13 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_parse_pin_defcfg); +/** + * snd_hda_get_input_pin_attr - Get the input pin attribute from pin config + * @def_conf: pin configuration value + * + * Guess the input pin attribute (INPUT_PIN_ATTR_XXX) from the given + * default pin configuration value. + */ int snd_hda_get_input_pin_attr(unsigned int def_conf) { unsigned int loc = get_defcfg_location(def_conf); @@ -464,12 +471,15 @@ EXPORT_SYMBOL_GPL(snd_hda_get_input_pin_attr); /** * hda_get_input_pin_label - Give a label for the given input pin + * @codec: the HDA codec + * @item: ping config item to refer + * @pin: the pin NID + * @check_location: flag to add the jack location prefix * - * When check_location is true, the function checks the pin location + * When @check_location is true, the function checks the pin location * for mic and line-in pins, and set an appropriate prefix like "Front", * "Rear", "Internal". */ - static const char *hda_get_input_pin_label(struct hda_codec *codec, const struct auto_pin_cfg_item *item, hda_nid_t pin, bool check_location) @@ -550,6 +560,9 @@ static int check_mic_location_need(struct hda_codec *codec, /** * hda_get_autocfg_input_label - Get a label for the given input + * @codec: the HDA codec + * @cfg: the parsed pin configuration + * @input: the input index number * * Get a label for the given input pin defined by the autocfg item. * Unlike hda_get_input_pin_label(), this function checks all inputs @@ -677,6 +690,12 @@ static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid, /** * snd_hda_get_pin_label - Get a label for the given I/O pin + * @codec: the HDA codec + * @nid: pin NID + * @cfg: the parsed pin configuration + * @label: the string buffer to store + * @maxlen: the max length of string buffer (including termination) + * @indexp: the pointer to return the index number (for multiple ctls) * * Get a label for the given pin. This function works for both input and * output pins. When @cfg is given as non-NULL, the function tries to get @@ -748,6 +767,14 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_get_pin_label); +/** + * snd_hda_add_verbs - Add verbs to the init list + * @codec: the HDA codec + * @list: zero-terminated verb list to add + * + * Append the given verb list to the execution list. The verbs will be + * performed at init and resume time via snd_hda_apply_verbs(). + */ int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list) { @@ -760,6 +787,10 @@ int snd_hda_add_verbs(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_add_verbs); +/** + * snd_hda_apply_verbs - Execute the init verb lists + * @codec: the HDA codec + */ void snd_hda_apply_verbs(struct hda_codec *codec) { int i; @@ -770,6 +801,11 @@ void snd_hda_apply_verbs(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_apply_verbs); +/** + * snd_hda_apply_pincfgs - Set each pin config in the given list + * @codec: the HDA codec + * @cfg: NULL-terminated pin config table + */ void snd_hda_apply_pincfgs(struct hda_codec *codec, const struct hda_pintbl *cfg) { @@ -837,6 +873,11 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) } } +/** + * snd_hda_apply_fixup - Apply the fixup chain with the given action + * @codec: the HDA codec + * @action: fixup action (HDA_FIXUP_ACT_XXX) + */ void snd_hda_apply_fixup(struct hda_codec *codec, int action) { if (codec->fixup_list) @@ -855,6 +896,12 @@ static bool pin_config_match(struct hda_codec *codec, return true; } +/** + * snd_hda_pick_pin_fixup - Pick up a fixup matching with the pin quirk list + * @codec: the HDA codec + * @pin_quirk: zero-terminated pin quirk list + * @fixlist: the fixup list + */ void snd_hda_pick_pin_fixup(struct hda_codec *codec, const struct snd_hda_pin_quirk *pin_quirk, const struct hda_fixup *fixlist) @@ -881,6 +928,21 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_pick_pin_fixup); +/** + * snd_hda_pick_fixup - Pick up a fixup matching with PCI/codec SSID or model string + * @codec: the HDA codec + * @models: NULL-terminated model string list + * @quirk: zero-terminated PCI/codec SSID quirk list + * @fixlist: the fixup list + * + * Pick up a fixup entry matching with the given model string or SSID. + * If a fixup was already set beforehand, the function doesn't do anything. + * When a special model string "nofixup" is given, also no fixup is applied. + * + * The function tries to find the matching model name at first, if given. + * If nothing matched, try to look up the PCI SSID. + * If still nothing matched, try to look up the codec SSID. + */ void snd_hda_pick_fixup(struct hda_codec *codec, const struct hda_model_fixup *models, const struct snd_pci_quirk *quirk, diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 8c6c50afc0b7..1e7de08e77cb 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -175,6 +175,11 @@ static int snd_hda_do_attach(struct hda_beep *beep) return 0; } +/** + * snd_hda_enable_beep_device - Turn on/off beep sound + * @codec: the HDA codec + * @enable: flag to turn on/off + */ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) { struct hda_beep *beep = codec->beep; @@ -191,6 +196,20 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) } EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device); +/** + * snd_hda_attach_beep_device - Attach a beep input device + * @codec: the HDA codec + * @nid: beep NID + * + * Attach a beep object to the given widget. If beep hint is turned off + * explicitly or beep_mode of the codec is turned off, this doesn't nothing. + * + * The attached beep device has to be registered via + * snd_hda_register_beep_device() and released via snd_hda_detach_beep_device() + * appropriately. + * + * Currently, only one beep device is allowed to each codec. + */ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) { struct hda_beep *beep; @@ -228,6 +247,10 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) } EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device); +/** + * snd_hda_detach_beep_device - Detach the beep device + * @codec: the HDA codec + */ void snd_hda_detach_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; @@ -240,6 +263,10 @@ void snd_hda_detach_beep_device(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device); +/** + * snd_hda_register_beep_device - Register the beep device + * @codec: the HDA codec + */ int snd_hda_register_beep_device(struct hda_codec *codec) { struct hda_beep *beep = codec->beep; @@ -269,6 +296,12 @@ static bool ctl_has_mute(struct snd_kcontrol *kcontrol) } /* get/put callbacks for beep mute mixer switches */ + +/** + * snd_hda_mixer_amp_switch_get_beep - Get callback for beep controls + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data + */ int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -283,6 +316,11 @@ int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get_beep); +/** + * snd_hda_mixer_amp_switch_put_beep - Put callback for beep controls + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data + */ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 15e0089492f7..2fe86d2e1b09 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -77,6 +77,10 @@ static struct hda_vendor_id hda_vendor_ids[] = { static DEFINE_MUTEX(preset_mutex); static LIST_HEAD(hda_preset_tables); +/** + * snd_hda_add_codec_preset - Add a codec preset to the chain + * @preset: codec preset table to add + */ int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) { mutex_lock(&preset_mutex); @@ -86,6 +90,10 @@ int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) } EXPORT_SYMBOL_GPL(snd_hda_add_codec_preset); +/** + * snd_hda_delete_codec_preset - Delete a codec preset from the chain + * @preset: codec preset table to delete + */ int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) { mutex_lock(&preset_mutex); @@ -338,8 +346,10 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, unsigned int parm; parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); - if (parm == -1) + if (parm == -1) { + *start_id = 0; return 0; + } *start_id = (parm >> 16) & 0x7fff; return (int)(parm & 0x7fff); } @@ -416,7 +426,6 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) * snd_hda_get_conn_list - get connection list * @codec: the HDA codec * @nid: NID to parse - * @len: number of connection list entries * @listp: the pointer to store NID list * * Parses the connection list of the given widget and stores the pointer @@ -827,8 +836,7 @@ static void snd_hda_bus_free(struct hda_bus *bus) WARN_ON(!list_empty(&bus->codec_list)); if (bus->workq) flush_workqueue(bus->workq); - if (bus->unsol) - kfree(bus->unsol); + kfree(bus->unsol); if (bus->ops.private_free) bus->ops.private_free(bus); if (bus->workq) @@ -966,14 +974,12 @@ find_codec_preset(struct hda_codec *codec) mutex_unlock(&preset_mutex); if (mod_requested < HDA_MODREQ_MAX_COUNT) { - char name[32]; if (!mod_requested) - snprintf(name, sizeof(name), "snd-hda-codec-id:%08x", - codec->vendor_id); + request_module("snd-hda-codec-id:%08x", + codec->vendor_id); else - snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*", - (codec->vendor_id >> 16) & 0xffff); - request_module(name); + request_module("snd-hda-codec-id:%04x*", + (codec->vendor_id >> 16) & 0xffff); mod_requested++; goto again; } @@ -1190,7 +1196,16 @@ unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_GPL(snd_hda_codec_get_pincfg); -/* remember the current pinctl target value */ +/** + * snd_hda_codec_set_pin_target - remember the current pinctl target value + * @codec: the HDA codec + * @nid: pin NID + * @val: assigned pinctl value + * + * This function stores the given value to a pinctl target value in the + * pincfg table. This isn't always as same as the actually written value + * but can be referred at any time via snd_hda_codec_get_pin_target(). + */ int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, unsigned int val) { @@ -1204,7 +1219,11 @@ int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_codec_set_pin_target); -/* return the current pinctl target value */ +/** + * snd_hda_codec_get_pin_target - return the current pinctl target value + * @codec: the HDA codec + * @nid: pin NID + */ int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid) { struct hda_pincfg *pin; @@ -1576,6 +1595,13 @@ int snd_hda_codec_new(struct hda_bus *bus, } EXPORT_SYMBOL_GPL(snd_hda_codec_new); +/** + * snd_hda_codec_update_widgets - Refresh widget caps and pin defaults + * @codec: the HDA codec + * + * Forcibly refresh the all widget caps and the init pin configurations of + * the given codec. + */ int snd_hda_codec_update_widgets(struct hda_codec *codec) { hda_nid_t fg; @@ -2006,6 +2032,7 @@ EXPORT_SYMBOL_GPL(query_amp_caps); * @codec: the HD-audio codec * @nid: the NID to query * @dir: either #HDA_INPUT or #HDA_OUTPUT + * @bits: bit mask to check the result * * Check whether the widget has the given amp capability for the direction. */ @@ -2025,7 +2052,7 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); * snd_hda_override_amp_caps - Override the AMP capabilities * @codec: the CODEC to clean up * @nid: the NID to clean up - * @direction: either #HDA_INPUT or #HDA_OUTPUT + * @dir: either #HDA_INPUT or #HDA_OUTPUT * @caps: the capability bits to set * * Override the cached AMP caps bits value by the given one. @@ -2241,7 +2268,17 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo); -/* Works like snd_hda_codec_amp_update() but it writes the value only at +/** + * snd_hda_codec_amp_init - initialize the AMP value + * @codec: the HDA codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Works like snd_hda_codec_amp_update() but it writes the value only at * the first access. If the amp was already initialized / updated beforehand, * this does nothing. */ @@ -2252,6 +2289,17 @@ int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); +/** + * snd_hda_codec_amp_init_stereo - initialize the stereo AMP value + * @codec: the HDA codec + * @nid: NID to read the AMP value + * @dir: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Call snd_hda_codec_amp_init() for both stereo channels. + */ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val) { @@ -2322,6 +2370,8 @@ static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, /** * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer + * @kcontrol: referred ctl element + * @uinfo: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -2383,6 +2433,8 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid, /** * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -2408,6 +2460,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_get); /** * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -2438,6 +2492,10 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); /** * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume + * @kcontrol: ctl element + * @op_flag: operation flag + * @size: byte size of input TLV + * @_tlv: TLV data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -2636,7 +2694,10 @@ void snd_hda_ctls_clear(struct hda_codec *codec) snd_array_free(&codec->nids); } -/* pseudo device locking +/** + * snd_hda_lock_devices - pseudo device locking + * @bus: the BUS + * * toggle card->shutdown to allow/disallow the device access (as a hack) */ int snd_hda_lock_devices(struct hda_bus *bus) @@ -2673,6 +2734,10 @@ int snd_hda_lock_devices(struct hda_bus *bus) } EXPORT_SYMBOL_GPL(snd_hda_lock_devices); +/** + * snd_hda_unlock_devices - pseudo device unlocking + * @bus: the BUS + */ void snd_hda_unlock_devices(struct hda_bus *bus) { struct snd_card *card = bus->card; @@ -2859,7 +2924,7 @@ static int add_slave(struct hda_codec *codec, } /** - * snd_hda_add_vmaster - create a virtual master control and add slaves + * __snd_hda_add_vmaster - create a virtual master control and add slaves * @codec: HD-audio codec * @name: vmaster control name * @tlv: TLV data (optional) @@ -2927,16 +2992,8 @@ static int vmaster_mute_mode_info(struct snd_kcontrol *kcontrol, static const char * const texts[] = { "On", "Off", "Follow Master" }; - unsigned int index; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - index = uinfo->value.enumerated.item; - if (index >= 3) - index = 2; - strcpy(uinfo->value.enumerated.name, texts[index]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int vmaster_mute_mode_get(struct snd_kcontrol *kcontrol, @@ -2970,10 +3027,15 @@ static struct snd_kcontrol_new vmaster_mute_mode = { .put = vmaster_mute_mode_put, }; -/* - * Add a mute-LED hook with the given vmaster switch kctl - * "Mute-LED Mode" control is automatically created and associated with - * the given hook. +/** + * snd_hda_add_vmaster_hook - Add a vmaster hook for mute-LED + * @codec: the HDA codec + * @hook: the vmaster hook object + * @expose_enum_ctl: flag to create an enum ctl + * + * Add a mute-LED hook with the given vmaster switch kctl. + * When @expose_enum_ctl is set, "Mute-LED Mode" control is automatically + * created and associated with the given hook. */ int snd_hda_add_vmaster_hook(struct hda_codec *codec, struct hda_vmaster_mute_hook *hook, @@ -2995,9 +3057,12 @@ int snd_hda_add_vmaster_hook(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_add_vmaster_hook); -/* - * Call the hook with the current value for synchronization - * Should be called in init callback +/** + * snd_hda_sync_vmaster_hook - Sync vmaster hook + * @hook: the vmaster hook + * + * Call the hook with the current value for synchronization. + * Should be called in init callback. */ void snd_hda_sync_vmaster_hook(struct hda_vmaster_mute_hook *hook) { @@ -3022,6 +3087,8 @@ EXPORT_SYMBOL_GPL(snd_hda_sync_vmaster_hook); /** * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch + * @kcontrol: referred ctl element + * @uinfo: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -3041,6 +3108,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_info); /** * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -3067,6 +3136,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_get); /** * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_COMPOSE_AMP_VAL*() or related macros. @@ -3110,6 +3181,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); /** * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_MUTE*() macros. @@ -3133,6 +3206,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_get); /** * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_MUTE*() macros. @@ -3163,6 +3238,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_switch_put); /** * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control + * @kcontrol: referred ctl element + * @uinfo: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. @@ -3186,6 +3263,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_info); /** * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. @@ -3209,6 +3288,8 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_get); /** * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control + * @kcontrol: ctl element + * @ucontrol: pointer to get/store the data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros. @@ -3238,6 +3319,10 @@ EXPORT_SYMBOL_GPL(snd_hda_mixer_bind_ctls_put); /** * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control + * @kcontrol: ctl element + * @op_flag: operation flag + * @size: byte size of input TLV + * @tlv: TLV data * * The control element is supposed to have the private_value field * set up via HDA_BIND_VOL() macro. @@ -3579,7 +3664,11 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_create_dig_out_ctls); -/* get the hda_spdif_out entry from the given NID +/** + * snd_hda_spdif_out_of_nid - get the hda_spdif_out entry from the given NID + * @codec: the HDA codec + * @nid: widget NID + * * call within spdif_mutex lock */ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, @@ -3596,6 +3685,13 @@ struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_spdif_out_of_nid); +/** + * snd_hda_spdif_ctls_unassign - Unassign the given SPDIF ctl + * @codec: the HDA codec + * @idx: the SPDIF ctl index + * + * Unassign the widget from the given SPDIF control. + */ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) { struct hda_spdif_out *spdif; @@ -3607,6 +3703,14 @@ void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx) } EXPORT_SYMBOL_GPL(snd_hda_spdif_ctls_unassign); +/** + * snd_hda_spdif_ctls_assign - Assign the SPDIF controls to the given NID + * @codec: the HDA codec + * @idx: the SPDIF ctl idx + * @nid: widget NID + * + * Assign the widget to the SPDIF control with the given index. + */ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid) { struct hda_spdif_out *spdif; @@ -3926,6 +4030,16 @@ void snd_hda_codec_flush_cache(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); +/** + * snd_hda_codec_set_power_to_all - Set the power state to all widgets + * @codec: the HDA codec + * @fg: function group (not used now) + * @power_state: the power state to set (AC_PWRST_*) + * + * Set the given power state to all widgets that have the power control. + * If the codec has power_filter set, it evaluates the power state and + * filter out if it's unchanged as D3. + */ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { @@ -3990,7 +4104,15 @@ static unsigned int hda_sync_power_state(struct hda_codec *codec, return state; } -/* don't power down the widget if it controls eapd and EAPD_BTLENABLE is set */ +/** + * snd_hda_codec_eapd_power_filter - A power filter callback for EAPD + * @codec: the HDA codec + * @nid: widget NID + * @power_state: power state to evalue + * + * Don't power down the widget if it controls eapd and EAPD_BTLENABLE is set. + * This can be used a codec power_filter callback. + */ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) @@ -4315,6 +4437,7 @@ static struct hda_rate_tbl rate_bits[] = { * @channels: the number of channels * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) * @maxbps: the max. bps + * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) * * Calculate the format bitset from the given rate, channels and th PCM format. * @@ -4650,6 +4773,17 @@ static int set_pcm_default_values(struct hda_codec *codec, /* * codec prepare/cleanup entries */ +/** + * snd_hda_codec_prepare - Prepare a stream + * @codec: the HDA codec + * @hinfo: PCM information + * @stream: stream tag to assign + * @format: format id to assign + * @substream: PCM substream to assign + * + * Calls the prepare callback set by the codec with the given arguments. + * Clean up the inactive streams when successful. + */ int snd_hda_codec_prepare(struct hda_codec *codec, struct hda_pcm_stream *hinfo, unsigned int stream, @@ -4666,6 +4800,14 @@ int snd_hda_codec_prepare(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_codec_prepare); +/** + * snd_hda_codec_cleanup - Prepare a stream + * @codec: the HDA codec + * @hinfo: PCM information + * @substream: PCM substream + * + * Calls the cleanup callback set by the codec with the given arguments. + */ void snd_hda_codec_cleanup(struct hda_codec *codec, struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) @@ -4990,6 +5132,7 @@ static void __snd_hda_power_down(struct hda_codec *codec) * snd_hda_power_save - Power-up/down/sync the codec * @codec: HD-audio codec * @delta: the counter delta to change + * @d3wait: sync for D3 transition complete * * Change the power-up counter via @delta, and power up or down the hardware * appropriately. For the power-down, queue to the delayed action. @@ -5065,6 +5208,10 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power); /** * snd_hda_ch_mode_info - Info callback helper for the channel mode enum + * @codec: the HDA codec + * @uinfo: pointer to get/store the data + * @chmode: channel mode array + * @num_chmodes: channel mode array size */ int snd_hda_ch_mode_info(struct hda_codec *codec, struct snd_ctl_elem_info *uinfo, @@ -5084,6 +5231,11 @@ EXPORT_SYMBOL_GPL(snd_hda_ch_mode_info); /** * snd_hda_ch_mode_get - Get callback helper for the channel mode enum + * @codec: the HDA codec + * @ucontrol: pointer to get/store the data + * @chmode: channel mode array + * @num_chmodes: channel mode array size + * @max_channels: max number of channels */ int snd_hda_ch_mode_get(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, @@ -5105,6 +5257,11 @@ EXPORT_SYMBOL_GPL(snd_hda_ch_mode_get); /** * snd_hda_ch_mode_put - Put callback helper for the channel mode enum + * @codec: the HDA codec + * @ucontrol: pointer to get/store the data + * @chmode: channel mode array + * @num_chmodes: channel mode array size + * @max_channelsp: pointer to store the max channels */ int snd_hda_ch_mode_put(struct hda_codec *codec, struct snd_ctl_elem_value *ucontrol, @@ -5133,6 +5290,8 @@ EXPORT_SYMBOL_GPL(snd_hda_ch_mode_put); /** * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum + * @imux: imux helper object + * @uinfo: pointer to get/store the data */ int snd_hda_input_mux_info(const struct hda_input_mux *imux, struct snd_ctl_elem_info *uinfo) @@ -5154,6 +5313,11 @@ EXPORT_SYMBOL_GPL(snd_hda_input_mux_info); /** * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum + * @codec: the HDA codec + * @imux: imux helper object + * @ucontrol: pointer to get/store the data + * @nid: input mux NID + * @cur_val: pointer to get/store the current imux value */ int snd_hda_input_mux_put(struct hda_codec *codec, const struct hda_input_mux *imux, @@ -5178,7 +5342,13 @@ int snd_hda_input_mux_put(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_input_mux_put); -/* +/** + * snd_hda_enum_helper_info - Helper for simple enum ctls + * @kcontrol: ctl element + * @uinfo: pointer to get/store the data + * @num_items: number of enum items + * @texts: enum item string array + * * process kcontrol info callback of a simple string enum array * when @num_items is 0 or @texts is NULL, assume a boolean enum array */ @@ -5195,14 +5365,7 @@ int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, texts = texts_default; } - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_items; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } EXPORT_SYMBOL_GPL(snd_hda_enum_helper_info); @@ -5274,6 +5437,8 @@ EXPORT_SYMBOL_GPL(snd_hda_bus_reboot_notify); /** * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_dig_open(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5290,6 +5455,11 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_open); /** * snd_hda_multi_out_dig_prepare - prepare the digital out stream + * @codec: the HDA codec + * @mout: hda_multi_out object + * @stream_tag: stream tag to assign + * @format: format id to assign + * @substream: PCM substream to assign */ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec, struct hda_multi_out *mout, @@ -5306,6 +5476,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_prepare); /** * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5319,6 +5491,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_cleanup); /** * snd_hda_multi_out_dig_close - release the digital out stream + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_dig_close(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5332,6 +5506,10 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_dig_close); /** * snd_hda_multi_out_analog_open - open analog outputs + * @codec: the HDA codec + * @mout: hda_multi_out object + * @substream: PCM substream to assign + * @hinfo: PCM information to assign * * Open analog outputs and set up the hw-constraints. * If the digital outputs can be opened as slave, open the digital @@ -5382,6 +5560,11 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_open); /** * snd_hda_multi_out_analog_prepare - Preapre the analog outputs. + * @codec: the HDA codec + * @mout: hda_multi_out object + * @stream_tag: stream tag to assign + * @format: format id to assign + * @substream: PCM substream to assign * * Set up the i/o for analog out. * When the digital out is available, copy the front out to digital out, too. @@ -5459,6 +5642,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_prepare); /** * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out + * @codec: the HDA codec + * @mout: hda_multi_out object */ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout) @@ -5490,6 +5675,8 @@ EXPORT_SYMBOL_GPL(snd_hda_multi_out_analog_cleanup); /** * snd_hda_get_default_vref - Get the default (mic) VREF pin bits + * @codec: the HDA codec + * @pin: referred pin NID * * Guess the suitable VREF pin bits to be set as the pin-control value. * Note: the function doesn't set the AC_PINCTL_IN_EN bit. @@ -5515,7 +5702,12 @@ unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin) } EXPORT_SYMBOL_GPL(snd_hda_get_default_vref); -/* correct the pin ctl value for matching with the pin cap */ +/** + * snd_hda_correct_pin_ctl - correct the pin ctl value for matching with the pin cap + * @codec: the HDA codec + * @pin: referred pin NID + * @val: pin ctl value to audit + */ unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val) { @@ -5566,6 +5758,19 @@ unsigned int snd_hda_correct_pin_ctl(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_correct_pin_ctl); +/** + * _snd_hda_pin_ctl - Helper to set pin ctl value + * @codec: the HDA codec + * @pin: referred pin NID + * @val: pin control value to set + * @cached: access over codec pinctl cache or direct write + * + * This function is a helper to set a pin ctl value more safely. + * It corrects the pin ctl value via snd_hda_correct_pin_ctl(), stores the + * value in pin target array via snd_hda_codec_set_pin_target(), then + * actually writes the value via either snd_hda_codec_update_cache() or + * snd_hda_codec_write() depending on @cached flag. + */ int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val, bool cached) { @@ -5582,6 +5787,11 @@ EXPORT_SYMBOL_GPL(_snd_hda_set_pin_ctl); /** * snd_hda_add_imux_item - Add an item to input_mux + * @codec: the HDA codec + * @imux: imux helper object + * @label: the name of imux item to assign + * @index: index number of imux item to assign + * @type_idx: pointer to store the resultant label index * * When the same label is used already in the existing items, the number * suffix is appended to the label. This label index number is stored diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index e1cd34d9011d..0e6d7534f491 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -371,7 +371,7 @@ error: return ret; } -/** +/* * SNDRV_PCM_RATE_* and AC_PAR_PCM values don't match, print correct rates with * hdmi-specific routine. */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 64220c08bd98..b680b4ec6331 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -40,7 +40,12 @@ #include "hda_generic.h" -/* initialize hda_gen_spec struct */ +/** + * snd_hda_gen_spec_init - initialize hda_gen_spec struct + * @spec: hda_gen_spec object to initialize + * + * Initialize the given hda_gen_spec object. + */ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) { snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); @@ -51,6 +56,17 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) } EXPORT_SYMBOL_GPL(snd_hda_gen_spec_init); +/** + * snd_hda_gen_add_kctl - Add a new kctl_new struct from the template + * @spec: hda_gen_spec object + * @name: name string to override the template, NULL if unchanged + * @temp: template for the new kctl + * + * Add a new kctl (actually snd_kcontrol_new to be instantiated later) + * element based on the given snd_kcontrol_new template @temp and the + * name string @name to the list in @spec. + * Returns the newly created object or NULL as error. + */ struct snd_kcontrol_new * snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, const struct snd_kcontrol_new *temp) @@ -259,8 +275,14 @@ static struct nid_path *get_nid_path(struct hda_codec *codec, return NULL; } -/* get the path between the given NIDs; - * passing 0 to either @pin or @dac behaves as a wildcard +/** + * snd_hda_get_nid_path - get the path between the given NIDs + * @codec: the HDA codec + * @from_nid: the NID where the path start from + * @to_nid: the NID where the path ends at + * + * Return the found nid_path object or NULL for error. + * Passing 0 to either @from_nid or @to_nid behaves as a wildcard. */ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) @@ -269,8 +291,14 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_get_nid_path); -/* get the index number corresponding to the path instance; - * the index starts from 1, for easier checking the invalid value +/** + * snd_hda_get_path_idx - get the index number corresponding to the path + * instance + * @codec: the HDA codec + * @path: nid_path object + * + * The returned index starts from 1, i.e. the actual array index with offset 1, + * and zero is handled as an invalid path */ int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) { @@ -287,7 +315,12 @@ int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) } EXPORT_SYMBOL_GPL(snd_hda_get_path_idx); -/* get the path instance corresponding to the given index number */ +/** + * snd_hda_get_path_from_idx - get the path instance corresponding to the + * given index number + * @codec: the HDA codec + * @idx: the path index + */ struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) { struct hda_gen_spec *spec = codec->spec; @@ -415,7 +448,18 @@ static bool __parse_nid_path(struct hda_codec *codec, return true; } -/* parse the widget path from the given nid to the target nid; +/** + * snd_hda_parse_nid_path - parse the widget path from the given nid to + * the target nid + * @codec: the HDA codec + * @from_nid: the NID where the path start from + * @to_nid: the NID where the path ends at + * @anchor_nid: the anchor indication + * @path: the path object to store the result + * + * Returns true if a matching path is found. + * + * The parsing behavior depends on parameters: * when @from_nid is 0, try to find an empty DAC; * when @anchor_nid is set to a positive value, only paths through the widget * with the given value are evaluated. @@ -436,9 +480,15 @@ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, } EXPORT_SYMBOL_GPL(snd_hda_parse_nid_path); -/* - * parse the path between the given NIDs and add to the path list. - * if no valid path is found, return NULL +/** + * snd_hda_add_new_path - parse the path between the given NIDs and + * add to the path list + * @codec: the HDA codec + * @from_nid: the NID where the path start from + * @to_nid: the NID where the path ends at + * @anchor_nid: the anchor indication, see snd_hda_parse_nid_path() + * + * If no valid path is found, returns NULL. */ struct nid_path * snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, @@ -724,8 +774,14 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, } } -/* activate or deactivate the given path - * if @add_aamix is set, enable the input from aa-mix NID as well (if any) +/** + * snd_hda_activate_path - activate or deactivate the given path + * @codec: the HDA codec + * @path: the path to activate/deactivate + * @enable: flag to activate or not + * @add_aamix: enable the input from aamix NID + * + * If @add_aamix is set, enable the input from aa-mix NID as well (if any). */ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix) @@ -1038,11 +1094,24 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch, break; *index = ch; return "Headphone"; + case AUTO_PIN_LINE_OUT: + /* This deals with the case where we have two DACs and + * one LO, one HP and one Speaker */ + if (!ch && cfg->speaker_outs && cfg->hp_outs) { + bool hp_lo_shared = !path_has_mixer(codec, spec->hp_paths[0], ctl_type); + bool spk_lo_shared = !path_has_mixer(codec, spec->speaker_paths[0], ctl_type); + if (hp_lo_shared && spk_lo_shared) + return spec->vmaster_mute.hook ? "PCM" : "Master"; + if (hp_lo_shared) + return "Headphone+LO"; + if (spk_lo_shared) + return "Speaker+LO"; + } } /* for a single channel output, we don't have to name the channel */ if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; + return "Line Out"; if (ch >= ARRAY_SIZE(channel_name)) { snd_BUG(); @@ -3149,12 +3218,13 @@ static int create_input_ctls(struct hda_codec *codec) } /* add stereo mix when explicitly enabled via hint */ - if (mixer && spec->add_stereo_mix_input && - snd_hda_get_bool_hint(codec, "add_stereo_mix_input") > 0) { + if (mixer && spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_ENABLE) { err = parse_capture_source(codec, mixer, CFG_IDX_MIX, num_adcs, "Stereo Mix", 0); if (err < 0) return err; + else + spec->suppress_auto_mic = 1; } return 0; @@ -3870,7 +3940,12 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, } } -/* Toggle outputs muting */ +/** + * snd_hda_gen_update_outputs - Toggle outputs muting + * @codec: the HDA codec + * + * Update the mute status of all outputs based on the current jack states. + */ void snd_hda_gen_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -3931,7 +4006,11 @@ static void call_update_outputs(struct hda_codec *codec) snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); } -/* standard HP-automute helper */ +/** + * snd_hda_gen_hp_automute - standard HP-automute helper + * @codec: the HDA codec + * @jack: jack object, NULL for the whole + */ void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { @@ -3952,7 +4031,11 @@ void snd_hda_gen_hp_automute(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_hp_automute); -/* standard line-out-automute helper */ +/** + * snd_hda_gen_line_automute - standard line-out-automute helper + * @codec: the HDA codec + * @jack: jack object, NULL for the whole + */ void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_callback *jack) { @@ -3973,7 +4056,11 @@ void snd_hda_gen_line_automute(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_gen_line_automute); -/* standard mic auto-switch helper */ +/** + * snd_hda_gen_mic_autoswitch - standard mic auto-switch helper + * @codec: the HDA codec + * @jack: jack object, NULL for the whole + */ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_callback *jack) { @@ -4305,7 +4392,13 @@ static int check_auto_mic_availability(struct hda_codec *codec) return 0; } -/* power_filter hook; make inactive widgets into power down */ +/** + * snd_hda_gen_path_power_filter - power_filter hook to make inactive widgets + * into power down + * @codec: the HDA codec + * @nid: NID to evalute + * @power_state: target power state + */ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) @@ -4341,8 +4434,11 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) } } -/* - * Parse the given BIOS configuration and set up the hda_gen_spec +/** + * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and + * set up the hda_gen_spec + * @codec: the HDA codec + * @cfg: Parsed pin configuration * * return 1 if successful, 0 if the proper config is not found, * or a negative error code @@ -4447,9 +4543,8 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, /* add stereo mix if available and not enabled yet */ if (!spec->auto_mic && spec->mixer_nid && - spec->add_stereo_mix_input && - spec->input_mux.num_items > 1 && - snd_hda_get_bool_hint(codec, "add_stereo_mix_input") < 0) { + spec->add_stereo_mix_input == HDA_HINT_STEREO_MIX_AUTO && + spec->input_mux.num_items > 1) { err = parse_capture_source(codec, spec->mixer_nid, CFG_IDX_MIX, spec->num_all_adcs, "Stereo Mix", 0); @@ -4524,10 +4619,16 @@ static const char * const slave_pfxs[] = { "CLFE", "Bass Speaker", "PCM", "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", "Headphone Front", "Headphone Surround", "Headphone CLFE", - "Headphone Side", + "Headphone Side", "Headphone+LO", "Speaker+LO", NULL, }; +/** + * snd_hda_gen_build_controls - Build controls from the parsed results + * @codec: the HDA codec + * + * Pass this to build_controls patch_ops. + */ int snd_hda_gen_build_controls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -5005,7 +5106,12 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, strlcat(str, sfx, len); } -/* build PCM streams based on the parsed results */ +/** + * snd_hda_gen_build_pcms - build PCM streams based on the parsed results + * @codec: the HDA codec + * + * Pass this to build_pcms patch_ops. + */ int snd_hda_gen_build_pcms(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -5300,9 +5406,11 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) } } -/* - * initialize the generic spec; - * this can be put as patch_ops.init function +/** + * snd_hda_gen_init - initialize the generic spec + * @codec: the HDA codec + * + * This can be put as patch_ops init function. */ int snd_hda_gen_init(struct hda_codec *codec) { @@ -5338,9 +5446,11 @@ int snd_hda_gen_init(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_gen_init); -/* - * free the generic spec; - * this can be put as patch_ops.free function +/** + * snd_hda_gen_free - free the generic spec + * @codec: the HDA codec + * + * This can be put as patch_ops free function. */ void snd_hda_gen_free(struct hda_codec *codec) { @@ -5352,9 +5462,12 @@ void snd_hda_gen_free(struct hda_codec *codec) EXPORT_SYMBOL_GPL(snd_hda_gen_free); #ifdef CONFIG_PM -/* - * check the loopback power save state; - * this can be put as patch_ops.check_power_status function +/** + * snd_hda_gen_check_power_status - check the loopback power save state + * @codec: the HDA codec + * @nid: NID to inspect + * + * This can be put as patch_ops check_power_status function. */ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { @@ -5380,6 +5493,12 @@ static const struct hda_codec_ops generic_patch_ops = { #endif }; +/** + * snd_hda_parse_generic_codec - Generic codec parser + * @codec: the HDA codec + * + * This should be called from the HDA codec core. + */ int snd_hda_parse_generic_codec(struct hda_codec *codec) { struct hda_gen_spec *spec; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 61dd5153f512..3d852660443a 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -222,7 +222,7 @@ struct hda_gen_spec { unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ unsigned int indep_hp:1; /* independent HP supported */ unsigned int prefer_hp_amp:1; /* enable HP amp for speaker if any */ - unsigned int add_stereo_mix_input:1; /* add aamix as a capture src */ + unsigned int add_stereo_mix_input:2; /* add aamix as a capture src */ unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */ unsigned int power_down_unused:1; /* power down unused widgets */ unsigned int dac_min_mute:1; /* minimal = mute for DACs */ @@ -291,6 +291,13 @@ struct hda_gen_spec { struct hda_jack_callback *cb); }; +/* values for add_stereo_mix_input flag */ +enum { + HDA_HINT_STEREO_MIX_DISABLE, /* No stereo mix input */ + HDA_HINT_STEREO_MIX_ENABLE, /* Add stereo mix input */ + HDA_HINT_STEREO_MIX_AUTO, /* Add only if auto-mic is disabled */ +}; + int snd_hda_gen_spec_init(struct hda_gen_spec *spec); int snd_hda_gen_init(struct hda_codec *codec); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 48b6c5a3884f..5ac0d39d59bc 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -196,8 +196,8 @@ MODULE_PARM_DESC(align_buffer_size, "Force buffer and period sizes to be multiple of 128 bytes."); #ifdef CONFIG_X86 -static bool hda_snoop = true; -module_param_named(snoop, hda_snoop, bool, 0444); +static int hda_snoop = -1; +module_param_named(snoop, hda_snoop, bint, 0444); MODULE_PARM_DESC(snoop, "Enable/disable snooping"); #else #define hda_snoop true @@ -272,43 +272,56 @@ enum { AZX_NUM_DRIVERS, /* keep this as last entry */ }; +#define azx_get_snoop_type(chip) \ + (((chip)->driver_caps & AZX_DCAPS_SNOOP_MASK) >> 10) +#define AZX_DCAPS_SNOOP_TYPE(type) ((AZX_SNOOP_TYPE_ ## type) << 10) + +/* quirks for old Intel chipsets */ +#define AZX_DCAPS_INTEL_ICH \ + (AZX_DCAPS_OLD_SSYNC | AZX_DCAPS_NO_ALIGN_BUFSIZE) + /* quirks for Intel PCH */ #define AZX_DCAPS_INTEL_PCH_NOPM \ - (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_BUFSIZE | \ - AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_REVERSE_ASSIGN) + (AZX_DCAPS_NO_ALIGN_BUFSIZE | AZX_DCAPS_COUNT_LPIB_DELAY |\ + AZX_DCAPS_REVERSE_ASSIGN | AZX_DCAPS_SNOOP_TYPE(SCH)) #define AZX_DCAPS_INTEL_PCH \ (AZX_DCAPS_INTEL_PCH_NOPM | AZX_DCAPS_PM_RUNTIME) #define AZX_DCAPS_INTEL_HASWELL \ - (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \ - AZX_DCAPS_COUNT_LPIB_DELAY | AZX_DCAPS_PM_RUNTIME | \ - AZX_DCAPS_I915_POWERWELL) + (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_COUNT_LPIB_DELAY |\ + AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ + AZX_DCAPS_SNOOP_TYPE(SCH)) /* Broadwell HDMI can't use position buffer reliably, force to use LPIB */ #define AZX_DCAPS_INTEL_BROADWELL \ - (AZX_DCAPS_SCH_SNOOP | AZX_DCAPS_ALIGN_BUFSIZE | \ - AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_PM_RUNTIME | \ - AZX_DCAPS_I915_POWERWELL) + (/*AZX_DCAPS_ALIGN_BUFSIZE |*/ AZX_DCAPS_POSFIX_LPIB |\ + AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ + AZX_DCAPS_SNOOP_TYPE(SCH)) /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ - (AZX_DCAPS_ATI_SNOOP | AZX_DCAPS_NO_TCSEL | \ - AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB) + (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB |\ + AZX_DCAPS_SNOOP_TYPE(ATI)) /* quirks for ATI/AMD HDMI */ #define AZX_DCAPS_PRESET_ATI_HDMI \ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB|\ AZX_DCAPS_NO_MSI64) +/* quirks for ATI HDMI with snoop off */ +#define AZX_DCAPS_PRESET_ATI_HDMI_NS \ + (AZX_DCAPS_PRESET_ATI_HDMI | AZX_DCAPS_SNOOP_OFF) + /* quirks for Nvidia */ #define AZX_DCAPS_PRESET_NVIDIA \ - (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\ - AZX_DCAPS_ALIGN_BUFSIZE | AZX_DCAPS_NO_64BIT |\ - AZX_DCAPS_CORBRP_SELF_CLEAR) + (AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI | /*AZX_DCAPS_ALIGN_BUFSIZE |*/ \ + AZX_DCAPS_NO_64BIT | AZX_DCAPS_CORBRP_SELF_CLEAR |\ + AZX_DCAPS_SNOOP_TYPE(NVIDIA)) #define AZX_DCAPS_PRESET_CTHDA \ - (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY) + (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB |\ + AZX_DCAPS_4K_BDLE_BOUNDARY | AZX_DCAPS_SNOOP_OFF) /* * VGA-switcher support @@ -437,6 +450,8 @@ static void update_pci_byte(struct pci_dev *pci, unsigned int reg, static void azx_init_pci(struct azx *chip) { + int snoop_type = azx_get_snoop_type(chip); + /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44) * TCSEL == Traffic Class Select Register, which sets PCI express QOS * Ensuring these bits are 0 clears playback static on some HD Audio @@ -451,7 +466,7 @@ static void azx_init_pci(struct azx *chip) /* For ATI SB450/600/700/800/900 and AMD Hudson azalia HD audio, * we need to enable snoop. */ - if (chip->driver_caps & AZX_DCAPS_ATI_SNOOP) { + if (snoop_type == AZX_SNOOP_TYPE_ATI) { dev_dbg(chip->card->dev, "Setting ATI snoop: %d\n", azx_snoop(chip)); update_pci_byte(chip->pci, @@ -460,7 +475,7 @@ static void azx_init_pci(struct azx *chip) } /* For NVIDIA HDA, enable snoop */ - if (chip->driver_caps & AZX_DCAPS_NVIDIA_SNOOP) { + if (snoop_type == AZX_SNOOP_TYPE_NVIDIA) { dev_dbg(chip->card->dev, "Setting Nvidia snoop: %d\n", azx_snoop(chip)); update_pci_byte(chip->pci, @@ -475,7 +490,7 @@ static void azx_init_pci(struct azx *chip) } /* Enable SCH/PCH snoop if needed */ - if (chip->driver_caps & AZX_DCAPS_SCH_SNOOP) { + if (snoop_type == AZX_SNOOP_TYPE_SCH) { unsigned short snoop; pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop); if ((!azx_snoop(chip) && !(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)) || @@ -1132,8 +1147,7 @@ static int azx_free(struct azx *chip) pci_disable_device(chip->pci); kfree(chip->azx_dev); #ifdef CONFIG_SND_HDA_PATCH_LOADER - if (chip->fw) - release_firmware(chip->fw); + release_firmware(chip->fw); #endif if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { hda_display_power(false); @@ -1361,35 +1375,33 @@ static void check_msi(struct azx *chip) /* check the snoop mode availability */ static void azx_check_snoop_available(struct azx *chip) { - bool snoop = chip->snoop; + int snoop = hda_snoop; - switch (chip->driver_type) { - case AZX_DRIVER_VIA: + if (snoop >= 0) { + dev_info(chip->card->dev, "Force to %s mode by module option\n", + snoop ? "snoop" : "non-snoop"); + chip->snoop = snoop; + return; + } + + snoop = true; + if (azx_get_snoop_type(chip) == AZX_SNOOP_TYPE_NONE && + chip->driver_type == AZX_DRIVER_VIA) { /* force to non-snoop mode for a new VIA controller * when BIOS is set */ - if (snoop) { - u8 val; - pci_read_config_byte(chip->pci, 0x42, &val); - if (!(val & 0x80) && chip->pci->revision == 0x30) - snoop = false; - } - break; - case AZX_DRIVER_ATIHDMI_NS: - /* new ATI HDMI requires non-snoop */ - snoop = false; - break; - case AZX_DRIVER_CTHDA: - case AZX_DRIVER_CMEDIA: - snoop = false; - break; + u8 val; + pci_read_config_byte(chip->pci, 0x42, &val); + if (!(val & 0x80) && chip->pci->revision == 0x30) + snoop = false; } - if (snoop != chip->snoop) { - dev_info(chip->card->dev, "Force to %s mode\n", - snoop ? "snoop" : "non-snoop"); - chip->snoop = snoop; - } + if (chip->driver_caps & AZX_DCAPS_SNOOP_OFF) + snoop = false; + + chip->snoop = snoop; + if (!snoop) + dev_info(chip->card->dev, "Force to non-snoop mode\n"); } static void azx_probe_work(struct work_struct *work) @@ -1449,7 +1461,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, check_probe_mask(chip, dev); chip->single_cmd = single_cmd; - chip->snoop = hda_snoop; azx_check_snoop_available(chip); if (bdl_pos_adj[dev] < 0) { @@ -1557,10 +1568,8 @@ static int azx_first_init(struct azx *chip) if (align_buffer_size >= 0) chip->align_buffer_size = !!align_buffer_size; else { - if (chip->driver_caps & AZX_DCAPS_BUFSIZE) + if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE) chip->align_buffer_size = 0; - else if (chip->driver_caps & AZX_DCAPS_ALIGN_BUFSIZE) - chip->align_buffer_size = 1; else chip->align_buffer_size = 1; } @@ -2047,36 +2056,35 @@ static const struct pci_device_id azx_ids[] = { /* Braswell */ { PCI_DEVICE(0x8086, 0x2284), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, - /* ICH */ + /* ICH6 */ { PCI_DEVICE(0x8086, 0x2668), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH6 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH7 */ { PCI_DEVICE(0x8086, 0x27d8), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH7 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ESB2 */ { PCI_DEVICE(0x8086, 0x269a), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ESB2 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH8 */ { PCI_DEVICE(0x8086, 0x284b), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH8 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH9 */ { PCI_DEVICE(0x8086, 0x293e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH9 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH9 */ { PCI_DEVICE(0x8086, 0x293f), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH9 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH10 */ { PCI_DEVICE(0x8086, 0x3a3e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH10 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, + /* ICH10 */ { PCI_DEVICE(0x8086, 0x3a6e), - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_OLD_SSYNC | - AZX_DCAPS_BUFSIZE }, /* ICH10 */ + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, /* Generic Intel */ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID), .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8, .class_mask = 0xffffff, - .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_BUFSIZE }, + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_NO_ALIGN_BUFSIZE }, /* ATI SB 450/600/700/800/900 */ { PCI_DEVICE(0x1002, 0x437b), .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB }, @@ -2131,13 +2139,13 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x1002, 0xaa98), .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI }, { PCI_DEVICE(0x1002, 0x9902), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0xaaa0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0xaaa8), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, { PCI_DEVICE(0x1002, 0xaab0), - .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI }, + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS }, /* VIA VT8251/VT8237A */ { PCI_DEVICE(0x1106, 0x3288), .driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA }, @@ -2184,7 +2192,7 @@ static const struct pci_device_id azx_ids[] = { /* CM8888 */ { PCI_DEVICE(0x13f6, 0x5011), .driver_data = AZX_DRIVER_CMEDIA | - AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB }, + AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_SNOOP_OFF }, /* Vortex86MX */ { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC }, /* VMware HDAudio */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index f56765ae73a7..e664307617bd 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -20,6 +20,16 @@ #include "hda_auto_parser.h" #include "hda_jack.h" +/** + * is_jack_detectable - Check whether the given pin is jack-detectable + * @codec: the HDA codec + * @nid: pin NID + * + * Check whether the given pin is capable to report the jack detection. + * The jack detection might not work by various reasons, e.g. the jack + * detection is prohibited in the codec level, the pin config has + * AC_DEFCFG_MISC_NO_PRESENCE bit, no unsol support, etc. + */ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) { if (codec->no_jack_detect) @@ -57,6 +67,8 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) /** * snd_hda_jack_tbl_get - query the jack-table entry for the given NID + * @codec: the HDA codec + * @nid: pin NID to refer to */ struct hda_jack_tbl * snd_hda_jack_tbl_get(struct hda_codec *codec, hda_nid_t nid) @@ -75,6 +87,8 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get); /** * snd_hda_jack_tbl_get_from_tag - query the jack-table entry for the given tag + * @codec: the HDA codec + * @tag: tag value to refer to */ struct hda_jack_tbl * snd_hda_jack_tbl_get_from_tag(struct hda_codec *codec, unsigned char tag) @@ -93,6 +107,8 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_tbl_get_from_tag); /** * snd_hda_jack_tbl_new - create a jack-table entry for the given NID + * @codec: the HDA codec + * @nid: pin NID to assign */ static struct hda_jack_tbl * snd_hda_jack_tbl_new(struct hda_codec *codec, hda_nid_t nid) @@ -162,6 +178,7 @@ static void jack_detect_update(struct hda_codec *codec, /** * snd_hda_set_dirty_all - Mark all the cached as dirty + * @codec: the HDA codec * * This function sets the dirty flag to all entries of jack table. * It's called from the resume path in hda_codec.c. @@ -218,6 +235,9 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state); /** * snd_hda_jack_detect_enable - enable the jack-detection + * @codec: the HDA codec + * @nid: pin NID to enable + * @func: callback function to register * * In the case of error, the return value will be a pointer embedded with * errno. Check and handle the return value appropriately with standard @@ -258,6 +278,14 @@ snd_hda_jack_detect_enable_callback(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable_callback); +/** + * snd_hda_jack_detect_enable - Enable the jack detection on the given pin + * @codec: the HDA codec + * @nid: pin NID to enable jack detection + * + * Enable the jack detection with the default callback. Returns zero if + * successful or a negative error code. + */ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid) { return PTR_ERR_OR_ZERO(snd_hda_jack_detect_enable_callback(codec, nid, NULL)); @@ -266,6 +294,9 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_detect_enable); /** * snd_hda_jack_set_gating_jack - Set gating jack. + * @codec: the HDA codec + * @gated_nid: gated pin NID + * @gating_nid: gating pin NID * * Indicates the gated jack is only valid when the gating jack is plugged. */ @@ -287,6 +318,7 @@ EXPORT_SYMBOL_GPL(snd_hda_jack_set_gating_jack); /** * snd_hda_jack_report_sync - sync the states of all jacks and report if changed + * @codec: the HDA codec */ void snd_hda_jack_report_sync(struct hda_codec *codec) { @@ -349,6 +381,11 @@ static void hda_free_jack_priv(struct snd_jack *jack) /** * snd_hda_jack_add_kctl - Add a kctl for the given pin + * @codec: the HDA codec + * @nid: pin NID to assign + * @name: string name for the jack + * @idx: index number for the jack + * @phantom_jack: flag to deal as a phantom jack * * This assigns a jack-detection kctl to the given pin. The kcontrol * will have the given name and index. @@ -391,6 +428,15 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, return 0; } +/** + * snd_hda_jack_add_kctl - Add a jack kctl for the given pin + * @codec: the HDA codec + * @nid: pin NID + * @name: the name string for the jack ctl + * @idx: the ctl index for the jack ctl + * + * This is a simple helper calling __snd_hda_jack_add_kctl(). + */ int snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, const char *name, int idx) { @@ -456,6 +502,8 @@ static int add_jack_kctl(struct hda_codec *codec, hda_nid_t nid, /** * snd_hda_jack_add_kctls - Add kctls for all pins included in the given pincfg + * @codec: the HDA codec + * @cfg: pin config table to parse */ int snd_hda_jack_add_kctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) @@ -531,6 +579,11 @@ static void call_jack_callback(struct hda_codec *codec, } } +/** + * snd_hda_jack_unsol_event - Handle an unsolicited event + * @codec: the HDA codec + * @res: the unsolicited event data + */ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) { struct hda_jack_tbl *event; @@ -546,6 +599,13 @@ void snd_hda_jack_unsol_event(struct hda_codec *codec, unsigned int res) } EXPORT_SYMBOL_GPL(snd_hda_jack_unsol_event); +/** + * snd_hda_jack_poll_all - Poll all jacks + * @codec: the HDA codec + * + * Poll all detectable jacks with dirty flag, update the status, call + * callbacks and call snd_hda_jack_report_sync() if any changes are found. + */ void snd_hda_jack_poll_all(struct hda_codec *codec) { struct hda_jack_tbl *jack = codec->jacktbl.list; diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 13cb375454f6..b279e327a23b 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -72,6 +72,11 @@ enum { int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid); +/** + * snd_hda_jack_detect - Detect the jack + * @codec: the HDA codec + * @nid: pin NID to check jack detection + */ static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT; diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h index 5016014e57f2..aa484fdf4338 100644 --- a/sound/pci/hda/hda_priv.h +++ b/sound/pci/hda/hda_priv.h @@ -152,9 +152,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; /* bits 0-7 are used for indicating driver type */ #define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ #define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ -#define AZX_DCAPS_ATI_SNOOP (1 << 10) /* ATI snoop enable */ -#define AZX_DCAPS_NVIDIA_SNOOP (1 << 11) /* Nvidia snoop enable */ -#define AZX_DCAPS_SCH_SNOOP (1 << 12) /* SCH/PCH snoop enable */ +#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ +#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ #define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */ #define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */ #define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ @@ -163,8 +162,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ #define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ #define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ -#define AZX_DCAPS_BUFSIZE (1 << 21) /* no buffer size alignment */ -#define AZX_DCAPS_ALIGN_BUFSIZE (1 << 22) /* buffer size alignment */ +#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ +/* 22 unused */ #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ #define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ @@ -173,6 +172,13 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; #define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ #define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ +enum { + AZX_SNOOP_TYPE_NONE , + AZX_SNOOP_TYPE_SCH, + AZX_SNOOP_TYPE_ATI, + AZX_SNOOP_TYPE_NVIDIA, +}; + /* HD Audio class code */ #define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index 9b49f156a12e..ccc962a1699f 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -417,8 +417,13 @@ static DEVICE_ATTR_RW(user_pin_configs); static DEVICE_ATTR_WO(reconfig); static DEVICE_ATTR_WO(clear); -/* - * Look for hint string +/** + * snd_hda_get_hint - Look for hint string + * @codec: the HDA codec + * @key: the hint key string + * + * Look for a hint key/value pair matching with the given key string + * and returns the value string. If nothing found, returns NULL. */ const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) { @@ -427,6 +432,15 @@ const char *snd_hda_get_hint(struct hda_codec *codec, const char *key) } EXPORT_SYMBOL_GPL(snd_hda_get_hint); +/** + * snd_hda_get_bool_hint - Get a boolean hint value + * @codec: the HDA codec + * @key: the hint key string + * + * Look for a hint key/value pair matching with the given key string + * and returns a boolean value parsed from the value. If no matching + * key is found, return a negative value. + */ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) { const char *p; @@ -453,6 +467,16 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key) } EXPORT_SYMBOL_GPL(snd_hda_get_bool_hint); +/** + * snd_hda_get_int_hint - Get an integer hint value + * @codec: the HDA codec + * @key: the hint key string + * @valp: pointer to store a value + * + * Look for a hint key/value pair matching with the given key string + * and stores the integer value to @valp. If no matching key is found, + * return a negative error code. Otherwise it returns zero. + */ int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp) { const char *p; @@ -690,8 +714,11 @@ static int get_line_from_fw(char *buf, int size, size_t *fw_size_p, return 1; } -/* - * load a "patch" firmware file and parse it +/** + * snd_hda_load_patch - load a "patch" firmware file and parse it + * @bus: HD-audio bus + * @fw_size: the firmware byte size + * @fw_buf: the firmware data */ int snd_hda_load_patch(struct hda_bus *bus, size_t fw_size, const void *fw_buf) { diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 06275f8807a8..eb54da757407 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -195,7 +195,8 @@ static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp) codec->no_sticky_stream = 1; spec->gen.indep_hp = indep_hp; - spec->gen.add_stereo_mix_input = 1; + if (!spec->gen.add_stereo_mix_input) + spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); if (err < 0) @@ -332,6 +333,7 @@ static const struct hda_fixup ad1986a_fixups[] = { static const struct snd_pci_quirk ad1986a_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), + SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD), SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD), SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK), SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK), @@ -351,6 +353,7 @@ static const struct hda_model_fixup ad1986a_fixup_models[] = { { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" }, { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" }, { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */ + { .id = AD1986A_FIXUP_EAPD, .name = "eapd" }, {} }; diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4f7ffa8c4a0d..e0383eea9880 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2417,7 +2417,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, * @reloc: Relocation address for loading single-segment overlays, or 0 for * no relocation * @sample_rate: sampling rate of the stream used for DSP download - * @number_channels: channels of the stream used for DSP download + * @channels: channels of the stream used for DSP download * @ovly: TRUE if overlay format is required * * Returns zero or a negative error code. @@ -2556,10 +2556,7 @@ static void dspload_post_setup(struct hda_codec *codec) } /** - * Download DSP from a DSP Image Fast Load structure. This structure is a - * linear, non-constant sized element array of structures, each of which - * contain the count of the data to be loaded, the data itself, and the - * corresponding starting chip address of the starting data location. + * dspload_image - Download DSP from a DSP Image Fast Load structure. * * @codec: the HDA codec * @fls: pointer to a fast load image @@ -2570,6 +2567,10 @@ static void dspload_post_setup(struct hda_codec *codec) * @router_chans: number of audio router channels to be allocated (0 means use * internal defaults; max is 32) * + * Download DSP from a DSP Image Fast Load structure. This structure is a + * linear, non-constant sized element array of structures, each of which + * contain the count of the data to be loaded, the data itself, and the + * corresponding starting chip address of the starting data location. * Returns zero or a negative error code. */ static int dspload_image(struct hda_codec *codec, diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index e9ebc7bd752c..fd3ed18670e9 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -855,14 +855,14 @@ static int patch_conexant_auto(struct hda_codec *codec) case 0x14f15045: codec->single_adc_amp = 1; spec->gen.mixer_nid = 0x17; - spec->gen.add_stereo_mix_input = 1; + spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; snd_hda_pick_fixup(codec, cxt5045_fixup_models, cxt5045_fixups, cxt_fixups); break; case 0x14f15047: codec->pin_amp_workaround = 1; spec->gen.mixer_nid = 0x19; - spec->gen.add_stereo_mix_input = 1; + spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; snd_hda_pick_fixup(codec, cxt5047_fixup_models, cxt5047_fixups, cxt_fixups); break; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 9dc9cf8c90e9..5f13d2d18079 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -47,7 +47,9 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); #define is_haswell(codec) ((codec)->vendor_id == 0x80862807) #define is_broadwell(codec) ((codec)->vendor_id == 0x80862808) -#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec)) +#define is_skylake(codec) ((codec)->vendor_id == 0x80862809) +#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \ + || is_skylake(codec)) #define is_valleyview(codec) ((codec)->vendor_id == 0x80862882) #define is_cherryview(codec) ((codec)->vendor_id == 0x80862883) @@ -3365,6 +3367,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862808, .name = "Broadwell HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862809, .name = "Skylake HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi }, @@ -3425,6 +3428,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862805"); MODULE_ALIAS("snd-hda-codec-id:80862806"); MODULE_ALIAS("snd-hda-codec-id:80862807"); MODULE_ALIAS("snd-hda-codec-id:80862808"); +MODULE_ALIAS("snd-hda-codec-id:80862809"); MODULE_ALIAS("snd-hda-codec-id:80862880"); MODULE_ALIAS("snd-hda-codec-id:80862882"); MODULE_ALIAS("snd-hda-codec-id:80862883"); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b118a5be18df..a722067c491c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -96,6 +96,8 @@ struct alc_spec { hda_nid_t cap_mute_led_nid; unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */ + unsigned int gpio_mute_led_mask; + unsigned int gpio_mic_led_mask; hda_nid_t headset_mic_pin; hda_nid_t headphone_mic_pin; @@ -3310,41 +3312,45 @@ static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec, } } -/* turn on/off mute LED per vmaster hook */ -static void alc269_fixup_hp_gpio_mute_hook(void *private_data, int enabled) +/* update LED status via GPIO */ +static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, + bool enabled) { - struct hda_codec *codec = private_data; struct alc_spec *spec = codec->spec; unsigned int oldval = spec->gpio_led; + if (spec->mute_led_polarity) + enabled = !enabled; + if (enabled) - spec->gpio_led &= ~0x08; + spec->gpio_led &= ~mask; else - spec->gpio_led |= 0x08; + spec->gpio_led |= mask; if (spec->gpio_led != oldval) snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_led); } -/* turn on/off mic-mute LED per capture hook */ -static void alc269_fixup_hp_gpio_mic_mute_hook(struct hda_codec *codec, - struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +/* turn on/off mute LED via GPIO per vmaster hook */ +static void alc_fixup_gpio_mute_hook(void *private_data, int enabled) { + struct hda_codec *codec = private_data; struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_led; - if (!ucontrol) - return; + alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled); +} - if (ucontrol->value.integer.value[0] || - ucontrol->value.integer.value[1]) - spec->gpio_led &= ~0x10; - else - spec->gpio_led |= 0x10; - if (spec->gpio_led != oldval) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); +/* turn on/off mic-mute LED via GPIO per capture hook */ +static void alc_fixup_gpio_mic_mute_hook(struct hda_codec *codec, + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct alc_spec *spec = codec->spec; + + if (ucontrol) + alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + ucontrol->value.integer.value[0] || + ucontrol->value.integer.value[1]); } static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, @@ -3358,9 +3364,33 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook; - spec->gen.cap_sync_hook = alc269_fixup_hp_gpio_mic_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; + spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; + spec->gpio_mic_led_mask = 0x10; + snd_hda_add_verbs(codec, gpio_init); + } +} + +static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const struct hda_verb gpio_init[] = { + { 0x01, AC_VERB_SET_GPIO_MASK, 0x22 }, + { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x22 }, + {} + }; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; + spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x02; + spec->gpio_mic_led_mask = 0x20; snd_hda_add_verbs(codec, gpio_init); } } @@ -3402,9 +3432,11 @@ static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; spec->cap_mute_led_nid = 0x18; snd_hda_add_verbs(codec, gpio_init); codec->power_filter = led_power_filter; @@ -3423,9 +3455,11 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 0; + spec->gpio_mute_led_mask = 0x08; spec->cap_mute_led_nid = 0x18; snd_hda_add_verbs(codec, gpio_init); codec->power_filter = led_power_filter; @@ -4300,6 +4334,7 @@ enum { ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, ALC282_FIXUP_ASPIRE_V5_PINS, ALC280_FIXUP_HP_GPIO4, + ALC286_FIXUP_HP_GPIO_LED, }; static const struct hda_fixup alc269_fixups[] = { @@ -4769,6 +4804,10 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc280_fixup_hp_gpio4, }, + [ALC286_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc286_fixup_hp_gpio_led, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -4809,6 +4848,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), + SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), @@ -4887,6 +4927,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), @@ -5697,22 +5738,6 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec, } } -/* turn on/off mute LED per vmaster hook */ -static void alc662_led_gpio1_mute_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - struct alc_spec *spec = codec->spec; - unsigned int oldval = spec->gpio_led; - - if (enabled) - spec->gpio_led |= 0x01; - else - spec->gpio_led &= ~0x01; - if (spec->gpio_led != oldval) - snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->gpio_led); -} - /* avoid D3 for keeping GPIO up */ static unsigned int gpio_led_power_filter(struct hda_codec *codec, hda_nid_t nid, @@ -5735,8 +5760,10 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec, }; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.vmaster_mute.hook = alc662_led_gpio1_mute_hook; + spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; spec->gpio_led = 0; + spec->mute_led_polarity = 1; + spec->gpio_mute_led_mask = 0x01; snd_hda_add_verbs(codec, gpio_init); codec->power_filter = gpio_led_power_filter; } diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 6c206b6c8d65..3de6d3d779c9 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -137,7 +137,7 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) spec->gen.indep_hp = 1; spec->gen.keep_eapd_on = 1; spec->gen.pcm_playback_hook = via_playback_pcm_hook; - spec->gen.add_stereo_mix_input = 1; + spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; return spec; } diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 3b3cf4ac9060..c9411dfff5a4 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -205,13 +205,7 @@ static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol, static const char * const texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol, @@ -1106,20 +1100,10 @@ static int wm_adc_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_in }; struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) { - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, universe_texts[uinfo->value.enumerated.item]); - } else { - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - } - return 0; + if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AUREON71_UNIVERSE) + return snd_ctl_enum_info(uinfo, 2, 8, universe_texts); + else + return snd_ctl_enum_info(uinfo, 2, 5, texts); } static int wm_adc_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1167,16 +1151,10 @@ static int aureon_cs8415_mux_info(struct snd_kcontrol *kcontrol, struct snd_ctl_ "CD", "Coax" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; if (ice->eeprom.subvendor == VT1724_SUBDEVICE_PRODIGY71) - strcpy(uinfo->value.enumerated.name, prodigy_texts[uinfo->value.enumerated.item]); + return snd_ctl_enum_info(uinfo, 1, 2, prodigy_texts); else - strcpy(uinfo->value.enumerated.name, aureon_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, aureon_texts); } static int aureon_cs8415_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1392,15 +1370,7 @@ static int aureon_oversampling_info(struct snd_kcontrol *k, struct snd_ctl_elem_ { static const char * const texts[2] = { "128x", "64x" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int aureon_oversampling_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 817a1bc50a60..5cb587cf360e 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -580,13 +580,7 @@ static int snd_ice1712_ewx_io_sense_info(struct snd_kcontrol *kcontrol, struct s static const char * const texts[2] = { "+4dBu", "-10dBV", }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_ice1712_ewx_io_sense_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -903,13 +897,7 @@ static int snd_ice1712_6fire_select_input_info(struct snd_kcontrol *kcontrol, st static const char * const texts[4] = { "Internal", "Front Input", "Rear Input", "Wave Table" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= 4) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_ice1712_6fire_select_input_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c index 59e37c581691..a40001c1d9e8 100644 --- a/sound/pci/ice1712/hoontech.c +++ b/sound/pci/ice1712/hoontech.c @@ -309,11 +309,7 @@ static int snd_ice1712_value_init(struct snd_ice1712 *ice) return err; /* ak4524 controls */ - err = snd_ice1712_akm4xxx_build_controls(ice); - if (err < 0) - return err; - - return 0; + return snd_ice1712_akm4xxx_build_controls(ice); } static int snd_ice1712_ez8_init(struct snd_ice1712 *ice) diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 206ed2cbcef9..b039b46152c6 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -620,10 +620,9 @@ static int snd_ice1712_playback_ds_prepare(struct snd_pcm_substream *substream) { struct snd_ice1712 *ice = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - u32 period_size, buf_size, rate, tmp, chn; + u32 period_size, rate, tmp, chn; period_size = snd_pcm_lib_period_bytes(substream) - 1; - buf_size = snd_pcm_lib_buffer_bytes(substream) - 1; tmp = 0x0064; if (snd_pcm_format_width(runtime->format) == 16) tmp &= ~0x04; @@ -1295,10 +1294,7 @@ static int snd_ice1712_pcm_profi(struct snd_ice1712 *ice, int device, struct snd return err; } - err = snd_ice1712_build_pro_mixer(ice); - if (err < 0) - return err; - return 0; + return snd_ice1712_build_pro_mixer(ice); } /* @@ -1545,10 +1541,9 @@ static int snd_ice1712_ac97_mixer(struct snd_ice1712 *ice) dev_warn(ice->card->dev, "cannot initialize ac97 for consumer, skipped\n"); else { - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice)); - if (err < 0) - return err; - return 0; + return snd_ctl_add(ice->card, + snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, + ice)); } } @@ -1839,13 +1834,7 @@ static int snd_ice1712_pro_internal_clock_info(struct snd_kcontrol *kcontrol, "96000", /* 12: 7 */ "IEC958 Input", /* 13: -- */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 14; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 14, texts); } static int snd_ice1712_pro_internal_clock_get(struct snd_kcontrol *kcontrol, @@ -1930,13 +1919,7 @@ static int snd_ice1712_pro_internal_clock_default_info(struct snd_kcontrol *kcon "96000", /* 12: 7 */ /* "IEC958 Input", 13: -- */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 13; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 13, texts); } static int snd_ice1712_pro_internal_clock_default_get(struct snd_kcontrol *kcontrol, @@ -2057,15 +2040,8 @@ static int snd_ice1712_pro_route_info(struct snd_kcontrol *kcontrol, "IEC958 In L", "IEC958 In R", /* 9-10 */ "Digital Mixer", /* 11 - optional */ }; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = - snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + int num_items = snd_ctl_get_ioffidx(kcontrol, &uinfo->id) < 2 ? 12 : 11; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_ice1712_pro_route_analog_get(struct snd_kcontrol *kcontrol, @@ -2516,11 +2492,8 @@ static int snd_ice1712_build_controls(struct snd_ice1712 *ice) err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice)); if (err < 0) return err; - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice)); - if (err < 0) - return err; - - return 0; + return snd_ctl_add(ice->card, + snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice)); } static int snd_ice1712_free(struct snd_ice1712 *ice) @@ -2905,8 +2878,7 @@ static int snd_ice1712_resume(struct device *dev) outw(ice->pm_saved_spdif_ctrl, ICEMT(ice, ROUTE_SPDOUT)); outw(ice->pm_saved_route, ICEMT(ice, ROUTE_PSDOUT03)); - if (ice->ac97) - snd_ac97_resume(ice->ac97); + snd_ac97_resume(ice->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index 08cb08ac85e6..d73da157ea14 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -2049,13 +2049,7 @@ static int snd_vt1724_pro_route_info(struct snd_kcontrol *kcontrol, "IEC958 In L", "IEC958 In R", /* 3-4 */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } static inline int analog_route_shift(int idx) @@ -2503,11 +2497,8 @@ static int snd_vt1724_build_controls(struct snd_ice1712 *ice) return err; } - err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice)); - if (err < 0) - return err; - - return 0; + return snd_ctl_add(ice->card, + snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice)); } static int snd_vt1724_free(struct snd_ice1712 *ice) @@ -2884,8 +2875,7 @@ static int snd_vt1724_resume(struct device *dev) outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG)); outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK)); - if (ice->ac97) - snd_ac97_resume(ice->ac97); + snd_ac97_resume(ice->ac97); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; diff --git a/sound/pci/ice1712/juli.c b/sound/pci/ice1712/juli.c index 7a6c0786c55c..a1536c1a7ed4 100644 --- a/sound/pci/ice1712/juli.c +++ b/sound/pci/ice1712/juli.c @@ -475,11 +475,8 @@ static int juli_add_controls(struct snd_ice1712 *ice) return err; /* only capture SPDIF over AK4114 */ - err = snd_ak4114_build(spec->ak4114, NULL, + return snd_ak4114_build(spec->ak4114, NULL, ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); - if (err < 0) - return err; - return 0; } /* diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c index 63aa39f06f02..7de25c4807fd 100644 --- a/sound/pci/ice1712/maya44.c +++ b/sound/pci/ice1712/maya44.c @@ -359,15 +359,7 @@ static int maya_rec_src_info(struct snd_kcontrol *kcontrol, { static const char * const texts[] = { "Line", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(texts); - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int maya_rec_src_get(struct snd_kcontrol *kcontrol, @@ -411,15 +403,7 @@ static int maya_pb_route_info(struct snd_kcontrol *kcontrol, "Input 1", "Input 2", "Input 3", "Input 4" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(texts); - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int maya_pb_route_shift(int idx) diff --git a/sound/pci/ice1712/phase.c b/sound/pci/ice1712/phase.c index 0011e04f36a2..e9ca89c9174b 100644 --- a/sound/pci/ice1712/phase.c +++ b/sound/pci/ice1712/phase.c @@ -723,17 +723,7 @@ static int phase28_oversampling_info(struct snd_kcontrol *k, { static const char * const texts[2] = { "128x", "64x" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int phase28_oversampling_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index 5555eb4b2400..5101f40f6fbd 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -417,13 +417,7 @@ static int cs_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_inf "Optical", /* RXP1 */ "CD", /* RXP2 */ }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int cs_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c index f3b491aa3e22..3919aed39ca0 100644 --- a/sound/pci/ice1712/prodigy192.c +++ b/sound/pci/ice1712/prodigy192.c @@ -284,15 +284,7 @@ static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol, { static const char * const texts[2] = { "Line In", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } @@ -563,13 +555,7 @@ static int ak4114_input_sw_info(struct snd_kcontrol *kcontrol, { static const char * const texts[2] = { "Toslink", "Coax" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } @@ -772,10 +758,8 @@ static int prodigy192_init(struct snd_ice1712 *ice) "AK4114 initialized with status %d\n", err); } else dev_dbg(ice->card->dev, "AK4114 not found\n"); - if (err < 0) - return err; - return 0; + return err; } diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c index 2261d1e49150..2697402b5195 100644 --- a/sound/pci/ice1712/prodigy_hifi.c +++ b/sound/pci/ice1712/prodigy_hifi.c @@ -537,7 +537,7 @@ static int wm_master_vol_put(struct snd_kcontrol *kcontrol, static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char* texts[32] = { + static const char * const texts[32] = { "NULL", WM_AIN1, WM_AIN2, WM_AIN1 "+" WM_AIN2, WM_AIN3, WM_AIN1 "+" WM_AIN3, WM_AIN2 "+" WM_AIN3, WM_AIN1 "+" WM_AIN2 "+" WM_AIN3, @@ -560,14 +560,7 @@ static int wm_adc_mux_enum_info(struct snd_kcontrol *kcontrol, WM_AIN1 "+" WM_AIN2 "+" WM_AIN3 "+" WM_AIN4 "+" WM_AIN5 }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 32; - if (uinfo->value.enumerated.item > 31) - uinfo->value.enumerated.item = 31; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 32, texts); } static int wm_adc_mux_enum_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c index 2c2df4b74e01..6f55e02e5c84 100644 --- a/sound/pci/ice1712/quartet.c +++ b/sound/pci/ice1712/quartet.c @@ -46,7 +46,7 @@ struct qtet_kcontrol_private { unsigned int bit; void (*set_register)(struct snd_ice1712 *ice, unsigned int val); unsigned int (*get_register)(struct snd_ice1712 *ice); - unsigned char * const texts[2]; + const char * const texts[2]; }; enum { @@ -554,17 +554,7 @@ static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol, { static const char * const texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(texts); - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int qtet_ain12_sw_get(struct snd_kcontrol *kcontrol, @@ -706,17 +696,8 @@ static int qtet_enum_info(struct snd_kcontrol *kcontrol, { struct qtet_kcontrol_private private = qtet_privates[kcontrol->private_value]; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ARRAY_SIZE(private.texts); - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, - private.texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(private.texts), + private.texts); } static int qtet_sw_get(struct snd_kcontrol *kcontrol, @@ -852,11 +833,8 @@ static int qtet_add_controls(struct snd_ice1712 *ice) if (err < 0) return err; /* only capture SPDIF over AK4113 */ - err = snd_ak4113_build(spec->ak4113, + return snd_ak4113_build(spec->ak4113, ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream); - if (err < 0) - return err; - return 0; } static inline int qtet_is_spdif_master(struct snd_ice1712 *ice) diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index 1112ec1953be..1d81ae677573 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -494,11 +494,13 @@ static int ap192_ak4114_init(struct snd_ice1712 *ice) ap192_ak4114_write, ak4114_init_vals, ak4114_init_txcsb, ice, &spec->ak4114); + if (err < 0) + return err; /* AK4114 in Revo cannot detect external rate correctly. * No reason to stop capture stream due to incorrect checks */ spec->ak4114->check_flags = AK4114_CHECK_NO_RATE; - return 0; /* error ignored; it's no fatal error */ + return 0; } static int revo_init(struct snd_ice1712 *ice) diff --git a/sound/pci/ice1712/se.c b/sound/pci/ice1712/se.c index ffd894bb4507..1c5d5b22c7a0 100644 --- a/sound/pci/ice1712/se.c +++ b/sound/pci/ice1712/se.c @@ -452,14 +452,7 @@ static int se200pci_cont_enum_info(struct snd_kcontrol *kc, c = se200pci_get_enum_count(n); if (!c) return -EINVAL; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = c; - if (uinfo->value.enumerated.item >= c) - uinfo->value.enumerated.item = c - 1; - strcpy(uinfo->value.enumerated.name, - se200pci_cont[n].member[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, c, se200pci_cont[n].member); } static int se200pci_cont_volume_get(struct snd_kcontrol *kc, diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 9fe549b2efdf..59d21c9401d2 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -444,9 +444,9 @@ static char *stateName[] = { "Invalid" }; -static char *clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" }; +static const char * const clockSourceTypeName[] = { "ADAT", "S/PDIF", "local" }; -static char *clockSourceName[] = { +static const char * const clockSourceName[] = { "ADAT at 44.1 kHz", "ADAT at 48 kHz", "S/PDIF at 44.1 kHz", @@ -455,7 +455,7 @@ static char *clockSourceName[] = { "local clock at 48 kHz" }; -static char *channelName[] = { +static const char * const channelName[] = { "ADAT-1", "ADAT-2", "ADAT-3", @@ -1844,14 +1844,9 @@ static int snd_korg1212_control_volume_put(struct snd_kcontrol *kcontrol, static int snd_korg1212_control_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = (kcontrol->private_value >= 8) ? 2 : 1; - uinfo->value.enumerated.items = kAudioChannels; - if (uinfo->value.enumerated.item > kAudioChannels-1) { - uinfo->value.enumerated.item = kAudioChannels-1; - } - strcpy(uinfo->value.enumerated.name, channelName[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, + (kcontrol->private_value >= 8) ? 2 : 1, + kAudioChannels, channelName); } static int snd_korg1212_control_route_get(struct snd_kcontrol *kcontrol, @@ -1961,14 +1956,7 @@ static int snd_korg1212_control_put(struct snd_kcontrol *kcontrol, static int snd_korg1212_control_sync_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) { - uinfo->value.enumerated.item = 2; - } - strcpy(uinfo->value.enumerated.name, clockSourceTypeName[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, clockSourceTypeName); } static int snd_korg1212_control_sync_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/lola/lola.c b/sound/pci/lola/lola.c index a75c8dc66dec..4cf4be5ef82a 100644 --- a/sound/pci/lola/lola.c +++ b/sound/pci/lola/lola.c @@ -719,7 +719,7 @@ static int lola_probe(struct pci_dev *pci, err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) { - dev_err(card->dev, "Error creating card!\n"); + dev_err(&pci->dev, "Error creating card!\n"); return err; } diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c index 782f4d8299ae..e7fe15dd5a90 100644 --- a/sound/pci/lola/lola_mixer.c +++ b/sound/pci/lola/lola_mixer.c @@ -108,8 +108,7 @@ int lola_init_pins(struct lola *chip, int dir, int *nidp) void lola_free_mixer(struct lola *chip) { - if (chip->mixer.array_saved) - vfree(chip->mixer.array_saved); + vfree(chip->mixer.array_saved); } int lola_init_mixer_widget(struct lola *chip, int nid) diff --git a/sound/pci/lx6464es/lx_defs.h b/sound/pci/lx6464es/lx_defs.h index 49d36bdd512c..469bcc685edf 100644 --- a/sound/pci/lx6464es/lx_defs.h +++ b/sound/pci/lx6464es/lx_defs.h @@ -175,7 +175,7 @@ enum buffer_flags { BF_ZERO = 0x00, /* no flags (init).*/ }; -/** +/* * Stream Flags definitions */ enum stream_flags { diff --git a/sound/pci/mixart/mixart_hwdep.c b/sound/pci/mixart/mixart_hwdep.c index 581e1e74863c..9996a4dead0f 100644 --- a/sound/pci/mixart/mixart_hwdep.c +++ b/sound/pci/mixart/mixart_hwdep.c @@ -37,10 +37,11 @@ /** * wait for a value on a peudo register, exit with a timeout * - * @param mgr pointer to miXart manager structure - * @param offset unsigned pseudo_register base + offset of value - * @param value value - * @param timeout timeout in centisenconds + * @mgr: pointer to miXart manager structure + * @offset: unsigned pseudo_register base + offset of value + * @is_egal: wait for the equal value + * @value: value + * @timeout: timeout in centisenconds */ static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr, u32 offset, int is_egal, diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index b854fc5e01f5..c6092e48ceb6 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -501,10 +501,10 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, /* * start or stop playback/capture substream */ -static int pcxhr_set_stream_state(struct pcxhr_stream *stream) +static int pcxhr_set_stream_state(struct snd_pcxhr *chip, + struct pcxhr_stream *stream) { int err; - struct snd_pcxhr *chip; struct pcxhr_rmh rmh; int stream_mask, start; @@ -512,8 +512,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream) start = 1; else { if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) { - snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state " - "CANNOT be stopped\n"); + dev_err(chip->card->dev, + "pcxhr_set_stream_state CANNOT be stopped\n"); return -EINVAL; } start = 0; @@ -560,6 +560,7 @@ static int pcxhr_set_format(struct pcxhr_stream *stream) struct pcxhr_rmh rmh; unsigned int header; + chip = snd_pcm_substream_chip(stream->substream); switch (stream->format) { case SNDRV_PCM_FORMAT_U8: header = HEADER_FMT_BASE_LIN; @@ -582,11 +583,10 @@ static int pcxhr_set_format(struct pcxhr_stream *stream) header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL; break; default: - snd_printk(KERN_ERR - "error pcxhr_set_format() : unknown format\n"); + dev_err(chip->card->dev, + "error pcxhr_set_format() : unknown format\n"); return -EINVAL; } - chip = snd_pcm_substream_chip(stream->substream); sample_rate = chip->mgr->sample_rate; if (sample_rate <= 32000 && sample_rate !=0) { @@ -643,11 +643,11 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream) is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE); stream_num = is_capture ? 0 : subs->number; - snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : " - "addr(%p) bytes(%zx) subs(%d)\n", - is_capture ? 'c' : 'p', - chip->chip_idx, (void *)(long)subs->runtime->dma_addr, - subs->runtime->dma_bytes, subs->number); + dev_dbg(chip->card->dev, + "pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)\n", + is_capture ? 'c' : 'p', + chip->chip_idx, (void *)(long)subs->runtime->dma_addr, + subs->runtime->dma_bytes, subs->number); pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS); pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, @@ -687,7 +687,7 @@ static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream, *sample_count = ((snd_pcm_uframes_t)rmh.stat[0]) << 24; *sample_count += (snd_pcm_uframes_t)rmh.stat[1]; } - snd_printdd("PIPE_SAMPLE_COUNT = %lx\n", *sample_count); + dev_dbg(chip->card->dev, "PIPE_SAMPLE_COUNT = %lx\n", *sample_count); return err; } #endif @@ -711,8 +711,9 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr) int playback_mask = 0; #ifdef CONFIG_SND_DEBUG_VERBOSE - struct timeval my_tv1, my_tv2; - do_gettimeofday(&my_tv1); + ktime_t start_time, stop_time, diff_time; + + start_time = ktime_get(); #endif mutex_lock(&mgr->setup_mutex); @@ -778,12 +779,12 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr) for (j = 0; j < chip->nb_streams_capt; j++) { stream = &chip->capture_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) - err = pcxhr_set_stream_state(stream); + err = pcxhr_set_stream_state(chip, stream); } for (j = 0; j < chip->nb_streams_play; j++) { stream = &chip->playback_stream[j]; if (pcxhr_stream_scheduled_get_pipe(stream, &pipe)) - err = pcxhr_set_stream_state(stream); + err = pcxhr_set_stream_state(chip, stream); } } @@ -823,9 +824,10 @@ static void pcxhr_start_linked_stream(struct pcxhr_mgr *mgr) mutex_unlock(&mgr->setup_mutex); #ifdef CONFIG_SND_DEBUG_VERBOSE - do_gettimeofday(&my_tv2); + stop_time = ktime_get(); + diff_time = ktime_sub(stop_time, start_time); dev_dbg(&mgr->pci->dev, "***TRIGGER START*** TIME = %ld (err = %x)\n", - (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); + (long)(ktime_to_ns(diff_time)), err); #endif } @@ -837,12 +839,12 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd) { struct pcxhr_stream *stream; struct snd_pcm_substream *s; + struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - snd_printdd("SNDRV_PCM_TRIGGER_START\n"); + dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_START\n"); if (snd_pcm_stream_linked(subs)) { - struct snd_pcxhr *chip = snd_pcm_substream_chip(subs); snd_pcm_group_for_each_entry(s, subs) { if (snd_pcm_substream_chip(s) != chip) continue; @@ -854,7 +856,7 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd) pcxhr_start_linked_stream(chip->mgr); } else { stream = subs->runtime->private_data; - snd_printdd("Only one Substream %c %d\n", + dev_dbg(chip->card->dev, "Only one Substream %c %d\n", stream->pipe->is_capture ? 'C' : 'P', stream->pipe->first_audio); if (pcxhr_set_format(stream)) @@ -863,17 +865,17 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd) return -EINVAL; stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN; - if (pcxhr_set_stream_state(stream)) + if (pcxhr_set_stream_state(chip, stream)) return -EINVAL; stream->status = PCXHR_STREAM_STATUS_RUNNING; } break; case SNDRV_PCM_TRIGGER_STOP: - snd_printdd("SNDRV_PCM_TRIGGER_STOP\n"); + dev_dbg(chip->card->dev, "SNDRV_PCM_TRIGGER_STOP\n"); snd_pcm_group_for_each_entry(s, subs) { stream = s->runtime->private_data; stream->status = PCXHR_STREAM_STATUS_SCHEDULE_STOP; - if (pcxhr_set_stream_state(stream)) + if (pcxhr_set_stream_state(chip, stream)) return -EINVAL; snd_pcm_trigger_done(s, subs); } @@ -1636,7 +1638,7 @@ static int pcxhr_probe(struct pci_dev *pci, 0, &card); if (err < 0) { - dev_err(card->dev, "cannot allocate the card %d\n", i); + dev_err(&pci->dev, "cannot allocate the card %d\n", i); pcxhr_free(mgr); return err; } diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index a584acb61c00..181f7729d409 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -910,8 +910,9 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int audio_mask; #ifdef CONFIG_SND_DEBUG_VERBOSE - struct timeval my_tv1, my_tv2; - do_gettimeofday(&my_tv1); + ktime_t start_time, stop_time, diff_time; + + start_time = ktime_get(); #endif audio_mask = (playback_mask | (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET)); @@ -960,9 +961,10 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, return err; } #ifdef CONFIG_SND_DEBUG_VERBOSE - do_gettimeofday(&my_tv2); + stop_time = ktime_get(); + diff_time = ktime_sub(stop_time, start_time); dev_dbg(&mgr->pci->dev, "***SET PIPE STATE*** TIME = %ld (err = %x)\n", - (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); + (long)(ktime_to_ns(diff_time)), err); #endif return 0; } diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c index 95c9571780d8..63136c4f3f3d 100644 --- a/sound/pci/pcxhr/pcxhr_mixer.c +++ b/sound/pci/pcxhr/pcxhr_mixer.c @@ -660,14 +660,7 @@ static int pcxhr_audio_src_info(struct snd_kcontrol *kcontrol, if (chip->mgr->board_has_mic) i = 5; /* Mic and MicroMix available */ } - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = i; - if (uinfo->value.enumerated.item > (i-1)) - uinfo->value.enumerated.item = i-1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, i, texts); } static int pcxhr_audio_src_get(struct snd_kcontrol *kcontrol, @@ -756,14 +749,7 @@ static int pcxhr_clock_type_info(struct snd_kcontrol *kcontrol, texts = textsPCXHR; snd_BUG_ON(clock_items > (PCXHR_CLOCK_TYPE_MAX+1)); } - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = clock_items; - if (uinfo->value.enumerated.item >= clock_items) - uinfo->value.enumerated.item = clock_items-1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, clock_items, texts); } static int pcxhr_clock_type_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 4afd3cab775b..6c60dcd2e5a1 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1608,30 +1608,24 @@ snd_rme32_info_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct rme32 *rme32 = snd_kcontrol_chip(kcontrol); - static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" }; + static const char * const texts[4] = { + "Optical", "Coaxial", "Internal", "XLR" + }; + int num_items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; switch (rme32->pci->device) { case PCI_DEVICE_ID_RME_DIGI32: case PCI_DEVICE_ID_RME_DIGI32_8: - uinfo->value.enumerated.items = 3; + num_items = 3; break; case PCI_DEVICE_ID_RME_DIGI32_PRO: - uinfo->value.enumerated.items = 4; + num_items = 4; break; default: snd_BUG(); - break; - } - if (uinfo->value.enumerated.item > - uinfo->value.enumerated.items - 1) { - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; + return -EINVAL; } - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_rme32_get_inputtype_control(struct snd_kcontrol *kcontrol, @@ -1695,20 +1689,12 @@ static int snd_rme32_info_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "AutoSync", + static const char * const texts[4] = { "AutoSync", "Internal 32.0kHz", "Internal 44.1kHz", "Internal 48.0kHz" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) { - uinfo->value.enumerated.item = 3; - } - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme32_get_clockmode_control(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 5a395c87c6fc..2f1a85185a16 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1884,39 +1884,38 @@ snd_rme96_put_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_ele static int snd_rme96_info_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" }; + static const char * const _texts[5] = { + "Optical", "Coaxial", "Internal", "XLR", "Analog" + }; struct rme96 *rme96 = snd_kcontrol_chip(kcontrol); - char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] }; + const char *texts[5] = { + _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] + }; + int num_items; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; switch (rme96->pci->device) { case PCI_DEVICE_ID_RME_DIGI96: case PCI_DEVICE_ID_RME_DIGI96_8: - uinfo->value.enumerated.items = 3; + num_items = 3; break; case PCI_DEVICE_ID_RME_DIGI96_8_PRO: - uinfo->value.enumerated.items = 4; + num_items = 4; break; case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST: if (rme96->rev > 4) { /* PST */ - uinfo->value.enumerated.items = 4; + num_items = 4; texts[3] = _texts[4]; /* Analog instead of XLR */ } else { /* PAD */ - uinfo->value.enumerated.items = 5; + num_items = 5; } break; default: snd_BUG(); - break; - } - if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) { - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; + return -EINVAL; } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_rme96_get_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2002,16 +2001,9 @@ snd_rme96_put_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el static int snd_rme96_info_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = { "AutoSync", "Internal", "Word" }; + static const char * const texts[3] = { "AutoSync", "Internal", "Word" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) { - uinfo->value.enumerated.item = 2; - } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_rme96_get_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2041,16 +2033,11 @@ snd_rme96_put_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_el static int snd_rme96_info_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" }; + static const char * const texts[4] = { + "0 dB", "-6 dB", "-12 dB", "-18 dB" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) { - uinfo->value.enumerated.item = 3; - } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme96_get_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2081,16 +2068,9 @@ snd_rme96_put_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_ static int snd_rme96_info_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" }; + static const char * const texts[4] = { "1+2", "3+4", "5+6", "7+8" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) { - uinfo->value.enumerated.item = 3; - } - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme96_get_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 7646ba1664eb..cf5a6c8b9a63 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1680,16 +1680,13 @@ static int hdsp_set_spdif_input(struct hdsp *hdsp, int in) static int snd_hdsp_info_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"Optical", "Coaxial", "Internal", "AES"}; + static const char * const texts[4] = { + "Optical", "Coaxial", "Internal", "AES" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = ((hdsp->io_type == H9632) ? 4 : 3); - if (uinfo->value.enumerated.item > ((hdsp->io_type == H9632) ? 3 : 2)) - uinfo->value.enumerated.item = ((hdsp->io_type == H9632) ? 3 : 2); - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 4 : 3, + texts); } static int snd_hdsp_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1786,16 +1783,14 @@ static int snd_hdsp_put_toggle_setting(struct snd_kcontrol *kcontrol, static int snd_hdsp_info_spdif_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"}; + static const char * const texts[] = { + "32000", "44100", "48000", "64000", "88200", "96000", + "None", "128000", "176400", "192000" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7, + texts); } static int snd_hdsp_get_spdif_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1872,14 +1867,13 @@ static int snd_hdsp_get_system_sample_rate(struct snd_kcontrol *kcontrol, struct static int snd_hdsp_info_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - static char *texts[] = {"32000", "44100", "48000", "64000", "88200", "96000", "None", "128000", "176400", "192000"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = (hdsp->io_type == H9632) ? 10 : 7 ; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + static const char * const texts[] = { + "32000", "44100", "48000", "64000", "88200", "96000", + "None", "128000", "176400", "192000" + }; + + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7, + texts); } static int snd_hdsp_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1940,15 +1934,9 @@ static int hdsp_system_clock_mode(struct hdsp *hdsp) static int snd_hdsp_info_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Master", "Slave" }; + static const char * const texts[] = {"Master", "Slave" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_hdsp_get_system_clock_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2049,19 +2037,16 @@ static int hdsp_set_clock_source(struct hdsp *hdsp, int mode) static int snd_hdsp_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", "Internal 192.0 KHz" }; + static const char * const texts[] = { + "AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz", + "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz", + "Internal 96.0 kHz", "Internal 128 kHz", "Internal 176.4 kHz", + "Internal 192.0 KHz" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - if (hdsp->io_type == H9632) - uinfo->value.enumerated.items = 10; - else - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, (hdsp->io_type == H9632) ? 10 : 7, + texts); } static int snd_hdsp_get_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2165,15 +2150,9 @@ static int hdsp_set_da_gain(struct hdsp *hdsp, int mode) static int snd_hdsp_info_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"}; + static const char * const texts[] = {"Hi Gain", "+4 dBu", "-10 dbV"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_hdsp_get_da_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2250,15 +2229,9 @@ static int hdsp_set_ad_gain(struct hdsp *hdsp, int mode) static int snd_hdsp_info_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"}; + static const char * const texts[] = {"-10 dBV", "+4 dBu", "Lo Gain"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_hdsp_get_ad_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2335,15 +2308,9 @@ static int hdsp_set_phone_gain(struct hdsp *hdsp, int mode) static int snd_hdsp_info_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"0 dB", "-6 dB", "-12 dB"}; + static const char * const texts[] = {"0 dB", "-6 dB", "-12 dB"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_hdsp_get_phone_gain(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2439,31 +2406,28 @@ static int hdsp_set_pref_sync_ref(struct hdsp *hdsp, int pref) static int snd_hdsp_info_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" }; + static const char * const texts[] = { + "Word", "IEC958", "ADAT1", "ADAT Sync", "ADAT2", "ADAT3" + }; struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; + int num_items; switch (hdsp->io_type) { case Digiface: case H9652: - uinfo->value.enumerated.items = 6; + num_items = 6; break; case Multiface: - uinfo->value.enumerated.items = 4; + num_items = 4; break; case H9632: - uinfo->value.enumerated.items = 3; + num_items = 3; break; default: return -EINVAL; } - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, num_items, texts); } static int snd_hdsp_get_pref_sync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2543,15 +2507,11 @@ static int hdsp_autosync_ref(struct hdsp *hdsp) static int snd_hdsp_info_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3" }; + static const char * const texts[] = { + "Word", "ADAT Sync", "IEC958", "None", "ADAT1", "ADAT2", "ADAT3" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 7, texts); } static int snd_hdsp_get_autosync_ref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -2738,14 +2698,9 @@ static int snd_hdsp_put_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem static int snd_hdsp_info_sync_check(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"No Lock", "Lock", "Sync" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + static const char * const texts[] = {"No Lock", "Lock", "Sync" }; + + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int hdsp_wc_sync_check(struct hdsp *hdsp) @@ -3101,15 +3056,11 @@ static int snd_hdsp_put_rpm_input12(struct snd_kcontrol *kcontrol, struct snd_ct static int snd_hdsp_info_rpm_input(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB"}; + static const char * const texts[] = { + "Phono +6dB", "Phono 0dB", "Phono -6dB", "Line 0dB", "Line -6dB" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 5; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 5, texts); } @@ -3234,15 +3185,9 @@ static int snd_hdsp_put_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl static int snd_hdsp_info_rpm_bypass(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"On", "Off"}; + static const char * const texts[] = {"On", "Off"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } @@ -3291,15 +3236,9 @@ static int snd_hdsp_put_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd static int snd_hdsp_info_rpm_disconnect(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[] = {"On", "Off"}; + static const char * const texts[] = {"On", "Off"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static struct snd_kcontrol_new snd_hdsp_rpm_controls[] = { @@ -5368,8 +5307,7 @@ static int snd_hdsp_free(struct hdsp *hdsp) snd_hdsp_free_buffers(hdsp); - if (hdsp->firmware) - release_firmware(hdsp->firmware); + release_firmware(hdsp->firmware); vfree(hdsp->fw_uploaded); if (hdsp->iobase) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 52d86af3ef2d..3342705a5715 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1257,14 +1257,13 @@ static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate) /* check for external sample rate, returns the sample rate in Hz*/ static int hdspm_external_sample_rate(struct hdspm *hdspm) { - unsigned int status, status2, timecode; + unsigned int status, status2; int syncref, rate = 0, rate_bits; switch (hdspm->io_type) { case AES32: status2 = hdspm_read(hdspm, HDSPM_statusRegister2); status = hdspm_read(hdspm, HDSPM_statusRegister); - timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); syncref = hdspm_autosync_ref(hdspm); switch (syncref) { @@ -2202,10 +2201,10 @@ static inline int hdspm_get_pll_freq(struct hdspm *hdspm) return rate; } -/** +/* * Calculate the real sample rate from the * current DDS value. - **/ + */ static int hdspm_get_system_sample_rate(struct hdspm *hdspm) { unsigned int rate; @@ -2271,9 +2270,9 @@ static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol, } -/** +/* * Returns the WordClock sample rate class for the given card. - **/ + */ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) { int status; @@ -2296,9 +2295,9 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) } -/** +/* * Returns the TCO sample rate class for the given card. - **/ + */ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) { int status; @@ -2322,9 +2321,9 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) } -/** +/* * Returns the SYNC_IN sample rate class for the given card. - **/ + */ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) { int status; @@ -2344,9 +2343,9 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) return 0; } -/** +/* * Returns the AES sample rate class for the given card. - **/ + */ static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index) { int timecode; @@ -2362,10 +2361,10 @@ static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index) return 0; } -/** +/* * Returns the sample rate class for input source <idx> for * 'new style' cards like the AIO and RayDAT. - **/ + */ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx) { int status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); @@ -2513,10 +2512,10 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol, } -/** +/* * Returns the system clock mode for the given card. * @returns 0 - master, 1 - slave - **/ + */ static int hdspm_system_clock_mode(struct hdspm *hdspm) { switch (hdspm->io_type) { @@ -2535,10 +2534,10 @@ static int hdspm_system_clock_mode(struct hdspm *hdspm) } -/** +/* * Sets the system clock mode. * @param mode 0 - master, 1 - slave - **/ + */ static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode) { hdspm_set_toggle_setting(hdspm, @@ -2645,18 +2644,7 @@ static int hdspm_set_clock_source(struct hdspm * hdspm, int mode) static int snd_hdspm_info_clock_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 9; - - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = - uinfo->value.enumerated.items - 1; - - strcpy(uinfo->value.enumerated.name, - texts_freq[uinfo->value.enumerated.item+1]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, 9, texts_freq + 1); } static int snd_hdspm_get_clock_source(struct snd_kcontrol *kcontrol, @@ -2704,11 +2692,11 @@ static int snd_hdspm_put_clock_source(struct snd_kcontrol *kcontrol, } -/** +/* * Returns the current preferred sync reference setting. * The semantics of the return value are depending on the * card, please see the comments for clarification. - **/ + */ static int hdspm_pref_sync_ref(struct hdspm * hdspm) { switch (hdspm->io_type) { @@ -2807,11 +2795,11 @@ static int hdspm_pref_sync_ref(struct hdspm * hdspm) } -/** +/* * Set the preferred sync reference to <pref>. The semantics * of <pref> are depending on the card type, see the comments * for clarification. - **/ + */ static int hdspm_set_pref_sync_ref(struct hdspm * hdspm, int pref) { int p = 0; @@ -4113,9 +4101,9 @@ static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol, -/** +/* * TCO controls - **/ + */ static void hdspm_tco_write(struct hdspm *hdspm) { unsigned int tc[4] = { 0, 0, 0, 0}; @@ -4873,18 +4861,15 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status, status2, control, freq; + unsigned int status, status2; char *pref_sync_ref; char *autosync_ref; char *system_clock_mode; - char *insel; int x, x2; status = hdspm_read(hdspm, HDSPM_statusRegister); status2 = hdspm_read(hdspm, HDSPM_statusRegister2); - control = hdspm->control_register; - freq = hdspm_read(hdspm, HDSPM_timecodeRegister); snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n", hdspm->card_name, hdspm->card->number + 1, @@ -4947,17 +4932,6 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry, snd_iprintf(buffer, "Line out: %s\n", (hdspm->control_register & HDSPM_LineOut) ? "on " : "off"); - switch (hdspm->control_register & HDSPM_InputMask) { - case HDSPM_InputOptical: - insel = "Optical"; - break; - case HDSPM_InputCoaxial: - insel = "Coaxial"; - break; - default: - insel = "Unknown"; - } - snd_iprintf(buffer, "ClearTrackMarker = %s, Transmit in %s Channel Mode, " "Auto Input %s\n", @@ -5202,15 +5176,13 @@ snd_hdspm_proc_read_raydat(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct hdspm *hdspm = entry->private_data; - unsigned int status1, status2, status3, control, i; + unsigned int status1, status2, status3, i; unsigned int lock, sync; status1 = hdspm_read(hdspm, HDSPM_RD_STATUS_1); /* s1 */ status2 = hdspm_read(hdspm, HDSPM_RD_STATUS_2); /* freq */ status3 = hdspm_read(hdspm, HDSPM_RD_STATUS_3); /* s2 */ - control = hdspm->control_register; - snd_iprintf(buffer, "STATUS1: 0x%08x\n", status1); snd_iprintf(buffer, "STATUS2: 0x%08x\n", status2); snd_iprintf(buffer, "STATUS3: 0x%08x\n", status3); @@ -5431,7 +5403,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) HDSPM_midi2IRQPending | HDSPM_midi3IRQPending); /* now = get_cycles(); */ - /** + /* * LAT_2..LAT_0 period counter (win) counter (mac) * 6 4096 ~256053425 ~514672358 * 5 2048 ~128024983 ~257373821 @@ -5440,7 +5412,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) * 2 256 ~16003039 ~32260176 * 1 128 ~7998738 ~16194507 * 0 64 ~3998231 ~8191558 - **/ + */ /* dev_info(hdspm->card->dev, "snd_hdspm_interrupt %llu @ %llx\n", now-hdspm->last_interrupt, status & 0xFFC0); diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index fa9a2a8dce5a..6521521853b8 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -920,15 +920,9 @@ static int rme9652_set_adat1_input(struct snd_rme9652 *rme9652, int internal) static int snd_rme9652_info_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = {"ADAT1", "Internal"}; + static const char * const texts[2] = {"ADAT1", "Internal"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_rme9652_get_adat1_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -991,15 +985,9 @@ static int rme9652_set_spdif_input(struct snd_rme9652 *rme9652, int in) static int snd_rme9652_info_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = {"ADAT1", "Coaxial", "Internal"}; + static const char * const texts[3] = {"ADAT1", "Coaxial", "Internal"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_rme9652_get_spdif_in(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1140,15 +1128,11 @@ static int rme9652_set_sync_mode(struct snd_rme9652 *rme9652, int mode) static int snd_rme9652_info_sync_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[3] = {"AutoSync", "Master", "Word Clock"}; + static const char * const texts[3] = { + "AutoSync", "Master", "Word Clock" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 3; - if (uinfo->value.enumerated.item > 2) - uinfo->value.enumerated.item = 2; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 3, texts); } static int snd_rme9652_get_sync_mode(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1231,16 +1215,14 @@ static int rme9652_set_sync_pref(struct snd_rme9652 *rme9652, int pref) static int snd_rme9652_info_sync_pref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In"}; + static const char * const texts[4] = { + "IEC958 In", "ADAT1 In", "ADAT2 In", "ADAT3 In" + }; struct snd_rme9652 *rme9652 = snd_kcontrol_chip(kcontrol); - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, + rme9652->ss_channels == RME9652_NCHANNELS ? 4 : 3, + texts); } static int snd_rme9652_get_sync_pref(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -1392,15 +1374,11 @@ static int snd_rme9652_get_spdif_rate(struct snd_kcontrol *kcontrol, struct snd_ static int snd_rme9652_info_adat_sync(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = {"No Lock", "Lock", "No Lock Sync", "Lock Sync"}; + static const char * const texts[4] = { + "No Lock", "Lock", "No Lock Sync", "Lock Sync" + }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) - uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 4, texts); } static int snd_rme9652_get_adat_sync(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 5b0d317cc9a6..313a7328bf9c 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -918,17 +918,11 @@ static int snd_sonicvibes_pcm(struct sonicvibes *sonic, int device, static int snd_sonicvibes_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[7] = { + static const char * const texts[7] = { "CD", "PCM", "Aux1", "Line", "Aux0", "Mic", "Mix" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 7; - if (uinfo->value.enumerated.item >= 7) - uinfo->value.enumerated.item = 6; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 2, 7, texts); } static int snd_sonicvibes_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index da875dced2ef..57cd757acfe7 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -3702,8 +3702,7 @@ static int snd_trident_free(struct snd_trident *trident) free_irq(trident->irq, trident); if (trident->tlb.buffer.area) { outl(0, TRID_REG(trident, NX_TLBC)); - if (trident->tlb.memhdr) - snd_util_memhdr_free(trident->tlb.memhdr); + snd_util_memhdr_free(trident->tlb.memhdr); if (trident->tlb.silent_page.area) snd_dma_free_pages(&trident->tlb.silent_page); vfree(trident->tlb.shadow_entries); diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index ecedf4dbfa2a..e088467fb736 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1610,16 +1610,10 @@ static int snd_via8233_capture_source_info(struct snd_kcontrol *kcontrol, /* formerly they were "Line" and "Mic", but it looks like that they * have nothing to do with the actual physical connections... */ - static char *texts[2] = { + static const char * const texts[2] = { "Input1", "Input2" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item >= 2) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snd_via8233_capture_source_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index 2d1570273e99..52c1a8d5b88a 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -92,6 +92,7 @@ static inline unsigned long vx2_reg_addr(struct vx_core *_chip, int reg) /** * snd_vx_inb - read a byte from the register + * @chip: VX core instance * @offset: register enum */ static unsigned char vx2_inb(struct vx_core *chip, int offset) @@ -101,6 +102,7 @@ static unsigned char vx2_inb(struct vx_core *chip, int offset) /** * snd_vx_outb - write a byte on the register + * @chip: VX core instance * @offset: the register offset * @val: the value to write */ @@ -114,6 +116,7 @@ static void vx2_outb(struct vx_core *chip, int offset, unsigned char val) /** * snd_vx_inl - read a 32bit word from the register + * @chip: VX core instance * @offset: register enum */ static unsigned int vx2_inl(struct vx_core *chip, int offset) @@ -123,6 +126,7 @@ static unsigned int vx2_inl(struct vx_core *chip, int offset) /** * snd_vx_outl - write a 32bit word on the register + * @chip: VX core instance * @offset: the register enum * @val: the value to write */ @@ -223,6 +227,7 @@ static int vx2_test_xilinx(struct vx_core *_chip) /** * vx_setup_pseudo_dma - set up the pseudo dma read/write mode. + * @chip: VX core instance * @do_write: 0 = read, 1 = set up for DMA write */ static void vx2_setup_pseudo_dma(struct vx_core *chip, int do_write) diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 92ec11456e3a..b16f42deed67 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -174,6 +174,7 @@ static int snd_vxpocket_new(struct snd_card *card, int ibl, /** * snd_vxpocket_assign_resources - initialize the hardware and card instance. + * @chip: VX core instance * @port: i/o port for the card * @irq: irq number for the card * diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 8a431bcb056c..5a13b22748b2 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -887,8 +887,7 @@ static int snd_pmac_free(struct snd_pmac *chip) } } - if (chip->pdev) - pci_dev_put(chip->pdev); + pci_dev_put(chip->pdev); of_node_put(chip->node); kfree(chip); return 0; diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index b9ffc17a4799..24c8766a925d 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -795,16 +795,11 @@ static int snapper_set_capture_source(struct pmac_tumbler *mix) static int snapper_info_capture_source(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[2] = { + static const char * const texts[2] = { "Line", "Mic" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); - return 0; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); } static int snapper_get_capture_source(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index b79a2a864513..33fb3bb133df 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -80,9 +80,7 @@ static void atmel_pcm_dma_irq(u32 ssc_sr, /* stop RX and capture: will be enabled again at restart */ ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable); - snd_pcm_stream_lock(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock(substream); + snd_pcm_stop_xrun(substream); /* now drain RHR and read status to remove xrun condition */ ssc_readx(prtd->ssc->regs, SSC_RHR); diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index a609aafc994d..b2b108805b24 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -151,14 +151,7 @@ static const struct snd_pcm_hardware fsl_dma_hardware = { */ static void fsl_dma_abort_stream(struct snd_pcm_substream *substream) { - unsigned long flags; - - snd_pcm_stream_lock_irqsave(substream, flags); - - if (snd_pcm_running(substream)) - snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); - - snd_pcm_stream_unlock_irqrestore(substream, flags); + snd_pcm_stop_xrun(substream); } /** diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 4e91bcaa3664..06606f9bbf78 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -1285,19 +1285,11 @@ static int snd_cs4231_timer(struct snd_card *card) static int snd_cs4231_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static char *texts[4] = { + static const char * const texts[4] = { "Line", "CD", "Mic", "Mix" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 2; - uinfo->value.enumerated.items = 4; - if (uinfo->value.enumerated.item > 3) - uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 2, 4, texts); } static int snd_cs4231_get_mux(struct snd_kcontrol *kcontrol, diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index 184e3987ac24..54656eed6e2e 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -25,8 +25,8 @@ #include "comm.h" #include "chip.h" -static char *opt_coax_texts[2] = { "Optical", "Coax" }; -static char *line_phono_texts[2] = { "Line", "Phono" }; +static const char * const opt_coax_texts[2] = { "Optical", "Coax" }; +static const char * const line_phono_texts[2] = { "Line", "Phono" }; /* * data that needs to be sent to device. sets up card internal stuff. @@ -327,14 +327,7 @@ static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol, static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - line_phono_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts); } static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol, @@ -361,14 +354,7 @@ static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol, static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - opt_coax_texts[uinfo->value.enumerated.item]); - return 0; + return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts); } static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol, diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 3b02e54b8f6d..62c25e74f0e5 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -316,7 +316,7 @@ static int usb6fire_fw_fpga_upload( while (c != end) { for (i = 0; c != end && i < FPGA_BUFSIZE; i++, c++) - buffer[i] = byte_rev_table[(u8) *c]; + buffer[i] = bitrev8((u8)*c); ret = usb6fire_fw_fpga_write(device, buffer, i); if (ret < 0) { diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index ba40489b2de4..36f4115eb1cd 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -679,25 +679,16 @@ int usb6fire_pcm_init(struct sfire_chip *chip) void usb6fire_pcm_abort(struct sfire_chip *chip) { struct pcm_runtime *rt = chip->pcm; - unsigned long flags; int i; if (rt) { rt->panic = true; - if (rt->playback.instance) { - snd_pcm_stream_lock_irqsave(rt->playback.instance, flags); - snd_pcm_stop(rt->playback.instance, - SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(rt->playback.instance, flags); - } + if (rt->playback.instance) + snd_pcm_stop_xrun(rt->playback.instance); - if (rt->capture.instance) { - snd_pcm_stream_lock_irqsave(rt->capture.instance, flags); - snd_pcm_stop(rt->capture.instance, - SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(rt->capture.instance, flags); - } + if (rt->capture.instance) + snd_pcm_stop_xrun(rt->capture.instance); for (i = 0; i < PCM_N_URBS; i++) { usb_poison_urb(&rt->in_urbs[i].instance); diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 2b92f0dcbc4c..bcee4060fd18 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -9,6 +9,7 @@ snd-usb-audio-objs := card.o \ helper.o \ mixer.o \ mixer_quirks.o \ + mixer_scarlett.o \ pcm.o \ proc.o \ quirks.o \ diff --git a/sound/usb/card.c b/sound/usb/card.c index f61ebb17cc64..1fab9778807a 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -112,15 +112,13 @@ static struct usb_driver usb_audio_driver; /* * disconnect streams - * called from snd_usb_audio_disconnect() + * called from usb_audio_disconnect() */ -static void snd_usb_stream_disconnect(struct list_head *head) +static void snd_usb_stream_disconnect(struct snd_usb_stream *as) { int idx; - struct snd_usb_stream *as; struct snd_usb_substream *subs; - as = list_entry(head, struct snd_usb_stream, list); for (idx = 0; idx < 2; idx++) { subs = &as->substream[idx]; if (!subs->num_formats) @@ -307,10 +305,10 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) static int snd_usb_audio_free(struct snd_usb_audio *chip) { - struct list_head *p, *n; + struct snd_usb_endpoint *ep, *n; - list_for_each_safe(p, n, &chip->ep_list) - snd_usb_endpoint_free(p); + list_for_each_entry_safe(ep, n, &chip->ep_list, list) + snd_usb_endpoint_free(ep); mutex_destroy(&chip->mutex); kfree(chip); @@ -323,16 +321,6 @@ static int snd_usb_audio_dev_free(struct snd_device *device) return snd_usb_audio_free(chip); } -static void remove_trailing_spaces(char *str) -{ - char *p; - - if (!*str) - return; - for (p = str + strlen(str) - 1; p >= str && isspace(*p); p--) - *p = 0; -} - /* * create a chip instance and set its names. */ @@ -416,7 +404,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, USB_ID_PRODUCT(chip->usb_id)); } } - remove_trailing_spaces(card->shortname); + strim(card->shortname); /* retrieve the vendor and device strings as longname */ if (quirk && quirk->vendor_name && *quirk->vendor_name) { @@ -430,7 +418,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, /* we don't really care if there isn't any vendor string */ } if (len > 0) { - remove_trailing_spaces(card->longname); + strim(card->longname); if (*card->longname) strlcat(card->longname, " ", sizeof(card->longname)); } @@ -475,14 +463,14 @@ static int snd_usb_audio_create(struct usb_interface *intf, * only at the first time. the successive calls of this function will * append the pcm interface to the corresponding card. */ -static struct snd_usb_audio * -snd_usb_audio_probe(struct usb_device *dev, - struct usb_interface *intf, - const struct usb_device_id *usb_id) +static int usb_audio_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) { - const struct snd_usb_audio_quirk *quirk = (const struct snd_usb_audio_quirk *)usb_id->driver_info; - int i, err; + struct usb_device *dev = interface_to_usbdev(intf); + const struct snd_usb_audio_quirk *quirk = + (const struct snd_usb_audio_quirk *)usb_id->driver_info; struct snd_usb_audio *chip; + int i, err; struct usb_host_interface *alts; int ifnum; u32 id; @@ -492,10 +480,11 @@ snd_usb_audio_probe(struct usb_device *dev, id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum) - goto __err_val; + return -ENXIO; - if (snd_usb_apply_boot_quirk(dev, intf, quirk) < 0) - goto __err_val; + err = snd_usb_apply_boot_quirk(dev, intf, quirk); + if (err < 0) + return err; /* * found a config. now register to ALSA @@ -508,6 +497,7 @@ snd_usb_audio_probe(struct usb_device *dev, if (usb_chip[i] && usb_chip[i]->dev == dev) { if (usb_chip[i]->shutdown) { dev_err(&dev->dev, "USB device is in the shutdown state, cannot create a card instance\n"); + err = -EIO; goto __error; } chip = usb_chip[i]; @@ -523,15 +513,16 @@ snd_usb_audio_probe(struct usb_device *dev, if (enable[i] && ! usb_chip[i] && (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) && (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) { - if (snd_usb_audio_create(intf, dev, i, quirk, - &chip) < 0) { + err = snd_usb_audio_create(intf, dev, i, quirk, + &chip); + if (err < 0) goto __error; - } chip->pm_intf = intf; break; } if (!chip) { dev_err(&dev->dev, "no available usb audio device\n"); + err = -ENODEV; goto __error; } } @@ -548,28 +539,32 @@ snd_usb_audio_probe(struct usb_device *dev, err = 1; /* continue */ if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) { /* need some special handlings */ - if ((err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk)) < 0) + err = snd_usb_create_quirk(chip, intf, &usb_audio_driver, quirk); + if (err < 0) goto __error; } if (err > 0) { /* create normal USB audio interfaces */ - if (snd_usb_create_streams(chip, ifnum) < 0 || - snd_usb_create_mixer(chip, ifnum, ignore_ctl_error) < 0) { + err = snd_usb_create_streams(chip, ifnum); + if (err < 0) + goto __error; + err = snd_usb_create_mixer(chip, ifnum, ignore_ctl_error); + if (err < 0) goto __error; - } } /* we are allowed to call snd_card_register() many times */ - if (snd_card_register(chip->card) < 0) { + err = snd_card_register(chip->card); + if (err < 0) goto __error; - } usb_chip[chip->index] = chip; chip->num_interfaces++; chip->probing = 0; + usb_set_intfdata(intf, chip); mutex_unlock(®ister_mutex); - return chip; + return 0; __error: if (chip) { @@ -578,17 +573,16 @@ snd_usb_audio_probe(struct usb_device *dev, chip->probing = 0; } mutex_unlock(®ister_mutex); - __err_val: - return NULL; + return err; } /* * we need to take care of counter, since disconnection can be called also * many times as well as usb_audio_probe(). */ -static void snd_usb_audio_disconnect(struct usb_device *dev, - struct snd_usb_audio *chip) +static void usb_audio_disconnect(struct usb_interface *intf) { + struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_card *card; struct list_head *p; bool was_shutdown; @@ -604,12 +598,14 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, mutex_lock(®ister_mutex); if (!was_shutdown) { + struct snd_usb_stream *as; struct snd_usb_endpoint *ep; + struct usb_mixer_interface *mixer; snd_card_disconnect(card); /* release the pcm resources */ - list_for_each(p, &chip->pcm_list) { - snd_usb_stream_disconnect(p); + list_for_each_entry(as, &chip->pcm_list, list) { + snd_usb_stream_disconnect(as); } /* release the endpoint resources */ list_for_each_entry(ep, &chip->ep_list, list) { @@ -620,8 +616,8 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, snd_usbmidi_disconnect(p); } /* release mixer resources */ - list_for_each(p, &chip->mixer_list) { - snd_usb_mixer_disconnect(p); + list_for_each_entry(mixer, &chip->mixer_list, list) { + snd_usb_mixer_disconnect(mixer); } } @@ -635,27 +631,6 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, } } -/* - * new 2.5 USB kernel API - */ -static int usb_audio_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct snd_usb_audio *chip; - chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id); - if (chip) { - usb_set_intfdata(intf, chip); - return 0; - } else - return -EIO; -} - -static void usb_audio_disconnect(struct usb_interface *intf) -{ - snd_usb_audio_disconnect(interface_to_usbdev(intf), - usb_get_intfdata(intf)); -} - #ifdef CONFIG_PM int snd_usb_autoresume(struct snd_usb_audio *chip) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 114e3e7ff511..03b074419964 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -348,6 +348,8 @@ static void snd_complete_urb(struct urb *urb) { struct snd_urb_ctx *ctx = urb->context; struct snd_usb_endpoint *ep = ctx->ep; + struct snd_pcm_substream *substream; + unsigned long flags; int err; if (unlikely(urb->status == -ENOENT || /* unlinked */ @@ -364,8 +366,6 @@ static void snd_complete_urb(struct urb *urb) goto exit_clear; if (snd_usb_endpoint_implicit_feedback_sink(ep)) { - unsigned long flags; - spin_lock_irqsave(&ep->lock, flags); list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); spin_unlock_irqrestore(&ep->lock, flags); @@ -389,7 +389,10 @@ static void snd_complete_urb(struct urb *urb) return; usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); - //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + if (ep->data_subs && ep->data_subs->pcm_substream) { + substream = ep->data_subs->pcm_substream; + snd_pcm_stop_xrun(substream); + } exit_clear: clear_bit(ctx->index, &ep->active_mask); @@ -1002,15 +1005,12 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep) /** * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint * - * @ep: the list header of the endpoint to free + * @ep: the endpoint to free * * This free all resources of the given ep. */ -void snd_usb_endpoint_free(struct list_head *head) +void snd_usb_endpoint_free(struct snd_usb_endpoint *ep) { - struct snd_usb_endpoint *ep; - - ep = list_entry(head, struct snd_usb_endpoint, list); kfree(ep); } diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index e61ee5c356a3..6428392d8f62 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -24,7 +24,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); -void snd_usb_endpoint_free(struct list_head *head); +void snd_usb_endpoint_free(struct snd_usb_endpoint *ep); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); diff --git a/sound/usb/midi.c b/sound/usb/midi.c index d3d49525a16b..5bfb695547f8 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -365,6 +365,8 @@ static void snd_usbmidi_error_timer(unsigned long data) if (in && in->error_resubmit) { in->error_resubmit = 0; for (j = 0; j < INPUT_URBS; ++j) { + if (atomic_read(&in->urbs[j]->use_count)) + continue; in->urbs[j]->dev = umidi->dev; snd_usbmidi_submit_urb(in->urbs[j], GFP_ATOMIC); } diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index a1bab149df4d..9581089c28c5 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -613,24 +613,14 @@ static int start_usb_playback(struct ua101 *ua) static void abort_alsa_capture(struct ua101 *ua) { - unsigned long flags; - - if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) { - snd_pcm_stream_lock_irqsave(ua->capture.substream, flags); - snd_pcm_stop(ua->capture.substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(ua->capture.substream, flags); - } + if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states)) + snd_pcm_stop_xrun(ua->capture.substream); } static void abort_alsa_playback(struct ua101 *ua) { - unsigned long flags; - - if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) { - snd_pcm_stream_lock_irqsave(ua->playback.substream, flags); - snd_pcm_stop(ua->playback.substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(ua->playback.substream, flags); - } + if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states)) + snd_pcm_stop_xrun(ua->playback.substream); } static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream, diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 6e354d326858..41650d5b93b7 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -136,6 +136,10 @@ check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen) return strlcpy(buf, p->name, buflen); } +/* ignore the error value if ignore_ctl_error flag is set */ +#define filter_error(cval, err) \ + ((cval)->head.mixer->ignore_ctl_error ? 0 : (err)) + /* check whether the control should be ignored */ static inline int check_ignored_ctl(const struct usbmix_name_map *p) @@ -286,13 +290,13 @@ static int get_abs_value(struct usb_mixer_elem_info *cval, int val) static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2]; int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; int timeout = 10; int idx = 0, err; - err = snd_usb_autoresume(cval->mixer->chip); + err = snd_usb_autoresume(chip); if (err < 0) return -EIO; @@ -300,7 +304,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, while (timeout-- > 0) { if (chip->shutdown) break; - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, val_len) >= val_len) { @@ -316,14 +320,14 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, out: up_read(&chip->shutdown_rwsem); - snd_usb_autosuspend(cval->mixer->chip); + snd_usb_autosuspend(chip); return err; } static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2 + 3 * sizeof(__u16)]; /* enough space for one range */ unsigned char *val; int idx = 0, ret, size; @@ -347,7 +351,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, if (chip->shutdown) { ret = -ENODEV; } else { - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, validx, idx, buf, size); @@ -392,7 +396,7 @@ static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, { validx += cval->idx_off; - return (cval->mixer->protocol == UAC_VERSION_1) ? + return (cval->head.mixer->protocol == UAC_VERSION_1) ? get_ctl_value_v1(cval, request, validx, value_ret) : get_ctl_value_v2(cval, request, validx, value_ret); } @@ -412,7 +416,7 @@ static inline int get_cur_mix_raw(struct usb_mixer_elem_info *cval, value); } -static int get_cur_mix_value(struct usb_mixer_elem_info *cval, +int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int *value) { int err; @@ -423,8 +427,8 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval, } err = get_cur_mix_raw(cval, channel, value); if (err < 0) { - if (!cval->mixer->ignore_ctl_error) - usb_audio_dbg(cval->mixer->chip, + if (!cval->head.mixer->ignore_ctl_error) + usb_audio_dbg(cval->head.mixer->chip, "cannot get current value for control %d ch %d: err = %d\n", cval->control, channel, err); return err; @@ -441,13 +445,13 @@ static int get_cur_mix_value(struct usb_mixer_elem_info *cval, int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; unsigned char buf[2]; int idx = 0, val_len, err, timeout = 10; validx += cval->idx_off; - if (cval->mixer->protocol == UAC_VERSION_1) { + if (cval->head.mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; } else { /* UAC_VERSION_2 */ /* audio class v2 controls are always 2 bytes in size */ @@ -472,7 +476,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, while (timeout-- > 0) { if (chip->shutdown) break; - idx = snd_usb_ctrl_intf(chip) | (cval->id << 8); + idx = snd_usb_ctrl_intf(chip) | (cval->head.id << 8); if (snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), request, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, @@ -497,7 +501,7 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, return snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, validx, value); } -static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, +int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, int index, int value) { int err; @@ -506,7 +510,7 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, cval->ch_readonly & (1 << (channel - 1)); if (read_only) { - usb_audio_dbg(cval->mixer->chip, + usb_audio_dbg(cval->head.mixer->chip, "%s(): channel %d of control %d is read_only\n", __func__, channel, cval->control); return 0; @@ -565,10 +569,10 @@ static int check_matrix_bitmap(unsigned char *bmap, * if failed, give up and free the control instance. */ -int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, +int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, struct snd_kcontrol *kctl) { - struct usb_mixer_elem_info *cval = kctl->private_data; + struct usb_mixer_interface *mixer = list->mixer; int err; while (snd_ctl_find_id(mixer->chip->card, &kctl->id)) @@ -578,9 +582,9 @@ int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, err); return err; } - cval->elem_id = &kctl->id; - cval->next_id_elem = mixer->id_elems[cval->id]; - mixer->id_elems[cval->id] = cval; + list->kctl = kctl; + list->next_id_elem = mixer->id_elems[list->id]; + mixer->id_elems[list->id] = list; return 0; } @@ -815,7 +819,7 @@ static struct usb_feature_control_info audio_feature_info[] = { }; /* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) +void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl) { kfree(kctl->private_data); kctl->private_data = NULL; @@ -829,7 +833,7 @@ static void usb_mixer_elem_free(struct snd_kcontrol *kctl) static void volume_control_quirks(struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { - struct snd_usb_audio *chip = cval->mixer->chip; + struct snd_usb_audio *chip = cval->head.mixer->chip; switch (chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ @@ -954,10 +958,10 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, } if (get_ctl_value(cval, UAC_GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 || get_ctl_value(cval, UAC_GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) { - usb_audio_err(cval->mixer->chip, + usb_audio_err(cval->head.mixer->chip, "%d:%d: cannot get min/max values for control %d (id %d)\n", - cval->id, snd_usb_ctrl_intf(cval->mixer->chip), - cval->control, cval->id); + cval->head.id, snd_usb_ctrl_intf(cval->head.mixer->chip), + cval->control, cval->head.id); return -EINVAL; } if (get_ctl_value(cval, UAC_GET_RES, @@ -998,7 +1002,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, else test -= cval->res; if (test < cval->min || test > cval->max || - set_cur_mix_value(cval, minchn, 0, test) || + snd_usb_set_cur_mix_value(cval, minchn, 0, test) || get_cur_mix_raw(cval, minchn, &check)) { cval->res = last_valid_res; break; @@ -1007,7 +1011,7 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, break; cval->res *= 2; } - set_cur_mix_value(cval, minchn, 0, saved); + snd_usb_set_cur_mix_value(cval, minchn, 0, saved); } cval->initialized = 1; @@ -1061,7 +1065,7 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, kcontrol->vd[0].access &= ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); - snd_ctl_notify(cval->mixer->chip->card, + snd_ctl_notify(cval->head.mixer->chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kcontrol->id); } @@ -1086,9 +1090,9 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & (1 << c))) continue; - err = get_cur_mix_value(cval, c + 1, cnt, &val); + err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &val); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = get_relative_value(cval, val); ucontrol->value.integer.value[cnt] = val; cnt++; @@ -1096,9 +1100,9 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, return 0; } else { /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &val); + err = snd_usb_get_cur_mix_value(cval, 0, 0, &val); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; } @@ -1118,26 +1122,26 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, for (c = 0; c < MAX_CHANNELS; c++) { if (!(cval->cmask & (1 << c))) continue; - err = get_cur_mix_value(cval, c + 1, cnt, &oval); + err = snd_usb_get_cur_mix_value(cval, c + 1, cnt, &oval); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = ucontrol->value.integer.value[cnt]; val = get_abs_value(cval, val); if (oval != val) { - set_cur_mix_value(cval, c + 1, cnt, val); + snd_usb_set_cur_mix_value(cval, c + 1, cnt, val); changed = 1; } cnt++; } } else { /* master channel */ - err = get_cur_mix_value(cval, 0, 0, &oval); + err = snd_usb_get_cur_mix_value(cval, 0, 0, &oval); if (err < 0) - return cval->mixer->ignore_ctl_error ? 0 : err; + return filter_error(cval, err); val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { - set_cur_mix_value(cval, 0, 0, val); + snd_usb_set_cur_mix_value(cval, 0, 0, val); changed = 1; } } @@ -1231,8 +1235,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = control; cval->cmask = ctl_mask; cval->val_type = audio_feature_info[control-1].type; @@ -1250,7 +1253,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, /* * If all channels in the mask are marked read-only, make the control - * read-only. set_cur_mix_value() will check the mask again and won't + * read-only. snd_usb_set_cur_mix_value() will check the mask again and won't * issue write commands to read-only channels. */ if (cval->channels == readonly_mask) @@ -1263,7 +1266,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kfree(cval); return; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); mapped_name = len != 0; @@ -1290,9 +1293,8 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, kctl->id.name, sizeof(kctl->id.name), 1); if (!len) - len = snprintf(kctl->id.name, - sizeof(kctl->id.name), - "Feature %d", unitid); + snprintf(kctl->id.name, sizeof(kctl->id.name), + "Feature %d", unitid); } if (!mapped_name) @@ -1305,9 +1307,9 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, */ if (!mapped_name && !(state->oterm.type >> 16)) { if ((state->oterm.type & 0xff00) == 0x0100) - len = append_ctl_name(kctl, " Capture"); + append_ctl_name(kctl, " Capture"); else - len = append_ctl_name(kctl, " Playback"); + append_ctl_name(kctl, " Playback"); } append_ctl_name(kctl, control == UAC_FU_MUTE ? " Switch" : " Volume"); @@ -1344,14 +1346,14 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, range); usb_audio_warn(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); } usb_audio_dbg(state->chip, "[%d] FU [%s] ch = %d, val = %d/%d/%d\n", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res); - snd_usb_mixer_add_control(state->mixer, kctl); + snd_usb_mixer_add_control(&cval->head, kctl); } /* @@ -1525,8 +1527,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, if (!cval) return; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = in_ch + 1; /* based on 1 */ cval->val_type = USB_MIXER_S16; for (i = 0; i < num_outs; i++) { @@ -1547,7 +1548,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, kfree(cval); return; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name)); if (!len) @@ -1558,8 +1559,8 @@ static void build_mixer_unit_ctl(struct mixer_build *state, append_ctl_name(kctl, " Volume"); usb_audio_dbg(state->chip, "[%d] MU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, cval->min, cval->max); - snd_usb_mixer_add_control(state->mixer, kctl); + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max); + snd_usb_mixer_add_control(&cval->head, kctl); } /* @@ -1629,12 +1630,10 @@ static int mixer_ctl_procunit_get(struct snd_kcontrol *kcontrol, int err, val; err = get_cur_ctl_value(cval, cval->control << 8, &val); - if (err < 0 && cval->mixer->ignore_ctl_error) { + if (err < 0) { ucontrol->value.integer.value[0] = cval->min; - return 0; + return filter_error(cval, err); } - if (err < 0) - return err; val = get_relative_value(cval, val); ucontrol->value.integer.value[0] = val; return 0; @@ -1648,11 +1647,8 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol, int val, oval, err; err = get_cur_ctl_value(cval, cval->control << 8, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } + if (err < 0) + return filter_error(cval, err); val = ucontrol->value.integer.value[0]; val = get_abs_value(cval, val); if (val != oval) { @@ -1814,8 +1810,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->control = valinfo->control; cval->val_type = valinfo->val_type; cval->channels = 1; @@ -1847,7 +1842,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, kfree(cval); return -ENOMEM; } - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) { /* nothing */ ; @@ -1868,10 +1863,10 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, usb_audio_dbg(state->chip, "[%d] PU [%s] ch = %d, val = %d/%d\n", - cval->id, kctl->id.name, cval->channels, + cval->head.id, kctl->id.name, cval->channels, cval->min, cval->max); - err = snd_usb_mixer_add_control(state->mixer, kctl); + err = snd_usb_mixer_add_control(&cval->head, kctl); if (err < 0) return err; } @@ -1924,11 +1919,8 @@ static int mixer_ctl_selector_get(struct snd_kcontrol *kcontrol, err = get_cur_ctl_value(cval, cval->control << 8, &val); if (err < 0) { - if (cval->mixer->ignore_ctl_error) { - ucontrol->value.enumerated.item[0] = 0; - return 0; - } - return err; + ucontrol->value.enumerated.item[0] = 0; + return filter_error(cval, err); } val = get_relative_value(cval, val); ucontrol->value.enumerated.item[0] = val; @@ -1943,11 +1935,8 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol, int val, oval, err; err = get_cur_ctl_value(cval, cval->control << 8, &oval); - if (err < 0) { - if (cval->mixer->ignore_ctl_error) - return 0; - return err; - } + if (err < 0) + return filter_error(cval, err); val = ucontrol->value.enumerated.item[0]; val = get_abs_value(cval, val); if (val != oval) { @@ -2024,8 +2013,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) return -ENOMEM; - cval->mixer = state->mixer; - cval->id = unitid; + snd_usb_mixer_elem_init_std(&cval->head, state->mixer, unitid); cval->val_type = USB_MIXER_U8; cval->channels = 1; cval->min = 1; @@ -2096,11 +2084,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, } usb_audio_dbg(state->chip, "[%d] SU [%s] items = %d\n", - cval->id, kctl->id.name, desc->bNrInPins); - if ((err = snd_usb_mixer_add_control(state->mixer, kctl)) < 0) - return err; - - return 0; + cval->head.id, kctl->id.name, desc->bNrInPins); + return snd_usb_mixer_add_control(&cval->head, kctl); } /* @@ -2245,25 +2230,21 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { - struct usb_mixer_elem_info *info; + struct usb_mixer_elem_list *list; - for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - info->elem_id); + &list->kctl->id); } static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer, - int unitid, - struct usb_mixer_elem_info *cval) + struct usb_mixer_elem_list *list) { + struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN", "S8", "U8", "S16", "U16"}; - snd_iprintf(buffer, " Unit: %i\n", unitid); - if (cval->elem_id) - snd_iprintf(buffer, " Control: name=\"%s\", index=%i\n", - cval->elem_id->name, cval->elem_id->index); snd_iprintf(buffer, " Info: id=%i, control=%i, cmask=0x%x, " - "channels=%i, type=\"%s\"\n", cval->id, + "channels=%i, type=\"%s\"\n", cval->head.id, cval->control, cval->cmask, cval->channels, val_types[cval->val_type]); snd_iprintf(buffer, " Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n", @@ -2275,7 +2256,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, { struct snd_usb_audio *chip = entry->private_data; struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *cval; + struct usb_mixer_elem_list *list; int unitid; list_for_each_entry(mixer, &chip->mixer_list, list) { @@ -2285,9 +2266,17 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, mixer->ignore_ctl_error); snd_iprintf(buffer, "Card: %s\n", chip->card->longname); for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) { - for (cval = mixer->id_elems[unitid]; cval; - cval = cval->next_id_elem) - snd_usb_mixer_dump_cval(buffer, unitid, cval); + for (list = mixer->id_elems[unitid]; list; + list = list->next_id_elem) { + snd_iprintf(buffer, " Unit: %i\n", list->id); + if (list->kctl) + snd_iprintf(buffer, + " Control: name=\"%s\", index=%i\n", + list->kctl->id.name, + list->kctl->id.index); + if (list->dump) + list->dump(buffer, list); + } } } } @@ -2295,7 +2284,7 @@ static void snd_usb_mixer_proc_read(struct snd_info_entry *entry, static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, int attribute, int value, int index) { - struct usb_mixer_elem_info *info; + struct usb_mixer_elem_list *list; __u8 unitid = (index >> 8) & 0xff; __u8 control = (value >> 8) & 0xff; __u8 channel = value & 0xff; @@ -2307,7 +2296,13 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; } - for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem) { + for (list = mixer->id_elems[unitid]; list; list = list->next_id_elem) { + struct usb_mixer_elem_info *info; + + if (!list->kctl) + continue; + + info = (struct usb_mixer_elem_info *)list; if (info->control != control) continue; @@ -2320,7 +2315,7 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, info->cached = 0; snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - info->elem_id); + &info->head.kctl->id); break; case UAC2_CS_RANGE: @@ -2485,11 +2480,8 @@ _error: return err; } -void snd_usb_mixer_disconnect(struct list_head *p) +void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) { - struct usb_mixer_interface *mixer; - - mixer = list_entry(p, struct usb_mixer_interface, list); usb_kill_urb(mixer->urb); usb_kill_urb(mixer->rc_urb); } @@ -2521,8 +2513,9 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer) return 0; } -static int restore_mixer_value(struct usb_mixer_elem_info *cval) +static int restore_mixer_value(struct usb_mixer_elem_list *list) { + struct usb_mixer_elem_info *cval = (struct usb_mixer_elem_info *)list; int c, err, idx; if (cval->cmask) { @@ -2531,7 +2524,7 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) if (!(cval->cmask & (1 << c))) continue; if (cval->cached & (1 << c)) { - err = set_cur_mix_value(cval, c + 1, idx, + err = snd_usb_set_cur_mix_value(cval, c + 1, idx, cval->cache_val[idx]); if (err < 0) return err; @@ -2541,7 +2534,7 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) } else { /* master */ if (cval->cached) { - err = set_cur_mix_value(cval, 0, 0, *cval->cache_val); + err = snd_usb_set_cur_mix_value(cval, 0, 0, *cval->cache_val); if (err < 0) return err; } @@ -2552,19 +2545,19 @@ static int restore_mixer_value(struct usb_mixer_elem_info *cval) int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) { - struct usb_mixer_elem_info *cval; + struct usb_mixer_elem_list *list; int id, err; - /* FIXME: any mixer quirks? */ - if (reset_resume) { /* restore cached mixer values */ for (id = 0; id < MAX_ID_ELEMS; id++) { - for (cval = mixer->id_elems[id]; cval; - cval = cval->next_id_elem) { - err = restore_mixer_value(cval); - if (err < 0) - return err; + for (list = mixer->id_elems[id]; list; + list = list->next_id_elem) { + if (list->resume) { + err = list->resume(list); + if (err < 0) + return err; + } } } } @@ -2572,3 +2565,15 @@ int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume) return snd_usb_mixer_activate(mixer); } #endif + +void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, + struct usb_mixer_interface *mixer, + int unitid) +{ + list->mixer = mixer; + list->id = unitid; + list->dump = snd_usb_mixer_dump_cval; +#ifdef CONFIG_PM + list->resume = restore_mixer_value; +#endif +} diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 73b1f649447b..d3268f0ee2b3 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -1,6 +1,8 @@ #ifndef __USBMIXER_H #define __USBMIXER_H +#include <sound/info.h> + struct usb_mixer_interface { struct snd_usb_audio *chip; struct usb_host_interface *hostif; @@ -8,7 +10,7 @@ struct usb_mixer_interface { unsigned int ignore_ctl_error; struct urb *urb; /* array[MAX_ID_ELEMS], indexed by unit id */ - struct usb_mixer_elem_info **id_elems; + struct usb_mixer_elem_list **id_elems; /* the usb audio specification version this interface complies to */ int protocol; @@ -20,9 +22,6 @@ struct usb_mixer_interface { struct urb *rc_urb; struct usb_ctrlrequest *rc_setup_packet; u8 rc_buffer[6]; - - u8 audigy2nx_leds[3]; - u8 xonar_u1_status; }; #define MAX_CHANNELS 16 /* max logical channels */ @@ -36,11 +35,21 @@ enum { USB_MIXER_U16, }; -struct usb_mixer_elem_info { +typedef void (*usb_mixer_elem_dump_func_t)(struct snd_info_buffer *buffer, + struct usb_mixer_elem_list *list); +typedef int (*usb_mixer_elem_resume_func_t)(struct usb_mixer_elem_list *elem); + +struct usb_mixer_elem_list { struct usb_mixer_interface *mixer; - struct usb_mixer_elem_info *next_id_elem; /* list of controls with same id */ - struct snd_ctl_elem_id *elem_id; + struct usb_mixer_elem_list *next_id_elem; /* list of controls with same id */ + struct snd_kcontrol *kctl; unsigned int id; + usb_mixer_elem_dump_func_t dump; + usb_mixer_elem_resume_func_t resume; +}; + +struct usb_mixer_elem_info { + struct usb_mixer_elem_list head; unsigned int control; /* CS or ICN (high byte) */ unsigned int cmask; /* channel mask bitmap: 0 = master */ unsigned int idx_off; /* Control index offset */ @@ -53,20 +62,25 @@ struct usb_mixer_elem_info { int cached; int cache_val[MAX_CHANNELS]; u8 initialized; + void *private_data; }; int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error); -void snd_usb_mixer_disconnect(struct list_head *p); +void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer); void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set); -int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer, +int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, struct snd_kcontrol *kctl); +void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, + struct usb_mixer_interface *mixer, + int unitid); + int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *_tlv); @@ -75,4 +89,12 @@ int snd_usb_mixer_suspend(struct usb_mixer_interface *mixer); int snd_usb_mixer_resume(struct usb_mixer_interface *mixer, bool reset_resume); #endif +int snd_usb_set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel, + int index, int value); + +int snd_usb_get_cur_mix_value(struct usb_mixer_elem_info *cval, + int channel, int index, int *value); + +extern void snd_usb_mixer_elem_free(struct snd_kcontrol *kctl); + #endif /* __USBMIXER_H */ diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index d1d72ff50347..1994d41348f8 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -179,6 +179,11 @@ static struct usbmix_name_map audigy2nx_map[] = { { 0 } /* terminator */ }; +static struct usbmix_name_map mbox1_map[] = { + { 1, "Clock" }, + { 0 } /* terminator */ +}; + static struct usbmix_selector_map c400_selectors[] = { { .id = 0x80, @@ -416,6 +421,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = { .map = aureon_51_2_map, }, { + .id = USB_ID(0x0dba, 0x1000), + .map = mbox1_map, + }, + { .id = USB_ID(0x13e5, 0x0001), .map = scratch_live_map, .ignore_ctl_error = 1, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 8c9bf4b7aaf0..dc9df007d3e3 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -41,6 +41,7 @@ #include "usbaudio.h" #include "mixer.h" #include "mixer_quirks.h" +#include "mixer_scarlett.h" #include "helper.h" extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; @@ -52,13 +53,6 @@ struct std_mono_table { snd_kcontrol_tlv_rw_t *tlv_callback; }; -/* private_free callback */ -static void usb_mixer_elem_free(struct snd_kcontrol *kctl) -{ - kfree(kctl->private_data); - kctl->private_data = NULL; -} - /* This function allows for the creation of standard UAC controls. * See the quirks for M-Audio FTUs or Ebox-44. * If you don't want to set a TLV callback pass NULL. @@ -75,7 +69,6 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, const char *name, snd_kcontrol_tlv_rw_t *tlv_callback) { - int err; struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; @@ -83,8 +76,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, if (!cval) return -ENOMEM; - cval->id = unitid; - cval->mixer = mixer; + snd_usb_mixer_elem_init_std(&cval->head, mixer, unitid); cval->val_type = val_type; cval->channels = 1; cval->control = control; @@ -108,7 +100,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, /* Set name */ snprintf(kctl->id.name, sizeof(kctl->id.name), name); - kctl->private_free = usb_mixer_elem_free; + kctl->private_free = snd_usb_mixer_elem_free; /* set TLV */ if (tlv_callback) { @@ -118,11 +110,7 @@ static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; } /* Add control to mixer */ - err = snd_usb_mixer_add_control(mixer, kctl); - if (err < 0) - return err; - - return 0; + return snd_usb_mixer_add_control(&cval->head, kctl); } static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, @@ -156,6 +144,32 @@ static int snd_create_std_mono_table(struct usb_mixer_interface *mixer, return 0; } +static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer, + int id, + usb_mixer_elem_resume_func_t resume, + const struct snd_kcontrol_new *knew, + struct usb_mixer_elem_list **listp) +{ + struct usb_mixer_elem_list *list; + struct snd_kcontrol *kctl; + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) + return -ENOMEM; + if (listp) + *listp = list; + list->mixer = mixer; + list->id = id; + list->resume = resume; + kctl = snd_ctl_new1(knew, list); + if (!kctl) { + kfree(list); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + return snd_usb_mixer_add_control(list, kctl); +} + /* * Sound Blaster remote control configuration * @@ -283,84 +297,90 @@ static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) static int snd_audigy2nx_led_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - - ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index]; + ucontrol->value.integer.value[0] = kcontrol->private_value >> 8; return 0; } -static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int snd_audigy2nx_led_update(struct usb_mixer_interface *mixer, + int value, int index) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int index = kcontrol->private_value; - int value = ucontrol->value.integer.value[0]; - int err, changed; + struct snd_usb_audio *chip = mixer->chip; + int err; - if (value > 1) - return -EINVAL; - changed = value != mixer->audigy2nx_leds[index]; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) { + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { err = -ENODEV; goto out; } - if (mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + if (chip->usb_id == USB_ID(0x041e, 0x3042)) + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0); /* USB X-Fi S51 Pro */ - if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df)) - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + if (chip->usb_id == USB_ID(0x041e, 0x30df)) + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, !value, 0, NULL, 0); else - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x24, + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x24, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, value, index + 2, NULL, 0); out: - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - mixer->audigy2nx_leds[index] = value; - return changed; + up_read(&chip->shutdown_rwsem); + return err; } -static struct snd_kcontrol_new snd_audigy2nx_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "CMSS LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 0, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Power LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 1, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Dolby Digital LED Switch", - .info = snd_audigy2nx_led_info, - .get = snd_audigy2nx_led_get, - .put = snd_audigy2nx_led_put, - .private_value = 2, - }, +static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + int index = kcontrol->private_value & 0xff; + int value = ucontrol->value.integer.value[0]; + int old_value = kcontrol->private_value >> 8; + int err; + + if (value > 1) + return -EINVAL; + if (value == old_value) + return 0; + kcontrol->private_value = (value << 8) | index; + err = snd_audigy2nx_led_update(mixer, value, index); + return err < 0 ? err : 1; +} + +static int snd_audigy2nx_led_resume(struct usb_mixer_elem_list *list) +{ + int priv_value = list->kctl->private_value; + + return snd_audigy2nx_led_update(list->mixer, priv_value >> 8, + priv_value & 0xff); +} + +/* name and private_value are set dynamically */ +static struct snd_kcontrol_new snd_audigy2nx_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_audigy2nx_led_info, + .get = snd_audigy2nx_led_get, + .put = snd_audigy2nx_led_put, +}; + +static const char * const snd_audigy2nx_led_names[] = { + "CMSS LED Switch", + "Power LED Switch", + "Dolby Digital LED Switch", }; static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) { int i, err; - for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) { + for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_led_names); ++i) { + struct snd_kcontrol_new knew; + /* USB X-Fi S51 doesn't have a CMSS LED */ if ((mixer->chip->usb_id == USB_ID(0x041e, 0x3042)) && i == 0) continue; @@ -373,12 +393,16 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer) mixer->chip->usb_id == USB_ID(0x041e, 0x30df) || mixer->chip->usb_id == USB_ID(0x041e, 0x3048))) break; - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_audigy2nx_controls[i], mixer)); + + knew = snd_audigy2nx_control; + knew.name = snd_audigy2nx_led_names[i]; + knew.private_value = (1 << 8) | i; /* LED on as default */ + err = add_single_ctl_with_resume(mixer, 0, + snd_audigy2nx_led_resume, + &knew, NULL); if (err < 0) return err; } - mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */ return 0; } @@ -437,19 +461,9 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, static int snd_emu0204_ch_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[2] = {"1/2", - "3/4" - }; + static const char * const texts[2] = {"1/2", "3/4"}; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 2; - if (uinfo->value.enumerated.item > 1) - uinfo->value.enumerated.item = 1; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol, @@ -459,100 +473,122 @@ static int snd_emu0204_ch_switch_get(struct snd_kcontrol *kcontrol, return 0; } -static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_emu0204_ch_switch_update(struct usb_mixer_interface *mixer, + int value) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - unsigned int value = ucontrol->value.enumerated.item[0]; - int err, changed; + struct snd_usb_audio *chip = mixer->chip; + int err; unsigned char buf[2]; - if (value > 1) - return -EINVAL; - - buf[0] = 0x01; - buf[1] = value ? 0x02 : 0x01; - - changed = value != kcontrol->private_value; - down_read(&mixer->chip->shutdown_rwsem); + down_read(&chip->shutdown_rwsem); if (mixer->chip->shutdown) { err = -ENODEV; goto out; } - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), UAC_SET_CUR, + + buf[0] = 0x01; + buf[1] = value ? 0x02 : 0x01; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, 0x0400, 0x0e00, buf, 2); out: - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; + up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_emu0204_ch_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + unsigned int value = ucontrol->value.enumerated.item[0]; + int err; + + if (value > 1) + return -EINVAL; + + if (value == kcontrol->private_value) + return 0; + kcontrol->private_value = value; - return changed; + err = snd_emu0204_ch_switch_update(mixer, value); + return err < 0 ? err : 1; } +static int snd_emu0204_ch_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_emu0204_ch_switch_update(list->mixer, + list->kctl->private_value); +} -static struct snd_kcontrol_new snd_emu0204_controls[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Front Jack Channels", - .info = snd_emu0204_ch_switch_info, - .get = snd_emu0204_ch_switch_get, - .put = snd_emu0204_ch_switch_put, - .private_value = 0, - }, +static struct snd_kcontrol_new snd_emu0204_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Front Jack Channels", + .info = snd_emu0204_ch_switch_info, + .get = snd_emu0204_ch_switch_get, + .put = snd_emu0204_ch_switch_put, + .private_value = 0, }; static int snd_emu0204_controls_create(struct usb_mixer_interface *mixer) { - int i, err; - - for (i = 0; i < ARRAY_SIZE(snd_emu0204_controls); ++i) { - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_emu0204_controls[i], mixer)); - if (err < 0) - return err; - } - - return 0; + return add_single_ctl_with_resume(mixer, 0, + snd_emu0204_ch_switch_resume, + &snd_emu0204_control, NULL); } + /* ASUS Xonar U1 / U3 controls */ static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = !!(mixer->xonar_u1_status & 0x02); + ucontrol->value.integer.value[0] = !!(kcontrol->private_value & 0x02); return 0; } +static int snd_xonar_u1_switch_update(struct usb_mixer_interface *mixer, + unsigned char status) +{ + struct snd_usb_audio *chip = mixer->chip; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) + err = -ENODEV; + else + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, + 50, 0, &status, 1); + up_read(&chip->shutdown_rwsem); + return err; +} + static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); u8 old_status, new_status; - int err, changed; + int err; - old_status = mixer->xonar_u1_status; + old_status = kcontrol->private_value; if (ucontrol->value.integer.value[0]) new_status = old_status | 0x02; else new_status = old_status & ~0x02; - changed = new_status != old_status; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, - 50, 0, &new_status, 1); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - mixer->xonar_u1_status = new_status; - return changed; + if (new_status == old_status) + return 0; + + kcontrol->private_value = new_status; + err = snd_xonar_u1_switch_update(list->mixer, new_status); + return err < 0 ? err : 1; +} + +static int snd_xonar_u1_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_xonar_u1_switch_update(list->mixer, + list->kctl->private_value); } static struct snd_kcontrol_new snd_xonar_u1_output_switch = { @@ -561,82 +597,213 @@ static struct snd_kcontrol_new snd_xonar_u1_output_switch = { .info = snd_ctl_boolean_mono_info, .get = snd_xonar_u1_switch_get, .put = snd_xonar_u1_switch_put, + .private_value = 0x05, }; static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer) { + return add_single_ctl_with_resume(mixer, 0, + snd_xonar_u1_switch_resume, + &snd_xonar_u1_output_switch, NULL); +} + +/* Digidesign Mbox 1 clock source switch (internal/spdif) */ + +static int snd_mbox1_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = kctl->private_value; + return 0; +} + +static int snd_mbox1_switch_update(struct usb_mixer_interface *mixer, int val) +{ + struct snd_usb_audio *chip = mixer->chip; int err; + unsigned char buff[3]; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto err; + } - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_xonar_u1_output_switch, mixer)); + /* Prepare for magic command to toggle clock source */ + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0x00, 0x500, buff, 1); if (err < 0) - return err; - mixer->xonar_u1_status = 0x05; - return 0; + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + + /* 2 possibilities: Internal -> send sample rate + * S/PDIF sync -> send zeroes + * NB: Sample rate locked to 48kHz on purpose to + * prevent user from resetting the sample rate + * while S/PDIF sync is enabled and confusing + * this configuration. + */ + if (val == 0) { + buff[0] = 0x80; + buff[1] = 0xbb; + buff[2] = 0x00; + } else { + buff[0] = buff[1] = buff[2] = 0x00; + } + + /* Send the magic command to toggle the clock source */ + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), 0x1, + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x81, buff, 3); + if (err < 0) + goto err; + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), 0x81, + USB_DIR_IN | + USB_TYPE_CLASS | + USB_RECIP_ENDPOINT, 0x100, 0x2, buff, 3); + if (err < 0) + goto err; + +err: + up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_mbox1_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + struct usb_mixer_interface *mixer = list->mixer; + int err; + bool cur_val, new_val; + + cur_val = kctl->private_value; + new_val = ucontrol->value.enumerated.item[0]; + if (cur_val == new_val) + return 0; + + kctl->private_value = new_val; + err = snd_mbox1_switch_update(mixer, new_val); + return err < 0 ? err : 1; +} + +static int snd_mbox1_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char *const texts[2] = { + "Internal", + "S/PDIF" + }; + + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); +} + +static int snd_mbox1_switch_resume(struct usb_mixer_elem_list *list) +{ + return snd_mbox1_switch_update(list->mixer, list->kctl->private_value); +} + +static struct snd_kcontrol_new snd_mbox1_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Clock Source", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_mbox1_switch_info, + .get = snd_mbox1_switch_get, + .put = snd_mbox1_switch_put, + .private_value = 0 +}; + +static int snd_mbox1_create_sync_switch(struct usb_mixer_interface *mixer) +{ + return add_single_ctl_with_resume(mixer, 0, + snd_mbox1_switch_resume, + &snd_mbox1_switch, NULL); } /* Native Instruments device quirks */ #define _MAKE_NI_CONTROL(bRequest,wIndex) ((bRequest) << 16 | (wIndex)) -static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_ni_control_init_val(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); struct usb_device *dev = mixer->chip->dev; - u8 bRequest = (kcontrol->private_value >> 16) & 0xff; - u16 wIndex = kcontrol->private_value & 0xffff; - u8 tmp; - int ret; - - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - ret = -ENODEV; - else - ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, wIndex, - &tmp, sizeof(tmp)); - up_read(&mixer->chip->shutdown_rwsem); + unsigned int pval = kctl->private_value; + u8 value; + int err; - if (ret < 0) { + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, pval & 0xffff, &value, 1); + if (err < 0) { dev_err(&dev->dev, - "unable to issue vendor read request (ret = %d)", ret); - return ret; + "unable to issue vendor read request (ret = %d)", err); + return err; } - ucontrol->value.integer.value[0] = tmp; + kctl->private_value |= (value << 24); + return 0; +} +static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = kcontrol->private_value >> 24; return 0; } +static int snd_ni_update_cur_val(struct usb_mixer_elem_list *list) +{ + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; + int err; + + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) + err = -ENODEV; + else + err = usb_control_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), + (pval >> 16) & 0xff, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + pval >> 24, pval & 0xffff, NULL, 0, 1000); + up_read(&chip->shutdown_rwsem); + return err; +} + static int snd_nativeinstruments_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - struct usb_device *dev = mixer->chip->dev; - u8 bRequest = (kcontrol->private_value >> 16) & 0xff; - u16 wIndex = kcontrol->private_value & 0xffff; - u16 wValue = ucontrol->value.integer.value[0]; - int ret; - - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - ret = -ENODEV; - else - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), bRequest, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - wValue, wIndex, - NULL, 0, 1000); - up_read(&mixer->chip->shutdown_rwsem); + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + u8 oldval = (kcontrol->private_value >> 24) & 0xff; + u8 newval = ucontrol->value.integer.value[0]; + int err; - if (ret < 0) { - dev_err(&dev->dev, - "unable to issue vendor write request (ret = %d)", ret); - return ret; - } + if (oldval == newval) + return 0; - return 0; + kcontrol->private_value &= ~(0xff << 24); + kcontrol->private_value |= newval; + err = snd_ni_update_cur_val(list); + return err < 0 ? err : 1; } static struct snd_kcontrol_new snd_nativeinstruments_ta6_mixers[] = { @@ -707,16 +874,17 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, }; for (i = 0; i < count; i++) { - struct snd_kcontrol *c; + struct usb_mixer_elem_list *list; template.name = kc[i].name; template.private_value = kc[i].private_value; - c = snd_ctl_new1(&template, mixer); - err = snd_ctl_add(mixer->chip->card, c); - + err = add_single_ctl_with_resume(mixer, 0, + snd_ni_update_cur_val, + &template, &list); if (err < 0) break; + snd_ni_control_init_val(mixer, list->kctl); } return err; @@ -724,170 +892,88 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, /* M-Audio FastTrack Ultra quirks */ /* FTU Effect switch (also used by C400/C600) */ -struct snd_ftu_eff_switch_priv_val { - struct usb_mixer_interface *mixer; - int cached_value; - int is_cached; - int bUnitID; - int validx; -}; - static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - static const char *texts[8] = {"Room 1", - "Room 2", - "Room 3", - "Hall 1", - "Hall 2", - "Plate", - "Delay", - "Echo" + static const char *const texts[8] = { + "Room 1", "Room 2", "Room 3", "Hall 1", + "Hall 2", "Plate", "Delay", "Echo" }; - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = 8; - if (uinfo->value.enumerated.item > 7) - uinfo->value.enumerated.item = 7; - strcpy(uinfo->value.enumerated.name, - texts[uinfo->value.enumerated.item]); - - return 0; + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(texts), texts); } -static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, - struct snd_ctl_elem_value *ucontrol) +static int snd_ftu_eff_switch_init(struct usb_mixer_interface *mixer, + struct snd_kcontrol *kctl) { - struct snd_usb_audio *chip; - struct usb_mixer_interface *mixer; - struct snd_ftu_eff_switch_priv_val *pval; + struct usb_device *dev = mixer->chip->dev; + unsigned int pval = kctl->private_value; int err; unsigned char value[2]; - int id, validx; - - const int val_len = 2; value[0] = 0x00; value[1] = 0x00; - pval = (struct snd_ftu_eff_switch_priv_val *) - kctl->private_value; + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + pval & 0xff00, + snd_usb_ctrl_intf(mixer->chip) | ((pval & 0xff) << 8), + value, 2); + if (err < 0) + return err; - if (pval->is_cached) { - ucontrol->value.enumerated.item[0] = pval->cached_value; - return 0; - } + kctl->private_value |= value[0] << 24; + return 0; +} - mixer = (struct usb_mixer_interface *) pval->mixer; - if (snd_BUG_ON(!mixer)) - return -EINVAL; +static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = kctl->private_value >> 24; + return 0; +} - chip = (struct snd_usb_audio *) mixer->chip; - if (snd_BUG_ON(!chip)) - return -EINVAL; +static int snd_ftu_eff_switch_update(struct usb_mixer_elem_list *list) +{ + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; + unsigned char value[2]; + int err; - id = pval->bUnitID; - validx = pval->validx; + value[0] = pval >> 24; + value[1] = 0; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) err = -ENODEV; else err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), - value, val_len); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - - ucontrol->value.enumerated.item[0] = value[0]; - pval->cached_value = value[0]; - pval->is_cached = 1; - - return 0; + usb_sndctrlpipe(chip->dev, 0), + UAC_SET_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + pval & 0xff00, + snd_usb_ctrl_intf(chip) | ((pval & 0xff) << 8), + value, 2); + up_read(&chip->shutdown_rwsem); + return err; } static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol) { - struct snd_usb_audio *chip; - struct snd_ftu_eff_switch_priv_val *pval; + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + unsigned int pval = list->kctl->private_value; + int cur_val, err, new_val; - struct usb_mixer_interface *mixer; - int changed, cur_val, err, new_val; - unsigned char value[2]; - int id, validx; - - const int val_len = 2; - - changed = 0; - - pval = (struct snd_ftu_eff_switch_priv_val *) - kctl->private_value; - cur_val = pval->cached_value; + cur_val = pval >> 24; new_val = ucontrol->value.enumerated.item[0]; + if (cur_val == new_val) + return 0; - mixer = (struct usb_mixer_interface *) pval->mixer; - if (snd_BUG_ON(!mixer)) - return -EINVAL; - - chip = (struct snd_usb_audio *) mixer->chip; - if (snd_BUG_ON(!chip)) - return -EINVAL; - - id = pval->bUnitID; - validx = pval->validx; - - if (!pval->is_cached) { - /* Read current value */ - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(chip->dev, - usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, - validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), - value, val_len); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - - cur_val = value[0]; - pval->cached_value = cur_val; - pval->is_cached = 1; - } - /* update value if needed */ - if (cur_val != new_val) { - value[0] = new_val; - value[1] = 0; - down_read(&mixer->chip->shutdown_rwsem); - if (mixer->chip->shutdown) - err = -ENODEV; - else - err = snd_usb_ctl_msg(chip->dev, - usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, - USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, - validx << 8, snd_usb_ctrl_intf(chip) | (id << 8), - value, val_len); - up_read(&mixer->chip->shutdown_rwsem); - if (err < 0) - return err; - - pval->cached_value = new_val; - pval->is_cached = 1; - changed = 1; - } - - return changed; -} - -static void kctl_private_value_free(struct snd_kcontrol *kctl) -{ - kfree((void *)kctl->private_value); + kctl->private_value &= ~(0xff << 24); + kctl->private_value |= new_val << 24; + err = snd_ftu_eff_switch_update(list); + return err < 0 ? err : 1; } static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, @@ -902,33 +988,16 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, .get = snd_ftu_eff_switch_get, .put = snd_ftu_eff_switch_put }; - + struct usb_mixer_elem_list *list; int err; - struct snd_kcontrol *kctl; - struct snd_ftu_eff_switch_priv_val *pval; - - pval = kzalloc(sizeof(*pval), GFP_KERNEL); - if (!pval) - return -ENOMEM; - - pval->cached_value = 0; - pval->is_cached = 0; - pval->mixer = mixer; - pval->bUnitID = bUnitID; - pval->validx = validx; - - template.private_value = (unsigned long) pval; - kctl = snd_ctl_new1(&template, mixer->chip); - if (!kctl) { - kfree(pval); - return -ENOMEM; - } - kctl->private_free = kctl_private_value_free; - err = snd_ctl_add(mixer->chip->card, kctl); + err = add_single_ctl_with_resume(mixer, bUnitID, + snd_ftu_eff_switch_update, + &template, &list); if (err < 0) return err; - + list->kctl->private_value = (validx << 8) | bUnitID; + snd_ftu_eff_switch_init(mixer, list->kctl); return 0; } @@ -1110,7 +1179,7 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, int unitid = 12; /* SamleRate ExtensionUnit ID */ list_for_each_entry(mixer, &chip->mixer_list, list) { - cval = mixer->id_elems[unitid]; + cval = (struct usb_mixer_elem_info *)mixer->id_elems[unitid]; if (cval) { snd_usb_mixer_set_ctl_value(cval, UAC_SET_CUR, cval->control << 8, @@ -1440,7 +1509,8 @@ static int snd_microii_spdif_info(struct snd_kcontrol *kcontrol, static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct snd_usb_audio *chip = list->mixer->chip; int err; struct usb_interface *iface; struct usb_host_interface *alts; @@ -1448,17 +1518,23 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, unsigned char data[3]; int rate; + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + } + ucontrol->value.iec958.status[0] = kcontrol->private_value & 0xff; ucontrol->value.iec958.status[1] = (kcontrol->private_value >> 8) & 0xff; ucontrol->value.iec958.status[2] = 0x00; /* use known values for that card: interface#1 altsetting#1 */ - iface = usb_ifnum_to_if(mixer->chip->dev, 1); + iface = usb_ifnum_to_if(chip->dev, 1); alts = &iface->altsetting[1]; ep = get_endpoint(alts, 0)->bEndpointAddress; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_rcvctrlpipe(mixer->chip->dev, 0), + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN, UAC_EP_CS_ATTR_SAMPLE_RATE << 8, @@ -1473,22 +1549,27 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, IEC958_AES3_CON_FS_48000 : IEC958_AES3_CON_FS_44100; err = 0; -end: + end: + up_read(&chip->shutdown_rwsem); return err; } -static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_microii_spdif_default_update(struct usb_mixer_elem_list *list) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - int err; + struct snd_usb_audio *chip = list->mixer->chip; + unsigned int pval = list->kctl->private_value; u8 reg; - unsigned long priv_backup = kcontrol->private_value; + int err; - reg = ((ucontrol->value.iec958.status[1] & 0x0f) << 4) | - (ucontrol->value.iec958.status[0] & 0x0f); - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + } + + reg = ((pval >> 4) & 0xf0) | (pval & 0x0f); + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1498,15 +1579,10 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, if (err < 0) goto end; - kcontrol->private_value &= 0xfffff0f0; - kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; - kcontrol->private_value |= (ucontrol->value.iec958.status[0] & 0x0f); - - reg = (ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO) ? - 0xa0 : 0x20; - reg |= (ucontrol->value.iec958.status[1] >> 4) & 0x0f; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + reg = (pval & IEC958_AES0_NONAUDIO) ? 0xa0 : 0x20; + reg |= (pval >> 12) & 0x0f; + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1516,16 +1592,36 @@ static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, if (err < 0) goto end; - kcontrol->private_value &= 0xffff0fff; - kcontrol->private_value |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; + end: + up_read(&chip->shutdown_rwsem); + return err; +} + +static int snd_microii_spdif_default_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + unsigned int pval, pval_old; + int err; + + pval = pval_old = kcontrol->private_value; + pval &= 0xfffff0f0; + pval |= (ucontrol->value.iec958.status[1] & 0x0f) << 8; + pval |= (ucontrol->value.iec958.status[0] & 0x0f); + + pval &= 0xffff0fff; + pval |= (ucontrol->value.iec958.status[1] & 0xf0) << 8; /* The frequency bits in AES3 cannot be set via register access. */ /* Silently ignore any bits from the request that cannot be set. */ - err = (priv_backup != kcontrol->private_value); -end: - return err; + if (pval == pval_old) + return 0; + + kcontrol->private_value = pval; + err = snd_microii_spdif_default_update(list); + return err < 0 ? err : 1; } static int snd_microii_spdif_mask_get(struct snd_kcontrol *kcontrol, @@ -1547,15 +1643,20 @@ static int snd_microii_spdif_switch_get(struct snd_kcontrol *kcontrol, return 0; } -static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int snd_microii_spdif_switch_update(struct usb_mixer_elem_list *list) { - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); + struct snd_usb_audio *chip = list->mixer->chip; + u8 reg = list->kctl->private_value; int err; - u8 reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a; - err = snd_usb_ctl_msg(mixer->chip->dev, - usb_sndctrlpipe(mixer->chip->dev, 0), + down_read(&chip->shutdown_rwsem); + if (chip->shutdown) { + err = -ENODEV; + goto end; + } + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER, reg, @@ -1563,15 +1664,27 @@ static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, NULL, 0); - if (!err) { - err = (reg != (kcontrol->private_value & 0x0ff)); - if (err) - kcontrol->private_value = reg; - } - + end: + up_read(&chip->shutdown_rwsem); return err; } +static int snd_microii_spdif_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + u8 reg; + int err; + + reg = ucontrol->value.integer.value[0] ? 0x28 : 0x2a; + if (reg != list->kctl->private_value) + return 0; + + kcontrol->private_value = reg; + err = snd_microii_spdif_switch_update(list); + return err < 0 ? err : 1; +} + static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { { .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -1601,10 +1714,17 @@ static struct snd_kcontrol_new snd_microii_mixer_spdif[] = { static int snd_microii_controls_create(struct usb_mixer_interface *mixer) { int err, i; + static usb_mixer_elem_resume_func_t resume_funcs[] = { + snd_microii_spdif_default_update, + NULL, + snd_microii_spdif_switch_update + }; for (i = 0; i < ARRAY_SIZE(snd_microii_mixer_spdif); ++i) { - err = snd_ctl_add(mixer->chip->card, - snd_ctl_new1(&snd_microii_mixer_spdif[i], mixer)); + err = add_single_ctl_with_resume(mixer, 0, + resume_funcs[i], + &snd_microii_mixer_spdif[i], + NULL); if (err < 0) return err; } @@ -1661,6 +1781,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) err = snd_microii_controls_create(mixer); break; + case USB_ID(0x0dba, 0x1000): /* Digidesign Mbox 1 */ + err = snd_mbox1_create_sync_switch(mixer); + break; + case USB_ID(0x17cc, 0x1011): /* Traktor Audio 6 */ err = snd_nativeinstruments_create_mixer(mixer, snd_nativeinstruments_ta6_mixers, @@ -1677,6 +1801,14 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) /* detection is disabled in mixer_maps.c */ err = snd_create_std_mono_table(mixer, ebox44_table); break; + + case USB_ID(0x1235, 0x8012): /* Focusrite Scarlett 6i6 */ + case USB_ID(0x1235, 0x8002): /* Focusrite Scarlett 8i6 */ + case USB_ID(0x1235, 0x8004): /* Focusrite Scarlett 18i6 */ + case USB_ID(0x1235, 0x8014): /* Focusrite Scarlett 18i8 */ + case USB_ID(0x1235, 0x800c): /* Focusrite Scarlett 18i20 */ + err = snd_scarlett_controls_create(mixer); + break; } return err; diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c new file mode 100644 index 000000000000..9109652b88b9 --- /dev/null +++ b/sound/usb/mixer_scarlett.c @@ -0,0 +1,1004 @@ +/* + * Scarlett Driver for ALSA + * + * Copyright (c) 2013 by Tobias Hoffmann + * Copyright (c) 2013 by Robin Gareus <robin at gareus.org> + * Copyright (c) 2002 by Takashi Iwai <tiwai at suse.de> + * Copyright (c) 2014 by Chris J Arges <chris.j.arges at canonical.com> + * + * Many codes borrowed from audio.c by + * Alan Cox (alan at lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer at ife.ee.ethz.ch) + * + * Code cleanup: + * David Henningsson <david.henningsson at canonical.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +/* + * Rewritten and extended to support more models, e.g. Scarlett 18i8. + * + * Auto-detection via UAC2 is not feasible to properly discover the vast + * majority of features. It's related to both Linux/ALSA's UAC2 as well as + * Focusrite's implementation of it. Eventually quirks may be sufficient but + * right now it's a major headache to work arount these things. + * + * NB. Neither the OSX nor the win driver provided by Focusrite performs + * discovery, they seem to operate the same as this driver. + */ + +/* Mixer Interface for the Focusrite Scarlett 18i6 audio interface. + * + * The protocol was reverse engineered by looking at communication between + * Scarlett MixControl (v 1.2.128.0) and the Focusrite(R) Scarlett 18i6 + * (firmware v305) using wireshark and usbmon in January 2013. + * Extended in July 2013. + * + * this mixer gives complete access to all features of the device: + * - change Impedance of inputs (Line-in, Mic / Instrument, Hi-Z) + * - select clock source + * - dynamic input to mixer-matrix assignment + * - 18 x 6 mixer-matrix gain stages + * - bus routing & volume control + * - automatic re-initialization on connect if device was power-cycled + * + * USB URB commands overview (bRequest = 0x01 = UAC2_CS_CUR) + * wIndex + * 0x01 Analog Input line/instrument impedance switch, wValue=0x0901 + + * channel, data=Line/Inst (2bytes) + * pad (-10dB) switch, wValue=0x0b01 + channel, data=Off/On (2bytes) + * ?? wValue=0x0803/04, ?? (2bytes) + * 0x0a Master Volume, wValue=0x0200+bus[0:all + only 1..4?] data(2bytes) + * Bus Mute/Unmute wValue=0x0100+bus[0:all + only 1..4?], data(2bytes) + * 0x28 Clock source, wValue=0x0100, data={1:int,2:spdif,3:adat} (1byte) + * 0x29 Set Sample-rate, wValue=0x0100, data=sample-rate(4bytes) + * 0x32 Mixer mux, wValue=0x0600 + mixer-channel, data=input-to-connect(2bytes) + * 0x33 Output mux, wValue=bus, data=input-to-connect(2bytes) + * 0x34 Capture mux, wValue=0...18, data=input-to-connect(2bytes) + * 0x3c Matrix Mixer gains, wValue=mixer-node data=gain(2bytes) + * ?? [sometimes](4bytes, e.g 0x000003be 0x000003bf ...03ff) + * + * USB reads: (i.e. actually issued by original software) + * 0x01 wValue=0x0901+channel (1byte!!), wValue=0x0b01+channed (1byte!!) + * 0x29 wValue=0x0100 sample-rate(4bytes) + * wValue=0x0200 ?? 1byte (only once) + * 0x2a wValue=0x0100 ?? 4bytes, sample-rate2 ?? + * + * USB reads with bRequest = 0x03 = UAC2_CS_MEM + * 0x3c wValue=0x0002 1byte: sync status (locked=1) + * wValue=0x0000 18*2byte: peak meter (inputs) + * wValue=0x0001 8(?)*2byte: peak meter (mix) + * wValue=0x0003 6*2byte: peak meter (pcm/daw) + * + * USB write with bRequest = 0x03 + * 0x3c Save settings to hardware: wValue=0x005a, data=0xa5 + * + * + * <ditaa> + * /--------------\ 18chn 6chn /--------------\ + * | Hardware in +--+-------\ /------+--+ ALSA PCM out | + * \--------------/ | | | | \--------------/ + * | | | | + * | v v | + * | +---------------+ | + * | \ Matrix Mux / | + * | +-----+-----+ | + * | | | + * | | 18chn | + * | v | + * | +-----------+ | + * | | Mixer | | + * | | Matrix | | + * | | | | + * | | 18x6 Gain | | + * | | stages | | + * | +-----+-----+ | + * | | | + * | | | + * | 18chn | 6chn | 6chn + * v v v + * ========================= + * +---------------+ +--—------------+ + * \ Output Mux / \ Capture Mux / + * +-----+-----+ +-----+-----+ + * | | + * | 6chn | + * v | + * +-------------+ | + * | Master Gain | | + * +------+------+ | + * | | + * | 6chn | 18chn + * | (3 stereo pairs) | + * /--------------\ | | /--------------\ + * | Hardware out |<--/ \-->| ALSA PCM in | + * \--------------/ \--------------/ + * </ditaa> + * + */ + +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/audio-v2.h> + +#include <sound/core.h> +#include <sound/control.h> +#include <sound/tlv.h> + +#include "usbaudio.h" +#include "mixer.h" +#include "helper.h" +#include "power.h" + +#include "mixer_scarlett.h" + +/* some gui mixers can't handle negative ctl values */ +#define SND_SCARLETT_LEVEL_BIAS 128 +#define SND_SCARLETT_MATRIX_IN_MAX 18 +#define SND_SCARLETT_CONTROLS_MAX 10 +#define SND_SCARLETT_OFFSETS_MAX 5 + +enum { + SCARLETT_OUTPUTS, + SCARLETT_SWITCH_IMPEDANCE, + SCARLETT_SWITCH_PAD, +}; + +enum { + SCARLETT_OFFSET_PCM = 0, + SCARLETT_OFFSET_ANALOG = 1, + SCARLETT_OFFSET_SPDIF = 2, + SCARLETT_OFFSET_ADAT = 3, + SCARLETT_OFFSET_MIX = 4, +}; + +struct scarlett_mixer_elem_enum_info { + int start; + int len; + int offsets[SND_SCARLETT_OFFSETS_MAX]; + char const * const *names; +}; + +struct scarlett_mixer_control { + unsigned char num; + unsigned char type; + const char *name; +}; + +struct scarlett_device_info { + int matrix_in; + int matrix_out; + int input_len; + int output_len; + + struct scarlett_mixer_elem_enum_info opt_master; + struct scarlett_mixer_elem_enum_info opt_matrix; + + /* initial values for matrix mux */ + int matrix_mux_init[SND_SCARLETT_MATRIX_IN_MAX]; + + int num_controls; /* number of items in controls */ + const struct scarlett_mixer_control controls[SND_SCARLETT_CONTROLS_MAX]; +}; + +/********************** Enum Strings *************************/ + +static const struct scarlett_mixer_elem_enum_info opt_pad = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "0dB", "-10dB" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_impedance = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "Line", "Hi-Z" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_clock = { + .start = 1, + .len = 3, + .offsets = {}, + .names = (char const * const []){ + "Internal", "SPDIF", "ADAT" + } +}; + +static const struct scarlett_mixer_elem_enum_info opt_sync = { + .start = 0, + .len = 2, + .offsets = {}, + .names = (char const * const []){ + "No Lock", "Locked" + } +}; + +static int scarlett_ctl_switch_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int scarlett_ctl_switch_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, err, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &val); + if (err < 0) + return err; + + val = !val; /* invert mute logic for mixer */ + ucontrol->value.integer.value[i] = val; + } + + return 0; +} + +static int scarlett_ctl_switch_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, changed = 0; + int err, oval, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[i]; + val = !val; + if (oval != val) { + err = snd_usb_set_cur_mix_value(elem, i, i, val); + if (err < 0) + return err; + + changed = 1; + } + } + + return changed; +} + +static int scarlett_ctl_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = + container_of(list, struct usb_mixer_elem_info, head); + int i; + + for (i = 0; i < elem->channels; i++) + if (elem->cached & (1 << i)) + snd_usb_set_cur_mix_value(elem, i, i, + elem->cache_val[i]); + return 0; +} + +static int scarlett_ctl_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = elem->channels; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)kctl->private_value + + SND_SCARLETT_LEVEL_BIAS; + uinfo->value.integer.step = 1; + return 0; +} + +static int scarlett_ctl_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, err, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &val); + if (err < 0) + return err; + + val = clamp(val / 256, -128, (int)kctl->private_value) + + SND_SCARLETT_LEVEL_BIAS; + ucontrol->value.integer.value[i] = val; + } + + return 0; +} + +static int scarlett_ctl_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + int i, changed = 0; + int err, oval, val; + + for (i = 0; i < elem->channels; i++) { + err = snd_usb_get_cur_mix_value(elem, i, i, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[i] - + SND_SCARLETT_LEVEL_BIAS; + val = val * 256; + if (oval != val) { + err = snd_usb_set_cur_mix_value(elem, i, i, val); + if (err < 0) + return err; + + changed = 1; + } + } + + return changed; +} + +static void scarlett_generate_name(int i, char *dst, int offsets[]) +{ + if (i > offsets[SCARLETT_OFFSET_MIX]) + sprintf(dst, "Mix %c", + 'A'+(i - offsets[SCARLETT_OFFSET_MIX] - 1)); + else if (i > offsets[SCARLETT_OFFSET_ADAT]) + sprintf(dst, "ADAT %d", i - offsets[SCARLETT_OFFSET_ADAT]); + else if (i > offsets[SCARLETT_OFFSET_SPDIF]) + sprintf(dst, "SPDIF %d", i - offsets[SCARLETT_OFFSET_SPDIF]); + else if (i > offsets[SCARLETT_OFFSET_ANALOG]) + sprintf(dst, "Analog %d", i - offsets[SCARLETT_OFFSET_ANALOG]); + else if (i > offsets[SCARLETT_OFFSET_PCM]) + sprintf(dst, "PCM %d", i - offsets[SCARLETT_OFFSET_PCM]); + else + sprintf(dst, "Off"); +} + +static int scarlett_ctl_enum_dynamic_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + unsigned int items = opt->len; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = elem->channels; + uinfo->value.enumerated.items = items; + + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + + /* generate name dynamically based on item number and offset info */ + scarlett_generate_name(uinfo->value.enumerated.item, + uinfo->value.enumerated.name, + opt->offsets); + + return 0; +} + +static int scarlett_ctl_enum_info(struct snd_kcontrol *kctl, + struct snd_ctl_elem_info *uinfo) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + + return snd_ctl_enum_info(uinfo, elem->channels, opt->len, + (const char * const *)opt->names); +} + +static int scarlett_ctl_enum_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, val; + + err = snd_usb_get_cur_mix_value(elem, 0, 0, &val); + if (err < 0) + return err; + + val = clamp(val - opt->start, 0, opt->len-1); + + ucontrol->value.enumerated.item[0] = val; + + return 0; +} + +static int scarlett_ctl_enum_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct scarlett_mixer_elem_enum_info *opt = elem->private_data; + int err, oval, val; + + err = snd_usb_get_cur_mix_value(elem, 0, 0, &oval); + if (err < 0) + return err; + + val = ucontrol->value.integer.value[0]; + val = val + opt->start; + if (val != oval) { + snd_usb_set_cur_mix_value(elem, 0, 0, val); + return 1; + } + return 0; +} + +static int scarlett_ctl_enum_resume(struct usb_mixer_elem_list *list) +{ + struct usb_mixer_elem_info *elem = + container_of(list, struct usb_mixer_elem_info, head); + + if (elem->cached) + snd_usb_set_cur_mix_value(elem, 0, 0, *elem->cache_val); + return 0; +} + +static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kctl->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + unsigned char buf[2 * MAX_CHANNELS] = {0, }; + int wValue = (elem->control << 8) | elem->idx_off; + int idx = snd_usb_ctrl_intf(chip) | (elem->head.id << 8); + int err; + + err = snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + UAC2_CS_MEM, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | + USB_DIR_IN, wValue, idx, buf, elem->channels); + if (err < 0) + return err; + + ucontrol->value.enumerated.item[0] = clamp((int)buf[0], 0, 1); + return 0; +} + +static struct snd_kcontrol_new usb_scarlett_ctl_switch = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_switch_info, + .get = scarlett_ctl_switch_get, + .put = scarlett_ctl_switch_put, +}; + +static const DECLARE_TLV_DB_SCALE(db_scale_scarlett_gain, -12800, 100, 0); + +static struct snd_kcontrol_new usb_scarlett_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett_ctl_info, + .get = scarlett_ctl_get, + .put = scarlett_ctl_put, + .private_value = 6, /* max value */ + .tlv = { .p = db_scale_scarlett_gain } +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_master = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .name = "", + .info = scarlett_ctl_info, + .get = scarlett_ctl_get, + .put = scarlett_ctl_put, + .private_value = 6, /* max value */ + .tlv = { .p = db_scale_scarlett_gain } +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_enum_info, + .get = scarlett_ctl_enum_get, + .put = scarlett_ctl_enum_put, +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", + .info = scarlett_ctl_enum_dynamic_info, + .get = scarlett_ctl_enum_get, + .put = scarlett_ctl_enum_put, +}; + +static struct snd_kcontrol_new usb_scarlett_ctl_sync = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .name = "", + .info = scarlett_ctl_enum_info, + .get = scarlett_ctl_meter_get, +}; + +static int add_new_ctl(struct usb_mixer_interface *mixer, + const struct snd_kcontrol_new *ncontrol, + usb_mixer_elem_resume_func_t resume, + int index, int offset, int num, + int val_type, int channels, const char *name, + const struct scarlett_mixer_elem_enum_info *opt, + struct usb_mixer_elem_info **elem_ret +) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *elem; + int err; + + elem = kzalloc(sizeof(*elem), GFP_KERNEL); + if (!elem) + return -ENOMEM; + + elem->head.mixer = mixer; + elem->head.resume = resume; + elem->control = offset; + elem->idx_off = num; + elem->head.id = index; + elem->val_type = val_type; + + elem->channels = channels; + + /* add scarlett_mixer_elem_enum_info struct */ + elem->private_data = (void *)opt; + + kctl = snd_ctl_new1(ncontrol, elem); + if (!kctl) { + kfree(elem); + return -ENOMEM; + } + kctl->private_free = snd_usb_mixer_elem_free; + + strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); + + err = snd_usb_mixer_add_control(&elem->head, kctl); + if (err < 0) + return err; + + if (elem_ret) + *elem_ret = elem; + + return 0; +} + +static int add_output_ctls(struct usb_mixer_interface *mixer, + int index, const char *name, + const struct scarlett_device_info *info) +{ + int err; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct usb_mixer_elem_info *elem; + + /* Add mute switch */ + snprintf(mx, sizeof(mx), "Master %d (%s) Playback Switch", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, + scarlett_ctl_resume, 0x0a, 0x01, + 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); + if (err < 0) + return err; + + /* Add volume control and initialize to 0 */ + snprintf(mx, sizeof(mx), "Master %d (%s) Playback Volume", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, + scarlett_ctl_resume, 0x0a, 0x02, + 2*index+1, USB_MIXER_S16, 2, mx, NULL, &elem); + if (err < 0) + return err; + + /* Add L channel source playback enumeration */ + snprintf(mx, sizeof(mx), "Master %dL (%s) Source Playback Enum", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, + 2*index, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; + + /* Add R channel source playback enumeration */ + snprintf(mx, sizeof(mx), "Master %dR (%s) Source Playback Enum", + index + 1, name); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x33, 0x00, + 2*index+1, USB_MIXER_S16, 1, mx, &info->opt_master, + &elem); + if (err < 0) + return err; + + return 0; +} + +/********************** device-specific config *************************/ + +/* untested... */ +static struct scarlett_device_info s6i6_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 6, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 27, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 19, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .num_controls = 0, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 12, 13, 14, 15, /* Analog -> 1..4 */ + 16, 17, /* SPDIF -> 5,6 */ + 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ + 8, 9, 10, 11 + } +}; + +/* untested... */ +static struct scarlett_device_info s8i6_info = { + .matrix_in = 18, + .matrix_out = 6, + .input_len = 8, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 25, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 19, + .offsets = {0, 12, 16, 18, 18}, + .names = NULL + }, + + .num_controls = 7, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 12, 13, 14, 15, /* Analog -> 1..4 */ + 16, 17, /* SPDIF -> 5,6 */ + 0, 1, 2, 3, 4, 5, 6, 7, /* PCM[1..12] -> 7..18 */ + 8, 9, 10, 11 + } +}; + +static struct scarlett_device_info s18i6_info = { + .matrix_in = 18, + .matrix_out = 6, + .input_len = 18, + .output_len = 6, + + .opt_master = { + .start = -1, + .len = 31, + .offsets = {0, 6, 14, 16, 24}, + .names = NULL, + }, + + .opt_matrix = { + .start = -1, + .len = 25, + .offsets = {0, 6, 14, 16, 24}, + .names = NULL, + }, + + .num_controls = 5, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + }, + + .matrix_mux_init = { + 6, 7, 8, 9, 10, 11, 12, 13, /* Analog -> 1..8 */ + 16, 17, 18, 19, 20, 21, /* ADAT[1..6] -> 9..14 */ + 14, 15, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + +static struct scarlett_device_info s18i8_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 18, + .output_len = 8, + + .opt_master = { + .start = -1, + .len = 35, + .offsets = {0, 8, 16, 18, 26}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 27, + .offsets = {0, 8, 16, 18, 26}, + .names = NULL + }, + + .num_controls = 10, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Headphone 1" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Headphone 2" }, + { .num = 3, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + }, + + .matrix_mux_init = { + 8, 9, 10, 11, 12, 13, 14, 15, /* Analog -> 1..8 */ + 18, 19, 20, 21, 22, 23, /* ADAT[1..6] -> 9..14 */ + 16, 17, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + +static struct scarlett_device_info s18i20_info = { + .matrix_in = 18, + .matrix_out = 8, + .input_len = 18, + .output_len = 20, + + .opt_master = { + .start = -1, + .len = 47, + .offsets = {0, 20, 28, 30, 38}, + .names = NULL + }, + + .opt_matrix = { + .start = -1, + .len = 39, + .offsets = {0, 20, 28, 30, 38}, + .names = NULL + }, + + .num_controls = 10, + .controls = { + { .num = 0, .type = SCARLETT_OUTPUTS, .name = "Monitor" }, + { .num = 1, .type = SCARLETT_OUTPUTS, .name = "Line 3/4" }, + { .num = 2, .type = SCARLETT_OUTPUTS, .name = "Line 5/6" }, + { .num = 3, .type = SCARLETT_OUTPUTS, .name = "Line 7/8" }, + { .num = 4, .type = SCARLETT_OUTPUTS, .name = "Line 9/10" }, + { .num = 5, .type = SCARLETT_OUTPUTS, .name = "SPDIF" }, + { .num = 6, .type = SCARLETT_OUTPUTS, .name = "ADAT 1/2" }, + { .num = 7, .type = SCARLETT_OUTPUTS, .name = "ADAT 3/4" }, + { .num = 8, .type = SCARLETT_OUTPUTS, .name = "ADAT 5/6" }, + { .num = 9, .type = SCARLETT_OUTPUTS, .name = "ADAT 7/8" }, + /*{ .num = 1, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 1, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_IMPEDANCE, .name = NULL}, + { .num = 2, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 3, .type = SCARLETT_SWITCH_PAD, .name = NULL}, + { .num = 4, .type = SCARLETT_SWITCH_PAD, .name = NULL},*/ + }, + + .matrix_mux_init = { + 20, 21, 22, 23, 24, 25, 26, 27, /* Analog -> 1..8 */ + 30, 31, 32, 33, 34, 35, /* ADAT[1..6] -> 9..14 */ + 28, 29, /* SPDIF -> 15,16 */ + 0, 1 /* PCM[1,2] -> 17,18 */ + } +}; + + +static int scarlett_controls_create_generic(struct usb_mixer_interface *mixer, + struct scarlett_device_info *info) +{ + int i, err; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + const struct scarlett_mixer_control *ctl; + struct usb_mixer_elem_info *elem; + + /* create master switch and playback volume */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_switch, + scarlett_ctl_resume, 0x0a, 0x01, 0, + USB_MIXER_S16, 1, "Master Playback Switch", NULL, + &elem); + if (err < 0) + return err; + + err = add_new_ctl(mixer, &usb_scarlett_ctl_master, + scarlett_ctl_resume, 0x0a, 0x02, 0, + USB_MIXER_S16, 1, "Master Playback Volume", NULL, + &elem); + if (err < 0) + return err; + + /* iterate through controls in info struct and create each one */ + for (i = 0; i < info->num_controls; i++) { + ctl = &info->controls[i]; + + switch (ctl->type) { + case SCARLETT_OUTPUTS: + err = add_output_ctls(mixer, ctl->num, ctl->name, info); + if (err < 0) + return err; + break; + case SCARLETT_SWITCH_IMPEDANCE: + sprintf(mx, "Input %d Impedance Switch", ctl->num); + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x01, + 0x09, ctl->num, USB_MIXER_S16, 1, mx, + &opt_impedance, &elem); + if (err < 0) + return err; + break; + case SCARLETT_SWITCH_PAD: + sprintf(mx, "Input %d Pad Switch", ctl->num); + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x01, + 0x0b, ctl->num, USB_MIXER_S16, 1, mx, + &opt_pad, &elem); + if (err < 0) + return err; + break; + } + } + + return 0; +} + +/* + * Create and initialize a mixer for the Focusrite(R) Scarlett + */ +int snd_scarlett_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i, o; + char mx[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct scarlett_device_info *info; + struct usb_mixer_elem_info *elem; + static char sample_rate_buffer[4] = { '\x80', '\xbb', '\x00', '\x00' }; + + /* only use UAC_VERSION_2 */ + if (!mixer->protocol) + return 0; + + switch (mixer->chip->usb_id) { + case USB_ID(0x1235, 0x8012): + info = &s6i6_info; + break; + case USB_ID(0x1235, 0x8002): + info = &s8i6_info; + break; + case USB_ID(0x1235, 0x8004): + info = &s18i6_info; + break; + case USB_ID(0x1235, 0x8014): + info = &s18i8_info; + break; + case USB_ID(0x1235, 0x800c): + info = &s18i20_info; + break; + default: /* device not (yet) supported */ + return -EINVAL; + } + + /* generic function to create controls */ + err = scarlett_controls_create_generic(mixer, info); + if (err < 0) + return err; + + /* setup matrix controls */ + for (i = 0; i < info->matrix_in; i++) { + snprintf(mx, sizeof(mx), "Matrix %02d Input Playback Route", + i+1); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x32, + 0x06, i, USB_MIXER_S16, 1, mx, + &info->opt_matrix, &elem); + if (err < 0) + return err; + + for (o = 0; o < info->matrix_out; o++) { + sprintf(mx, "Matrix %02d Mix %c Playback Volume", i+1, + o+'A'); + err = add_new_ctl(mixer, &usb_scarlett_ctl, + scarlett_ctl_resume, 0x3c, 0x00, + (i << 3) + (o & 0x07), USB_MIXER_S16, + 1, mx, NULL, &elem); + if (err < 0) + return err; + + } + } + + for (i = 0; i < info->input_len; i++) { + snprintf(mx, sizeof(mx), "Input Source %02d Capture Route", + i+1); + err = add_new_ctl(mixer, &usb_scarlett_ctl_dynamic_enum, + scarlett_ctl_enum_resume, 0x34, + 0x00, i, USB_MIXER_S16, 1, mx, + &info->opt_master, &elem); + if (err < 0) + return err; + } + + /* val_len == 1 needed here */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_enum, + scarlett_ctl_enum_resume, 0x28, 0x01, 0, + USB_MIXER_U8, 1, "Sample Clock Source", + &opt_clock, &elem); + if (err < 0) + return err; + + /* val_len == 1 and UAC2_CS_MEM */ + err = add_new_ctl(mixer, &usb_scarlett_ctl_sync, NULL, 0x3c, 0x00, 2, + USB_MIXER_U8, 1, "Sample Clock Sync Status", + &opt_sync, &elem); + if (err < 0) + return err; + + /* initialize sampling rate to 48000 */ + err = snd_usb_ctl_msg(mixer->chip->dev, + usb_sndctrlpipe(mixer->chip->dev, 0), UAC2_CS_CUR, + USB_RECIP_INTERFACE | USB_TYPE_CLASS | + USB_DIR_OUT, 0x0100, snd_usb_ctrl_intf(mixer->chip) | + (0x29 << 8), sample_rate_buffer, 4); + if (err < 0) + return err; + + return err; +} diff --git a/sound/usb/mixer_scarlett.h b/sound/usb/mixer_scarlett.h new file mode 100644 index 000000000000..19c592ab0332 --- /dev/null +++ b/sound/usb/mixer_scarlett.h @@ -0,0 +1,6 @@ +#ifndef __USB_MIXER_SCARLETT_H +#define __USB_MIXER_SCARLETT_H + +int snd_scarlett_controls_create(struct usb_mixer_interface *mixer); + +#endif /* __USB_MIXER_SCARLETT_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c62a1659106d..0d8aba5fe1a8 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -482,6 +482,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* set interface */ if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) { + + err = snd_usb_select_mode_quirk(subs, fmt); + if (err < 0) + return -EIO; + err = usb_set_interface(dev, fmt->iface, fmt->altsetting); if (err < 0) { dev_err(&dev->dev, diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index c657752a420c..73d2ba47cc31 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2667,57 +2667,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_MIDI_NOVATION } }, -{ - /* - * Focusrite Scarlett 18i6 - * - * Avoid mixer creation, which otherwise fails because some of - * the interface descriptor subtypes for interface 0 are - * unknown. That should be fixed or worked-around but this at - * least allows the device to be used successfully with a DAW - * and an external mixer. See comments below about other - * ignored interfaces. - */ - USB_DEVICE(0x1235, 0x8004), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Focusrite", - .product_name = "Scarlett 18i6", - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = & (const struct snd_usb_audio_quirk[]) { - { - /* InterfaceSubClass 1 (Control Device) */ - .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 1, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_STANDARD_INTERFACE - }, - { - /* InterfaceSubClass 1 (Control Device) */ - .ifnum = 3, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = 4, - .type = QUIRK_MIDI_STANDARD_INTERFACE - }, - { - /* InterfaceSubClass 1 (Device Firmware Update) */ - .ifnum = 5, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, /* Access Music devices */ { @@ -2944,7 +2893,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), .data = (const struct snd_usb_audio_quirk[]){ { .ifnum = 0, - .type = QUIRK_IGNORE_INTERFACE, + .type = QUIRK_AUDIO_STANDARD_MIXER, }, { .ifnum = 1, @@ -2955,16 +2904,40 @@ YAMAHA_DEVICE(0x7010, "UB99"), .iface = 1, .altsetting = 1, .altset_idx = 1, - .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .attributes = 0x4, .endpoint = 0x02, - .ep_attr = 0x01, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000, - .rate_min = 44100, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_SYNC, + .maxpacksize = 0x130, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, .rate_max = 48000, - .nr_rates = 2, + .nr_rates = 1, .rate_table = (unsigned int[]) { - 44100, 48000 + 48000 + } + } + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3BE, + .channels = 2, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0x4, + .endpoint = 0x81, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC, + .maxpacksize = 0x130, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { + 48000 } } }, @@ -2972,7 +2945,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .ifnum = -1 } } - } }, @@ -3203,6 +3175,46 @@ YAMAHA_DEVICE(0x7010, "UB99"), { /* + * ZOOM R16/24 in audio interface mode. + * Mixer descriptors are garbage, further quirks will be needed + * to make any of it functional, thus disabled for now. + * Playback stream appears to start and run fine but no sound + * is produced, so also disabled for now. + */ + USB_DEVICE(0x1686, 0x00dd), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + /* Mixer */ + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE, + }, + { + /* Playback */ + .ifnum = 1, + .type = QUIRK_IGNORE_INTERFACE, + }, + { + /* Capture */ + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE, + }, + { + /* Midi */ + .ifnum = 3, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + .ifnum = -1 + }, + } + } +}, + +{ + /* * 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/quirks.c b/sound/usb/quirks.c index 60dfe0d28771..4dbfb3d18ee2 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -43,12 +43,13 @@ static int create_composite_quirk(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, - const struct snd_usb_audio_quirk *quirk) + const struct snd_usb_audio_quirk *quirk_comp) { int probed_ifnum = get_iface_desc(iface->altsetting)->bInterfaceNumber; + const struct snd_usb_audio_quirk *quirk; int err; - for (quirk = quirk->data; quirk->ifnum >= 0; ++quirk) { + for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) { iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); if (!iface) continue; @@ -58,9 +59,17 @@ static int create_composite_quirk(struct snd_usb_audio *chip, err = snd_usb_create_quirk(chip, iface, driver, quirk); if (err < 0) return err; - if (quirk->ifnum != probed_ifnum) + } + + for (quirk = quirk_comp->data; quirk->ifnum >= 0; ++quirk) { + iface = usb_ifnum_to_if(chip->dev, quirk->ifnum); + if (!iface) + continue; + if (quirk->ifnum != probed_ifnum && + !usb_interface_claimed(iface)) usb_driver_claim_interface(driver, iface, (void *)-1L); } + return 0; } @@ -1102,6 +1111,44 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, } } + +/* Marantz/Denon USB DACs need a vendor cmd to switch + * between PCM and native DSD mode + */ +int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt) +{ + struct usb_device *dev = subs->dev; + int err; + + switch (subs->stream->chip->usb_id) { + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + + /* First switch to alt set 0, otherwise the mode switch cmd + * will not be accepted by the DAC + */ + err = usb_set_interface(dev, fmt->iface, 0); + if (err < 0) + return err; + + mdelay(20); /* Delay needed after setting the interface */ + + switch (fmt->altsetting) { + case 2: /* DSD mode requested */ + case 1: /* PCM mode requested */ + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_INTERFACE, + fmt->altsetting - 1, 1, NULL, 0); + if (err < 0) + return err; + break; + } + mdelay(20); + } + return 0; +} + void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) { /* @@ -1160,6 +1207,14 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, break; } } + + /* Zoom R16/24 needs a tiny delay here, otherwise requests like + * get/set frequency return as failed despite actually succeeding. + */ + if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) && + (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) && + (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) + mdelay(1); } /* @@ -1204,5 +1259,16 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, break; } + /* Denon/Marantz devices with USB DAC functionality */ + switch (chip->usb_id) { + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + if (fp->altsetting == 2) + return SNDRV_PCM_FMTBIT_DSD_U32_BE; + break; + default: + break; + } + return 0; } diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 665e972a1b40..1b862386577d 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -31,6 +31,9 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size); +int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, + struct audioformat *fmt); + u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct audioformat *fp, unsigned int sample_bytes); diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index a63330dd1407..61d5dc2a3421 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -272,13 +272,8 @@ static void usX2Y_clients_stop(struct usX2Ydev *usX2Y) for (s = 0; s < 4; s++) { struct snd_usX2Y_substream *subs = usX2Y->subs[s]; if (subs) { - if (atomic_read(&subs->state) >= state_PRERUNNING) { - unsigned long flags; - - snd_pcm_stream_lock_irqsave(subs->pcm_substream, flags); - snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN); - snd_pcm_stream_unlock_irqrestore(subs->pcm_substream, flags); - } + if (atomic_read(&subs->state) >= state_PRERUNNING) + snd_pcm_stop_xrun(subs->pcm_substream); for (u = 0; u < NRURBS; u++) { struct urb *urb = subs->urb[u]; if (NULL != urb) |