diff options
author | Takashi Iwai <tiwai@suse.de> | 2021-11-01 07:33:49 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2021-11-01 07:34:19 +0100 |
commit | 8beea313507580ea2a18bf68f7589d40677c28ad (patch) | |
tree | 0a1cd76a9b2a414f9dcb64db2432cbf964be3a44 /sound | |
parent | 3ab7992018455ac63c33e9b3eaa7264e293e40f4 (diff) | |
parent | 2672e1970ab051f0333fdbc61a55b7616f4f5778 (diff) |
Merge branch 'for-next' into for-linus
Merge 5.16-devel branch for upstreaming
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
48 files changed, 1606 insertions, 452 deletions
diff --git a/sound/core/Makefile b/sound/core/Makefile index d774792850f3..350d704ced98 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -9,7 +9,9 @@ ifneq ($(CONFIG_SND_PROC_FS),) snd-y += info.o snd-$(CONFIG_SND_OSSEMUL) += info_oss.o endif +ifneq ($(CONFIG_M68K),y) snd-$(CONFIG_ISA_DMA_API) += isadma.o +endif snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o snd-$(CONFIG_SND_VMASTER) += vmaster.o snd-$(CONFIG_SND_JACK) += ctljack.o jack.o @@ -17,7 +19,6 @@ snd-$(CONFIG_SND_JACK) += ctljack.o jack.o snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \ pcm_memory.o memalloc.o snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o -snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index c7c943c661e6..99cd0f67daa1 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -10,6 +10,7 @@ #include <linux/mm.h> #include <linux/dma-mapping.h> #include <linux/genalloc.h> +#include <linux/highmem.h> #include <linux/vmalloc.h> #ifdef CONFIG_X86 #include <asm/set_memory.h> @@ -39,9 +40,11 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) } /** - * snd_dma_alloc_pages - allocate the buffer area according to the given type + * snd_dma_alloc_dir_pages - allocate the buffer area according to the given + * type and direction * @type: the DMA buffer type * @device: the device pointer + * @dir: DMA direction * @size: the buffer size to allocate * @dmab: buffer allocation record to store the allocated data * @@ -51,8 +54,9 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size) * Return: Zero if the buffer with the given size is allocated successfully, * otherwise a negative value on error. */ -int snd_dma_alloc_pages(int type, struct device *device, size_t size, - struct snd_dma_buffer *dmab) +int snd_dma_alloc_dir_pages(int type, struct device *device, + enum dma_data_direction dir, size_t size, + struct snd_dma_buffer *dmab) { if (WARN_ON(!size)) return -ENXIO; @@ -62,6 +66,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, size = PAGE_ALIGN(size); dmab->dev.type = type; dmab->dev.dev = device; + dmab->dev.dir = dir; dmab->bytes = 0; dmab->addr = 0; dmab->private_data = NULL; @@ -71,7 +76,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->bytes = size; return 0; } -EXPORT_SYMBOL(snd_dma_alloc_pages); +EXPORT_SYMBOL(snd_dma_alloc_dir_pages); /** * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback @@ -129,9 +134,10 @@ static void __snd_release_pages(struct device *dev, void *res) } /** - * snd_devm_alloc_pages - allocate the buffer and manage with devres + * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres * @dev: the device pointer * @type: the DMA buffer type + * @dir: DMA direction * @size: the buffer size to allocate * * Allocate buffer pages depending on the given type and manage using devres. @@ -144,7 +150,8 @@ static void __snd_release_pages(struct device *dev, void *res) * The function returns the snd_dma_buffer object at success, or NULL if failed. */ struct snd_dma_buffer * -snd_devm_alloc_pages(struct device *dev, int type, size_t size) +snd_devm_alloc_dir_pages(struct device *dev, int type, + enum dma_data_direction dir, size_t size) { struct snd_dma_buffer *dmab; int err; @@ -157,7 +164,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size) if (!dmab) return NULL; - err = snd_dma_alloc_pages(type, dev, size, dmab); + err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab); if (err < 0) { devres_free(dmab); return NULL; @@ -166,7 +173,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size) devres_add(dev, dmab); return dmab; } -EXPORT_SYMBOL_GPL(snd_devm_alloc_pages); +EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages); /** * snd_dma_buffer_mmap - perform mmap of the given DMA buffer @@ -185,6 +192,26 @@ int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab, } EXPORT_SYMBOL(snd_dma_buffer_mmap); +#ifdef CONFIG_HAS_DMA +/** + * snd_dma_buffer_sync - sync DMA buffer between CPU and device + * @dmab: buffer allocation information + * @mode: sync mode + */ +void snd_dma_buffer_sync(struct snd_dma_buffer *dmab, + enum snd_dma_sync_mode mode) +{ + const struct snd_malloc_ops *ops; + + if (!dmab || !dmab->dev.need_sync) + return; + ops = snd_dma_get_ops(dmab); + if (ops && ops->sync) + ops->sync(dmab, mode); +} +EXPORT_SYMBOL_GPL(snd_dma_buffer_sync); +#endif /* CONFIG_HAS_DMA */ + /** * snd_sgbuf_get_addr - return the physical address at the corresponding offset * @dmab: buffer allocation information @@ -468,6 +495,161 @@ static const struct snd_malloc_ops snd_dma_wc_ops = { .mmap = snd_dma_wc_mmap, }; #endif /* CONFIG_X86 */ + +/* + * Non-contiguous pages allocator + */ +static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + struct sg_table *sgt; + void *p; + + sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir, + DEFAULT_GFP, 0); + if (!sgt) + return NULL; + dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir); + p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt); + if (p) + dmab->private_data = sgt; + else + dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir); + return p; +} + +static void snd_dma_noncontig_free(struct snd_dma_buffer *dmab) +{ + dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area); + dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data, + dmab->dev.dir); +} + +static int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + return dma_mmap_noncontiguous(dmab->dev.dev, area, + dmab->bytes, dmab->private_data); +} + +static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab, + enum snd_dma_sync_mode mode) +{ + if (mode == SNDRV_DMA_SYNC_CPU) { + if (dmab->dev.dir == DMA_TO_DEVICE) + return; + dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data, + dmab->dev.dir); + invalidate_kernel_vmap_range(dmab->area, dmab->bytes); + } else { + if (dmab->dev.dir == DMA_FROM_DEVICE) + return; + flush_kernel_vmap_range(dmab->area, dmab->bytes); + dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data, + dmab->dev.dir); + } +} + +static const struct snd_malloc_ops snd_dma_noncontig_ops = { + .alloc = snd_dma_noncontig_alloc, + .free = snd_dma_noncontig_free, + .mmap = snd_dma_noncontig_mmap, + .sync = snd_dma_noncontig_sync, + /* re-use vmalloc helpers for get_* ops */ + .get_addr = snd_dma_vmalloc_get_addr, + .get_page = snd_dma_vmalloc_get_page, + .get_chunk_size = snd_dma_vmalloc_get_chunk_size, +}; + +/* x86-specific SG-buffer with WC pages */ +#ifdef CONFIG_SND_DMA_SGBUF +#define vmalloc_to_virt(v) (unsigned long)page_to_virt(vmalloc_to_page(v)) + +static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + void *p = snd_dma_noncontig_alloc(dmab, size); + size_t ofs; + + if (!p) + return NULL; + for (ofs = 0; ofs < size; ofs += PAGE_SIZE) + set_memory_uc(vmalloc_to_virt(p + ofs), 1); + return p; +} + +static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab) +{ + size_t ofs; + + for (ofs = 0; ofs < dmab->bytes; ofs += PAGE_SIZE) + set_memory_wb(vmalloc_to_virt(dmab->area + ofs), 1); + snd_dma_noncontig_free(dmab); +} + +static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + /* FIXME: dma_mmap_noncontiguous() works? */ + return -ENOENT; /* continue with the default mmap handler */ +} + +const struct snd_malloc_ops snd_dma_sg_wc_ops = { + .alloc = snd_dma_sg_wc_alloc, + .free = snd_dma_sg_wc_free, + .mmap = snd_dma_sg_wc_mmap, + .sync = snd_dma_noncontig_sync, + .get_addr = snd_dma_vmalloc_get_addr, + .get_page = snd_dma_vmalloc_get_page, + .get_chunk_size = snd_dma_vmalloc_get_chunk_size, +}; +#endif /* CONFIG_SND_DMA_SGBUF */ + +/* + * Non-coherent pages allocator + */ +static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size) +{ + dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir); + return dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr, + dmab->dev.dir, DEFAULT_GFP); +} + +static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab) +{ + dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area, + dmab->addr, dmab->dev.dir); +} + +static int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab, + struct vm_area_struct *area) +{ + area->vm_page_prot = vm_get_page_prot(area->vm_flags); + return dma_mmap_pages(dmab->dev.dev, area, + area->vm_end - area->vm_start, + virt_to_page(dmab->area)); +} + +static void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab, + enum snd_dma_sync_mode mode) +{ + if (mode == SNDRV_DMA_SYNC_CPU) { + if (dmab->dev.dir != DMA_TO_DEVICE) + dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr, + dmab->bytes, dmab->dev.dir); + } else { + if (dmab->dev.dir != DMA_FROM_DEVICE) + dma_sync_single_for_device(dmab->dev.dev, dmab->addr, + dmab->bytes, dmab->dev.dir); + } +} + +static const struct snd_malloc_ops snd_dma_noncoherent_ops = { + .alloc = snd_dma_noncoherent_alloc, + .free = snd_dma_noncoherent_free, + .mmap = snd_dma_noncoherent_mmap, + .sync = snd_dma_noncoherent_sync, +}; + #endif /* CONFIG_HAS_DMA */ /* @@ -479,14 +661,15 @@ static const struct snd_malloc_ops *dma_ops[] = { #ifdef CONFIG_HAS_DMA [SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops, [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops, + [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops, + [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops, +#ifdef CONFIG_SND_DMA_SGBUF + [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops, +#endif #ifdef CONFIG_GENERIC_ALLOCATOR [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops, #endif /* CONFIG_GENERIC_ALLOCATOR */ #endif /* CONFIG_HAS_DMA */ -#ifdef CONFIG_SND_DMA_SGBUF - [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops, - [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops, -#endif }; static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab) diff --git a/sound/core/memalloc_local.h b/sound/core/memalloc_local.h index 9f2e0a608b49..a6f3a87194da 100644 --- a/sound/core/memalloc_local.h +++ b/sound/core/memalloc_local.h @@ -10,6 +10,7 @@ struct snd_malloc_ops { unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab, unsigned int ofs, unsigned int size); int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area); + void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode); }; #ifdef CONFIG_SND_DMA_SGBUF diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index dfe5a64e19d2..e4e176854ce7 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -453,6 +453,8 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, sstatus.suspended_state = status->suspended_state; sstatus.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (put_user(sstatus.state, &src->s.status.state) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || @@ -533,6 +535,8 @@ static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream, sync_ptr.s.status.suspended_state = status->suspended_state; sync_ptr.s.status.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) return -EFAULT; return 0; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a144a3f68e9e..4f4b4739f987 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -106,6 +106,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram frames -= transfer; ofs = 0; } + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); } #ifdef CONFIG_SND_DEBUG @@ -2256,8 +2257,12 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, goto _end_unlock; } snd_pcm_stream_unlock_irq(substream); + if (!is_playback) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU); err = writer(substream, appl_ofs, data, offset, frames, transfer); + if (is_playback) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h index fe9689b8a6a6..ecb21697ae3a 100644 --- a/sound/core/pcm_local.h +++ b/sound/core/pcm_local.h @@ -73,4 +73,11 @@ void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq); for ((subs) = (pcm)->streams[str].substream; (subs); \ (subs) = (subs)->next) +static inline void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream, + enum snd_dma_sync_mode mode) +{ + if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC) + snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode); +} + #endif /* __SOUND_CORE_PCM_LOCAL_H */ diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 7fbd1ccbb5b0..b70ce3b69ab4 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -32,15 +32,20 @@ module_param(max_alloc_per_card, ulong, 0644); MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card."); static int do_alloc_pages(struct snd_card *card, int type, struct device *dev, - size_t size, struct snd_dma_buffer *dmab) + int str, size_t size, struct snd_dma_buffer *dmab) { + enum dma_data_direction dir; int err; if (max_alloc_per_card && card->total_pcm_alloc_bytes + size > max_alloc_per_card) return -ENOMEM; - err = snd_dma_alloc_pages(type, dev, size, dmab); + if (str == SNDRV_PCM_STREAM_PLAYBACK) + dir = DMA_TO_DEVICE; + else + dir = DMA_FROM_DEVICE; + err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab); if (!err) { mutex_lock(&card->memory_mutex); card->total_pcm_alloc_bytes += dmab->bytes; @@ -77,7 +82,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, do { err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev, - size, dmab); + substream->stream, size, dmab); if (err != -ENOMEM) return err; if (no_fallback) @@ -177,6 +182,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry, if (do_alloc_pages(card, substream->dma_buffer.dev.type, substream->dma_buffer.dev.dev, + substream->stream, size, &new_dmab) < 0) { buffer->error = -ENOMEM; pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n", @@ -418,6 +424,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) if (do_alloc_pages(card, substream->dma_buffer.dev.type, substream->dma_buffer.dev.dev, + substream->stream, size, dmab) < 0) { kfree(dmab); pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n", diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index d233cb3b41d8..621883e71194 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2685,6 +2685,13 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, goto error; } + /* automatically set EXPLICIT_SYNC flag in the managed mode whenever + * the DMA buffer requires it + */ + if (substream->managed_buffer_alloc && + substream->dma_buffer.dev.need_sync) + substream->runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC; + *rsubstream = substream; return 0; @@ -2912,6 +2919,8 @@ static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream, ret = rewind_appl_ptr(substream, frames, snd_pcm_hw_avail(substream)); snd_pcm_stream_unlock_irq(substream); + if (ret >= 0) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); return ret; } @@ -2929,35 +2938,31 @@ static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream, ret = forward_appl_ptr(substream, frames, snd_pcm_avail(substream)); snd_pcm_stream_unlock_irq(substream); + if (ret >= 0) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); return ret; } -static int snd_pcm_hwsync(struct snd_pcm_substream *substream) -{ - int err; - - snd_pcm_stream_lock_irq(substream); - err = do_pcm_hwsync(substream); - snd_pcm_stream_unlock_irq(substream); - return err; -} - static int snd_pcm_delay(struct snd_pcm_substream *substream, snd_pcm_sframes_t *delay) { int err; - snd_pcm_sframes_t n = 0; snd_pcm_stream_lock_irq(substream); err = do_pcm_hwsync(substream); - if (!err) - n = snd_pcm_calc_delay(substream); + if (delay && !err) + *delay = snd_pcm_calc_delay(substream); snd_pcm_stream_unlock_irq(substream); - if (!err) - *delay = n; + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU); + return err; } +static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream) +{ + return snd_pcm_delay(substream, NULL); +} + static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, struct snd_pcm_sync_ptr __user *_sync_ptr) { @@ -3000,6 +3005,8 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, sync_ptr.s.status.suspended_state = status->suspended_state; sync_ptr.s.status.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr))) return -EFAULT; return 0; @@ -3096,6 +3103,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, sstatus.suspended_state = status->suspended_state; sstatus.audio_tstamp = status->audio_tstamp; snd_pcm_stream_unlock_irq(substream); + if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) + snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE); if (put_user(sstatus.state, &src->s.status.state) || put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) || @@ -3218,6 +3227,9 @@ static int snd_pcm_common_ioctl(struct file *file, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; + if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; + res = snd_power_wait(substream->pcm->card); if (res < 0) return res; @@ -3272,7 +3284,7 @@ static int snd_pcm_common_ioctl(struct file *file, return snd_pcm_hwsync(substream); case SNDRV_PCM_IOCTL_DELAY: { - snd_pcm_sframes_t delay; + snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t __user *res = arg; int err; @@ -3344,6 +3356,9 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, snd_pcm_uframes_t *frames = arg; snd_pcm_sframes_t result; + if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; + switch (cmd) { case SNDRV_PCM_IOCTL_FORWARD: { @@ -3386,7 +3401,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!frame_aligned(runtime, count)) return -EINVAL; @@ -3410,7 +3426,8 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf, if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!frame_aligned(runtime, count)) return -EINVAL; @@ -3436,7 +3453,8 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!iter_is_iovec(to)) return -EINVAL; @@ -3472,7 +3490,8 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) + if (runtime->status->state == SNDRV_PCM_STATE_OPEN || + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; if (!iter_is_iovec(from)) return -EINVAL; @@ -3511,6 +3530,9 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait) return ok | EPOLLERR; runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return ok | EPOLLERR; + poll_wait(file, &runtime->sleep, wait); mask = 0; @@ -3820,6 +3842,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) substream = pcm_file->substream; if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; + if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { @@ -3856,6 +3880,8 @@ static int snd_pcm_fasync(int fd, struct file * file, int on) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; + if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) + return -EBADFD; return fasync_helper(fd, file, on, &runtime->fasync); } diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c deleted file mode 100644 index 8352a5cdb19f..000000000000 --- a/sound/core/sgbuf.c +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Scatter-Gather buffer - * - * Copyright (c) by Takashi Iwai <tiwai@suse.de> - */ - -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/vmalloc.h> -#include <linux/export.h> -#include <sound/memalloc.h> -#include "memalloc_local.h" - -struct snd_sg_page { - void *buf; - dma_addr_t addr; -}; - -struct snd_sg_buf { - int size; /* allocated byte size */ - int pages; /* allocated pages */ - int tblsize; /* allocated table size */ - struct snd_sg_page *table; /* address table */ - struct page **page_table; /* page table (for vmap/vunmap) */ - struct device *dev; -}; - -/* table entries are align to 32 */ -#define SGBUF_TBL_ALIGN 32 -#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN) - -static void snd_dma_sg_free(struct snd_dma_buffer *dmab) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - struct snd_dma_buffer tmpb; - int i; - - if (!sgbuf) - return; - - vunmap(dmab->area); - dmab->area = NULL; - - tmpb.dev.type = SNDRV_DMA_TYPE_DEV; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) - tmpb.dev.type = SNDRV_DMA_TYPE_DEV_WC; - tmpb.dev.dev = sgbuf->dev; - for (i = 0; i < sgbuf->pages; i++) { - if (!(sgbuf->table[i].addr & ~PAGE_MASK)) - continue; /* continuous pages */ - tmpb.area = sgbuf->table[i].buf; - tmpb.addr = sgbuf->table[i].addr & PAGE_MASK; - tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT; - snd_dma_free_pages(&tmpb); - } - - kfree(sgbuf->table); - kfree(sgbuf->page_table); - kfree(sgbuf); - dmab->private_data = NULL; -} - -#define MAX_ALLOC_PAGES 32 - -static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size) -{ - struct snd_sg_buf *sgbuf; - unsigned int i, pages, chunk, maxpages; - struct snd_dma_buffer tmpb; - struct snd_sg_page *table; - struct page **pgtable; - int type = SNDRV_DMA_TYPE_DEV; - pgprot_t prot = PAGE_KERNEL; - void *area; - - dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL); - if (!sgbuf) - return NULL; - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) { - type = SNDRV_DMA_TYPE_DEV_WC; -#ifdef pgprot_noncached - prot = pgprot_noncached(PAGE_KERNEL); -#endif - } - sgbuf->dev = dmab->dev.dev; - pages = snd_sgbuf_aligned_pages(size); - sgbuf->tblsize = sgbuf_align_table(pages); - table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL); - if (!table) - goto _failed; - sgbuf->table = table; - pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL); - if (!pgtable) - goto _failed; - sgbuf->page_table = pgtable; - - /* allocate pages */ - maxpages = MAX_ALLOC_PAGES; - while (pages > 0) { - chunk = pages; - /* don't be too eager to take a huge chunk */ - if (chunk > maxpages) - chunk = maxpages; - chunk <<= PAGE_SHIFT; - if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev, - chunk, &tmpb) < 0) { - if (!sgbuf->pages) - goto _failed; - size = sgbuf->pages * PAGE_SIZE; - break; - } - chunk = tmpb.bytes >> PAGE_SHIFT; - for (i = 0; i < chunk; i++) { - table->buf = tmpb.area; - table->addr = tmpb.addr; - if (!i) - table->addr |= chunk; /* mark head */ - table++; - *pgtable++ = virt_to_page(tmpb.area); - tmpb.area += PAGE_SIZE; - tmpb.addr += PAGE_SIZE; - } - sgbuf->pages += chunk; - pages -= chunk; - if (chunk < maxpages) - maxpages = chunk; - } - - sgbuf->size = size; - area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot); - if (!area) - goto _failed; - return area; - - _failed: - snd_dma_sg_free(dmab); /* free the table */ - return NULL; -} - -static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab, - size_t offset) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - dma_addr_t addr; - - addr = sgbuf->table[offset >> PAGE_SHIFT].addr; - addr &= ~((dma_addr_t)PAGE_SIZE - 1); - return addr + offset % PAGE_SIZE; -} - -static struct page *snd_dma_sg_get_page(struct snd_dma_buffer *dmab, - size_t offset) -{ - struct snd_sg_buf *sgbuf = dmab->private_data; - unsigned int idx = offset >> PAGE_SHIFT; - - if (idx >= (unsigned int)sgbuf->pages) - return NULL; - return sgbuf->page_table[idx]; -} - -static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab, - unsigned int ofs, - unsigned int size) -{ - struct snd_sg_buf *sg = dmab->private_data; - unsigned int start, end, pg; - - start = ofs >> PAGE_SHIFT; - end = (ofs + size - 1) >> PAGE_SHIFT; - /* check page continuity */ - pg = sg->table[start].addr >> PAGE_SHIFT; - for (;;) { - start++; - if (start > end) - break; - pg++; - if ((sg->table[start].addr >> PAGE_SHIFT) != pg) - return (start << PAGE_SHIFT) - ofs; - } - /* ok, all on continuous pages */ - return size; -} - -static int snd_dma_sg_mmap(struct snd_dma_buffer *dmab, - struct vm_area_struct *area) -{ - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) - area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); - return -ENOENT; /* continue with the default mmap handler */ -} - -const struct snd_malloc_ops snd_dma_sg_ops = { - .alloc = snd_dma_sg_alloc, - .free = snd_dma_sg_free, - .get_addr = snd_dma_sg_get_addr, - .get_page = snd_dma_sg_get_page, - .get_chunk_size = snd_dma_sg_get_chunk_size, - .mmap = snd_dma_sg_mmap, -}; diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile index acdf66564fb0..3bef2a0b1e2e 100644 --- a/sound/firewire/motu/Makefile +++ b/sound/firewire/motu/Makefile @@ -4,5 +4,6 @@ CFLAGS_amdtp-motu.o := -I$(src) snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \ motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \ motu-protocol-v2.o motu-protocol-v3.o \ - motu-protocol-v1.o + motu-protocol-v1.o motu-register-dsp-message-parser.o \ + motu-command-dsp-message-parser.o obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index a18c2c033e83..2fb52f481d12 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -333,6 +333,7 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, unsigned int packets, struct snd_pcm_substream *pcm) { + struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream); struct amdtp_motu *p = s->protocol; unsigned int pcm_frames = 0; int i; @@ -357,6 +358,14 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s, read_midi_messages(s, buf, data_blocks); } + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { + snd_motu_register_dsp_message_parser_parse(motu, descs, packets, + s->data_block_quadlets); + } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { + snd_motu_command_dsp_message_parser_parse(motu, descs, packets, + s->data_block_quadlets); + } + // For tracepoints. if (trace_data_block_sph_enabled() || trace_data_block_message_enabled()) @@ -415,8 +424,6 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, if (p->midi_ports) write_midi_messages(s, buf, data_blocks); - // TODO: how to interact control messages between userspace? - write_sph(p->cache, buf, data_blocks, s->data_block_quadlets); } diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c new file mode 100644 index 000000000000..9efe4d364baf --- /dev/null +++ b/sound/firewire/motu/motu-command-dsp-message-parser.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series +// +// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp> + +// Below models allow software to configure their DSP function by command transferred in +// asynchronous transaction: +// * 828 mk3 (FireWire only and Hybrid) +// * 896 mk3 (FireWire only and Hybrid) +// * Ultralite mk3 (FireWire only and Hybrid) +// * Traveler mk3 +// * Track 16 +// +// Isochronous packets from the above models includes messages to report state of hardware meter. + +#include "motu.h" + +enum msg_parser_state { + INITIALIZED, + FRAGMENT_DETECTED, + AVAILABLE, +}; + +struct msg_parser { + spinlock_t lock; + enum msg_parser_state state; + unsigned int interval; + unsigned int message_count; + unsigned int fragment_pos; + unsigned int value_index; + u64 value; + struct snd_firewire_motu_command_dsp_meter meter; +}; + +int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu) +{ + struct msg_parser *parser; + + parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL); + if (!parser) + return -ENOMEM; + spin_lock_init(&parser->lock); + motu->message_parser = parser; + + return 0; +} + +int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc) +{ + struct msg_parser *parser = motu->message_parser; + + parser->state = INITIALIZED; + + // All of data blocks don't have messages with meaningful information. + switch (sfc) { + case CIP_SFC_176400: + case CIP_SFC_192000: + parser->interval = 4; + break; + case CIP_SFC_88200: + case CIP_SFC_96000: + parser->interval = 2; + break; + case CIP_SFC_32000: + case CIP_SFC_44100: + case CIP_SFC_48000: + default: + parser->interval = 1; + break; + } + + return 0; +} + +#define FRAGMENT_POS 6 +#define MIDI_BYTE_POS 7 +#define MIDI_FLAG_POS 8 +// One value of hardware meter consists of 4 messages. +#define FRAGMENTS_PER_VALUE 4 +#define VALUES_AT_IMAGE_END 0xffffffffffffffff + +void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, + unsigned int desc_count, unsigned int data_block_quadlets) +{ + struct msg_parser *parser = motu->message_parser; + unsigned int interval = parser->interval; + unsigned long flags; + int i; + + spin_lock_irqsave(&parser->lock, flags); + + for (i = 0; i < desc_count; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buffer = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; + int j; + + for (j = 0; j < data_blocks; ++j) { + u8 *b = (u8 *)buffer; + buffer += data_block_quadlets; + + switch (parser->state) { + case INITIALIZED: + { + u8 fragment = b[FRAGMENT_POS]; + + if (fragment > 0) { + parser->value = fragment; + parser->message_count = 1; + parser->state = FRAGMENT_DETECTED; + } + break; + } + case FRAGMENT_DETECTED: + { + if (parser->message_count % interval == 0) { + u8 fragment = b[FRAGMENT_POS]; + + parser->value >>= 8; + parser->value |= (u64)fragment << 56; + + if (parser->value == VALUES_AT_IMAGE_END) { + parser->state = AVAILABLE; + parser->fragment_pos = 0; + parser->value_index = 0; + parser->message_count = 0; + } + } + ++parser->message_count; + break; + } + case AVAILABLE: + default: + { + if (parser->message_count % interval == 0) { + u8 fragment = b[FRAGMENT_POS]; + + parser->value >>= 8; + parser->value |= (u64)fragment << 56; + ++parser->fragment_pos; + + if (parser->fragment_pos == 4) { + // Skip the last two quadlets since they could be + // invalid value (0xffffffff) as floating point + // number. + if (parser->value_index < + SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) { + u32 val = (u32)(parser->value >> 32); + parser->meter.data[parser->value_index] = val; + } + ++parser->value_index; + parser->fragment_pos = 0; + } + + if (parser->value == VALUES_AT_IMAGE_END) { + parser->value_index = 0; + parser->fragment_pos = 0; + parser->message_count = 0; + } + } + ++parser->message_count; + break; + } + } + } + } + + spin_unlock_irqrestore(&parser->lock, flags); +} + +void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu, + struct snd_firewire_motu_command_dsp_meter *meter) +{ + struct msg_parser *parser = motu->message_parser; + unsigned long flags; + + spin_lock_irqsave(&parser->lock, flags); + memcpy(meter, &parser->meter, sizeof(*meter)); + spin_unlock_irqrestore(&parser->lock, flags); +} diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c index b5ced5d27758..a900fc0e7644 100644 --- a/sound/firewire/motu/motu-hwdep.c +++ b/sound/firewire/motu/motu-hwdep.c @@ -16,6 +16,14 @@ #include "motu.h" +static bool has_dsp_event(struct snd_motu *motu) +{ + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) + return (snd_motu_register_dsp_message_parser_count_event(motu) > 0); + else + return false; +} + static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, loff_t *offset) { @@ -25,7 +33,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, spin_lock_irq(&motu->lock); - while (!motu->dev_lock_changed && motu->msg == 0) { + while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) { prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE); spin_unlock_irq(&motu->lock); schedule(); @@ -40,20 +48,46 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; event.lock_status.status = (motu->dev_lock_count > 0); motu->dev_lock_changed = false; + spin_unlock_irq(&motu->lock); - count = min_t(long, count, sizeof(event.lock_status)); - } else { + count = min_t(long, count, sizeof(event)); + if (copy_to_user(buf, &event, count)) + return -EFAULT; + } else if (motu->msg > 0) { event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION; event.motu_notification.message = motu->msg; motu->msg = 0; + spin_unlock_irq(&motu->lock); - count = min_t(long, count, sizeof(event.motu_notification)); - } + count = min_t(long, count, sizeof(event)); + if (copy_to_user(buf, &event, count)) + return -EFAULT; + } else if (has_dsp_event(motu)) { + size_t consumed = 0; + u32 __user *ptr; + u32 ev; - spin_unlock_irq(&motu->lock); + spin_unlock_irq(&motu->lock); - if (copy_to_user(buf, &event, count)) - return -EFAULT; + // Header is filled later. + consumed += sizeof(event.motu_register_dsp_change); + + while (consumed < count && + snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { + ptr = (u32 __user *)(buf + consumed); + if (put_user(ev, ptr)) + return -EFAULT; + consumed += sizeof(ev); + } + + event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; + event.motu_register_dsp_change.count = + (consumed - sizeof(event.motu_register_dsp_change)) / 4; + if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) + return -EFAULT; + + count = consumed; + } return count; } @@ -67,7 +101,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_wait(file, &motu->hwdep_wait, wait); spin_lock_irq(&motu->lock); - if (motu->dev_lock_changed || motu->msg) + if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu)) events = EPOLLIN | EPOLLRDNORM; else events = 0; @@ -155,6 +189,71 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, return hwdep_lock(motu); case SNDRV_FIREWIRE_IOCTL_UNLOCK: return hwdep_unlock(motu); + case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER: + { + struct snd_firewire_motu_register_dsp_meter *meter; + int err; + + if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) + return -ENXIO; + + meter = kzalloc(sizeof(*meter), GFP_KERNEL); + if (!meter) + return -ENOMEM; + + snd_motu_register_dsp_message_parser_copy_meter(motu, meter); + + err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); + kfree(meter); + + if (err) + return -EFAULT; + + return 0; + } + case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER: + { + struct snd_firewire_motu_command_dsp_meter *meter; + int err; + + if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)) + return -ENXIO; + + meter = kzalloc(sizeof(*meter), GFP_KERNEL); + if (!meter) + return -ENOMEM; + + snd_motu_command_dsp_message_parser_copy_meter(motu, meter); + + err = copy_to_user((void __user *)arg, meter, sizeof(*meter)); + kfree(meter); + + if (err) + return -EFAULT; + + return 0; + } + case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER: + { + struct snd_firewire_motu_register_dsp_parameter *param; + int err; + + if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)) + return -ENXIO; + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) + return -ENOMEM; + + snd_motu_register_dsp_message_parser_copy_parameter(motu, param); + + err = copy_to_user((void __user *)arg, param, sizeof(*param)); + kfree(param); + if (err) + return -EFAULT; + + return 0; + } default: return -ENOIOCTLCMD; } @@ -193,5 +292,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu) hwdep->private_data = motu; hwdep->exclusive = true; + motu->hwdep = hwdep; + return 0; } diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c index 2bd4485e4bc7..a5f70efa2e88 100644 --- a/sound/firewire/motu/motu-protocol-v2.c +++ b/sound/firewire/motu/motu-protocol-v2.c @@ -275,7 +275,8 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = { .name = "828mk2", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 0}, .rx_fixed_pcm_chunks = {14, 14, 0}, }; @@ -283,7 +284,7 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = { const struct snd_motu_spec snd_motu_spec_896hd = { .name = "896HD", .protocol_version = SND_MOTU_PROTOCOL_V2, - // No support for MIDI. + .flags = SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 8}, .rx_fixed_pcm_chunks = {14, 14, 8}, }; @@ -292,7 +293,8 @@ const struct snd_motu_spec snd_motu_spec_traveler = { .name = "Traveler", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 8}, .rx_fixed_pcm_chunks = {14, 14, 8}, }; @@ -301,7 +303,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite = { .name = "UltraLite", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {14, 14, 0}, .rx_fixed_pcm_chunks = {14, 14, 0}, }; @@ -310,7 +313,8 @@ const struct snd_motu_spec snd_motu_spec_8pre = { .name = "8pre", .protocol_version = SND_MOTU_PROTOCOL_V2, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, + SND_MOTU_SPEC_TX_MIDI_2ND_Q | + SND_MOTU_SPEC_REGISTER_DSP, // Two dummy chunks always in the end of data block. .tx_fixed_pcm_chunks = {10, 10, 0}, .rx_fixed_pcm_chunks = {6, 6, 0}, diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c index 56e4504e7ec9..05608e8ca0bc 100644 --- a/sound/firewire/motu/motu-protocol-v3.c +++ b/sound/firewire/motu/motu-protocol-v3.c @@ -261,12 +261,12 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) return 0; } - const struct snd_motu_spec snd_motu_spec_828mk3_fw = { .name = "828mk3", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, .tx_fixed_pcm_chunks = {18, 18, 14}, .rx_fixed_pcm_chunks = {14, 14, 10}, }; @@ -275,7 +275,8 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = { .name = "828mk3", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, .tx_fixed_pcm_chunks = {18, 18, 14}, .rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate. }; @@ -284,7 +285,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = { .name = "UltraLiteMk3", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_COMMAND_DSP, .tx_fixed_pcm_chunks = {18, 14, 10}, .rx_fixed_pcm_chunks = {14, 14, 14}, }; @@ -293,7 +295,8 @@ const struct snd_motu_spec snd_motu_spec_audio_express = { .name = "AudioExpress", .protocol_version = SND_MOTU_PROTOCOL_V3, .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, + SND_MOTU_SPEC_TX_MIDI_3RD_Q | + SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {10, 10, 0}, .rx_fixed_pcm_chunks = {10, 10, 0}, }; @@ -301,6 +304,7 @@ const struct snd_motu_spec snd_motu_spec_audio_express = { const struct snd_motu_spec snd_motu_spec_4pre = { .name = "4pre", .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_REGISTER_DSP, .tx_fixed_pcm_chunks = {10, 10, 0}, .rx_fixed_pcm_chunks = {10, 10, 0}, }; diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c new file mode 100644 index 000000000000..0c587567540f --- /dev/null +++ b/sound/firewire/motu/motu-register-dsp-message-parser.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// motu-register-dsp-message-parser.c - a part of driver for MOTU FireWire series +// +// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp> + +// Below models allow software to configure their DSP functions by asynchronous transaction +// to access their internal registers. +// * 828 mk2 +// * 896hd +// * Traveler +// * 8 pre +// * Ultralite +// * 4 pre +// * Audio Express +// +// Additionally, isochronous packets from the above models include messages to notify state of +// DSP. The messages are two set of 3 byte data in 2nd and 3rd quadlet of data block. When user +// operates hardware components such as dial and switch, corresponding messages are transferred. +// The messages include Hardware metering and MIDI messages as well. + +#include "motu.h" + +#define MSG_FLAG_POS 4 +#define MSG_FLAG_TYPE_MASK 0xf8 +#define MSG_FLAG_MIDI_MASK 0x01 +#define MSG_FLAG_MODEL_SPECIFIC_MASK 0x06 +#define MSG_FLAG_8PRE 0x00 +#define MSG_FLAG_ULTRALITE 0x04 +#define MSG_FLAG_TRAVELER 0x04 +#define MSG_FLAG_828MK2 0x04 +#define MSG_FLAG_896HD 0x04 +#define MSG_FLAG_4PRE 0x05 // MIDI mask is in 8th byte. +#define MSG_FLAG_AUDIOEXPRESS 0x05 // MIDI mask is in 8th byte. +#define MSG_FLAG_TYPE_SHIFT 3 +#define MSG_VALUE_POS 5 +#define MSG_MIDI_BYTE_POS 6 +#define MSG_METER_IDX_POS 7 + +// In 4 pre and Audio express, meter index is in 6th byte. MIDI flag is in 8th byte and MIDI byte +// is in 7th byte. +#define MSG_METER_IDX_POS_4PRE_AE 6 +#define MSG_MIDI_BYTE_POS_4PRE_AE 7 +#define MSG_FLAG_MIDI_POS_4PRE_AE 8 + +enum register_dsp_msg_type { + // Used for messages with no information. + INVALID = 0x00, + MIXER_SELECT = 0x01, + MIXER_SRC_GAIN = 0x02, + MIXER_SRC_PAN = 0x03, + MIXER_SRC_FLAG = 0x04, + MIXER_OUTPUT_PAIRED_VOLUME = 0x05, + MIXER_OUTPUT_PAIRED_FLAG = 0x06, + MAIN_OUTPUT_PAIRED_VOLUME = 0x07, + HP_OUTPUT_PAIRED_VOLUME = 0x08, + HP_OUTPUT_PAIRED_ASSIGNMENT = 0x09, + // Transferred by all models but the purpose is still unknown. + UNKNOWN_0 = 0x0a, + // Specific to 828mk2, 896hd, Traveler. + UNKNOWN_2 = 0x0c, + // Specific to 828mk2, Traveler, and 896hd (not functional). + LINE_INPUT_BOOST = 0x0d, + // Specific to 828mk2, Traveler, and 896hd (not functional). + LINE_INPUT_NOMINAL_LEVEL = 0x0e, + // Specific to Ultralite, 4 pre, Audio express, and 8 pre (not functional). + INPUT_GAIN_AND_INVERT = 0x15, + // Specific to 4 pre, and Audio express. + INPUT_FLAG = 0x16, + // Specific to 4 pre, and Audio express. + MIXER_SRC_PAIRED_BALANCE = 0x17, + // Specific to 4 pre, and Audio express. + MIXER_SRC_PAIRED_WIDTH = 0x18, + // Transferred by all models. This type of message interposes the series of the other + // messages. The message delivers signal level up to 96.0 kHz. In 828mk2, 896hd, and + // Traveler, one of physical outputs is selected for the message. The selection is done + // by LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c. + METER = 0x1f, +}; + +#define EVENT_QUEUE_SIZE 16 + +struct msg_parser { + spinlock_t lock; + struct snd_firewire_motu_register_dsp_meter meter; + bool meter_pos_quirk; + + struct snd_firewire_motu_register_dsp_parameter param; + u8 prev_mixer_src_type; + u8 mixer_ch; + u8 mixer_src_ch; + + u8 input_ch; + u8 prev_msg_type; + + u32 event_queue[EVENT_QUEUE_SIZE]; + unsigned int push_pos; + unsigned int pull_pos; +}; + +int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu) +{ + struct msg_parser *parser; + parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL); + if (!parser) + return -ENOMEM; + spin_lock_init(&parser->lock); + if (motu->spec == &snd_motu_spec_4pre || motu->spec == &snd_motu_spec_audio_express) + parser->meter_pos_quirk = true; + motu->message_parser = parser; + return 0; +} + +int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu) +{ + struct msg_parser *parser = motu->message_parser; + + parser->prev_mixer_src_type = INVALID; + parser->mixer_ch = 0xff; + parser->mixer_src_ch = 0xff; + parser->prev_msg_type = INVALID; + + return 0; +} + +// Rough implementaion of queue without overrun check. +static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 identifier1, u8 val) +{ + struct msg_parser *parser = motu->message_parser; + unsigned int pos = parser->push_pos; + u32 entry; + + if (!motu->hwdep || motu->hwdep->used == 0) + return; + + entry = (msg_type << 24) | (identifier0 << 16) | (identifier1 << 8) | val; + parser->event_queue[pos] = entry; + + ++pos; + if (pos >= EVENT_QUEUE_SIZE) + pos = 0; + parser->push_pos = pos; +} + +void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, + unsigned int desc_count, unsigned int data_block_quadlets) +{ + struct msg_parser *parser = motu->message_parser; + bool meter_pos_quirk = parser->meter_pos_quirk; + unsigned int pos = parser->push_pos; + unsigned long flags; + int i; + + spin_lock_irqsave(&parser->lock, flags); + + for (i = 0; i < desc_count; ++i) { + const struct pkt_desc *desc = descs + i; + __be32 *buffer = desc->ctx_payload; + unsigned int data_blocks = desc->data_blocks; + int j; + + for (j = 0; j < data_blocks; ++j) { + u8 *b = (u8 *)buffer; + u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT; + u8 val = b[MSG_VALUE_POS]; + + buffer += data_block_quadlets; + + switch (msg_type) { + case MIXER_SELECT: + { + u8 mixer_ch = val / 0x20; + if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) { + parser->mixer_src_ch = 0; + parser->mixer_ch = mixer_ch; + } + break; + } + case MIXER_SRC_GAIN: + case MIXER_SRC_PAN: + case MIXER_SRC_FLAG: + case MIXER_SRC_PAIRED_BALANCE: + case MIXER_SRC_PAIRED_WIDTH: + { + struct snd_firewire_motu_register_dsp_parameter *param = &parser->param; + u8 mixer_ch = parser->mixer_ch; + u8 mixer_src_ch = parser->mixer_src_ch; + + if (msg_type != parser->prev_mixer_src_type) + mixer_src_ch = 0; + else + ++mixer_src_ch; + parser->prev_mixer_src_type = msg_type; + + if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT && + mixer_src_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT) { + u8 mixer_ch = parser->mixer_ch; + + switch (msg_type) { + case MIXER_SRC_GAIN: + if (param->mixer.source[mixer_ch].gain[mixer_src_ch] != val) { + queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val); + param->mixer.source[mixer_ch].gain[mixer_src_ch] = val; + } + break; + case MIXER_SRC_PAN: + if (param->mixer.source[mixer_ch].pan[mixer_src_ch] != val) { + queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val); + param->mixer.source[mixer_ch].pan[mixer_src_ch] = val; + } + break; + case MIXER_SRC_FLAG: + if (param->mixer.source[mixer_ch].flag[mixer_src_ch] != val) { + queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val); + param->mixer.source[mixer_ch].flag[mixer_src_ch] = val; + } + break; + case MIXER_SRC_PAIRED_BALANCE: + if (param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] != val) { + queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val); + param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] = val; + } + break; + case MIXER_SRC_PAIRED_WIDTH: + if (param->mixer.source[mixer_ch].paired_width[mixer_src_ch] != val) { + queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val); + param->mixer.source[mixer_ch].paired_width[mixer_src_ch] = val; + } + break; + default: + break; + } + + parser->mixer_src_ch = mixer_src_ch; + } + break; + } + case MIXER_OUTPUT_PAIRED_VOLUME: + case MIXER_OUTPUT_PAIRED_FLAG: + { + struct snd_firewire_motu_register_dsp_parameter *param = &parser->param; + u8 mixer_ch = parser->mixer_ch; + + if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) { + switch (msg_type) { + case MIXER_OUTPUT_PAIRED_VOLUME: + if (param->mixer.output.paired_volume[mixer_ch] != val) { + queue_event(motu, msg_type, mixer_ch, 0, val); + param->mixer.output.paired_volume[mixer_ch] = val; + } + break; + case MIXER_OUTPUT_PAIRED_FLAG: + if (param->mixer.output.paired_flag[mixer_ch] != val) { + queue_event(motu, msg_type, mixer_ch, 0, val); + param->mixer.output.paired_flag[mixer_ch] = val; + } + break; + default: + break; + } + } + break; + } + case MAIN_OUTPUT_PAIRED_VOLUME: + if (parser->param.output.main_paired_volume != val) { + queue_event(motu, msg_type, 0, 0, val); + parser->param.output.main_paired_volume = val; + } + break; + case HP_OUTPUT_PAIRED_VOLUME: + if (parser->param.output.hp_paired_volume != val) { + queue_event(motu, msg_type, 0, 0, val); + parser->param.output.hp_paired_volume = val; + } + break; + case HP_OUTPUT_PAIRED_ASSIGNMENT: + if (parser->param.output.hp_paired_assignment != val) { + queue_event(motu, msg_type, 0, 0, val); + parser->param.output.hp_paired_assignment = val; + } + break; + case LINE_INPUT_BOOST: + if (parser->param.line_input.boost_flag != val) { + queue_event(motu, msg_type, 0, 0, val); + parser->param.line_input.boost_flag = val; + } + break; + case LINE_INPUT_NOMINAL_LEVEL: + if (parser->param.line_input.nominal_level_flag != val) { + queue_event(motu, msg_type, 0, 0, val); + parser->param.line_input.nominal_level_flag = val; + } + break; + case INPUT_GAIN_AND_INVERT: + case INPUT_FLAG: + { + struct snd_firewire_motu_register_dsp_parameter *param = &parser->param; + u8 input_ch = parser->input_ch; + + if (parser->prev_msg_type != msg_type) + input_ch = 0; + else + ++input_ch; + + if (input_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT) { + switch (msg_type) { + case INPUT_GAIN_AND_INVERT: + if (param->input.gain_and_invert[input_ch] != val) { + queue_event(motu, msg_type, input_ch, 0, val); + param->input.gain_and_invert[input_ch] = val; + } + break; + case INPUT_FLAG: + if (param->input.flag[input_ch] != val) { + queue_event(motu, msg_type, input_ch, 0, val); + param->input.flag[input_ch] = val; + } + break; + default: + break; + } + parser->input_ch = input_ch; + } + break; + } + case UNKNOWN_0: + case UNKNOWN_2: + break; + case METER: + { + u8 pos; + + if (!meter_pos_quirk) + pos = b[MSG_METER_IDX_POS]; + else + pos = b[MSG_METER_IDX_POS_4PRE_AE]; + + if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT) { + parser->meter.data[pos] = val; + } else if (pos >= 0x80) { + pos -= (0x80 - SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT); + + if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT) + parser->meter.data[pos] = val; + } + + // The message for meter is interruptible to the series of other + // types of messages. Don't cache it. + fallthrough; + } + case INVALID: + default: + // Don't cache it. + continue; + } + + parser->prev_msg_type = msg_type; + } + } + + if (pos != parser->push_pos) + wake_up(&motu->hwdep_wait); + + spin_unlock_irqrestore(&parser->lock, flags); +} + +void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu, + struct snd_firewire_motu_register_dsp_meter *meter) +{ + struct msg_parser *parser = motu->message_parser; + unsigned long flags; + + spin_lock_irqsave(&parser->lock, flags); + memcpy(meter, &parser->meter, sizeof(*meter)); + spin_unlock_irqrestore(&parser->lock, flags); +} + +void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu, + struct snd_firewire_motu_register_dsp_parameter *param) +{ + struct msg_parser *parser = motu->message_parser; + unsigned long flags; + + spin_lock_irqsave(&parser->lock, flags); + memcpy(param, &parser->param, sizeof(*param)); + spin_unlock_irqrestore(&parser->lock, flags); +} + +unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu) +{ + struct msg_parser *parser = motu->message_parser; + + if (parser->pull_pos > parser->push_pos) + return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos; + else + return parser->push_pos - parser->pull_pos; +} + +bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event) +{ + struct msg_parser *parser = motu->message_parser; + unsigned int pos = parser->pull_pos; + unsigned long flags; + + if (pos == parser->push_pos) + return false; + + spin_lock_irqsave(&parser->lock, flags); + + *event = parser->event_queue[pos]; + + ++pos; + if (pos >= EVENT_QUEUE_SIZE) + pos = 0; + parser->pull_pos = pos; + + spin_unlock_irqrestore(&parser->lock, flags); + + return true; +} diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 9e6ca39ebd7f..64aec9c3eefd 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -255,6 +255,16 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) if (err < 0) return err; + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { + err = snd_motu_register_dsp_message_parser_init(motu); + if (err < 0) + return err; + } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { + err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc); + if (err < 0) + return err; + } + err = begin_session(motu); if (err < 0) { dev_err(&motu->unit->device, diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index f65426238d4c..5fc7ae475537 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -112,6 +112,16 @@ static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent if (err < 0) goto error; + if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) { + err = snd_motu_register_dsp_message_parser_new(motu); + if (err < 0) + goto error; + } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) { + err = snd_motu_command_dsp_message_parser_new(motu); + if (err < 0) + goto error; + } + err = snd_card_register(card); if (err < 0) goto error; diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index f1a830b358d4..79704ae6a73e 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -74,10 +74,13 @@ struct snd_motu { int dev_lock_count; bool dev_lock_changed; wait_queue_head_t hwdep_wait; + struct snd_hwdep *hwdep; struct amdtp_domain domain; struct amdtp_motu_cache cache; + + void *message_parser; }; enum snd_motu_spec_flags { @@ -85,6 +88,8 @@ enum snd_motu_spec_flags { SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002, SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004, SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008, + SND_MOTU_SPEC_REGISTER_DSP = 0x0010, + SND_MOTU_SPEC_COMMAND_DSP = 0x0020, }; #define SND_MOTU_CLOCK_RATE_COUNT 6 @@ -270,4 +275,22 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu) return -ENXIO; } +int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu); +int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu); +void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, + unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu, + struct snd_firewire_motu_register_dsp_meter *meter); +void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu, + struct snd_firewire_motu_register_dsp_parameter *params); +unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu); +bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event); + +int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu); +int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc); +void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs, + unsigned int desc_count, unsigned int data_block_quadlets); +void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu, + struct snd_firewire_motu_command_dsp_meter *meter); + #endif diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index fff18b5d4e05..f4a702def397 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -9,7 +9,7 @@ #include <linux/delay.h> #define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512 -#define READY_TIMEOUT_MS 200 +#define READY_TIMEOUT_MS 600 /* * According to datasheet of Oxford Semiconductor: @@ -367,6 +367,11 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) // Just after changing sampling transfer frequency, many cycles are // skipped for packet transmission. tx_init_skip_cycles = 400; + } else if (oxfw->quirks & SND_OXFW_QUIRK_VOLUNTARY_RECOVERY) { + // It takes a bit time for target device to adjust event frequency + // according to nominal event frequency in isochronous packets from + // ALSA oxfw driver. + tx_init_skip_cycles = 4000; } else { replay_seq = true; } diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index daf731364695..b496f87841ae 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -25,6 +25,7 @@ #define MODEL_SATELLITE 0x00200f #define MODEL_SCS1M 0x001000 #define MODEL_DUET_FW 0x01dddd +#define MODEL_ONYX_1640I 0x001640 #define SPECIFIER_1394TA 0x00a02d #define VERSION_AVC 0x010001 @@ -192,6 +193,13 @@ static int detect_quirks(struct snd_oxfw *oxfw, const struct ieee1394_device_id // OXFW971-based models may transfer events by blocking method. if (!(oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD)) oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION; + + if (model == MODEL_ONYX_1640I) { + //Unless receiving packets without NOINFO packet, the device transfers + //mostly half of events in packets than expected. + oxfw->quirks |= SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET | + SND_OXFW_QUIRK_VOLUNTARY_RECOVERY; + } } return 0; diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index c13034f6c2ca..d728e451a25c 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -47,6 +47,11 @@ enum snd_oxfw_quirk { // the device to process audio data even if the value is invalid in a point of // IEC 61883-1/6. SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET = 0x10, + // Loud Technologies Mackie Onyx 1640i seems to configure OXFW971 ASIC so that it decides + // event frequency according to events in received isochronous packets. The device looks to + // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO + // are ignored, thus driver should transfer packets with timestamp. + SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20, }; /* This is an arbitrary number for convinience. */ diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 0c005d67fa89..37154ed43bd5 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -106,20 +106,14 @@ void snd_hdac_stream_free_all(struct hdac_bus *bus) } EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); -/** - * snd_hdac_ext_stream_decouple - decouple the hdac stream - * @bus: HD-audio core bus - * @stream: HD-audio ext core stream object to initialize - * @decouple: flag to decouple - */ -void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, - struct hdac_ext_stream *stream, bool decouple) +void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, + struct hdac_ext_stream *stream, + bool decouple) { struct hdac_stream *hstream = &stream->hstream; u32 val; int mask = AZX_PPCTL_PROCEN(hstream->index); - spin_lock_irq(&bus->reg_lock); val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask; if (decouple && !val) @@ -128,6 +122,20 @@ void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); stream->decoupled = decouple; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); + +/** + * snd_hdac_ext_stream_decouple - decouple the hdac stream + * @bus: HD-audio core bus + * @stream: HD-audio ext core stream object to initialize + * @decouple: flag to decouple + */ +void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, + struct hdac_ext_stream *stream, bool decouple) +{ + spin_lock_irq(&bus->reg_lock); + snd_hdac_ext_stream_decouple_locked(bus, stream, decouple); spin_unlock_irq(&bus->reg_lock); } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); @@ -252,6 +260,7 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, return NULL; } + spin_lock_irq(&bus->reg_lock); list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, @@ -266,17 +275,16 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus, } if (!hstream->link_locked) { - snd_hdac_ext_stream_decouple(bus, hstream, true); + snd_hdac_ext_stream_decouple_locked(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&bus->reg_lock); res->link_locked = 1; res->link_substream = substream; - spin_unlock_irq(&bus->reg_lock); } + spin_unlock_irq(&bus->reg_lock); return res; } @@ -292,6 +300,7 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus, return NULL; } + spin_lock_irq(&bus->reg_lock); list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = container_of(stream, struct hdac_ext_stream, @@ -301,18 +310,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus, if (!stream->opened) { if (!hstream->decoupled) - snd_hdac_ext_stream_decouple(bus, hstream, true); + snd_hdac_ext_stream_decouple_locked(bus, hstream, true); res = hstream; break; } } if (res) { - spin_lock_irq(&bus->reg_lock); res->hstream.opened = 1; res->hstream.running = 0; res->hstream.substream = substream; - spin_unlock_irq(&bus->reg_lock); } + spin_unlock_irq(&bus->reg_lock); return res; } @@ -378,15 +386,17 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) break; case HDAC_EXT_STREAM_TYPE_HOST: + spin_lock_irq(&bus->reg_lock); if (stream->decoupled && !stream->link_locked) - snd_hdac_ext_stream_decouple(bus, stream, false); + snd_hdac_ext_stream_decouple_locked(bus, stream, false); + spin_unlock_irq(&bus->reg_lock); snd_hdac_stream_release(&stream->hstream); break; case HDAC_EXT_STREAM_TYPE_LINK: - if (stream->decoupled && !stream->hstream.opened) - snd_hdac_ext_stream_decouple(bus, stream, false); spin_lock_irq(&bus->reg_lock); + if (stream->decoupled && !stream->hstream.opened) + snd_hdac_ext_stream_decouple_locked(bus, stream, false); stream->link_locked = 0; stream->link_substream = NULL; spin_unlock_irq(&bus->reg_lock); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 1eb8563db2df..9867555883c3 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -296,6 +296,7 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, int key = (substream->pcm->device << 16) | (substream->number << 2) | (substream->stream + 1); + spin_lock_irq(&bus->reg_lock); list_for_each_entry(azx_dev, &bus->stream_list, list) { if (azx_dev->direction != substream->stream) continue; @@ -309,13 +310,12 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, res = azx_dev; } if (res) { - spin_lock_irq(&bus->reg_lock); res->opened = 1; res->running = 0; res->assigned_key = key; res->substream = substream; - spin_unlock_irq(&bus->reg_lock); } + spin_unlock_irq(&bus->reg_lock); return res; } EXPORT_SYMBOL_GPL(snd_hdac_stream_assign); diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index 6ffa48dd5983..570b88e0b201 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -22,7 +22,7 @@ config SND_SB16_DSP menuconfig SND_ISA bool "ISA sound devices" depends on ISA || COMPILE_TEST - depends on ISA_DMA_API + depends on ISA_DMA_API && !M68K default y help Support for sound devices connected via the ISA bus. diff --git a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c index a1c770d826dd..6d664dd8dde0 100644 --- a/sound/isa/gus/gus_dma.c +++ b/sound/isa/gus/gus_dma.c @@ -126,6 +126,8 @@ static void snd_gf1_dma_interrupt(struct snd_gus_card * gus) } block = snd_gf1_dma_next_block(gus); spin_unlock(&gus->dma_lock); + if (!block) + return; snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd); kfree(block); #if 0 diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 93bc9bef7641..41ce12597177 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -279,6 +279,7 @@ config SND_CS46XX_NEW_DSP config SND_CS5530 tristate "CS5530 Audio" depends on ISA_DMA_API && (X86_32 || COMPILE_TEST) + depends on !M68K select SND_SB16_DSP help Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips. diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 4d22e7adeee8..7762718cf429 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -638,13 +638,17 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) * the update-IRQ timing. The IRQ is issued before actually the * data is processed. So, we need to process it afterwords in a * workqueue. + * + * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update */ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) { struct snd_pcm_substream *substream = azx_dev->core.substream; + struct snd_pcm_runtime *runtime = substream->runtime; int stream = substream->stream; u32 wallclk; unsigned int pos; + snd_pcm_uframes_t hwptr, target; wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk; if (wallclk < (azx_dev->core.period_wallclk * 2) / 3) @@ -681,6 +685,24 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) /* NG - it's below the first next period boundary */ return chip->bdl_pos_adj ? 0 : -1; azx_dev->core.start_wallclk += wallclk; + + if (azx_dev->core.no_period_wakeup) + return 1; /* OK, no need to check period boundary */ + + if (runtime->hw_ptr_base != runtime->hw_ptr_interrupt) + return 1; /* OK, already in hwptr updating process */ + + /* check whether the period gets really elapsed */ + pos = bytes_to_frames(runtime, pos); + hwptr = runtime->hw_ptr_base + pos; + if (hwptr < runtime->status->hw_ptr) + hwptr += runtime->buffer_size; + target = runtime->hw_ptr_interrupt + runtime->period_size; + if (hwptr < target) { + /* too early wakeup, process it later */ + return chip->bdl_pos_adj ? 0 : -1; + } + return 1; /* OK, it's fine */ } @@ -859,31 +881,6 @@ static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev, return substream->runtime->delay; } -static unsigned int azx_skl_get_dpib_pos(struct azx *chip, - struct azx_dev *azx_dev) -{ - return _snd_hdac_chip_readl(azx_bus(chip), - AZX_REG_VS_SDXDPIB_XBASE + - (AZX_REG_VS_SDXDPIB_XINTERVAL * - azx_dev->core.index)); -} - -/* get the current DMA position with correction on SKL+ chips */ -static unsigned int azx_get_pos_skl(struct azx *chip, struct azx_dev *azx_dev) -{ - /* DPIB register gives a more accurate position for playback */ - if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - return azx_skl_get_dpib_pos(chip, azx_dev); - - /* For capture, we need to read posbuf, but it requires a delay - * for the possible boundary overlap; the read of DPIB fetches the - * actual posbuf - */ - udelay(20); - azx_skl_get_dpib_pos(chip, azx_dev); - return azx_get_pos_posbuf(chip, azx_dev); -} - static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset) { azx_stop_chip(chip); @@ -1573,7 +1570,7 @@ static void assign_position_fix(struct azx *chip, int fix) [POS_FIX_POSBUF] = azx_get_pos_posbuf, [POS_FIX_VIACOMBO] = azx_via_get_position, [POS_FIX_COMBO] = azx_get_pos_lpib, - [POS_FIX_SKL] = azx_get_pos_skl, + [POS_FIX_SKL] = azx_get_pos_posbuf, [POS_FIX_FIFO] = azx_get_pos_fifo, }; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e82d756ab850..6322fac9e694 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -8634,6 +8634,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index c1f9f0f58464..6704dbcd101c 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -68,6 +68,7 @@ static struct hdac_ext_stream * return NULL; } + spin_lock_irq(&bus->reg_lock); list_for_each_entry(stream, &bus->stream_list, list) { struct hdac_ext_stream *hstream = stream_to_hdac_ext_stream(stream); @@ -107,12 +108,12 @@ static struct hdac_ext_stream * * is updated in snd_hdac_ext_stream_decouple(). */ if (!res->decoupled) - snd_hdac_ext_stream_decouple(bus, res, true); - spin_lock_irq(&bus->reg_lock); + snd_hdac_ext_stream_decouple_locked(bus, res, true); + res->link_locked = 1; res->link_substream = substream; - spin_unlock_irq(&bus->reg_lock); } + spin_unlock_irq(&bus->reg_lock); return res; } diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index 43a2a62d66f7..49629d4bb327 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -95,7 +95,7 @@ static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev) int actual_len; ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP), - buffer, buffer[1] + 2, &actual_len, HZ); + buffer, buffer[1] + 2, &actual_len, 1000); if (ret < 0) return ret; else if (actual_len != buffer[1] + 2) diff --git a/sound/usb/6fire/firmware.c b/sound/usb/6fire/firmware.c index 8981e61f2da4..c51abc54d2f8 100644 --- a/sound/usb/6fire/firmware.c +++ b/sound/usb/6fire/firmware.c @@ -160,7 +160,7 @@ static int usb6fire_fw_ezusb_write(struct usb_device *device, { return usb_control_msg_send(device, 0, type, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0, data, len, HZ, GFP_KERNEL); + value, 0, data, len, 1000, GFP_KERNEL); } static int usb6fire_fw_ezusb_read(struct usb_device *device, @@ -168,7 +168,7 @@ static int usb6fire_fw_ezusb_read(struct usb_device *device, { return usb_control_msg_recv(device, 0, type, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, 0, data, len, HZ, GFP_KERNEL); + value, 0, data, len, 1000, GFP_KERNEL); } static int usb6fire_fw_fpga_write(struct usb_device *device, @@ -178,7 +178,7 @@ static int usb6fire_fw_fpga_write(struct usb_device *device, int ret; ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len, - &actual_len, HZ); + &actual_len, 1000); if (ret < 0) return ret; else if (actual_len != len) diff --git a/sound/usb/card.h b/sound/usb/card.h index 5b19901f305a..87f042d06ce0 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -74,8 +74,9 @@ struct snd_usb_endpoint { atomic_t state; /* running state */ - void (*prepare_data_urb) (struct snd_usb_substream *subs, - struct urb *urb); + int (*prepare_data_urb) (struct snd_usb_substream *subs, + struct urb *urb, + bool in_stream_lock); void (*retire_data_urb) (struct snd_usb_substream *subs, struct urb *urb); @@ -94,9 +95,9 @@ struct snd_usb_endpoint { struct list_head ready_playback_urbs; /* playback URB FIFO for implicit fb */ unsigned int nurbs; /* # urbs */ - unsigned int nominal_queue_size; /* total buffer sizes in URBs */ unsigned long active_mask; /* bitmask of active urbs */ unsigned long unlink_mask; /* bitmask of unlinked urbs */ + atomic_t submitted_urbs; /* currently submitted urbs */ char *syncbuf; /* sync buffer for all sync URBs */ dma_addr_t sync_dma; /* DMA address of syncbuf */ @@ -125,6 +126,7 @@ struct snd_usb_endpoint { int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ bool implicit_fb_sync; /* syncs with implicit feedback */ + bool lowlatency_playback; /* low-latency playback mode */ bool need_setup; /* (re-)need for configure? */ /* for hw constraints */ @@ -136,6 +138,7 @@ struct snd_usb_endpoint { unsigned int cur_period_frames; unsigned int cur_period_bytes; unsigned int cur_buffer_periods; + unsigned char cur_clock; spinlock_t lock; struct list_head list; @@ -188,7 +191,7 @@ struct snd_usb_substream { } dsd_dop; bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */ - bool early_playback_start; /* early start needed for playback? */ + bool lowlatency_playback; /* low-latency playback mode */ struct media_ctl *media_ctl; }; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 81d5ce07d548..4dfe76416794 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -271,7 +271,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, return -EINVAL; } - /* first, see if the ID we're looking for is a clock source already */ + /* first, see if the ID we're looking at is a clock source already */ source = snd_usb_find_clock_source(chip, entity_id, proto); if (source) { entity_id = GET_VAL(source, proto, bClockID); @@ -297,7 +297,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, goto find_source; } - /* the entity ID we are looking for is a selector. + /* the entity ID we are looking at is a selector. * find out what it currently selects */ ret = uac_clock_selector_get_val(chip, clock_id); if (ret < 0) { @@ -496,6 +496,10 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, union uac23_clock_source_desc *cs_desc; cs_desc = snd_usb_find_clock_source(chip, clock, fmt->protocol); + + if (!cs_desc) + return 0; + if (fmt->protocol == UAC_VERSION_3) bmControls = le32_to_cpu(cs_desc->v3.bmControls); else diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 533919a28856..743b8287cfcd 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -148,18 +148,23 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep) * This won't be used for implicit feedback which takes the packet size * returned from the sync source */ -static int slave_next_packet_size(struct snd_usb_endpoint *ep) +static int slave_next_packet_size(struct snd_usb_endpoint *ep, + unsigned int avail) { unsigned long flags; + unsigned int phase; int ret; if (ep->fill_max) return ep->maxframesize; spin_lock_irqsave(&ep->lock, flags); - ep->phase = (ep->phase & 0xffff) - + (ep->freqm << ep->datainterval); - ret = min(ep->phase >> 16, ep->maxframesize); + phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval); + ret = min(phase >> 16, ep->maxframesize); + if (avail && ret >= avail) + ret = -EAGAIN; + else + ep->phase = phase; spin_unlock_irqrestore(&ep->lock, flags); return ret; @@ -169,20 +174,25 @@ static int slave_next_packet_size(struct snd_usb_endpoint *ep) * Return the number of samples to be sent in the next packet * for adaptive and synchronous endpoints */ -static int next_packet_size(struct snd_usb_endpoint *ep) +static int next_packet_size(struct snd_usb_endpoint *ep, unsigned int avail) { + unsigned int sample_accum; int ret; if (ep->fill_max) return ep->maxframesize; - ep->sample_accum += ep->sample_rem; - if (ep->sample_accum >= ep->pps) { - ep->sample_accum -= ep->pps; + sample_accum = ep->sample_accum + ep->sample_rem; + if (sample_accum >= ep->pps) { + sample_accum -= ep->pps; ret = ep->packsize[1]; } else { ret = ep->packsize[0]; } + if (avail && ret >= avail) + ret = -EAGAIN; + else + ep->sample_accum = sample_accum; return ret; } @@ -190,16 +200,27 @@ static int next_packet_size(struct snd_usb_endpoint *ep) /* * snd_usb_endpoint_next_packet_size: Return the number of samples to be sent * in the next packet + * + * If the size is equal or exceeds @avail, don't proceed but return -EAGAIN + * Exception: @avail = 0 for skipping the check. */ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, - struct snd_urb_ctx *ctx, int idx) + struct snd_urb_ctx *ctx, int idx, + unsigned int avail) { - if (ctx->packet_size[idx]) - return ctx->packet_size[idx]; - else if (ep->sync_source) - return slave_next_packet_size(ep); + unsigned int packet; + + packet = ctx->packet_size[idx]; + if (packet) { + if (avail && packet >= avail) + return -EAGAIN; + return packet; + } + + if (ep->sync_source) + return slave_next_packet_size(ep, avail); else - return next_packet_size(ep); + return next_packet_size(ep, avail); } static void call_retire_callback(struct snd_usb_endpoint *ep, @@ -263,7 +284,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, unsigned int length; int counts; - counts = snd_usb_endpoint_next_packet_size(ep, ctx, i); + counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0); length = counts * ep->stride; /* number of silent bytes */ offset = offs * ep->stride + extra * i; urb->iso_frame_desc[i].offset = offset; @@ -286,8 +307,9 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, /* * Prepare a PLAYBACK urb for submission to the bus. */ -static void prepare_outbound_urb(struct snd_usb_endpoint *ep, - struct snd_urb_ctx *ctx) +static int prepare_outbound_urb(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx, + bool in_stream_lock) { struct urb *urb = ctx->urb; unsigned char *cp = urb->transfer_buffer; @@ -299,9 +321,9 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep, case SND_USB_ENDPOINT_TYPE_DATA: data_subs = READ_ONCE(ep->data_subs); if (data_subs && ep->prepare_data_urb) - ep->prepare_data_urb(data_subs, urb); - else /* no data provider, so send silence */ - prepare_silent_urb(ep, ctx); + return ep->prepare_data_urb(data_subs, urb, in_stream_lock); + /* no data provider, so send silence */ + prepare_silent_urb(ep, ctx); break; case SND_USB_ENDPOINT_TYPE_SYNC: @@ -330,13 +352,14 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep, break; } + return 0; } /* * Prepare a CAPTURE or SYNC urb for submission to the bus. */ -static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, - struct snd_urb_ctx *urb_ctx) +static int prepare_inbound_urb(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *urb_ctx) { int i, offs; struct urb *urb = urb_ctx->urb; @@ -361,6 +384,7 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, urb->iso_frame_desc[0].offset = 0; break; } + return 0; } /* notify an error as XRUN to the assigned PCM data substream */ @@ -396,6 +420,16 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep) return p; } +static void push_back_to_ready_list(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx) +{ + 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); +} + /* * Send output urbs that have been prepared previously. URBs are dequeued * from ep->ready_playback_urbs and in case there aren't any available @@ -406,12 +440,14 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep) * is that host controllers don't guarantee the order in which they return * inbound and outbound packets to their submitters. * - * This function is only used for implicit feedback endpoints. For endpoints - * driven by dedicated sync endpoints, URBs are immediately re-submitted - * from their completion handler. + * This function is used both for implicit feedback endpoints and in low- + * latency playback mode. */ -static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) +void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, + bool in_stream_lock) { + bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep); + while (ep_state_running(ep)) { unsigned long flags; @@ -420,14 +456,14 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) int err, i; spin_lock_irqsave(&ep->lock, flags); - if (ep->next_packet_queued > 0 && + if ((!implicit_fb || ep->next_packet_queued > 0) && !list_empty(&ep->ready_playback_urbs)) { /* take URB out of FIFO */ ctx = list_first_entry(&ep->ready_playback_urbs, struct snd_urb_ctx, ready_list); list_del_init(&ctx->ready_list); - - packet = next_packet_fifo_dequeue(ep); + if (implicit_fb) + packet = next_packet_fifo_dequeue(ep); } spin_unlock_irqrestore(&ep->lock, flags); @@ -435,11 +471,24 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) return; /* copy over the length information */ - for (i = 0; i < packet->packets; i++) - ctx->packet_size[i] = packet->packet_size[i]; + if (implicit_fb) { + for (i = 0; i < packet->packets; i++) + ctx->packet_size[i] = packet->packet_size[i]; + } /* call the data handler to fill in playback data */ - prepare_outbound_urb(ep, ctx); + err = prepare_outbound_urb(ep, ctx, in_stream_lock); + /* can be stopped during prepare callback */ + if (unlikely(!ep_state_running(ep))) + break; + if (err < 0) { + /* push back to ready list again for -EAGAIN */ + if (err == -EAGAIN) + push_back_to_ready_list(ep, ctx); + else + notify_xrun(ep); + return; + } err = usb_submit_urb(ctx->urb, GFP_ATOMIC); if (err < 0) { @@ -451,6 +500,7 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) } set_bit(ctx->index, &ep->active_mask); + atomic_inc(&ep->submitted_urbs); } } @@ -461,7 +511,6 @@ static void snd_complete_urb(struct urb *urb) { struct snd_urb_ctx *ctx = urb->context; struct snd_usb_endpoint *ep = ctx->ep; - unsigned long flags; int err; if (unlikely(urb->status == -ENOENT || /* unlinked */ @@ -482,16 +531,20 @@ static void snd_complete_urb(struct urb *urb) if (unlikely(!ep_state_running(ep))) goto exit_clear; - if (snd_usb_endpoint_implicit_feedback_sink(ep)) { - spin_lock_irqsave(&ep->lock, flags); - list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + /* in low-latency and implicit-feedback modes, push back the + * URB to ready list at first, then process as much as possible + */ + if (ep->lowlatency_playback || + snd_usb_endpoint_implicit_feedback_sink(ep)) { + push_back_to_ready_list(ep, ctx); clear_bit(ctx->index, &ep->active_mask); - spin_unlock_irqrestore(&ep->lock, flags); - queue_pending_output_urbs(ep); + snd_usb_queue_pending_output_urbs(ep, false); + atomic_dec(&ep->submitted_urbs); /* decrement at last */ return; } - prepare_outbound_urb(ep, ctx); + /* in non-lowlatency mode, no error handling for prepare */ + prepare_outbound_urb(ep, ctx, false); /* can be stopped during prepare callback */ if (unlikely(!ep_state_running(ep))) goto exit_clear; @@ -513,6 +566,7 @@ static void snd_complete_urb(struct urb *urb) exit_clear: clear_bit(ctx->index, &ep->active_mask); + atomic_dec(&ep->submitted_urbs); } /* @@ -596,6 +650,7 @@ int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type) ep->type = type; ep->ep_num = ep_num; INIT_LIST_HEAD(&ep->ready_playback_urbs); + atomic_set(&ep->submitted_urbs, 0); is_playback = ((ep_num & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); ep_num &= USB_ENDPOINT_NUMBER_MASK; @@ -722,6 +777,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip, ep->cur_period_frames = params_period_size(params); ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes; ep->cur_buffer_periods = params_periods(params); + ep->cur_clock = fp->clock; if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC) endpoint_set_syncinterval(chip, ep); @@ -781,14 +837,19 @@ void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip, * Pass NULL to deactivate each callback. */ void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, - void (*prepare)(struct snd_usb_substream *subs, - struct urb *urb), + int (*prepare)(struct snd_usb_substream *subs, + struct urb *urb, + bool in_stream_lock), void (*retire)(struct snd_usb_substream *subs, struct urb *urb), struct snd_usb_substream *data_subs) { ep->prepare_data_urb = prepare; ep->retire_data_urb = retire; + if (data_subs) + ep->lowlatency_playback = data_subs->lowlatency_playback; + else + ep->lowlatency_playback = false; WRITE_ONCE(ep->data_subs, data_subs); } @@ -833,6 +894,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip, ep->altsetting = 0; ep->cur_audiofmt = NULL; ep->cur_rate = 0; + ep->cur_clock = 0; ep->iface_ref = NULL; usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num); } @@ -859,7 +921,7 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) return 0; do { - alive = bitmap_weight(&ep->active_mask, ep->nurbs); + alive = atomic_read(&ep->submitted_urbs); if (!alive) break; @@ -893,9 +955,10 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep) * * This function moves the EP to STOPPING state if it's being RUNNING. */ -static int stop_urbs(struct snd_usb_endpoint *ep, bool force) +static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending) { unsigned int i; + unsigned long flags; if (!force && atomic_read(&ep->running)) return -EBUSY; @@ -903,9 +966,14 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force) if (!ep_state_update(ep, EP_STATE_RUNNING, EP_STATE_STOPPING)) return 0; + spin_lock_irqsave(&ep->lock, flags); INIT_LIST_HEAD(&ep->ready_playback_urbs); ep->next_packet_head = 0; ep->next_packet_queued = 0; + spin_unlock_irqrestore(&ep->lock, flags); + + if (keep_pending) + return 0; for (i = 0; i < ep->nurbs; i++) { if (test_bit(i, &ep->active_mask)) { @@ -930,7 +998,7 @@ static int release_urbs(struct snd_usb_endpoint *ep, bool force) snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); /* stop and unlink urbs */ - err = stop_urbs(ep, force); + err = stop_urbs(ep, force, false); if (err) return err; @@ -1132,10 +1200,6 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) INIT_LIST_HEAD(&u->ready_list); } - /* total buffer bytes of all URBs plus the next queue; - * referred in pcm.c - */ - ep->nominal_queue_size = maxsize * urb_packs * (ep->nurbs + 1); return 0; out_of_memory: @@ -1340,6 +1404,25 @@ unlock: return err; } +/* get the current rate set to the given clock by any endpoint */ +int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock) +{ + struct snd_usb_endpoint *ep; + int rate = 0; + + if (!clock) + return 0; + mutex_lock(&chip->mutex); + list_for_each_entry(ep, &chip->ep_list, list) { + if (ep->cur_clock == clock && ep->cur_rate) { + rate = ep->cur_rate; + break; + } + } + mutex_unlock(&chip->mutex); + return rate; +} + /** * snd_usb_endpoint_start: start an snd_usb_endpoint * @@ -1355,6 +1438,7 @@ unlock: */ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) { + bool is_playback = usb_pipeout(ep->pipe); int err; unsigned int i; @@ -1391,13 +1475,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (snd_usb_endpoint_implicit_feedback_sink(ep) && !(ep->chip->quirk_flags & QUIRK_FLAG_PLAYBACK_FIRST)) { - for (i = 0; i < ep->nurbs; i++) { - struct snd_urb_ctx *ctx = ep->urb + i; - list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); - } - usb_audio_dbg(ep->chip, "No URB submission due to implicit fb sync\n"); - return 0; + i = 0; + goto fill_rest; } for (i = 0; i < ep->nurbs; i++) { @@ -1406,10 +1486,18 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (snd_BUG_ON(!urb)) goto __error; - if (usb_pipeout(ep->pipe)) { - prepare_outbound_urb(ep, urb->context); - } else { - prepare_inbound_urb(ep, urb->context); + if (is_playback) + err = prepare_outbound_urb(ep, urb->context, true); + else + err = prepare_inbound_urb(ep, urb->context); + if (err < 0) { + /* stop filling at applptr */ + if (err == -EAGAIN) + break; + usb_audio_dbg(ep->chip, + "EP 0x%x: failed to prepare urb: %d\n", + ep->ep_num, err); + goto __error; } err = usb_submit_urb(urb, GFP_ATOMIC); @@ -1420,14 +1508,29 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) goto __error; } set_bit(i, &ep->active_mask); + atomic_inc(&ep->submitted_urbs); + } + + if (!i) { + usb_audio_dbg(ep->chip, "XRUN at starting EP 0x%x\n", + ep->ep_num); + goto __error; } usb_audio_dbg(ep->chip, "%d URBs submitted for EP 0x%x\n", - ep->nurbs, ep->ep_num); + i, ep->ep_num); + + fill_rest: + /* put the remaining URBs to ready list */ + if (is_playback) { + for (; i < ep->nurbs; i++) + push_back_to_ready_list(ep, ep->urb + i); + } + return 0; __error: - snd_usb_endpoint_stop(ep); + snd_usb_endpoint_stop(ep, false); return -EPIPE; } @@ -1435,6 +1538,7 @@ __error: * snd_usb_endpoint_stop: stop an snd_usb_endpoint * * @ep: the endpoint to stop (may be NULL) + * @keep_pending: keep in-flight URBs * * A call to this function will decrement the running count of the endpoint. * In case the last user has requested the endpoint stop, the URBs will @@ -1445,7 +1549,7 @@ __error: * The caller needs to synchronize the pending stop operation via * snd_usb_endpoint_sync_pending_stop(). */ -void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending) { if (!ep) return; @@ -1460,7 +1564,7 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (!atomic_dec_return(&ep->running)) { if (ep->sync_source) WRITE_ONCE(ep->sync_source->sync_sink, NULL); - stop_urbs(ep, false); + stop_urbs(ep, false, keep_pending); } } @@ -1575,7 +1679,7 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, } spin_unlock_irqrestore(&ep->lock, flags); - queue_pending_output_urbs(ep); + snd_usb_queue_pending_output_urbs(ep, false); return; } diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index a668f675b52b..6a9af04cf175 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -19,6 +19,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip, struct snd_usb_endpoint *ep); int snd_usb_endpoint_configure(struct snd_usb_audio *chip, struct snd_usb_endpoint *ep); +int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock); bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip, struct snd_usb_endpoint *ep, @@ -29,14 +30,15 @@ void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip, struct snd_usb_endpoint *data_ep, struct snd_usb_endpoint *sync_ep); void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, - void (*prepare)(struct snd_usb_substream *subs, - struct urb *urb), + int (*prepare)(struct snd_usb_substream *subs, + struct urb *urb, + bool in_stream_lock), void (*retire)(struct snd_usb_substream *subs, struct urb *urb), struct snd_usb_substream *data_subs); int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); -void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); @@ -45,6 +47,9 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, - struct snd_urb_ctx *ctx, int idx); + struct snd_urb_ctx *ctx, int idx, + unsigned int avail); +void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep, + bool in_stream_lock); #endif /* __USBAUDIO_ENDPOINT_H */ diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 23767a14d126..70319c822c10 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -54,8 +54,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { /* Fixed EP */ /* FIXME: check the availability of generic matching */ - IMPLICIT_FB_FIXED_DEV(0x1397, 0x0001, 0x81, 1), /* Behringer UFX1604 */ - IMPLICIT_FB_FIXED_DEV(0x1397, 0x0002, 0x81, 1), /* Behringer UFX1204 */ IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */ IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0001, 0x81, 2), /* Solid State Logic SSL2 */ IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0002, 0x81, 2), /* Solid State Logic SSL2+ */ diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index 9602929b7de9..59faa5a9a714 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -113,12 +113,12 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, retval = usb_interrupt_msg(line6->usbdev, usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w), (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); + &partial, LINE6_TIMEOUT); } else { retval = usb_bulk_msg(line6->usbdev, usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w), (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); + &partial, LINE6_TIMEOUT); } if (retval) { @@ -347,7 +347,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, ret = usb_control_msg_send(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, (datalen << 8) | 0x21, address, NULL, 0, - LINE6_TIMEOUT * HZ, GFP_KERNEL); + LINE6_TIMEOUT, GFP_KERNEL); if (ret) { dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); goto exit; @@ -360,7 +360,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, ret = usb_control_msg_recv(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x0012, 0x0000, &len, 1, - LINE6_TIMEOUT * HZ, GFP_KERNEL); + LINE6_TIMEOUT, GFP_KERNEL); if (ret) { dev_err(line6->ifcdev, "receive length failed (error %d)\n", ret); @@ -387,7 +387,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data, /* receive the result: */ ret = usb_control_msg_recv(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x0013, 0x0000, data, datalen, LINE6_TIMEOUT * HZ, + 0x0013, 0x0000, data, datalen, LINE6_TIMEOUT, GFP_KERNEL); if (ret) dev_err(line6->ifcdev, "read failed (error %d)\n", ret); @@ -417,7 +417,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, ret = usb_control_msg_send(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - 0x0022, address, data, datalen, LINE6_TIMEOUT * HZ, + 0x0022, address, data, datalen, LINE6_TIMEOUT, GFP_KERNEL); if (ret) { dev_err(line6->ifcdev, @@ -430,7 +430,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data, ret = usb_control_msg_recv(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x0012, 0x0000, status, 1, LINE6_TIMEOUT * HZ, + 0x0012, 0x0000, status, 1, LINE6_TIMEOUT, GFP_KERNEL); if (ret) { dev_err(line6->ifcdev, diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index 71d3da1db8c8..ecf3a2b39c7e 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -27,7 +27,7 @@ #define LINE6_FALLBACK_INTERVAL 10 #define LINE6_FALLBACK_MAXPACKETSIZE 16 -#define LINE6_TIMEOUT 1 +#define LINE6_TIMEOUT 1000 #define LINE6_BUFSIZE_LISTEN 64 #define LINE6_MIDI_MESSAGE_MAXLEN 256 diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 28794a35949d..b24bc82f89e3 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -190,7 +190,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod) ret = usb_control_msg_send(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, 0x11, 0, - NULL, 0, LINE6_TIMEOUT * HZ, GFP_KERNEL); + NULL, 0, LINE6_TIMEOUT, GFP_KERNEL); if (ret) { dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret); goto exit; @@ -200,7 +200,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod) ret = usb_control_msg_recv(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x11, 0x0, - init_bytes, 3, LINE6_TIMEOUT * HZ, GFP_KERNEL); + init_bytes, 3, LINE6_TIMEOUT, GFP_KERNEL); if (ret) { dev_err(pod->line6.ifcdev, "receive length failed (error %d)\n", ret); @@ -220,7 +220,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod) USB_REQ_SET_FEATURE, USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT, 1, 0, - NULL, 0, LINE6_TIMEOUT * HZ, GFP_KERNEL); + NULL, 0, LINE6_TIMEOUT, GFP_KERNEL); exit: return ret; } diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index 4e5693c97aa4..e33df58740a9 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -128,7 +128,7 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) ret = usb_control_msg_send(usbdev, 0, 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ, + cmd1, cmd2, NULL, 0, LINE6_TIMEOUT, GFP_KERNEL); if (ret) { diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 5834d1dc317e..4f6b20ed29dd 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -1000,7 +1000,7 @@ static int detect_usb_format(struct ua101 *ua) fmt_playback->bSubframeSize * ua->playback.channels; epd = &ua->intf[INTF_CAPTURE]->altsetting[1].endpoint[0].desc; - if (!usb_endpoint_is_isoc_in(epd)) { + if (!usb_endpoint_is_isoc_in(epd) || usb_endpoint_maxp(epd) == 0) { dev_err(&ua->dev->dev, "invalid capture endpoint\n"); return -ENXIO; } @@ -1008,7 +1008,7 @@ static int detect_usb_format(struct ua101 *ua) ua->capture.max_packet_bytes = usb_endpoint_maxp(epd); epd = &ua->intf[INTF_PLAYBACK]->altsetting[1].endpoint[0].desc; - if (!usb_endpoint_is_isoc_out(epd)) { + if (!usb_endpoint_is_isoc_out(epd) || usb_endpoint_maxp(epd) == 0) { dev_err(&ua->dev->dev, "invalid playback endpoint\n"); return -ENXIO; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 8e030b1c061a..6e7bac8203ba 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -361,9 +361,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, memset(buf, 0, sizeof(buf)); - ret = snd_usb_lock_shutdown(chip) ? -EIO : 0; - if (ret) - goto error; + if (snd_usb_lock_shutdown(chip)) + return -EIO; idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8); ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest, @@ -372,8 +371,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, snd_usb_unlock_shutdown(chip); if (ret < 0) { -error: - usb_audio_err(chip, + usb_audio_dbg(chip, "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", request, validx, idx, cval->val_type); return ret; @@ -1208,12 +1206,32 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, } } +/* forcibly initialize the current mixer value; if GET_CUR fails, set to + * the minimum as default + */ +static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx) +{ + int val, err; + + err = snd_usb_get_cur_mix_value(cval, ch, idx, &val); + if (!err) + return; + if (!cval->head.mixer->ignore_ctl_error) + usb_audio_warn(cval->head.mixer->chip, + "%d:%d: failed to get current value for ch %d (%d)\n", + cval->head.id, mixer_ctrl_intf(cval->head.mixer), + ch, err); + snd_usb_set_cur_mix_value(cval, ch, idx, cval->min); +} + /* * retrieve the minimum and maximum values for the specified control */ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, int default_min, struct snd_kcontrol *kctl) { + int i, idx; + /* for failsafe */ cval->min = default_min; cval->max = cval->min + 1; @@ -1226,7 +1244,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval, } else { int minchn = 0; if (cval->cmask) { - int i; for (i = 0; i < MAX_CHANNELS; i++) if (cval->cmask & (1 << i)) { minchn = i + 1; @@ -1327,6 +1344,19 @@ no_res_check: } } + /* initialize all elements */ + if (!cval->cmask) { + init_cur_mix_raw(cval, 0, 0); + } else { + idx = 0; + for (i = 0; i < MAX_CHANNELS; i++) { + if (cval->cmask & (1 << i)) { + init_cur_mix_raw(cval, i + 1, idx); + idx++; + } + } + } + return 0; } diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 46082dc57be0..d489c1de3bae 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -2795,6 +2795,7 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer) #define SND_DJM_750_IDX 0x1 #define SND_DJM_850_IDX 0x2 #define SND_DJM_900NXS2_IDX 0x3 +#define SND_DJM_750MK2_IDX 0x4 #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \ @@ -2984,10 +2985,40 @@ static const struct snd_djm_ctl snd_djm_ctls_900nxs2[] = { SND_DJM_CTL("Ch5 Input", 900nxs2_cap5, 3, SND_DJM_WINDEX_CAP) }; +// DJM-750MK2 +static const u16 snd_djm_opts_750mk2_cap1[] = { + 0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a }; +static const u16 snd_djm_opts_750mk2_cap2[] = { + 0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a }; +static const u16 snd_djm_opts_750mk2_cap3[] = { + 0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a }; +static const u16 snd_djm_opts_750mk2_cap4[] = { + 0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a }; +static const u16 snd_djm_opts_750mk2_cap5[] = { + 0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 }; + +static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 }; +static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 }; +static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 }; + + +static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = { + SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL), + SND_DJM_CTL("Ch1 Input", 750mk2_cap1, 2, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch2 Input", 750mk2_cap2, 2, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch3 Input", 750mk2_cap3, 2, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch4 Input", 750mk2_cap4, 2, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch5 Input", 750mk2_cap5, 3, SND_DJM_WINDEX_CAP), + SND_DJM_CTL("Ch1 Output", 750mk2_pb1, 0, SND_DJM_WINDEX_PB), + SND_DJM_CTL("Ch2 Output", 750mk2_pb2, 1, SND_DJM_WINDEX_PB), + SND_DJM_CTL("Ch3 Output", 750mk2_pb3, 2, SND_DJM_WINDEX_PB) +}; + static const struct snd_djm_device snd_djm_devices[] = { SND_DJM_DEVICE(250mk2), SND_DJM_DEVICE(750), + SND_DJM_DEVICE(750mk2), SND_DJM_DEVICE(850), SND_DJM_DEVICE(900nxs2) }; @@ -3235,6 +3266,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x08e4, 0x017f): /* Pioneer DJ DJM-750 */ err = snd_djm_controls_create(mixer, SND_DJM_750_IDX); break; + case USB_ID(0x2b73, 0x001b): /* Pioneer DJ DJM-750MK2 */ + err = snd_djm_controls_create(mixer, SND_DJM_750MK2_IDX); + break; case USB_ID(0x08e4, 0x0163): /* Pioneer DJ DJM-850 */ err = snd_djm_controls_create(mixer, SND_DJM_850_IDX); break; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 5dc9266180e3..95ec8eec1bb0 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -219,16 +219,16 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, return 0; } -static bool stop_endpoints(struct snd_usb_substream *subs) +static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending) { bool stopped = 0; if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { - snd_usb_endpoint_stop(subs->sync_endpoint); + snd_usb_endpoint_stop(subs->sync_endpoint, keep_pending); stopped = true; } if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { - snd_usb_endpoint_stop(subs->data_endpoint); + snd_usb_endpoint_stop(subs->data_endpoint, keep_pending); stopped = true; } return stopped; @@ -261,7 +261,7 @@ static int start_endpoints(struct snd_usb_substream *subs) return 0; error: - stop_endpoints(subs); + stop_endpoints(subs, false); return err; } @@ -437,7 +437,7 @@ static int configure_endpoints(struct snd_usb_audio *chip, if (subs->data_endpoint->need_setup) { /* stop any running stream beforehand */ - if (stop_endpoints(subs)) + if (stop_endpoints(subs, false)) sync_pending_stops(subs); err = snd_usb_endpoint_configure(chip, subs->data_endpoint); if (err < 0) @@ -572,7 +572,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_audiofmt = NULL; mutex_unlock(&chip->mutex); if (!snd_usb_lock_shutdown(chip)) { - if (stop_endpoints(subs)) + if (stop_endpoints(subs, false)) sync_pending_stops(subs); close_endpoints(chip, subs); snd_usb_unlock_shutdown(chip); @@ -581,6 +581,26 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) return 0; } +/* check whether early start is needed for playback stream */ +static int lowlatency_playback_available(struct snd_pcm_runtime *runtime, + struct snd_usb_substream *subs) +{ + struct snd_usb_audio *chip = subs->stream->chip; + + if (subs->direction == SNDRV_PCM_STREAM_CAPTURE) + return false; + /* disabled via module option? */ + if (!chip->lowlatency) + return false; + /* free-wheeling mode? (e.g. dmix) */ + if (runtime->stop_threshold > runtime->buffer_size) + return false; + /* implicit feedback mode has own operation mode */ + if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) + return false; + return true; +} + /* * prepare callback * @@ -614,13 +634,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) subs->period_elapsed_pending = 0; runtime->delay = 0; - /* check whether early start is needed for playback stream */ - subs->early_playback_start = - subs->direction == SNDRV_PCM_STREAM_PLAYBACK && - (!chip->lowlatency || - (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes)); - - if (subs->early_playback_start) + subs->lowlatency_playback = lowlatency_playback_available(runtime, subs); + if (!subs->lowlatency_playback) ret = start_endpoints(subs); unlock: @@ -734,6 +749,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_usb_substream *subs = rule->private; + struct snd_usb_audio *chip = subs->stream->chip; const struct audioformat *fp; struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); unsigned int rmin, rmax, r; @@ -745,6 +761,14 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, list_for_each_entry(fp, &subs->fmt_list, list) { if (!hw_check_valid_format(subs, params, fp)) continue; + r = snd_usb_endpoint_get_clock_rate(chip, fp->clock); + if (r > 0) { + if (!snd_interval_test(it, r)) + continue; + rmin = min(rmin, r); + rmax = max(rmax, r); + continue; + } if (fp->rate_table && fp->nr_rates) { for (i = 0; i < fp->nr_rates; i++) { r = fp->rate_table[i]; @@ -1056,6 +1080,13 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre if (err < 0) return err; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (fp->implicit_fb) { + runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; + break; + } + } + return 0; } @@ -1068,6 +1099,10 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) int ret; runtime->hw = snd_usb_hardware; + /* need an explicit sync to catch applptr update in low-latency mode */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK && + as->chip->lowlatency) + runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR; runtime->private_data = subs; subs->pcm_substream = substream; /* runtime PM is also done there */ @@ -1320,44 +1355,66 @@ static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs, return bytes; } -static void prepare_playback_urb(struct snd_usb_substream *subs, - struct urb *urb) +static int prepare_playback_urb(struct snd_usb_substream *subs, + struct urb *urb, + bool in_stream_lock) { struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; struct snd_usb_endpoint *ep = subs->data_endpoint; struct snd_urb_ctx *ctx = urb->context; - unsigned int counts, frames, bytes; + unsigned int frames, bytes; + int counts; + unsigned int transfer_done, frame_limit, avail = 0; int i, stride, period_elapsed = 0; unsigned long flags; + int err = 0; stride = ep->stride; frames = 0; ctx->queued = 0; urb->number_of_packets = 0; + spin_lock_irqsave(&subs->lock, flags); - subs->frame_limit += ep->max_urb_frames; + frame_limit = subs->frame_limit + ep->max_urb_frames; + transfer_done = subs->transfer_done; + + if (subs->lowlatency_playback && + runtime->status->state != SNDRV_PCM_STATE_DRAINING) { + unsigned int hwptr = subs->hwptr_done / stride; + + /* calculate the byte offset-in-buffer of the appl_ptr */ + avail = (runtime->control->appl_ptr - runtime->hw_ptr_base) + % runtime->buffer_size; + if (avail <= hwptr) + avail += runtime->buffer_size; + avail -= hwptr; + } + for (i = 0; i < ctx->packets; i++) { - counts = snd_usb_endpoint_next_packet_size(ep, ctx, i); + counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail); + if (counts < 0) + break; /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * stride; urb->iso_frame_desc[i].length = counts * stride; frames += counts; + avail -= counts; urb->number_of_packets++; - subs->transfer_done += counts; - if (subs->transfer_done >= runtime->period_size) { - subs->transfer_done -= runtime->period_size; - subs->frame_limit = 0; + transfer_done += counts; + if (transfer_done >= runtime->period_size) { + transfer_done -= runtime->period_size; + frame_limit = 0; period_elapsed = 1; if (subs->fmt_type == UAC_FORMAT_TYPE_II) { - if (subs->transfer_done > 0) { + if (transfer_done > 0) { /* FIXME: fill-max mode is not * supported yet */ - frames -= subs->transfer_done; - counts -= subs->transfer_done; + frames -= transfer_done; + counts -= transfer_done; urb->iso_frame_desc[i].length = counts * stride; - subs->transfer_done = 0; + transfer_done = 0; } i++; if (i < ctx->packets) { @@ -1371,13 +1428,19 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, } } /* finish at the period boundary or after enough frames */ - if ((period_elapsed || - subs->transfer_done >= subs->frame_limit) && + if ((period_elapsed || transfer_done >= frame_limit) && !snd_usb_endpoint_implicit_feedback_sink(ep)) break; } - bytes = frames * stride; + if (!frames) { + err = -EAGAIN; + goto unlock; + } + + bytes = frames * stride; + subs->transfer_done = transfer_done; + subs->frame_limit = frame_limit; if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE && subs->cur_audiofmt->dsd_dop)) { fill_playback_urb_dsd_dop(subs, urb, bytes); @@ -1403,14 +1466,23 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->trigger_tstamp_pending_update = false; } - if (period_elapsed && !subs->running && !subs->early_playback_start) { + if (period_elapsed && !subs->running && subs->lowlatency_playback) { subs->period_elapsed_pending = 1; period_elapsed = 0; } + + unlock: spin_unlock_irqrestore(&subs->lock, flags); + if (err < 0) + return err; urb->transfer_buffer_length = bytes; - if (period_elapsed) - snd_pcm_period_elapsed(subs->pcm_substream); + if (period_elapsed) { + if (in_stream_lock) + snd_pcm_period_elapsed_under_stream_lock(subs->pcm_substream); + else + snd_pcm_period_elapsed(subs->pcm_substream); + } + return 0; } /* @@ -1442,6 +1514,27 @@ static void retire_playback_urb(struct snd_usb_substream *subs, snd_pcm_period_elapsed(subs->pcm_substream); } +/* PCM ack callback for the playback stream; + * this plays a role only when the stream is running in low-latency mode. + */ +static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream) +{ + struct snd_usb_substream *subs = substream->runtime->private_data; + struct snd_usb_endpoint *ep; + + if (!subs->lowlatency_playback || !subs->running) + return 0; + ep = subs->data_endpoint; + if (!ep) + return 0; + /* When no more in-flight URBs available, try to process the pending + * outputs here + */ + if (!ep->active_mask) + snd_usb_queue_pending_output_urbs(ep, true); + return 0; +} + static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -1457,7 +1550,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea prepare_playback_urb, retire_playback_urb, subs); - if (!subs->early_playback_start && + if (subs->lowlatency_playback && cmd == SNDRV_PCM_TRIGGER_START) { err = start_endpoints(subs); if (err < 0) { @@ -1473,7 +1566,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea return 0; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs); + stop_endpoints(subs, substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING); snd_usb_endpoint_set_callback(subs->data_endpoint, NULL, NULL, NULL); subs->running = 0; @@ -1521,7 +1614,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream return 0; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs); + stop_endpoints(subs, false); fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_usb_endpoint_set_callback(subs->data_endpoint, @@ -1545,6 +1638,7 @@ static const struct snd_pcm_ops snd_usb_playback_ops = { .trigger = snd_usb_substream_playback_trigger, .sync_stop = snd_usb_pcm_sync_stop, .pointer = snd_usb_pcm_pointer, + .ack = snd_usb_pcm_playback_ack, }; static const struct snd_pcm_ops snd_usb_capture_ops = { diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 2af8c68fac27..b1522e43173e 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3894,6 +3894,64 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, { /* + * Pioneer DJ DJM-750MK2 + * 10 channels playback & 12 channels capture @ 48kHz S24LE + */ + USB_DEVICE_VENDOR_SPEC(0x2b73, 0x001b), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 10, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { + 48000 + } + } + }, + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 12, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x82, + .ep_idx = 1, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC| + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 48000 } + } + }, + { + .ifnum = -1 + } + } + } +}, +{ + /* * Pioneer DJ DJM-850 * 8 channels playback and 8 channels capture @ 44.1/48/96kHz S24LE * Playback on EP 0x05 diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index c39cc6851e2d..cfc1ea53978d 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -668,14 +668,15 @@ static void i_usx2y_04int(struct urb *urb) static int usx2y_rate_set(struct usx2ydev *usx2y, int rate) { - int err = 0, i; - struct snd_usx2y_urb_seq *us = NULL; - int *usbdata = NULL; - const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100; + int err = 0, i; + struct snd_usx2y_urb_seq *us = NULL; + int *usbdata = NULL; + const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100; struct urb *urb; if (usx2y->rate != rate) { - us = kzalloc(sizeof(*us) + sizeof(struct urb *) * NOOF_SETRATE_URBS, GFP_KERNEL); + us = kzalloc(struct_size(us, urb, NOOF_SETRATE_URBS), + GFP_KERNEL); if (!us) { err = -ENOMEM; goto cleanup; diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c index f88c8f29cbd8..aca2dc1989ba 100644 --- a/sound/virtio/virtio_pcm_msg.c +++ b/sound/virtio/virtio_pcm_msg.c @@ -20,7 +20,7 @@ struct virtio_pcm_msg { struct virtio_snd_pcm_xfer xfer; struct virtio_snd_pcm_status status; size_t length; - struct scatterlist sgs[0]; + struct scatterlist sgs[]; }; /** @@ -146,8 +146,7 @@ int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss, int sg_num = virtsnd_pcm_sg_num(data, period_bytes); struct virtio_pcm_msg *msg; - msg = kzalloc(sizeof(*msg) + sizeof(*msg->sgs) * (sg_num + 2), - GFP_KERNEL); + msg = kzalloc(struct_size(msg, sgs, sg_num + 2), GFP_KERNEL); if (!msg) return -ENOMEM; |