diff options
Diffstat (limited to 'sound')
103 files changed, 2745 insertions, 3122 deletions
diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig index d70ca0f33c67..c58308ae4dc1 100644 --- a/sound/aoa/Kconfig +++ b/sound/aoa/Kconfig @@ -3,7 +3,7 @@ menuconfig SND_AOA tristate "Apple Onboard Audio driver" depends on PPC_PMAC select SND_PCM - ---help--- + help This option enables the new driver for the various Apple Onboard Audio components. diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig index 8ac13fdbcfe0..03f89dbcca75 100644 --- a/sound/aoa/codecs/Kconfig +++ b/sound/aoa/codecs/Kconfig @@ -3,7 +3,7 @@ config SND_AOA_ONYX tristate "support Onyx chip" select I2C select I2C_POWERMAC - ---help--- + help This option enables support for the Onyx (pcm3052) codec chip found in the latest Apple machines (most of those with digital audio output). @@ -12,14 +12,14 @@ config SND_AOA_TAS tristate "support TAS chips" select I2C select I2C_POWERMAC - ---help--- + help This option enables support for the tas chips found in a lot of Apple Machines, especially iBooks and PowerBooks without digital. config SND_AOA_TOONIE tristate "support Toonie chip" - ---help--- + help This option enables support for the toonie codec found in the Mac Mini. If you have a Mac Mini and want to hear sound, select this option. diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig index 1d8a718e9c56..0fa2da247ad2 100644 --- a/sound/aoa/fabrics/Kconfig +++ b/sound/aoa/fabrics/Kconfig @@ -3,7 +3,7 @@ config SND_AOA_FABRIC_LAYOUT tristate "layout-id fabric" select SND_AOA_SOUNDBUS select SND_AOA_SOUNDBUS_I2S - ---help--- + help This enables the layout-id fabric for the Apple Onboard Audio driver, the module holding it all together based on the device-tree's layout-id property. diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig index ac084df0f97b..00189aac8014 100644 --- a/sound/aoa/soundbus/Kconfig +++ b/sound/aoa/soundbus/Kconfig @@ -2,7 +2,7 @@ config SND_AOA_SOUNDBUS tristate "Apple Soundbus support" select SND_PCM - ---help--- + help This option enables the generic driver for the soundbus support on Apple machines. @@ -11,5 +11,5 @@ config SND_AOA_SOUNDBUS config SND_AOA_SOUNDBUS_I2S tristate "I2S bus support" depends on SND_AOA_SOUNDBUS && PCI - ---help--- + help This option enables support for Apple I2S busses. diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 509290f2efa8..0e53f6f31916 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -764,6 +764,9 @@ static int snd_compr_stop(struct snd_compr_stream *stream) retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP); if (!retval) { + /* clear flags and stop any drain wait */ + stream->partial_drain = false; + stream->metadata_set = false; snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; @@ -921,6 +924,7 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) if (stream->next_track == false) return -EPERM; + stream->partial_drain = true; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); if (retval) { pr_debug("Partial drain returned failure\n"); diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index b412d3b3d5ff..21edb8ac95eb 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -216,12 +216,12 @@ static int snd_hwdep_dsp_load(struct snd_hwdep *hw, if (info.index >= 32) return -EINVAL; /* check whether the dsp was already loaded */ - if (hw->dsp_loaded & (1 << info.index)) + if (hw->dsp_loaded & (1u << info.index)) return -EBUSY; err = hw->ops.dsp_load(hw, &info); if (err < 0) return err; - hw->dsp_loaded |= (1 << info.index); + hw->dsp_loaded |= (1u << info.index); return 0; } diff --git a/sound/core/info.c b/sound/core/info.c index 8c6bc5241df5..9fec3070f8ba 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -606,7 +606,9 @@ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) { int c; - if (snd_BUG_ON(!buffer || !buffer->buffer)) + if (snd_BUG_ON(!buffer)) + return 1; + if (!buffer->buffer) return 1; if (len <= 0 || buffer->stop || buffer->error) return 1; diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index a83553fbedf0..bea46ed157a6 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -143,7 +143,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, break; case SNDRV_DMA_TYPE_VMALLOC: gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL | __GFP_HIGHMEM); - dmab->area = __vmalloc(size, gfp, PAGE_KERNEL); + dmab->area = __vmalloc(size, gfp); dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 930def8201f4..68630244b00f 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2876,7 +2876,7 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) if (runtime->oss.params) { /* use mutex_trylock() for params_lock for avoiding a deadlock - * between mmap_sem and params_lock taken by + * between mmap_lock and params_lock taken by * copy_from/to_user() in snd_pcm_oss_write/read() */ err = snd_pcm_oss_change_params(substream, true); diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 59d62f05658f..1545f8fdb4db 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -205,13 +205,14 @@ static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug, plugin = snd_pcm_plug_first(plug); while (plugin && frames > 0) { plugin_next = plugin->next; + if (check_size && plugin->buf_frames && + frames > plugin->buf_frames) + frames = plugin->buf_frames; if (plugin->dst_frames) { frames = plugin->dst_frames(plugin, frames); if (frames < 0) return frames; } - if (check_size && frames > plugin->buf_frames) - frames = plugin->buf_frames; plugin = plugin_next; } return frames; @@ -225,14 +226,15 @@ static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug, plugin = snd_pcm_plug_last(plug); while (plugin && frames > 0) { - if (check_size && frames > plugin->buf_frames) - frames = plugin->buf_frames; plugin_prev = plugin->prev; if (plugin->src_frames) { frames = plugin->src_frames(plugin, frames); if (frames < 0) return frames; } + if (check_size && plugin->buf_frames && + frames > plugin->buf_frames) + frames = plugin->buf_frames; plugin = plugin_prev; } return frames; diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index 8d2f7a4e3ab6..46e273bd4a78 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -64,7 +64,7 @@ struct snd_pcm_plugin { char *buf; snd_pcm_uframes_t buf_frames; struct snd_pcm_plugin_channel *buf_channels; - char extra_data[0]; + char extra_data[]; }; int snd_pcm_plugin_build(struct snd_pcm_substream *handle, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 872a852de75c..d531e1bc2b81 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -433,6 +433,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, no_delta_check: if (runtime->status->hw_ptr == new_hw_ptr) { + runtime->hw_ptr_jiffies = curr_jiffies; update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return 0; } diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index fcab37ea6641..860935e3aea4 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -460,7 +460,7 @@ int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream, return 0; /* already large enough */ vfree(runtime->dma_area); } - runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL); + runtime->dma_area = __vmalloc(size, gfp_flags); if (!runtime->dma_area) return -ENOMEM; runtime->dma_bytes = size; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index aef860256278..9630d2523948 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -138,6 +138,16 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); +static void snd_pcm_stream_lock_nested(struct snd_pcm_substream *substream) +{ + struct snd_pcm_group *group = &substream->self_group; + + if (substream->pcm->nonatomic) + mutex_lock_nested(&group->mutex, SINGLE_DEPTH_NESTING); + else + spin_lock_nested(&group->lock, SINGLE_DEPTH_NESTING); +} + /** * snd_pcm_stream_unlock_irq - Unlock the PCM stream * @substream: PCM substream @@ -2166,6 +2176,12 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) } pcm_file = f.file->private_data; substream1 = pcm_file->substream; + + if (substream == substream1) { + res = -EINVAL; + goto _badf; + } + group = kzalloc(sizeof(*group), GFP_KERNEL); if (!group) { res = -ENOMEM; @@ -2194,7 +2210,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) snd_pcm_stream_unlock_irq(substream); snd_pcm_group_lock_irq(target_group, nonatomic); - snd_pcm_stream_lock(substream1); + snd_pcm_stream_lock_nested(substream1); snd_pcm_group_assign(substream1, target_group); refcount_inc(&target_group->refs); snd_pcm_stream_unlock(substream1); @@ -2210,7 +2226,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) static void relink_to_local(struct snd_pcm_substream *substream) { - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_nested(substream); snd_pcm_group_assign(substream, &substream->self_group); snd_pcm_stream_unlock(substream); } @@ -3093,7 +3109,8 @@ static int snd_pcm_xferi_frames_ioctl(struct snd_pcm_substream *substream, result = snd_pcm_lib_write(substream, xferi.buf, xferi.frames); else result = snd_pcm_lib_read(substream, xferi.buf, xferi.frames); - __put_user(result, &_xferi->result); + if (put_user(result, &_xferi->result)) + return -EFAULT; return result < 0 ? result : 0; } @@ -3122,7 +3139,8 @@ static int snd_pcm_xfern_frames_ioctl(struct snd_pcm_substream *substream, else result = snd_pcm_lib_readv(substream, bufs, xfern.frames); kfree(bufs); - __put_user(result, &_xfern->result); + if (put_user(result, &_xfern->result)) + return -EFAULT; return result < 0 ? result : 0; } @@ -3137,7 +3155,8 @@ static int snd_pcm_rewind_ioctl(struct snd_pcm_substream *substream, if (put_user(0, _frames)) return -EFAULT; result = snd_pcm_rewind(substream, frames); - __put_user(result, _frames); + if (put_user(result, _frames)) + return -EFAULT; return result < 0 ? result : 0; } @@ -3152,7 +3171,8 @@ static int snd_pcm_forward_ioctl(struct snd_pcm_substream *substream, if (put_user(0, _frames)) return -EFAULT; result = snd_pcm_forward(substream, frames); - __put_user(result, _frames); + if (put_user(result, _frames)) + return -EFAULT; return result < 0 ? result : 0; } diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 20dd08e1f675..2a688b711a9a 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -120,6 +120,17 @@ static void snd_rawmidi_input_event_work(struct work_struct *work) runtime->event(runtime->substream); } +/* buffer refcount management: call with runtime->lock held */ +static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime) +{ + runtime->buffer_ref++; +} + +static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime) +{ + runtime->buffer_ref--; +} + static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) { struct snd_rawmidi_runtime *runtime; @@ -669,6 +680,11 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime, if (!newbuf) return -ENOMEM; spin_lock_irq(&runtime->lock); + if (runtime->buffer_ref) { + spin_unlock_irq(&runtime->lock); + kvfree(newbuf); + return -EBUSY; + } oldbuf = runtime->buffer; runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; @@ -1019,8 +1035,10 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, long result = 0, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; + int err = 0; spin_lock_irqsave(&runtime->lock, flags); + snd_rawmidi_buffer_ref(runtime); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) @@ -1039,16 +1057,19 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_to_user(userbuf + result, - runtime->buffer + appl_ptr, count1)) { - return result > 0 ? result : -EFAULT; - } + runtime->buffer + appl_ptr, count1)) + err = -EFAULT; spin_lock_irqsave(&runtime->lock, flags); + if (err) + goto out; } result += count1; count -= count1; } + out: + snd_rawmidi_buffer_unref(runtime); spin_unlock_irqrestore(&runtime->lock, flags); - return result; + return result > 0 ? result : err; } long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, @@ -1342,6 +1363,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, return -EAGAIN; } } + snd_rawmidi_buffer_ref(runtime); while (count > 0 && runtime->avail > 0) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) @@ -1373,6 +1395,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, } __end: count1 = runtime->avail < runtime->buffer_size; + snd_rawmidi_buffer_unref(runtime); spin_unlock_irqrestore(&runtime->lock, flags); if (count1) snd_rawmidi_output_trigger(substream, 1); diff --git a/sound/core/seq/oss/seq_oss_timer.h b/sound/core/seq/oss/seq_oss_timer.h index 2d86125b5d0f..dee190b4ec6b 100644 --- a/sound/core/seq/oss/seq_oss_timer.h +++ b/sound/core/seq/oss/seq_oss_timer.h @@ -44,14 +44,4 @@ snd_seq_oss_timer_cur_tick(struct seq_oss_timer *timer) return timer->cur_tick; } - -/* - * is realtime event? - */ -static inline int -snd_seq_oss_timer_is_realtime(struct seq_oss_timer *timer) -{ - return timer->realtime; -} - #endif diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index feefdfc3bcca..c42217e2dd19 100644 --- a/sound/core/sgbuf.c +++ b/sound/core/sgbuf.c @@ -9,7 +9,6 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/export.h> -#include <asm/pgtable.h> #include <sound/memalloc.h> diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 577c8e03ec4d..7141f73cddd3 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -186,18 +186,6 @@ config SND_PORTMAN2X4 To compile this driver as a module, choose M here: the module will be called snd-portman2x4. -config SND_ML403_AC97CR - tristate "Xilinx ML403 AC97 Controller Reference" - depends on XILINX_VIRTEX - select SND_AC97_CODEC - help - Say Y here to include support for the - opb_ac97_controller_ref_v1_00_a ip core found in Xilinx's ML403 - reference design. - - To compile this driver as a module, choose M here: the module - will be called snd-ml403_ac97cr. - config SND_AC97_POWER_SAVE bool "AC97 Power-Saving Mode" depends on SND_AC97_CODEC diff --git a/sound/drivers/Makefile b/sound/drivers/Makefile index 615558a281c8..c0fe4eccdaef 100644 --- a/sound/drivers/Makefile +++ b/sound/drivers/Makefile @@ -11,7 +11,6 @@ snd-mts64-objs := mts64.o snd-portman2x4-objs := portman2x4.o snd-serial-u16550-objs := serial-u16550.o snd-virmidi-objs := virmidi.o -snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o # Toplevel Module Dependency obj-$(CONFIG_SND_DUMMY) += snd-dummy.o @@ -21,6 +20,5 @@ obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o obj-$(CONFIG_SND_MTS64) += snd-mts64.o obj-$(CONFIG_SND_PORTMAN2X4) += snd-portman2x4.o -obj-$(CONFIG_SND_ML403_AC97CR) += snd-ml403-ac97cr.o obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/ pcsp/ diff --git a/sound/drivers/ml403-ac97cr.c b/sound/drivers/ml403-ac97cr.c deleted file mode 100644 index 0710707da8c1..000000000000 --- a/sound/drivers/ml403-ac97cr.c +++ /dev/null @@ -1,1298 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * ALSA driver for Xilinx ML403 AC97 Controller Reference - * IP: opb_ac97_controller_ref_v1_00_a (EDK 8.1i) - * IP: opb_ac97_controller_ref_v1_00_a (EDK 9.1i) - * - * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de> - */ - -/* Some notes / status of this driver: - * - * - Don't wonder about some strange implementations of things - especially the - * (heavy) shadowing of codec registers, with which I tried to reduce read - * accesses to a minimum, because after a variable amount of accesses, the AC97 - * controller doesn't raise the register access finished bit anymore ... - * - * - Playback support seems to be pretty stable - no issues here. - * - Capture support "works" now, too. Overruns don't happen any longer so often. - * But there might still be some ... - */ - -#include <linux/init.h> -#include <linux/module.h> - -#include <linux/platform_device.h> - -#include <linux/ioport.h> -#include <linux/slab.h> -#include <linux/io.h> -#include <linux/interrupt.h> - -/* HZ */ -#include <linux/param.h> -/* jiffies, time_*() */ -#include <linux/jiffies.h> -/* schedule_timeout*() */ -#include <linux/sched.h> -/* spin_lock*() */ -#include <linux/spinlock.h> -/* struct mutex, mutex_init(), mutex_*lock() */ -#include <linux/mutex.h> - -/* snd_printk(), snd_printd() */ -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/initval.h> -#include <sound/ac97_codec.h> - -#include "pcm-indirect2.h" - - -#define SND_ML403_AC97CR_DRIVER "ml403-ac97cr" - -MODULE_AUTHOR("Joachim Foerster <JOFT@gmx.de>"); -MODULE_DESCRIPTION("Xilinx ML403 AC97 Controller Reference"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("{{Xilinx,ML403 AC97 Controller Reference}}"); - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; - -module_param_array(index, int, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for ML403 AC97 Controller Reference."); -module_param_array(id, charp, NULL, 0444); -MODULE_PARM_DESC(id, "ID string for ML403 AC97 Controller Reference."); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(enable, "Enable this ML403 AC97 Controller Reference."); - -/* Special feature options */ -/*#define CODEC_WRITE_CHECK_RAF*/ /* don't return after a write to a codec - * register, while RAF bit is not set - */ -/* Debug options for code which may be removed completely in a final version */ -#ifdef CONFIG_SND_DEBUG -/*#define CODEC_STAT*/ /* turn on some minimal "statistics" - * about codec register usage - */ -#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the - * process of copying bytes from the - * intermediate buffer to the hardware - * fifo and the other way round - */ -#endif - -/* Definition of a "level/facility dependent" printk(); may be removed - * completely in a final version - */ -#undef PDEBUG -#ifdef CONFIG_SND_DEBUG -/* "facilities" for PDEBUG */ -#define UNKNOWN (1<<0) -#define CODEC_SUCCESS (1<<1) -#define CODEC_FAKE (1<<2) -#define INIT_INFO (1<<3) -#define INIT_FAILURE (1<<4) -#define WORK_INFO (1<<5) -#define WORK_FAILURE (1<<6) - -#define PDEBUG_FACILITIES (UNKNOWN | INIT_FAILURE | WORK_FAILURE) - -#define PDEBUG(fac, fmt, args...) do { \ - if (fac & PDEBUG_FACILITIES) \ - snd_printd(KERN_DEBUG SND_ML403_AC97CR_DRIVER ": " \ - fmt, ##args); \ - } while (0) -#else -#define PDEBUG(fac, fmt, args...) /* nothing */ -#endif - - - -/* Defines for "waits"/timeouts (portions of HZ=250 on arch/ppc by default) */ -#define CODEC_TIMEOUT_ON_INIT 5 /* timeout for checking for codec - * readiness (after insmod) - */ -#ifndef CODEC_WRITE_CHECK_RAF -#define CODEC_WAIT_AFTER_WRITE 100 /* general, static wait after a write - * access to a codec register, may be - * 0 to completely remove wait - */ -#else -#define CODEC_TIMEOUT_AFTER_WRITE 5 /* timeout after a write access to a - * codec register, if RAF bit is used - */ -#endif -#define CODEC_TIMEOUT_AFTER_READ 5 /* timeout after a read access to a - * codec register (checking RAF bit) - */ - -/* Infrastructure for codec register shadowing */ -#define LM4550_REG_OK (1<<0) /* register exists */ -#define LM4550_REG_DONEREAD (1<<1) /* read register once, value should be - * the same currently in the register - */ -#define LM4550_REG_NOSAVE (1<<2) /* values written to this register will - * not be saved in the register - */ -#define LM4550_REG_NOSHADOW (1<<3) /* don't do register shadowing, use plain - * hardware access - */ -#define LM4550_REG_READONLY (1<<4) /* register is read only */ -#define LM4550_REG_FAKEPROBE (1<<5) /* fake write _and_ read actions during - * probe() correctly - */ -#define LM4550_REG_FAKEREAD (1<<6) /* fake read access, always return - * default value - */ -#define LM4550_REG_ALLFAKE (LM4550_REG_FAKEREAD | LM4550_REG_FAKEPROBE) - -struct lm4550_reg { - u16 value; - u16 flag; - u16 wmask; - u16 def; -}; - -struct lm4550_reg lm4550_regfile[64] = { - [AC97_RESET / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_NOSAVE \ - | LM4550_REG_FAKEREAD, - .def = 0x0D50}, - [AC97_MASTER / 2] = {.flag = LM4550_REG_OK - | LM4550_REG_FAKEPROBE, - .wmask = 0x9F1F, - .def = 0x8000}, - [AC97_HEADPHONE / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x9F1F, - .def = 0x8000}, - [AC97_MASTER_MONO / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x801F, - .def = 0x8000}, - [AC97_PC_BEEP / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x801E, - .def = 0x0}, - [AC97_PHONE / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x801F, - .def = 0x8008}, - [AC97_MIC / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x805F, - .def = 0x8008}, - [AC97_LINE / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x9F1F, - .def = 0x8808}, - [AC97_CD / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x9F1F, - .def = 0x8808}, - [AC97_VIDEO / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x9F1F, - .def = 0x8808}, - [AC97_AUX / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x9F1F, - .def = 0x8808}, - [AC97_PCM / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x9F1F, - .def = 0x8008}, - [AC97_REC_SEL / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x707, - .def = 0x0}, - [AC97_REC_GAIN / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .wmask = 0x8F0F, - .def = 0x8000}, - [AC97_GENERAL_PURPOSE / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .def = 0x0, - .wmask = 0xA380}, - [AC97_3D_CONTROL / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEREAD \ - | LM4550_REG_READONLY, - .def = 0x0101}, - [AC97_POWERDOWN / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_NOSHADOW \ - | LM4550_REG_NOSAVE, - .wmask = 0xFF00}, - /* may not write ones to - * REF/ANL/DAC/ADC bits - * FIXME: Is this ok? - */ - [AC97_EXTENDED_ID / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEREAD \ - | LM4550_REG_READONLY, - .def = 0x0201}, /* primary codec */ - [AC97_EXTENDED_STATUS / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_NOSHADOW \ - | LM4550_REG_NOSAVE, - .wmask = 0x1}, - [AC97_PCM_FRONT_DAC_RATE / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .def = 0xBB80, - .wmask = 0xFFFF}, - [AC97_PCM_LR_ADC_RATE / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_FAKEPROBE, - .def = 0xBB80, - .wmask = 0xFFFF}, - [AC97_VENDOR_ID1 / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_READONLY \ - | LM4550_REG_FAKEREAD, - .def = 0x4E53}, - [AC97_VENDOR_ID2 / 2] = {.flag = LM4550_REG_OK \ - | LM4550_REG_READONLY \ - | LM4550_REG_FAKEREAD, - .def = 0x4350} -}; - -#define LM4550_RF_OK(reg) (lm4550_regfile[reg / 2].flag & LM4550_REG_OK) - -static void lm4550_regfile_init(void) -{ - int i; - for (i = 0; i < 64; i++) - if (lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) - lm4550_regfile[i].value = lm4550_regfile[i].def; -} - -static void lm4550_regfile_write_values_after_init(struct snd_ac97 *ac97) -{ - int i; - for (i = 0; i < 64; i++) - if ((lm4550_regfile[i].flag & LM4550_REG_FAKEPROBE) && - (lm4550_regfile[i].value != lm4550_regfile[i].def)) { - PDEBUG(CODEC_FAKE, "lm4550_regfile_write_values_after_" - "init(): reg=0x%x value=0x%x / %d is different " - "from def=0x%x / %d\n", - i, lm4550_regfile[i].value, - lm4550_regfile[i].value, lm4550_regfile[i].def, - lm4550_regfile[i].def); - snd_ac97_write(ac97, i * 2, lm4550_regfile[i].value); - lm4550_regfile[i].flag |= LM4550_REG_DONEREAD; - } -} - - -/* direct registers */ -#define CR_REG(ml403_ac97cr, x) ((ml403_ac97cr)->port + CR_REG_##x) - -#define CR_REG_PLAYFIFO 0x00 -#define CR_PLAYDATA(a) ((a) & 0xFFFF) - -#define CR_REG_RECFIFO 0x04 -#define CR_RECDATA(a) ((a) & 0xFFFF) - -#define CR_REG_STATUS 0x08 -#define CR_RECOVER (1<<7) -#define CR_PLAYUNDER (1<<6) -#define CR_CODECREADY (1<<5) -#define CR_RAF (1<<4) -#define CR_RECEMPTY (1<<3) -#define CR_RECFULL (1<<2) -#define CR_PLAYHALF (1<<1) -#define CR_PLAYFULL (1<<0) - -#define CR_REG_RESETFIFO 0x0C -#define CR_RECRESET (1<<1) -#define CR_PLAYRESET (1<<0) - -#define CR_REG_CODEC_ADDR 0x10 -/* UG082 says: - * #define CR_CODEC_ADDR(a) ((a) << 1) - * #define CR_CODEC_READ (1<<0) - * #define CR_CODEC_WRITE (0<<0) - */ -/* RefDesign example says: */ -#define CR_CODEC_ADDR(a) ((a) << 0) -#define CR_CODEC_READ (1<<7) -#define CR_CODEC_WRITE (0<<7) - -#define CR_REG_CODEC_DATAREAD 0x14 -#define CR_CODEC_DATAREAD(v) ((v) & 0xFFFF) - -#define CR_REG_CODEC_DATAWRITE 0x18 -#define CR_CODEC_DATAWRITE(v) ((v) & 0xFFFF) - -#define CR_FIFO_SIZE 32 - -struct snd_ml403_ac97cr { - /* lock for access to (controller) registers */ - spinlock_t reg_lock; - /* mutex for the whole sequence of accesses to (controller) registers - * which affect codec registers - */ - struct mutex cdc_mutex; - - int irq; /* for playback */ - int enable_irq; /* for playback */ - - int capture_irq; - int enable_capture_irq; - - struct resource *res_port; - void *port; - - struct snd_ac97 *ac97; - int ac97_fake; -#ifdef CODEC_STAT - int ac97_read; - int ac97_write; -#endif - - struct platform_device *pfdev; - struct snd_card *card; - struct snd_pcm *pcm; - struct snd_pcm_substream *playback_substream; - struct snd_pcm_substream *capture_substream; - - struct snd_pcm_indirect2 ind_rec; /* for playback */ - struct snd_pcm_indirect2 capture_ind2_rec; -}; - -static const struct snd_pcm_hardware snd_ml403_ac97cr_playback = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_BE, - .rates = (SNDRV_PCM_RATE_CONTINUOUS | - SNDRV_PCM_RATE_8000_48000), - .rate_min = 4000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = CR_FIFO_SIZE/2, - .period_bytes_max = (64*1024), - .periods_min = 2, - .periods_max = (128*1024)/(CR_FIFO_SIZE/2), - .fifo_size = 0, -}; - -static const struct snd_pcm_hardware snd_ml403_ac97cr_capture = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_S16_BE, - .rates = (SNDRV_PCM_RATE_CONTINUOUS | - SNDRV_PCM_RATE_8000_48000), - .rate_min = 4000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = CR_FIFO_SIZE/2, - .period_bytes_max = (64*1024), - .periods_min = 2, - .periods_max = (128*1024)/(CR_FIFO_SIZE/2), - .fifo_size = 0, -}; - -static size_t -snd_ml403_ac97cr_playback_ind2_zero(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - int copied_words = 0; - u32 full = 0; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - - spin_lock(&ml403_ac97cr->reg_lock); - while ((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & - CR_PLAYFULL)) != CR_PLAYFULL) { - out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), 0); - copied_words++; - } - rec->hw_ready = 0; - spin_unlock(&ml403_ac97cr->reg_lock); - - return (size_t) (copied_words * 2); -} - -static size_t -snd_ml403_ac97cr_playback_ind2_copy(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - size_t bytes) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - u16 *src; - int copied_words = 0; - u32 full = 0; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - src = (u16 *)(substream->runtime->dma_area + rec->sw_data); - - spin_lock(&ml403_ac97cr->reg_lock); - while (((full = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & - CR_PLAYFULL)) != CR_PLAYFULL) && (bytes > 1)) { - out_be32(CR_REG(ml403_ac97cr, PLAYFIFO), - CR_PLAYDATA(src[copied_words])); - copied_words++; - bytes = bytes - 2; - } - if (full != CR_PLAYFULL) - rec->hw_ready = 1; - else - rec->hw_ready = 0; - spin_unlock(&ml403_ac97cr->reg_lock); - - return (size_t) (copied_words * 2); -} - -static size_t -snd_ml403_ac97cr_capture_ind2_null(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - int copied_words = 0; - u32 empty = 0; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - - spin_lock(&ml403_ac97cr->reg_lock); - while ((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & - CR_RECEMPTY)) != CR_RECEMPTY) { - volatile u32 trash; - - trash = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, RECFIFO))); - /* Hmmmm, really necessary? Don't want call to in_be32() - * to be optimised away! - */ - trash++; - copied_words++; - } - rec->hw_ready = 0; - spin_unlock(&ml403_ac97cr->reg_lock); - - return (size_t) (copied_words * 2); -} - -static size_t -snd_ml403_ac97cr_capture_ind2_copy(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, size_t bytes) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - u16 *dst; - int copied_words = 0; - u32 empty = 0; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - dst = (u16 *)(substream->runtime->dma_area + rec->sw_data); - - spin_lock(&ml403_ac97cr->reg_lock); - while (((empty = (in_be32(CR_REG(ml403_ac97cr, STATUS)) & - CR_RECEMPTY)) != CR_RECEMPTY) && (bytes > 1)) { - dst[copied_words] = CR_RECDATA(in_be32(CR_REG(ml403_ac97cr, - RECFIFO))); - copied_words++; - bytes = bytes - 2; - } - if (empty != CR_RECEMPTY) - rec->hw_ready = 1; - else - rec->hw_ready = 0; - spin_unlock(&ml403_ac97cr->reg_lock); - - return (size_t) (copied_words * 2); -} - -static snd_pcm_uframes_t -snd_ml403_ac97cr_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - struct snd_pcm_indirect2 *ind2_rec = NULL; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - - if (substream == ml403_ac97cr->playback_substream) - ind2_rec = &ml403_ac97cr->ind_rec; - if (substream == ml403_ac97cr->capture_substream) - ind2_rec = &ml403_ac97cr->capture_ind2_rec; - - if (ind2_rec != NULL) - return snd_pcm_indirect2_pointer(substream, ind2_rec); - return (snd_pcm_uframes_t) 0; -} - -static int -snd_ml403_ac97cr_pcm_playback_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - int err = 0; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - PDEBUG(WORK_INFO, "trigger(playback): START\n"); - ml403_ac97cr->ind_rec.hw_ready = 1; - - /* clear play FIFO */ - out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_PLAYRESET); - - /* enable play irq */ - ml403_ac97cr->enable_irq = 1; - enable_irq(ml403_ac97cr->irq); - break; - case SNDRV_PCM_TRIGGER_STOP: - PDEBUG(WORK_INFO, "trigger(playback): STOP\n"); - ml403_ac97cr->ind_rec.hw_ready = 0; -#ifdef SND_PCM_INDIRECT2_STAT - snd_pcm_indirect2_stat(substream, &ml403_ac97cr->ind_rec); -#endif - /* disable play irq */ - disable_irq_nosync(ml403_ac97cr->irq); - ml403_ac97cr->enable_irq = 0; - break; - default: - err = -EINVAL; - break; - } - PDEBUG(WORK_INFO, "trigger(playback): (done)\n"); - return err; -} - -static int -snd_ml403_ac97cr_pcm_capture_trigger(struct snd_pcm_substream *substream, - int cmd) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - int err = 0; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - PDEBUG(WORK_INFO, "trigger(capture): START\n"); - ml403_ac97cr->capture_ind2_rec.hw_ready = 0; - - /* clear record FIFO */ - out_be32(CR_REG(ml403_ac97cr, RESETFIFO), CR_RECRESET); - - /* enable record irq */ - ml403_ac97cr->enable_capture_irq = 1; - enable_irq(ml403_ac97cr->capture_irq); - break; - case SNDRV_PCM_TRIGGER_STOP: - PDEBUG(WORK_INFO, "trigger(capture): STOP\n"); - ml403_ac97cr->capture_ind2_rec.hw_ready = 0; -#ifdef SND_PCM_INDIRECT2_STAT - snd_pcm_indirect2_stat(substream, - &ml403_ac97cr->capture_ind2_rec); -#endif - /* disable capture irq */ - disable_irq_nosync(ml403_ac97cr->capture_irq); - ml403_ac97cr->enable_capture_irq = 0; - break; - default: - err = -EINVAL; - break; - } - PDEBUG(WORK_INFO, "trigger(capture): (done)\n"); - return err; -} - -static int -snd_ml403_ac97cr_pcm_playback_prepare(struct snd_pcm_substream *substream) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - struct snd_pcm_runtime *runtime; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - runtime = substream->runtime; - - PDEBUG(WORK_INFO, - "prepare(): period_bytes=%d, minperiod_bytes=%d\n", - snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2); - - /* set sampling rate */ - snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_FRONT_DAC_RATE, - runtime->rate); - PDEBUG(WORK_INFO, "prepare(): rate=%d\n", runtime->rate); - - /* init struct for intermediate buffer */ - memset(&ml403_ac97cr->ind_rec, 0, - sizeof(struct snd_pcm_indirect2)); - ml403_ac97cr->ind_rec.hw_buffer_size = CR_FIFO_SIZE; - ml403_ac97cr->ind_rec.sw_buffer_size = - snd_pcm_lib_buffer_bytes(substream); - ml403_ac97cr->ind_rec.min_periods = -1; - ml403_ac97cr->ind_rec.min_multiple = - snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2); - PDEBUG(WORK_INFO, "prepare(): hw_buffer_size=%d, " - "sw_buffer_size=%d, min_multiple=%d\n", - CR_FIFO_SIZE, ml403_ac97cr->ind_rec.sw_buffer_size, - ml403_ac97cr->ind_rec.min_multiple); - return 0; -} - -static int -snd_ml403_ac97cr_pcm_capture_prepare(struct snd_pcm_substream *substream) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - struct snd_pcm_runtime *runtime; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - runtime = substream->runtime; - - PDEBUG(WORK_INFO, - "prepare(capture): period_bytes=%d, minperiod_bytes=%d\n", - snd_pcm_lib_period_bytes(substream), CR_FIFO_SIZE / 2); - - /* set sampling rate */ - snd_ac97_set_rate(ml403_ac97cr->ac97, AC97_PCM_LR_ADC_RATE, - runtime->rate); - PDEBUG(WORK_INFO, "prepare(capture): rate=%d\n", runtime->rate); - - /* init struct for intermediate buffer */ - memset(&ml403_ac97cr->capture_ind2_rec, 0, - sizeof(struct snd_pcm_indirect2)); - ml403_ac97cr->capture_ind2_rec.hw_buffer_size = CR_FIFO_SIZE; - ml403_ac97cr->capture_ind2_rec.sw_buffer_size = - snd_pcm_lib_buffer_bytes(substream); - ml403_ac97cr->capture_ind2_rec.min_multiple = - snd_pcm_lib_period_bytes(substream) / (CR_FIFO_SIZE / 2); - PDEBUG(WORK_INFO, "prepare(capture): hw_buffer_size=%d, " - "sw_buffer_size=%d, min_multiple=%d\n", CR_FIFO_SIZE, - ml403_ac97cr->capture_ind2_rec.sw_buffer_size, - ml403_ac97cr->capture_ind2_rec.min_multiple); - return 0; -} - -static int snd_ml403_ac97cr_playback_open(struct snd_pcm_substream *substream) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - struct snd_pcm_runtime *runtime; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - runtime = substream->runtime; - - PDEBUG(WORK_INFO, "open(playback)\n"); - ml403_ac97cr->playback_substream = substream; - runtime->hw = snd_ml403_ac97cr_playback; - - snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - CR_FIFO_SIZE / 2); - return 0; -} - -static int snd_ml403_ac97cr_capture_open(struct snd_pcm_substream *substream) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - struct snd_pcm_runtime *runtime; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - runtime = substream->runtime; - - PDEBUG(WORK_INFO, "open(capture)\n"); - ml403_ac97cr->capture_substream = substream; - runtime->hw = snd_ml403_ac97cr_capture; - - snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, - CR_FIFO_SIZE / 2); - return 0; -} - -static int snd_ml403_ac97cr_playback_close(struct snd_pcm_substream *substream) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - - PDEBUG(WORK_INFO, "close(playback)\n"); - ml403_ac97cr->playback_substream = NULL; - return 0; -} - -static int snd_ml403_ac97cr_capture_close(struct snd_pcm_substream *substream) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - - ml403_ac97cr = snd_pcm_substream_chip(substream); - - PDEBUG(WORK_INFO, "close(capture)\n"); - ml403_ac97cr->capture_substream = NULL; - return 0; -} - -static const struct snd_pcm_ops snd_ml403_ac97cr_playback_ops = { - .open = snd_ml403_ac97cr_playback_open, - .close = snd_ml403_ac97cr_playback_close, - .prepare = snd_ml403_ac97cr_pcm_playback_prepare, - .trigger = snd_ml403_ac97cr_pcm_playback_trigger, - .pointer = snd_ml403_ac97cr_pcm_pointer, -}; - -static const struct snd_pcm_ops snd_ml403_ac97cr_capture_ops = { - .open = snd_ml403_ac97cr_capture_open, - .close = snd_ml403_ac97cr_capture_close, - .prepare = snd_ml403_ac97cr_pcm_capture_prepare, - .trigger = snd_ml403_ac97cr_pcm_capture_trigger, - .pointer = snd_ml403_ac97cr_pcm_pointer, -}; - -static irqreturn_t snd_ml403_ac97cr_irq(int irq, void *dev_id) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - struct platform_device *pfdev; - int cmp_irq; - - ml403_ac97cr = (struct snd_ml403_ac97cr *)dev_id; - if (ml403_ac97cr == NULL) - return IRQ_NONE; - - pfdev = ml403_ac97cr->pfdev; - - /* playback interrupt */ - cmp_irq = platform_get_irq(pfdev, 0); - if (irq == cmp_irq) { - if (ml403_ac97cr->enable_irq) - snd_pcm_indirect2_playback_interrupt( - ml403_ac97cr->playback_substream, - &ml403_ac97cr->ind_rec, - snd_ml403_ac97cr_playback_ind2_copy, - snd_ml403_ac97cr_playback_ind2_zero); - else - goto __disable_irq; - } else { - /* record interrupt */ - cmp_irq = platform_get_irq(pfdev, 1); - if (irq == cmp_irq) { - if (ml403_ac97cr->enable_capture_irq) - snd_pcm_indirect2_capture_interrupt( - ml403_ac97cr->capture_substream, - &ml403_ac97cr->capture_ind2_rec, - snd_ml403_ac97cr_capture_ind2_copy, - snd_ml403_ac97cr_capture_ind2_null); - else - goto __disable_irq; - } else - return IRQ_NONE; - } - return IRQ_HANDLED; - -__disable_irq: - PDEBUG(INIT_INFO, "irq(): irq %d is meant to be disabled! So, now try " - "to disable it _really_!\n", irq); - disable_irq_nosync(irq); - return IRQ_HANDLED; -} - -static unsigned short -snd_ml403_ac97cr_codec_read(struct snd_ac97 *ac97, unsigned short reg) -{ - struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data; -#ifdef CODEC_STAT - u32 stat; - u32 rafaccess = 0; -#endif - unsigned long end_time; - u16 value = 0; - - if (!LM4550_RF_OK(reg)) { - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "access to unknown/unused codec register 0x%x " - "ignored!\n", reg); - return 0; - } - /* check if we can fake/answer this access from our shadow register */ - if ((lm4550_regfile[reg / 2].flag & - (LM4550_REG_DONEREAD | LM4550_REG_ALLFAKE)) && - !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) { - if (lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEREAD) { - PDEBUG(CODEC_FAKE, "codec_read(): faking read from " - "reg=0x%x, val=0x%x / %d\n", - reg, lm4550_regfile[reg / 2].def, - lm4550_regfile[reg / 2].def); - return lm4550_regfile[reg / 2].def; - } else if ((lm4550_regfile[reg / 2].flag & - LM4550_REG_FAKEPROBE) && - ml403_ac97cr->ac97_fake) { - PDEBUG(CODEC_FAKE, "codec_read(): faking read from " - "reg=0x%x, val=0x%x / %d (probe)\n", - reg, lm4550_regfile[reg / 2].value, - lm4550_regfile[reg / 2].value); - return lm4550_regfile[reg / 2].value; - } else { -#ifdef CODEC_STAT - PDEBUG(CODEC_FAKE, "codec_read(): read access " - "answered by shadow register 0x%x (value=0x%x " - "/ %d) (cw=%d cr=%d)\n", - reg, lm4550_regfile[reg / 2].value, - lm4550_regfile[reg / 2].value, - ml403_ac97cr->ac97_write, - ml403_ac97cr->ac97_read); -#else - PDEBUG(CODEC_FAKE, "codec_read(): read access " - "answered by shadow register 0x%x (value=0x%x " - "/ %d)\n", - reg, lm4550_regfile[reg / 2].value, - lm4550_regfile[reg / 2].value); -#endif - return lm4550_regfile[reg / 2].value; - } - } - /* if we are here, we _have_ to access the codec really, no faking */ - if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0) - return 0; -#ifdef CODEC_STAT - ml403_ac97cr->ac97_read++; -#endif - spin_lock(&ml403_ac97cr->reg_lock); - out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR), - CR_CODEC_ADDR(reg) | CR_CODEC_READ); - spin_unlock(&ml403_ac97cr->reg_lock); - end_time = jiffies + (HZ / CODEC_TIMEOUT_AFTER_READ); - do { - spin_lock(&ml403_ac97cr->reg_lock); -#ifdef CODEC_STAT - rafaccess++; - stat = in_be32(CR_REG(ml403_ac97cr, STATUS)); - if ((stat & CR_RAF) == CR_RAF) { - value = CR_CODEC_DATAREAD( - in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD))); - PDEBUG(CODEC_SUCCESS, "codec_read(): (done) reg=0x%x, " - "value=0x%x / %d (STATUS=0x%x)\n", - reg, value, value, stat); -#else - if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) & - CR_RAF) == CR_RAF) { - value = CR_CODEC_DATAREAD( - in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD))); - PDEBUG(CODEC_SUCCESS, "codec_read(): (done) " - "reg=0x%x, value=0x%x / %d\n", - reg, value, value); -#endif - lm4550_regfile[reg / 2].value = value; - lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD; - spin_unlock(&ml403_ac97cr->reg_lock); - mutex_unlock(&ml403_ac97cr->cdc_mutex); - return value; - } - spin_unlock(&ml403_ac97cr->reg_lock); - schedule_timeout_uninterruptible(1); - } while (time_after(end_time, jiffies)); - /* read the DATAREAD register anyway, see comment below */ - spin_lock(&ml403_ac97cr->reg_lock); - value = - CR_CODEC_DATAREAD(in_be32(CR_REG(ml403_ac97cr, CODEC_DATAREAD))); - spin_unlock(&ml403_ac97cr->reg_lock); -#ifdef CODEC_STAT - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "timeout while codec read! " - "(reg=0x%x, last STATUS=0x%x, DATAREAD=0x%x / %d, %d) " - "(cw=%d, cr=%d)\n", - reg, stat, value, value, rafaccess, - ml403_ac97cr->ac97_write, ml403_ac97cr->ac97_read); -#else - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "timeout while codec read! " - "(reg=0x%x, DATAREAD=0x%x / %d)\n", - reg, value, value); -#endif - /* BUG: This is PURE speculation! But after _most_ read timeouts the - * value in the register is ok! - */ - lm4550_regfile[reg / 2].value = value; - lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD; - mutex_unlock(&ml403_ac97cr->cdc_mutex); - return value; -} - -static void -snd_ml403_ac97cr_codec_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) -{ - struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data; - -#ifdef CODEC_STAT - u32 stat; - u32 rafaccess = 0; -#endif -#ifdef CODEC_WRITE_CHECK_RAF - unsigned long end_time; -#endif - - if (!LM4550_RF_OK(reg)) { - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "access to unknown/unused codec register 0x%x " - "ignored!\n", reg); - return; - } - if (lm4550_regfile[reg / 2].flag & LM4550_REG_READONLY) { - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "write access to read only codec register 0x%x " - "ignored!\n", reg); - return; - } - if ((val & lm4550_regfile[reg / 2].wmask) != val) { - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "write access to codec register 0x%x " - "with bad value 0x%x / %d!\n", - reg, val, val); - val = val & lm4550_regfile[reg / 2].wmask; - } - if (((lm4550_regfile[reg / 2].flag & LM4550_REG_FAKEPROBE) && - ml403_ac97cr->ac97_fake) && - !(lm4550_regfile[reg / 2].flag & LM4550_REG_NOSHADOW)) { - PDEBUG(CODEC_FAKE, "codec_write(): faking write to reg=0x%x, " - "val=0x%x / %d\n", reg, val, val); - lm4550_regfile[reg / 2].value = (val & - lm4550_regfile[reg / 2].wmask); - return; - } - if (mutex_lock_interruptible(&ml403_ac97cr->cdc_mutex) != 0) - return; -#ifdef CODEC_STAT - ml403_ac97cr->ac97_write++; -#endif - spin_lock(&ml403_ac97cr->reg_lock); - out_be32(CR_REG(ml403_ac97cr, CODEC_DATAWRITE), - CR_CODEC_DATAWRITE(val)); - out_be32(CR_REG(ml403_ac97cr, CODEC_ADDR), - CR_CODEC_ADDR(reg) | CR_CODEC_WRITE); - spin_unlock(&ml403_ac97cr->reg_lock); -#ifdef CODEC_WRITE_CHECK_RAF - /* check CR_CODEC_RAF bit to see if write access to register is done; - * loop until bit is set or timeout happens - */ - end_time = jiffies + HZ / CODEC_TIMEOUT_AFTER_WRITE; - do { - spin_lock(&ml403_ac97cr->reg_lock); -#ifdef CODEC_STAT - rafaccess++; - stat = in_be32(CR_REG(ml403_ac97cr, STATUS)) - if ((stat & CR_RAF) == CR_RAF) { -#else - if ((in_be32(CR_REG(ml403_ac97cr, STATUS)) & - CR_RAF) == CR_RAF) { -#endif - PDEBUG(CODEC_SUCCESS, "codec_write(): (done) " - "reg=0x%x, value=%d / 0x%x\n", - reg, val, val); - if (!(lm4550_regfile[reg / 2].flag & - LM4550_REG_NOSHADOW) && - !(lm4550_regfile[reg / 2].flag & - LM4550_REG_NOSAVE)) - lm4550_regfile[reg / 2].value = val; - lm4550_regfile[reg / 2].flag |= LM4550_REG_DONEREAD; - spin_unlock(&ml403_ac97cr->reg_lock); - mutex_unlock(&ml403_ac97cr->cdc_mutex); - return; - } - spin_unlock(&ml403_ac97cr->reg_lock); - schedule_timeout_uninterruptible(1); - } while (time_after(end_time, jiffies)); -#ifdef CODEC_STAT - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "timeout while codec write " - "(reg=0x%x, val=0x%x / %d, last STATUS=0x%x, %d) " - "(cw=%d, cr=%d)\n", - reg, val, val, stat, rafaccess, ml403_ac97cr->ac97_write, - ml403_ac97cr->ac97_read); -#else - snd_printk(KERN_WARNING SND_ML403_AC97CR_DRIVER ": " - "timeout while codec write (reg=0x%x, val=0x%x / %d)\n", - reg, val, val); -#endif -#else /* CODEC_WRITE_CHECK_RAF */ -#if CODEC_WAIT_AFTER_WRITE > 0 - /* officially, in AC97 spec there is no possibility for a AC97 - * controller to determine, if write access is done or not - so: How - * is Xilinx able to provide a RAF bit for write access? - * => very strange, thus just don't check RAF bit (compare with - * Xilinx's example app in EDK 8.1i) and wait - */ - schedule_timeout_uninterruptible(HZ / CODEC_WAIT_AFTER_WRITE); -#endif - PDEBUG(CODEC_SUCCESS, "codec_write(): (done) " - "reg=0x%x, value=%d / 0x%x (no RAF check)\n", - reg, val, val); -#endif - mutex_unlock(&ml403_ac97cr->cdc_mutex); - return; -} - -static int -snd_ml403_ac97cr_chip_init(struct snd_ml403_ac97cr *ml403_ac97cr) -{ - unsigned long end_time; - PDEBUG(INIT_INFO, "chip_init():\n"); - end_time = jiffies + HZ / CODEC_TIMEOUT_ON_INIT; - do { - if (in_be32(CR_REG(ml403_ac97cr, STATUS)) & CR_CODECREADY) { - /* clear both hardware FIFOs */ - out_be32(CR_REG(ml403_ac97cr, RESETFIFO), - CR_RECRESET | CR_PLAYRESET); - PDEBUG(INIT_INFO, "chip_init(): (done)\n"); - return 0; - } - schedule_timeout_uninterruptible(1); - } while (time_after(end_time, jiffies)); - snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " - "timeout while waiting for codec, " - "not ready!\n"); - return -EBUSY; -} - -static int snd_ml403_ac97cr_free(struct snd_ml403_ac97cr *ml403_ac97cr) -{ - PDEBUG(INIT_INFO, "free():\n"); - /* irq release */ - if (ml403_ac97cr->irq >= 0) - free_irq(ml403_ac97cr->irq, ml403_ac97cr); - if (ml403_ac97cr->capture_irq >= 0) - free_irq(ml403_ac97cr->capture_irq, ml403_ac97cr); - /* give back "port" */ - iounmap(ml403_ac97cr->port); - kfree(ml403_ac97cr); - PDEBUG(INIT_INFO, "free(): (done)\n"); - return 0; -} - -static int snd_ml403_ac97cr_dev_free(struct snd_device *snddev) -{ - struct snd_ml403_ac97cr *ml403_ac97cr = snddev->device_data; - PDEBUG(INIT_INFO, "dev_free():\n"); - return snd_ml403_ac97cr_free(ml403_ac97cr); -} - -static int -snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev, - struct snd_ml403_ac97cr **rml403_ac97cr) -{ - struct snd_ml403_ac97cr *ml403_ac97cr; - int err; - static const struct snd_device_ops ops = { - .dev_free = snd_ml403_ac97cr_dev_free, - }; - struct resource *resource; - int irq; - - *rml403_ac97cr = NULL; - ml403_ac97cr = kzalloc(sizeof(*ml403_ac97cr), GFP_KERNEL); - if (ml403_ac97cr == NULL) - return -ENOMEM; - spin_lock_init(&ml403_ac97cr->reg_lock); - mutex_init(&ml403_ac97cr->cdc_mutex); - ml403_ac97cr->card = card; - ml403_ac97cr->pfdev = pfdev; - ml403_ac97cr->irq = -1; - ml403_ac97cr->enable_irq = 0; - ml403_ac97cr->capture_irq = -1; - ml403_ac97cr->enable_capture_irq = 0; - ml403_ac97cr->port = NULL; - ml403_ac97cr->res_port = NULL; - - PDEBUG(INIT_INFO, "Trying to reserve resources now ...\n"); - resource = platform_get_resource(pfdev, IORESOURCE_MEM, 0); - /* get "port" */ - ml403_ac97cr->port = ioremap(resource->start, - (resource->end) - - (resource->start) + 1); - if (ml403_ac97cr->port == NULL) { - snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " - "unable to remap memory region (%pR)\n", - resource); - snd_ml403_ac97cr_free(ml403_ac97cr); - return -EBUSY; - } - snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": " - "remap controller memory region to " - "0x%x done\n", (unsigned int)ml403_ac97cr->port); - /* get irq */ - irq = platform_get_irq(pfdev, 0); - if (request_irq(irq, snd_ml403_ac97cr_irq, 0, - dev_name(&pfdev->dev), (void *)ml403_ac97cr)) { - snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " - "unable to grab IRQ %d\n", - irq); - snd_ml403_ac97cr_free(ml403_ac97cr); - return -EBUSY; - } - ml403_ac97cr->irq = irq; - snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": " - "request (playback) irq %d done\n", - ml403_ac97cr->irq); - irq = platform_get_irq(pfdev, 1); - if (request_irq(irq, snd_ml403_ac97cr_irq, 0, - dev_name(&pfdev->dev), (void *)ml403_ac97cr)) { - snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": " - "unable to grab IRQ %d\n", - irq); - snd_ml403_ac97cr_free(ml403_ac97cr); - return -EBUSY; - } - ml403_ac97cr->capture_irq = irq; - snd_printk(KERN_INFO SND_ML403_AC97CR_DRIVER ": " - "request (capture) irq %d done\n", - ml403_ac97cr->capture_irq); - - err = snd_ml403_ac97cr_chip_init(ml403_ac97cr); - if (err < 0) { - snd_ml403_ac97cr_free(ml403_ac97cr); - return err; - } - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ml403_ac97cr, &ops); - if (err < 0) { - PDEBUG(INIT_FAILURE, "probe(): snd_device_new() failed!\n"); - snd_ml403_ac97cr_free(ml403_ac97cr); - return err; - } - - *rml403_ac97cr = ml403_ac97cr; - return 0; -} - -static void snd_ml403_ac97cr_mixer_free(struct snd_ac97 *ac97) -{ - struct snd_ml403_ac97cr *ml403_ac97cr = ac97->private_data; - PDEBUG(INIT_INFO, "mixer_free():\n"); - ml403_ac97cr->ac97 = NULL; - PDEBUG(INIT_INFO, "mixer_free(): (done)\n"); -} - -static int -snd_ml403_ac97cr_mixer(struct snd_ml403_ac97cr *ml403_ac97cr) -{ - struct snd_ac97_bus *bus; - struct snd_ac97_template ac97; - int err; - static const struct snd_ac97_bus_ops ops = { - .write = snd_ml403_ac97cr_codec_write, - .read = snd_ml403_ac97cr_codec_read, - }; - PDEBUG(INIT_INFO, "mixer():\n"); - err = snd_ac97_bus(ml403_ac97cr->card, 0, &ops, NULL, &bus); - if (err < 0) - return err; - - memset(&ac97, 0, sizeof(ac97)); - ml403_ac97cr->ac97_fake = 1; - lm4550_regfile_init(); -#ifdef CODEC_STAT - ml403_ac97cr->ac97_read = 0; - ml403_ac97cr->ac97_write = 0; -#endif - ac97.private_data = ml403_ac97cr; - ac97.private_free = snd_ml403_ac97cr_mixer_free; - ac97.scaps = AC97_SCAP_AUDIO | AC97_SCAP_SKIP_MODEM | - AC97_SCAP_NO_SPDIF; - err = snd_ac97_mixer(bus, &ac97, &ml403_ac97cr->ac97); - ml403_ac97cr->ac97_fake = 0; - lm4550_regfile_write_values_after_init(ml403_ac97cr->ac97); - PDEBUG(INIT_INFO, "mixer(): (done) snd_ac97_mixer()=%d\n", err); - return err; -} - -static int -snd_ml403_ac97cr_pcm(struct snd_ml403_ac97cr *ml403_ac97cr, int device) -{ - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(ml403_ac97cr->card, "ML403AC97CR/1", device, 1, 1, - &pcm); - if (err < 0) - return err; - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_ml403_ac97cr_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_ml403_ac97cr_capture_ops); - pcm->private_data = ml403_ac97cr; - pcm->info_flags = 0; - strcpy(pcm->name, "ML403AC97CR DAC/ADC"); - ml403_ac97cr->pcm = pcm; - - snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - NULL, - 64 * 1024, - 128 * 1024); - return 0; -} - -static int snd_ml403_ac97cr_probe(struct platform_device *pfdev) -{ - struct snd_card *card; - struct snd_ml403_ac97cr *ml403_ac97cr = NULL; - int err; - int dev = pfdev->id; - - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!enable[dev]) - return -ENOENT; - - err = snd_card_new(&pfdev->dev, index[dev], id[dev], THIS_MODULE, - 0, &card); - if (err < 0) - return err; - err = snd_ml403_ac97cr_create(card, pfdev, &ml403_ac97cr); - if (err < 0) { - PDEBUG(INIT_FAILURE, "probe(): create failed!\n"); - snd_card_free(card); - return err; - } - PDEBUG(INIT_INFO, "probe(): create done\n"); - card->private_data = ml403_ac97cr; - err = snd_ml403_ac97cr_mixer(ml403_ac97cr); - if (err < 0) { - snd_card_free(card); - return err; - } - PDEBUG(INIT_INFO, "probe(): mixer done\n"); - err = snd_ml403_ac97cr_pcm(ml403_ac97cr, 0); - if (err < 0) { - snd_card_free(card); - return err; - } - PDEBUG(INIT_INFO, "probe(): PCM done\n"); - strcpy(card->driver, SND_ML403_AC97CR_DRIVER); - strcpy(card->shortname, "ML403 AC97 Controller Reference"); - sprintf(card->longname, "%s %s at 0x%lx, irq %i & %i, device %i", - card->shortname, card->driver, - (unsigned long)ml403_ac97cr->port, ml403_ac97cr->irq, - ml403_ac97cr->capture_irq, dev + 1); - - err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } - platform_set_drvdata(pfdev, card); - PDEBUG(INIT_INFO, "probe(): (done)\n"); - return 0; -} - -static int snd_ml403_ac97cr_remove(struct platform_device *pfdev) -{ - snd_card_free(platform_get_drvdata(pfdev)); - return 0; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:" SND_ML403_AC97CR_DRIVER); - -static struct platform_driver snd_ml403_ac97cr_driver = { - .probe = snd_ml403_ac97cr_probe, - .remove = snd_ml403_ac97cr_remove, - .driver = { - .name = SND_ML403_AC97CR_DRIVER, - }, -}; - -module_platform_driver(snd_ml403_ac97cr_driver); diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c index e69a4ef0d6bd..08c10ac9d6c8 100644 --- a/sound/drivers/opl3/opl3_synth.c +++ b/sound/drivers/opl3/opl3_synth.c @@ -91,6 +91,8 @@ int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file, { struct snd_dm_fm_info info; + memset(&info, 0, sizeof(info)); + info.fm_mode = opl3->fm_mode; info.rhythm = opl3->rhythm; if (copy_to_user(argp, &info, sizeof(struct snd_dm_fm_info))) diff --git a/sound/drivers/pcm-indirect2.c b/sound/drivers/pcm-indirect2.c deleted file mode 100644 index 4c491d0ff071..000000000000 --- a/sound/drivers/pcm-indirect2.c +++ /dev/null @@ -1,560 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Helper functions for indirect PCM data transfer to a simple FIFO in - * hardware (small, no possibility to read "hardware io position", - * updating position done by interrupt, ...) - * - * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de> - * - * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by - * - * Copyright (c) by Takashi Iwai <tiwai@suse.de> - * Jaroslav Kysela <perex@suse.cz> - */ - -/* snd_printk/d() */ -#include <sound/core.h> -/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t - * snd_pcm_period_elapsed() */ -#include <sound/pcm.h> - -#include "pcm-indirect2.h" - -#ifdef SND_PCM_INDIRECT2_STAT -/* jiffies */ -#include <linux/jiffies.h> - -void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int i; - int j; - int k; - int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ; - - snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, " - "irq_occurred: %d\n", - rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured); - snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n", - rec->min_multiple); - snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, " - "firstzerotime: %lu\n", - rec->firstbytetime, rec->lastbytetime, rec->firstzerotime); - snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) " - "length: %d s\n", - rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate); - snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => " - "rate: %d Bytes/s = %d Frames/s|Hz\n", - seconds, rec->bytes2hw / seconds, - rec->bytes2hw / 2 / 2 / seconds); - snd_printk(KERN_DEBUG - "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n", - rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) / - runtime->rate, - rec->zeros2hw / (rec->hw_buffer_size / 2), - (rec->hw_buffer_size / 2)); - snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n", - rec->pointer_calls, rec->lastdifftime); - snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io, - rec->sw_data); - snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n"); - k = 0; - for (j = 0; j < 8; j++) { - for (i = j * 8; i < (j + 1) * 8; i++) - if (rec->byte_sizes[i] != 0) { - snd_printk(KERN_DEBUG "%u: %u", - i, rec->byte_sizes[i]); - k++; - } - if (((k % 8) == 0) && (k != 0)) { - snd_printk(KERN_DEBUG "\n"); - k = 0; - } - } - snd_printk(KERN_DEBUG "\n"); - snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n"); - for (j = 0; j < 8; j++) { - k = 0; - for (i = j * 8; i < (j + 1) * 8; i++) - if (rec->zero_sizes[i] != 0) - snd_printk(KERN_DEBUG "%u: %u", - i, rec->zero_sizes[i]); - else - k++; - if (!k) - snd_printk(KERN_DEBUG "\n"); - } - snd_printk(KERN_DEBUG "\n"); - snd_printk(KERN_DEBUG "STAT: min_adds[]:\n"); - for (j = 0; j < 8; j++) { - if (rec->min_adds[j] != 0) - snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]); - } - snd_printk(KERN_DEBUG "\n"); - snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n"); - for (j = 0; j < 8; j++) { - if (rec->mul_adds[j] != 0) - snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]); - } - snd_printk(KERN_DEBUG "\n"); - snd_printk(KERN_DEBUG - "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n", - rec->zero_times_saved, rec->zero_times_notsaved); - /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n"); - i = 0; - for (j = 0; j < 3750; j++) { - if (rec->zero_times[j] != 0) { - snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]); - i++; - } - if (((i % 8) == 0) && (i != 0)) - snd_printk(KERN_DEBUG "\n"); - } - snd_printk(KERN_DEBUG "\n"); */ - return; -} -#endif - -/* - * _internal_ helper function for playback/capture transfer function - */ -static void -snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - int isplay, int iscopy, - unsigned int bytes) -{ - if (rec->min_periods >= 0) { - if (iscopy) { - rec->sw_io += bytes; - if (rec->sw_io >= rec->sw_buffer_size) - rec->sw_io -= rec->sw_buffer_size; - } else if (isplay) { - /* If application does not write data in multiples of - * a period, move sw_data to the next correctly aligned - * position, so that sw_io can converge to it (in the - * next step). - */ - if (!rec->check_alignment) { - if (rec->bytes2hw % - snd_pcm_lib_period_bytes(substream)) { - unsigned bytes2hw_aligned = - (1 + - (rec->bytes2hw / - snd_pcm_lib_period_bytes - (substream))) * - snd_pcm_lib_period_bytes - (substream); - rec->sw_data = - bytes2hw_aligned % - rec->sw_buffer_size; -#ifdef SND_PCM_INDIRECT2_STAT - snd_printk(KERN_DEBUG - "STAT: @re-align: aligned " - "bytes2hw to next period " - "size boundary: %d " - "(instead of %d)\n", - bytes2hw_aligned, - rec->bytes2hw); - snd_printk(KERN_DEBUG - "STAT: @re-align: sw_data " - "moves to: %d\n", - rec->sw_data); -#endif - } - rec->check_alignment = 1; - } - /* We are at the end and are copying zeros into the - * fifo. - * Now, we have to make sure that sw_io is increased - * until the position of sw_data: Filling the fifo with - * the first zeros means, the last bytes were played. - */ - if (rec->sw_io != rec->sw_data) { - unsigned int diff; - if (rec->sw_data > rec->sw_io) - diff = rec->sw_data - rec->sw_io; - else - diff = (rec->sw_buffer_size - - rec->sw_io) + - rec->sw_data; - if (bytes >= diff) - rec->sw_io = rec->sw_data; - else { - rec->sw_io += bytes; - if (rec->sw_io >= rec->sw_buffer_size) - rec->sw_io -= - rec->sw_buffer_size; - } - } - } - rec->min_period_count += bytes; - if (rec->min_period_count >= (rec->hw_buffer_size / 2)) { - rec->min_periods += (rec->min_period_count / - (rec->hw_buffer_size / 2)); -#ifdef SND_PCM_INDIRECT2_STAT - if ((rec->min_period_count / - (rec->hw_buffer_size / 2)) > 7) - snd_printk(KERN_DEBUG - "STAT: more than 7 (%d) min_adds " - "at once - too big to save!\n", - (rec->min_period_count / - (rec->hw_buffer_size / 2))); - else - rec->min_adds[(rec->min_period_count / - (rec->hw_buffer_size / 2))]++; -#endif - rec->min_period_count = (rec->min_period_count % - (rec->hw_buffer_size / 2)); - } - } else if (isplay && iscopy) - rec->min_periods = 0; -} - -/* - * helper function for playback/capture pointer callback - */ -snd_pcm_uframes_t -snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec) -{ -#ifdef SND_PCM_INDIRECT2_STAT - rec->pointer_calls++; -#endif - return bytes_to_frames(substream->runtime, rec->sw_io); -} - -/* - * _internal_ helper function for playback interrupt callback - */ -static void -snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - snd_pcm_indirect2_copy_t copy, - snd_pcm_indirect2_zero_t zero) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; - - /* runtime->control->appl_ptr: position where ALSA will write next time - * rec->appl_ptr: position where ALSA was last time - * diff: obviously ALSA wrote that much bytes into the intermediate - * buffer since we checked last time - */ - snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; - - if (diff) { -#ifdef SND_PCM_INDIRECT2_STAT - rec->lastdifftime = jiffies; -#endif - if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) - diff += runtime->boundary; - /* number of bytes "added" by ALSA increases the number of - * bytes which are ready to "be transferred to HW"/"played" - * Then, set rec->appl_ptr to not count bytes twice next time. - */ - rec->sw_ready += (int)frames_to_bytes(runtime, diff); - rec->appl_ptr = appl_ptr; - } - if (rec->hw_ready && (rec->sw_ready <= 0)) { - unsigned int bytes; - -#ifdef SND_PCM_INDIRECT2_STAT - if (rec->firstzerotime == 0) { - rec->firstzerotime = jiffies; - snd_printk(KERN_DEBUG - "STAT: @firstzerotime: mul_elapsed: %d, " - "min_period_count: %d\n", - rec->mul_elapsed, rec->min_period_count); - snd_printk(KERN_DEBUG - "STAT: @firstzerotime: sw_io: %d, " - "sw_data: %d, appl_ptr: %u\n", - rec->sw_io, rec->sw_data, - (unsigned int)appl_ptr); - } - if ((jiffies - rec->firstzerotime) < 3750) { - rec->zero_times[(jiffies - rec->firstzerotime)]++; - rec->zero_times_saved++; - } else - rec->zero_times_notsaved++; -#endif - bytes = zero(substream, rec); - -#ifdef SND_PCM_INDIRECT2_STAT - rec->zeros2hw += bytes; - if (bytes < 64) - rec->zero_sizes[bytes]++; - else - snd_printk(KERN_DEBUG - "STAT: %d zero Bytes copied to hardware at " - "once - too big to save!\n", - bytes); -#endif - snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0, - bytes); - return; - } - while (rec->hw_ready && (rec->sw_ready > 0)) { - /* sw_to_end: max. number of bytes that can be read/take from - * the current position (sw_data) in _one_ step - */ - unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data; - - /* bytes: number of bytes we have available (for reading) */ - unsigned int bytes = rec->sw_ready; - - if (sw_to_end < bytes) - bytes = sw_to_end; - if (!bytes) - break; - -#ifdef SND_PCM_INDIRECT2_STAT - if (rec->firstbytetime == 0) - rec->firstbytetime = jiffies; - rec->lastbytetime = jiffies; -#endif - /* copy bytes from intermediate buffer position sw_data to the - * HW and return number of bytes actually written - * Furthermore, set hw_ready to 0, if the fifo isn't empty - * now => more could be transferred to fifo - */ - bytes = copy(substream, rec, bytes); - rec->bytes2hw += bytes; - -#ifdef SND_PCM_INDIRECT2_STAT - if (bytes < 64) - rec->byte_sizes[bytes]++; - else - snd_printk(KERN_DEBUG - "STAT: %d Bytes copied to hardware at once " - "- too big to save!\n", - bytes); -#endif - /* increase sw_data by the number of actually written bytes - * (= number of taken bytes from intermediate buffer) - */ - rec->sw_data += bytes; - if (rec->sw_data == rec->sw_buffer_size) - rec->sw_data = 0; - /* now sw_data is the position where ALSA is going to write - * in the intermediate buffer next time = position we are going - * to read from next time - */ - - snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1, - bytes); - - /* we read bytes from intermediate buffer, so we need to say - * that the number of bytes ready for transfer are decreased - * now - */ - rec->sw_ready -= bytes; - } - return; -} - -/* - * helper function for playback interrupt routine - */ -void -snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - snd_pcm_indirect2_copy_t copy, - snd_pcm_indirect2_zero_t zero) -{ -#ifdef SND_PCM_INDIRECT2_STAT - rec->irq_occured++; -#endif - /* hardware played some bytes, so there is room again (in fifo) */ - rec->hw_ready = 1; - - /* don't call ack() now, instead call transfer() function directly - * (normally called by ack() ) - */ - snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero); - - if (rec->min_periods >= rec->min_multiple) { -#ifdef SND_PCM_INDIRECT2_STAT - if ((rec->min_periods / rec->min_multiple) > 7) - snd_printk(KERN_DEBUG - "STAT: more than 7 (%d) mul_adds - too big " - "to save!\n", - (rec->min_periods / rec->min_multiple)); - else - rec->mul_adds[(rec->min_periods / - rec->min_multiple)]++; - rec->mul_elapsed_real += (rec->min_periods / - rec->min_multiple); - rec->mul_elapsed++; -#endif - rec->min_periods = (rec->min_periods % rec->min_multiple); - snd_pcm_period_elapsed(substream); - } -} - -/* - * _internal_ helper function for capture interrupt callback - */ -static void -snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - snd_pcm_indirect2_copy_t copy, - snd_pcm_indirect2_zero_t null) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; - snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; - - if (diff) { -#ifdef SND_PCM_INDIRECT2_STAT - rec->lastdifftime = jiffies; -#endif - if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) - diff += runtime->boundary; - rec->sw_ready -= frames_to_bytes(runtime, diff); - rec->appl_ptr = appl_ptr; - } - /* if hardware has something, but the intermediate buffer is full - * => skip contents of buffer - */ - if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) { - unsigned int bytes; - -#ifdef SND_PCM_INDIRECT2_STAT - if (rec->firstzerotime == 0) { - rec->firstzerotime = jiffies; - snd_printk(KERN_DEBUG "STAT: (capture) " - "@firstzerotime: mul_elapsed: %d, " - "min_period_count: %d\n", - rec->mul_elapsed, rec->min_period_count); - snd_printk(KERN_DEBUG "STAT: (capture) " - "@firstzerotime: sw_io: %d, sw_data: %d, " - "appl_ptr: %u\n", - rec->sw_io, rec->sw_data, - (unsigned int)appl_ptr); - } - if ((jiffies - rec->firstzerotime) < 3750) { - rec->zero_times[(jiffies - rec->firstzerotime)]++; - rec->zero_times_saved++; - } else - rec->zero_times_notsaved++; -#endif - bytes = null(substream, rec); - -#ifdef SND_PCM_INDIRECT2_STAT - rec->zeros2hw += bytes; - if (bytes < 64) - rec->zero_sizes[bytes]++; - else - snd_printk(KERN_DEBUG - "STAT: (capture) %d zero Bytes copied to " - "hardware at once - too big to save!\n", - bytes); -#endif - snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0, - bytes); - /* report an overrun */ - rec->sw_io = SNDRV_PCM_POS_XRUN; - return; - } - while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) { - /* sw_to_end: max. number of bytes that we can write to the - * intermediate buffer (until it's end) - */ - size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; - - /* bytes: max. number of bytes, which may be copied to the - * intermediate buffer without overflow (in _one_ step) - */ - size_t bytes = rec->sw_buffer_size - rec->sw_ready; - - /* limit number of bytes (for transfer) by available room in - * the intermediate buffer - */ - if (sw_to_end < bytes) - bytes = sw_to_end; - if (!bytes) - break; - -#ifdef SND_PCM_INDIRECT2_STAT - if (rec->firstbytetime == 0) - rec->firstbytetime = jiffies; - rec->lastbytetime = jiffies; -#endif - /* copy bytes from the intermediate buffer (position sw_data) - * to the HW at most and return number of bytes actually copied - * from HW - * Furthermore, set hw_ready to 0, if the fifo is empty now. - */ - bytes = copy(substream, rec, bytes); - rec->bytes2hw += bytes; - -#ifdef SND_PCM_INDIRECT2_STAT - if (bytes < 64) - rec->byte_sizes[bytes]++; - else - snd_printk(KERN_DEBUG - "STAT: (capture) %d Bytes copied to " - "hardware at once - too big to save!\n", - bytes); -#endif - /* increase sw_data by the number of actually copied bytes from - * HW - */ - rec->sw_data += bytes; - if (rec->sw_data == rec->sw_buffer_size) - rec->sw_data = 0; - - snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1, - bytes); - - /* number of bytes in the intermediate buffer, which haven't - * been fetched by ALSA yet. - */ - rec->sw_ready += bytes; - } - return; -} - -/* - * helper function for capture interrupt routine - */ -void -snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - snd_pcm_indirect2_copy_t copy, - snd_pcm_indirect2_zero_t null) -{ -#ifdef SND_PCM_INDIRECT2_STAT - rec->irq_occured++; -#endif - /* hardware recorded some bytes, so there is something to read from the - * record fifo: - */ - rec->hw_ready = 1; - - /* don't call ack() now, instead call transfer() function directly - * (normally called by ack() ) - */ - snd_pcm_indirect2_capture_transfer(substream, rec, copy, null); - - if (rec->min_periods >= rec->min_multiple) { - -#ifdef SND_PCM_INDIRECT2_STAT - if ((rec->min_periods / rec->min_multiple) > 7) - snd_printk(KERN_DEBUG - "STAT: more than 7 (%d) mul_adds - " - "too big to save!\n", - (rec->min_periods / rec->min_multiple)); - else - rec->mul_adds[(rec->min_periods / - rec->min_multiple)]++; - rec->mul_elapsed_real += (rec->min_periods / - rec->min_multiple); - rec->mul_elapsed++; -#endif - rec->min_periods = (rec->min_periods % rec->min_multiple); - snd_pcm_period_elapsed(substream); - } -} diff --git a/sound/drivers/pcm-indirect2.h b/sound/drivers/pcm-indirect2.h deleted file mode 100644 index 355ce76d2403..000000000000 --- a/sound/drivers/pcm-indirect2.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Helper functions for indirect PCM data transfer to a simple FIFO in - * hardware (small, no possibility to read "hardware io position", - * updating position done by interrupt, ...) - * - * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de> - * - * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by - * - * Copyright (c) by Takashi Iwai <tiwai@suse.de> - * Jaroslav Kysela <perex@suse.cz> - */ - -#ifndef __SOUND_PCM_INDIRECT2_H -#define __SOUND_PCM_INDIRECT2_H - -/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t */ -#include <sound/pcm.h> - -/* Debug options for code which may be removed completely in a final version */ -#ifdef CONFIG_SND_DEBUG -#define SND_PCM_INDIRECT2_STAT /* turn on some "statistics" about the - * process of copying bytes from the - * intermediate buffer to the hardware - * fifo and the other way round - */ -#endif - -struct snd_pcm_indirect2 { - unsigned int hw_buffer_size; /* Byte size of hardware buffer */ - int hw_ready; /* playback: 1 = hw fifo has room left, - * 0 = hw fifo is full - */ - unsigned int min_multiple; - int min_periods; /* counts number of min. periods until - * min_multiple is reached - */ - int min_period_count; /* counts bytes to count number of - * min. periods - */ - - unsigned int sw_buffer_size; /* Byte size of software buffer */ - - /* sw_data: position in intermediate buffer, where we will read (or - * write) from/to next time (to transfer data to/from HW) - */ - unsigned int sw_data; /* Offset to next dst (or src) in sw - * ring buffer - */ - /* easiest case (playback): - * sw_data is nearly the same as ~ runtime->control->appl_ptr, with the - * exception that sw_data is "behind" by the number if bytes ALSA wrote - * to the intermediate buffer last time. - * A call to ack() callback synchronizes both indirectly. - */ - - /* We have no real sw_io pointer here. Usually sw_io is pointing to the - * current playback/capture position _inside_ the hardware. Devices - * with plain FIFOs often have no possibility to publish this position. - * So we say: if sw_data is updated, that means bytes were copied to - * the hardware, we increase sw_io by that amount, because there have - * to be as much bytes which were played. So sw_io will stay behind - * sw_data all the time and has to converge to sw_data at the end of - * playback. - */ - unsigned int sw_io; /* Current software pointer in bytes */ - - /* sw_ready: number of bytes ALSA copied to the intermediate buffer, so - * it represents the number of bytes which wait for transfer to the HW - */ - int sw_ready; /* Bytes ready to be transferred to/from hw */ - - /* appl_ptr: last known position of ALSA (where ALSA is going to write - * next time into the intermediate buffer - */ - snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */ - - unsigned int bytes2hw; - int check_alignment; - -#ifdef SND_PCM_INDIRECT2_STAT - unsigned int zeros2hw; - unsigned int mul_elapsed; - unsigned int mul_elapsed_real; - unsigned long firstbytetime; - unsigned long lastbytetime; - unsigned long firstzerotime; - unsigned int byte_sizes[64]; - unsigned int zero_sizes[64]; - unsigned int min_adds[8]; - unsigned int mul_adds[8]; - unsigned int zero_times[3750]; /* = 15s */ - unsigned int zero_times_saved; - unsigned int zero_times_notsaved; - unsigned int irq_occured; - unsigned int pointer_calls; - unsigned int lastdifftime; -#endif -}; - -typedef size_t (*snd_pcm_indirect2_copy_t) (struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - size_t bytes); -typedef size_t (*snd_pcm_indirect2_zero_t) (struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec); - -#ifdef SND_PCM_INDIRECT2_STAT -void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec); -#endif - -snd_pcm_uframes_t -snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec); -void -snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - snd_pcm_indirect2_copy_t copy, - snd_pcm_indirect2_zero_t zero); -void -snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream, - struct snd_pcm_indirect2 *rec, - snd_pcm_indirect2_copy_t copy, - snd_pcm_indirect2_zero_t null); - -#endif /* __SOUND_PCM_INDIRECT2_H */ diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index ecefa7c83134..38603cb2bd5b 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -457,7 +457,7 @@ static int portman_probe(struct parport *p) /* Set for RXDATA0 where no damage will be done. */ /* 5 */ - parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */ + parport_write_control(p, RXDATA0 | STROBE); /* Write Strobe=1 to command reg. */ /* 6 */ if ((parport_read_status(p) & ESTB) != ESTB) diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 995c2cefc222..25778765cbfe 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -150,8 +150,12 @@ config SND_FIREWIRE_MOTU Say Y here to enable support for FireWire devices which MOTU produced: * 828mk2 * Traveler - * 828mk3 + * Ultralite + * 8pre + * 828mk3 (FireWire only) + * 828mk3 (Hybrid) * Audio Express + * 4pre To compile this driver as a module, choose M here: the module will be called snd-firewire-motu. @@ -164,6 +168,8 @@ config SND_FIREFACE Say Y here to include support for RME fireface series. * Fireface 400 * Fireface 800 + * Fireface UFX * Fireface UCX + * Fireface 802 endif # SND_FIREWIRE diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 67d735e9a6a4..fea92e148790 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -82,7 +82,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, if (err < 0) return err; - s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc; + if (s->direction == AMDTP_OUT_STREAM) + s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc; p->pcm_channels = pcm_channels; p->midi_ports = midi_ports; diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h index 16c7f6605511..26e7cb555d3c 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -66,8 +66,7 @@ TRACE_EVENT(amdtp_packet, __entry->irq, __entry->index, __print_array(__get_dynamic_array(cip_header), - __get_dynamic_array_len(cip_header), - sizeof(u8))) + __get_dynamic_array_len(cip_header), 1)) ); #endif diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 37d38efb4c87..f8586f75441d 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -20,6 +20,8 @@ #define CYCLES_PER_SECOND 8000 #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) +#define OHCI_MAX_SECOND 8 + /* Always support Linux tracing subsystem. */ #define CREATE_TRACE_POINTS #include "amdtp-stream-trace.h" @@ -337,25 +339,26 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_pcm_prepare); -static unsigned int calculate_data_blocks(struct amdtp_stream *s, - unsigned int syt) +static unsigned int calculate_data_blocks(unsigned int *data_block_state, + bool is_blocking, bool is_no_info, + unsigned int syt_interval, enum cip_sfc sfc) { - unsigned int phase, data_blocks; + unsigned int data_blocks; /* Blocking mode. */ - if (s->flags & CIP_BLOCKING) { + if (is_blocking) { /* This module generate empty packet for 'no data'. */ - if (syt == CIP_SYT_NO_INFO) + if (is_no_info) data_blocks = 0; else - data_blocks = s->syt_interval; + data_blocks = syt_interval; /* Non-blocking mode. */ } else { - if (!cip_sfc_is_base_44100(s->sfc)) { + if (!cip_sfc_is_base_44100(sfc)) { // Sample_rate / 8000 is an integer, and precomputed. - data_blocks = s->ctx_data.rx.data_block_state; + data_blocks = *data_block_state; } else { - phase = s->ctx_data.rx.data_block_state; + unsigned int phase = *data_block_state; /* * This calculates the number of data blocks per packet so that @@ -365,31 +368,30 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s, * as possible in the sequence (to prevent underruns of the * device's buffer). */ - if (s->sfc == CIP_SFC_44100) + if (sfc == CIP_SFC_44100) /* 6 6 5 6 5 6 5 ... */ data_blocks = 5 + ((phase & 1) ^ (phase == 0 || phase >= 40)); else /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ - data_blocks = 11 * (s->sfc >> 1) + (phase == 0); - if (++phase >= (80 >> (s->sfc >> 1))) + data_blocks = 11 * (sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (sfc >> 1))) phase = 0; - s->ctx_data.rx.data_block_state = phase; + *data_block_state = phase; } } return data_blocks; } -static unsigned int calculate_syt(struct amdtp_stream *s, - unsigned int cycle) +static unsigned int calculate_syt_offset(unsigned int *last_syt_offset, + unsigned int *syt_offset_state, enum cip_sfc sfc) { - unsigned int syt_offset, phase, index, syt; + unsigned int syt_offset; - if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) { - if (!cip_sfc_is_base_44100(s->sfc)) - syt_offset = s->ctx_data.rx.last_syt_offset + - s->ctx_data.rx.syt_offset_state; + if (*last_syt_offset < TICKS_PER_CYCLE) { + if (!cip_sfc_is_base_44100(sfc)) + syt_offset = *last_syt_offset + *syt_offset_state; else { /* * The time, in ticks, of the n'th SYT_INTERVAL sample is: @@ -401,28 +403,24 @@ static unsigned int calculate_syt(struct amdtp_stream *s, * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... * This code generates _exactly_ the same sequence. */ - phase = s->ctx_data.rx.syt_offset_state; - index = phase % 13; - syt_offset = s->ctx_data.rx.last_syt_offset; + unsigned int phase = *syt_offset_state; + unsigned int index = phase % 13; + + syt_offset = *last_syt_offset; syt_offset += 1386 + ((index && !(index & 3)) || phase == 146); if (++phase >= 147) phase = 0; - s->ctx_data.rx.syt_offset_state = phase; + *syt_offset_state = phase; } } else - syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE; - s->ctx_data.rx.last_syt_offset = syt_offset; + syt_offset = *last_syt_offset - TICKS_PER_CYCLE; + *last_syt_offset = syt_offset; - if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += s->ctx_data.rx.transfer_delay; - syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; - syt += syt_offset % TICKS_PER_CYCLE; + if (syt_offset >= TICKS_PER_CYCLE) + syt_offset = CIP_SYT_NO_INFO; - return syt & CIP_SYT_MASK; - } else { - return CIP_SYT_NO_INFO; - } + return syt_offset; } static void update_pcm_pointers(struct amdtp_stream *s, @@ -680,8 +678,8 @@ static inline u32 compute_cycle_count(__be32 ctx_header_tstamp) static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) { cycle += addend; - if (cycle >= 8 * CYCLES_PER_SECOND) - cycle -= 8 * CYCLES_PER_SECOND; + if (cycle >= OHCI_MAX_SECOND * CYCLES_PER_SECOND) + cycle -= OHCI_MAX_SECOND * CYCLES_PER_SECOND; return cycle; } @@ -738,21 +736,41 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, return 0; } -static void generate_ideal_pkt_descs(struct amdtp_stream *s, - struct pkt_desc *descs, - const __be32 *ctx_header, - unsigned int packets) +static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle, + unsigned int transfer_delay) +{ + unsigned int syt; + + syt_offset += transfer_delay; + syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) | + (syt_offset % TICKS_PER_CYCLE); + return syt & CIP_SYT_MASK; +} + +static void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs, + const __be32 *ctx_header, unsigned int packets, + const struct seq_desc *seq_descs, + unsigned int seq_size) { unsigned int dbc = s->data_block_counter; + unsigned int seq_index = s->ctx_data.rx.seq_index; int i; for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; unsigned int index = (s->packet_index + i) % s->queue_size; + const struct seq_desc *seq = seq_descs + seq_index; + unsigned int syt; desc->cycle = compute_it_cycle(*ctx_header, s->queue_size); - desc->syt = calculate_syt(s, desc->cycle); - desc->data_blocks = calculate_data_blocks(s, desc->syt); + + syt = seq->syt_offset; + if (syt != CIP_SYT_NO_INFO) { + syt = compute_syt(syt, desc->cycle, + s->ctx_data.rx.transfer_delay); + } + desc->syt = syt; + desc->data_blocks = seq->data_blocks; if (s->flags & CIP_DBC_IS_END_EVENT) dbc = (dbc + desc->data_blocks) & 0xff; @@ -764,10 +782,13 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s, desc->ctx_payload = s->buffer.packets[index].buffer; + seq_index = (seq_index + 1) % seq_size; + ++ctx_header; } s->data_block_counter = dbc; + s->ctx_data.rx.seq_index = seq_index; } static inline void cancel_stream(struct amdtp_stream *s) @@ -791,24 +812,16 @@ static void process_ctx_payloads(struct amdtp_stream *s, update_pcm_pointers(s, pcm, pcm_frames); } -static void amdtp_stream_master_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data); - -static void amdtp_stream_master_first_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data); - static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { struct amdtp_stream *s = private_data; + const struct amdtp_domain *d = s->domain; const __be32 *ctx_header = header; unsigned int events_per_period = s->ctx_data.rx.events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; unsigned int packets; - bool is_irq_target; int i; if (s->packet_index < 0) @@ -817,14 +830,11 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, // Calculate the number of packets in buffer and check XRUN. packets = header_length / sizeof(*ctx_header); - generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets); + generate_pkt_descs(s, s->pkt_descs, ctx_header, packets, d->seq_descs, + d->seq_size); process_ctx_payloads(s, s->pkt_descs, packets); - is_irq_target = - !!(context->callback.sc == amdtp_stream_master_callback || - context->callback.sc == amdtp_stream_master_first_callback); - for (i = 0; i < packets; ++i) { const struct pkt_desc *desc = s->pkt_descs + i; unsigned int syt; @@ -843,7 +853,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, desc->data_blocks, desc->data_block_counter, syt, i); - if (is_irq_target) { + if (s == s->domain->irq_target) { event_count += desc->data_blocks; if (event_count >= events_per_period) { event_count -= events_per_period; @@ -896,14 +906,63 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, } } -static void amdtp_stream_master_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data) +static void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets) { - struct amdtp_domain *d = private_data; struct amdtp_stream *irq_target = d->irq_target; + unsigned int seq_tail = d->seq_tail; + unsigned int seq_size = d->seq_size; + unsigned int min_avail; struct amdtp_stream *s; + min_avail = d->seq_size; + list_for_each_entry(s, &d->streams, list) { + unsigned int seq_index; + unsigned int avail; + + if (s->direction == AMDTP_IN_STREAM) + continue; + + seq_index = s->ctx_data.rx.seq_index; + avail = d->seq_tail; + if (seq_index > avail) + avail += d->seq_size; + avail -= seq_index; + + if (avail < min_avail) + min_avail = avail; + } + + while (min_avail < packets) { + struct seq_desc *desc = d->seq_descs + seq_tail; + + desc->syt_offset = calculate_syt_offset(&d->last_syt_offset, + &d->syt_offset_state, irq_target->sfc); + desc->data_blocks = calculate_data_blocks(&d->data_block_state, + !!(irq_target->flags & CIP_BLOCKING), + desc->syt_offset == CIP_SYT_NO_INFO, + irq_target->syt_interval, irq_target->sfc); + + ++seq_tail; + seq_tail %= seq_size; + + ++min_avail; + } + + d->seq_tail = seq_tail; +} + +static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *irq_target = private_data; + struct amdtp_domain *d = irq_target->domain; + unsigned int packets = header_length / sizeof(__be32); + struct amdtp_stream *s; + + // Record enough entries with extra 3 cycles at least. + pool_ideal_seq_descs(d, packets + 3); + out_stream_callback(context, tstamp, header_length, header, irq_target); if (amdtp_streaming_error(irq_target)) goto error; @@ -950,7 +1009,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, } else { cycle = compute_it_cycle(*ctx_header, s->queue_size); - context->callback.sc = out_stream_callback; + if (s == s->domain->irq_target) + context->callback.sc = irq_target_callback; + else + context->callback.sc = out_stream_callback; } s->start_cycle = cycle; @@ -958,64 +1020,29 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, context->callback.sc(context, tstamp, header_length, header, s); } -static void amdtp_stream_master_first_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data) -{ - struct amdtp_domain *d = private_data; - struct amdtp_stream *s = d->irq_target; - const __be32 *ctx_header = header; - - s->callbacked = true; - wake_up(&s->callback_wait); - - s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size); - - context->callback.sc = amdtp_stream_master_callback; - - context->callback.sc(context, tstamp, header_length, header, d); -} - /** * amdtp_stream_start - start transferring packets * @s: the AMDTP stream to start * @channel: the isochronous channel on the bus * @speed: firewire speed code - * @d: the AMDTP domain to which the AMDTP stream belongs - * @is_irq_target: whether isoc context for the AMDTP stream is used to generate - * hardware IRQ. * @start_cycle: the isochronous cycle to start the context. Start immediately * if negative value is given. + * @queue_size: The number of packets in the queue. + * @idle_irq_interval: the interval to queue packet during initial state. * * The stream cannot be started until it has been configured with * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI * device can be started. */ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, - struct amdtp_domain *d, bool is_irq_target, - int start_cycle) + int start_cycle, unsigned int queue_size, + unsigned int idle_irq_interval) { - static const struct { - unsigned int data_block; - unsigned int syt_offset; - } *entry, initial_state[] = { - [CIP_SFC_32000] = { 4, 3072 }, - [CIP_SFC_48000] = { 6, 1024 }, - [CIP_SFC_96000] = { 12, 1024 }, - [CIP_SFC_192000] = { 24, 1024 }, - [CIP_SFC_44100] = { 0, 67 }, - [CIP_SFC_88200] = { 0, 67 }, - [CIP_SFC_176400] = { 0, 67 }, - }; - unsigned int events_per_buffer = d->events_per_buffer; - unsigned int events_per_period = d->events_per_period; - unsigned int idle_irq_interval; + bool is_irq_target = (s == s->domain->irq_target); unsigned int ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; int type, tag, err; - fw_iso_callback_t ctx_cb; - void *ctx_data; mutex_lock(&s->mutex); @@ -1034,12 +1061,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, s->data_block_counter = UINT_MAX; } else { - entry = &initial_state[s->sfc]; - s->data_block_counter = 0; - s->ctx_data.rx.data_block_state = entry->data_block; - s->ctx_data.rx.syt_offset_state = entry->syt_offset; - s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE; } /* initialize packet buffer */ @@ -1063,37 +1085,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; } - // This is a case that AMDTP streams in domain run just for MIDI - // substream. Use the number of events equivalent to 10 msec as - // interval of hardware IRQ. - if (events_per_period == 0) - events_per_period = amdtp_rate_table[s->sfc] / 100; - if (events_per_buffer == 0) - events_per_buffer = events_per_period * 3; - - idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, - amdtp_rate_table[s->sfc]); - s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, - amdtp_rate_table[s->sfc]); - - err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size, + err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, max_ctx_payload_size, dir); if (err < 0) goto err_unlock; - - if (is_irq_target) { - s->ctx_data.rx.events_per_period = events_per_period; - s->ctx_data.rx.event_count = 0; - ctx_cb = amdtp_stream_master_first_callback; - ctx_data = d; - } else { - ctx_cb = amdtp_stream_first_callback; - ctx_data = s; - } + s->queue_size = queue_size; s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, ctx_header_size, - ctx_cb, ctx_data); + amdtp_stream_first_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -1302,6 +1302,8 @@ int amdtp_domain_init(struct amdtp_domain *d) d->events_per_period = 0; + d->seq_descs = NULL; + return 0; } EXPORT_SYMBOL_GPL(amdtp_domain_init); @@ -1338,6 +1340,7 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, s->channel = channel; s->speed = speed; + s->domain = d; return 0; } @@ -1374,6 +1377,22 @@ static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle) */ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) { + static const struct { + unsigned int data_block; + unsigned int syt_offset; + } *entry, initial_state[] = { + [CIP_SFC_32000] = { 4, 3072 }, + [CIP_SFC_48000] = { 6, 1024 }, + [CIP_SFC_96000] = { 12, 1024 }, + [CIP_SFC_192000] = { 24, 1024 }, + [CIP_SFC_44100] = { 0, 67 }, + [CIP_SFC_88200] = { 0, 67 }, + [CIP_SFC_176400] = { 0, 67 }, + }; + unsigned int events_per_buffer = d->events_per_buffer; + unsigned int events_per_period = d->events_per_period; + unsigned int idle_irq_interval; + unsigned int queue_size; struct amdtp_stream *s; int cycle; int err; @@ -1387,12 +1406,34 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) return -ENXIO; d->irq_target = s; + // This is a case that AMDTP streams in domain run just for MIDI + // substream. Use the number of events equivalent to 10 msec as + // interval of hardware IRQ. + if (events_per_period == 0) + events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100; + if (events_per_buffer == 0) + events_per_buffer = events_per_period * 3; + + queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, + amdtp_rate_table[d->irq_target->sfc]); + + d->seq_descs = kcalloc(queue_size, sizeof(*d->seq_descs), GFP_KERNEL); + if (!d->seq_descs) + return -ENOMEM; + d->seq_size = queue_size; + d->seq_tail = 0; + + entry = &initial_state[s->sfc]; + d->data_block_state = entry->data_block; + d->syt_offset_state = entry->syt_offset; + d->last_syt_offset = TICKS_PER_CYCLE; + if (ir_delay_cycle > 0) { struct fw_card *fw_card = fw_parent_device(s->unit)->card; err = get_current_cycle_time(fw_card, &cycle); if (err < 0) - return err; + goto error; // No need to care overflow in cycle field because of enough // width. @@ -1423,18 +1464,26 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) } else { // IT context starts immediately. cycle_match = -1; + s->ctx_data.rx.seq_index = 0; } if (s != d->irq_target) { - err = amdtp_stream_start(s, s->channel, s->speed, d, - false, cycle_match); + err = amdtp_stream_start(s, s->channel, s->speed, + cycle_match, queue_size, 0); if (err < 0) goto error; } } s = d->irq_target; - err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1); + s->ctx_data.rx.events_per_period = events_per_period; + s->ctx_data.rx.event_count = 0; + s->ctx_data.rx.seq_index = 0; + + idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, + amdtp_rate_table[d->irq_target->sfc]); + err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size, + idle_irq_interval); if (err < 0) goto error; @@ -1442,6 +1491,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) error: list_for_each_entry(s, &d->streams, list) amdtp_stream_stop(s); + kfree(d->seq_descs); + d->seq_descs = NULL; return err; } EXPORT_SYMBOL_GPL(amdtp_domain_start); @@ -1466,5 +1517,8 @@ void amdtp_domain_stop(struct amdtp_domain *d) d->events_per_period = 0; d->irq_target = NULL; + + kfree(d->seq_descs); + d->seq_descs = NULL; } EXPORT_SYMBOL_GPL(amdtp_domain_stop); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index f2d44e2dc3c8..703b710aaf7f 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -108,6 +108,8 @@ typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)( const struct pkt_desc *desc, unsigned int packets, struct snd_pcm_substream *pcm); + +struct amdtp_domain; struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; @@ -136,9 +138,7 @@ struct amdtp_stream { struct { // To calculate CIP data blocks and tstamp. unsigned int transfer_delay; - unsigned int data_block_state; - unsigned int last_syt_offset; - unsigned int syt_offset_state; + unsigned int seq_index; // To generate CIP header. unsigned int fdf; @@ -180,6 +180,7 @@ struct amdtp_stream { int channel; int speed; struct list_head list; + struct amdtp_domain *domain; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -273,6 +274,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, msecs_to_jiffies(timeout)) > 0; } +struct seq_desc { + unsigned int syt_offset; + unsigned int data_blocks; +}; + struct amdtp_domain { struct list_head streams; @@ -280,6 +286,14 @@ struct amdtp_domain { unsigned int events_per_buffer; struct amdtp_stream *irq_target; + + struct seq_desc *seq_descs; + unsigned int seq_size; + unsigned int seq_tail; + + unsigned int data_block_state; + unsigned int syt_offset_state; + unsigned int last_syt_offset; }; int amdtp_domain_init(struct amdtp_domain *d); diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index 0e4c3a9ed5e4..8d3b23778eb2 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -16,7 +16,8 @@ #define LATTER_SYNC_STATUS 0x0000801c0000ULL static int parse_clock_bits(u32 data, unsigned int *rate, - enum snd_ff_clock_src *src) + enum snd_ff_clock_src *src, + enum snd_ff_unit_version unit_version) { static const struct { unsigned int rate; @@ -43,6 +44,11 @@ static int parse_clock_bits(u32 data, unsigned int *rate, }; int i; + if (unit_version != SND_FF_UNIT_VERSION_UCX) { + // e.g. 0x00fe0f20 but expected 0x00eff002. + data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4); + } + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { rate_entry = rate_entries + i; if ((data & 0x0f000000) == rate_entry->flag) { @@ -79,7 +85,7 @@ static int latter_get_clock(struct snd_ff *ff, unsigned int *rate, return err; data = le32_to_cpu(reg); - return parse_clock_bits(data, rate, src); + return parse_clock_bits(data, rate, src, ff->unit_version); } static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable) @@ -107,18 +113,18 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate) int err; // Set the number of data blocks transferred in a second. - if (rate % 32000 == 0) - code = 0x00; + if (rate % 48000 == 0) + code = 0x04; else if (rate % 44100 == 0) code = 0x02; - else if (rate % 48000 == 0) - code = 0x04; + else if (rate % 32000 == 0) + code = 0x00; else return -EINVAL; if (rate >= 64000 && rate < 128000) code |= 0x08; - else if (rate >= 128000 && rate < 192000) + else if (rate >= 128000) code |= 0x10; reg = cpu_to_le32(code); @@ -140,7 +146,7 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate) if (curr_rate == rate) break; } - if (count == 10) + if (count > 10) return -ETIMEDOUT; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) { @@ -181,14 +187,30 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate) __le32 reg; int err; - if (rate >= 32000 && rate <= 48000) - flag = 0x92; - else if (rate >= 64000 && rate <= 96000) - flag = 0x8e; - else if (rate >= 128000 && rate <= 192000) - flag = 0x8c; - else - return -EINVAL; + if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) { + // For Fireface UCX. Always use the maximum number of data + // channels in data block of packet. + if (rate >= 32000 && rate <= 48000) + flag = 0x92; + else if (rate >= 64000 && rate <= 96000) + flag = 0x8e; + else if (rate >= 128000 && rate <= 192000) + flag = 0x8c; + else + return -EINVAL; + } else { + // For Fireface UFX and 802. Due to bandwidth limitation on + // IEEE 1394a (400 Mbps), Analog 1-12 and AES are available + // without any ADAT at quadruple speed. + if (rate >= 32000 && rate <= 48000) + flag = 0x9e; + else if (rate >= 64000 && rate <= 96000) + flag = 0x96; + else if (rate >= 128000 && rate <= 192000) + flag = 0x8e; + else + return -EINVAL; + } if (generation != fw_parent_device(ff->unit)->card->generation) { err = fw_iso_resources_update(&ff->tx_resources); @@ -207,8 +229,6 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - // Always use the maximum number of data channels in data block of - // packet. reg = cpu_to_le32(flag); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, LATTER_ISOC_START, ®, sizeof(reg), 0); @@ -263,7 +283,7 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer } } - err = parse_clock_bits(data, &rate, &src); + err = parse_clock_bits(data, &rate, &src, ff->unit_version); if (err < 0) return; label = snd_ff_proc_get_clk_label(src); diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 63b79c4a5405..5452115c0ef9 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -184,7 +184,6 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) */ if (!amdtp_stream_running(&ff->rx_stream)) { int spd = fw_parent_device(ff->unit)->max_speed; - unsigned int ir_delay_cycle; err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) @@ -200,14 +199,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) goto error; - // The device postpones start of transmission mostly for several - // cycles after receiving packets firstly. - if (ff->spec->protocol == &snd_ff_protocol_ff800) - ir_delay_cycle = 800; // = 100 msec - else - ir_delay_cycle = 16; // = 2 msec - - err = amdtp_domain_start(&ff->domain, ir_delay_cycle); + err = amdtp_domain_start(&ff->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index b62a4fd22407..bc39269415d2 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -16,12 +16,22 @@ MODULE_LICENSE("GPL v2"); static void name_card(struct snd_ff *ff) { struct fw_device *fw_dev = fw_parent_device(ff->unit); + const char *const names[] = { + [SND_FF_UNIT_VERSION_FF800] = "Fireface800", + [SND_FF_UNIT_VERSION_FF400] = "Fireface400", + [SND_FF_UNIT_VERSION_UFX] = "FirefaceUFX", + [SND_FF_UNIT_VERSION_UCX] = "FirefaceUCX", + [SND_FF_UNIT_VERSION_802] = "Fireface802", + }; + const char *name; + + name = names[ff->unit_version]; strcpy(ff->card->driver, "Fireface"); - strcpy(ff->card->shortname, ff->spec->name); - strcpy(ff->card->mixername, ff->spec->name); + strcpy(ff->card->shortname, name); + strcpy(ff->card->mixername, name); snprintf(ff->card->longname, sizeof(ff->card->longname), - "RME %s, GUID %08x%08x at %s, S%d", ff->spec->name, + "RME %s, GUID %08x%08x at %s, S%d", name, fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&ff->unit->device), 100 << fw_dev->max_speed); } @@ -101,6 +111,7 @@ static int snd_ff_probe(struct fw_unit *unit, spin_lock_init(&ff->lock); init_waitqueue_head(&ff->hwdep_wait); + ff->unit_version = entry->version; ff->spec = (const struct snd_ff_spec *)entry->driver_data; /* Register this sound card later. */ @@ -145,7 +156,6 @@ static void snd_ff_remove(struct fw_unit *unit) } static const struct snd_ff_spec spec_ff800 = { - .name = "Fireface800", .pcm_capture_channels = {28, 20, 12}, .pcm_playback_channels = {28, 20, 12}, .midi_in_ports = 1, @@ -157,7 +167,6 @@ static const struct snd_ff_spec spec_ff800 = { }; static const struct snd_ff_spec spec_ff400 = { - .name = "Fireface400", .pcm_capture_channels = {18, 14, 10}, .pcm_playback_channels = {18, 14, 10}, .midi_in_ports = 2, @@ -169,7 +178,6 @@ static const struct snd_ff_spec spec_ff400 = { }; static const struct snd_ff_spec spec_ucx = { - .name = "FirefaceUCX", .pcm_capture_channels = {18, 14, 12}, .pcm_playback_channels = {18, 14, 12}, .midi_in_ports = 2, @@ -180,6 +188,17 @@ static const struct snd_ff_spec spec_ucx = { .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, }; +static const struct snd_ff_spec spec_ufx_802 = { + .pcm_capture_channels = {30, 22, 14}, + .pcm_playback_channels = {30, 22, 14}, + .midi_in_ports = 1, + .midi_out_ports = 1, + .protocol = &snd_ff_protocol_latter, + .midi_high_addr = 0xffff00000034ull, + .midi_addr_range = 0x80, + .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, +}; + static const struct ieee1394_device_id snd_ff_id_table[] = { /* Fireface 800 */ { @@ -189,7 +208,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, .specifier_id = OUI_RME, - .version = 0x000001, + .version = SND_FF_UNIT_VERSION_FF800, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff800, }, @@ -201,10 +220,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, .specifier_id = OUI_RME, - .version = 0x000002, + .version = SND_FF_UNIT_VERSION_FF400, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, }, + // Fireface UFX. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = SND_FF_UNIT_VERSION_UFX, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ufx_802, + }, // Fireface UCX. { .match_flags = IEEE1394_MATCH_VENDOR_ID | @@ -213,10 +244,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, .specifier_id = OUI_RME, - .version = 0x000004, + .version = SND_FF_UNIT_VERSION_UCX, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ucx, }, + // Fireface 802. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = SND_FF_UNIT_VERSION_802, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ufx_802, + }, {} }; MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index dc7a20f75983..705e7df4f929 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -34,6 +34,14 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2 +enum snd_ff_unit_version { + SND_FF_UNIT_VERSION_FF800 = 0x000001, + SND_FF_UNIT_VERSION_FF400 = 0x000002, + SND_FF_UNIT_VERSION_UFX = 0x000003, + SND_FF_UNIT_VERSION_UCX = 0x000004, + SND_FF_UNIT_VERSION_802 = 0x000005, +}; + enum snd_ff_stream_mode { SND_FF_STREAM_MODE_LOW = 0, SND_FF_STREAM_MODE_MID, @@ -43,8 +51,6 @@ enum snd_ff_stream_mode { struct snd_ff_protocol; struct snd_ff_spec { - const char *const name; - const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT]; const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT]; @@ -66,6 +72,7 @@ struct snd_ff { bool registered; struct delayed_work dwork; + enum snd_ff_unit_version unit_version; const struct snd_ff_spec *spec; /* To handle MIDI tx. */ diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index dda797209a27..654e28a6669f 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -177,7 +177,7 @@ struct snd_efw_phys_meters { u32 in_meters; u32 reserved4; u32 reserved5; - u32 values[0]; + u32 values[]; } __packed; enum snd_efw_clock_source { SND_EFW_CLOCK_SOURCE_INTERNAL = 0, diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index 0fd36e469ad0..edb31ac26868 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -76,15 +76,11 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, if (i == ARRAY_SIZE(snd_motu_clock_rates)) return -EINVAL; - pcm_chunks = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + // Each data block includes SPH in its head. Data chunks follow with + // 3 byte alignment. Padding follows with zero to conform to quadlet + // alignment. + pcm_chunks = formats->pcm_chunks[mode]; data_chunks = formats->msg_chunks + pcm_chunks; - - /* - * Each data block includes SPH in its head. Data chunks follow with - * 3 byte alignment. Padding follows with zero to conform to quadlet - * alignment. - */ data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4); err = amdtp_stream_set_parameters(s, rate, data_block_quadlets); @@ -440,7 +436,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, - const struct snd_motu_protocol *const protocol) + const struct snd_motu_spec *spec) { amdtp_stream_process_ctx_payloads_t process_ctx_payloads; int fmt = CIP_FMT_MOTU; @@ -454,14 +450,15 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, * Units of version 3 transmits packets with invalid CIP header * against IEC 61883-1. */ - if (protocol == &snd_motu_protocol_v3) { + if (spec->protocol_version == SND_MOTU_PROTOCOL_V3) { flags |= CIP_WRONG_DBS | CIP_SKIP_DBC_ZERO_CHECK | CIP_HEADER_WITHOUT_EOH; fmt = CIP_FMT_MOTU_TX_V3; } - if (protocol == &snd_motu_protocol_v2) { + if (spec == &snd_motu_spec_8pre || + spec == &snd_motu_spec_ultralite) { // 8pre has some quirks. flags |= CIP_WRONG_DBS | CIP_SKIP_DBC_ZERO_CHECK; diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index 2d41a1a4052c..8e1437371263 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -26,8 +26,7 @@ static int motu_rate_constraint(struct snd_pcm_hw_params *params, rate = snd_motu_clock_rates[i]; mode = i / 2; - pcm_channels = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + pcm_channels = formats->pcm_chunks[mode]; if (!snd_interval_test(c, pcm_channels)) continue; @@ -59,8 +58,7 @@ static int motu_channels_constraint(struct snd_pcm_hw_params *params, if (!snd_interval_test(r, rate)) continue; - pcm_channels = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + pcm_channels = formats->pcm_chunks[mode]; channels.min = min(channels.min, pcm_channels); channels.max = max(channels.max, pcm_channels); } @@ -82,8 +80,7 @@ static void limit_channels_and_rates(struct snd_motu *motu, rate = snd_motu_clock_rates[i]; mode = i / 2; - pcm_channels = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + pcm_channels = formats->pcm_chunks[mode]; if (pcm_channels == 0) continue; @@ -133,7 +130,6 @@ static int init_hw_info(struct snd_motu *motu, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - const struct snd_motu_protocol *const protocol = motu->spec->protocol; struct amdtp_domain *d = &motu->domain; enum snd_motu_clock_source src; int err; @@ -152,7 +148,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - err = protocol->get_clock_source(motu, &src); + err = snd_motu_protocol_get_clock_source(motu, &src); if (err < 0) goto err_locked; @@ -166,7 +162,7 @@ static int pcm_open(struct snd_pcm_substream *substream) unsigned int frames_per_buffer = d->events_per_buffer; unsigned int rate; - err = protocol->get_clock_rate(motu, &rate); + err = snd_motu_protocol_get_clock_rate(motu, &rate); if (err < 0) goto err_locked; diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c index 187f6abd878c..f009cf7aa074 100644 --- a/sound/firewire/motu/motu-proc.c +++ b/sound/firewire/motu/motu-proc.c @@ -28,13 +28,12 @@ static void proc_read_clock(struct snd_info_entry *entry, { struct snd_motu *motu = entry->private_data; - const struct snd_motu_protocol *const protocol = motu->spec->protocol; unsigned int rate; enum snd_motu_clock_source source; - if (protocol->get_clock_rate(motu, &rate) < 0) + if (snd_motu_protocol_get_clock_rate(motu, &rate) < 0) return; - if (protocol->get_clock_source(motu, &source) < 0) + if (snd_motu_protocol_get_clock_source(motu, &source) < 0) return; snd_iprintf(buffer, "Rate:\t%d\n", rate); @@ -45,15 +44,14 @@ static void proc_read_format(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_motu *motu = entry->private_data; - const struct snd_motu_protocol *const protocol = motu->spec->protocol; unsigned int mode; struct snd_motu_packet_format *formats; int i; - if (protocol->cache_packet_formats(motu) < 0) + if (snd_motu_protocol_cache_packet_formats(motu) < 0) return; - snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n"); + snd_iprintf(buffer, "tx:\tmsg\tfixed\ttotal\n"); for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) { mode = i >> 1; @@ -62,11 +60,11 @@ static void proc_read_format(struct snd_info_entry *entry, "%u:\t%u\t%u\t%u\n", snd_motu_clock_rates[i], formats->msg_chunks, - formats->fixed_part_pcm_chunks[mode], - formats->differed_part_pcm_chunks[mode]); + motu->spec->tx_fixed_pcm_chunks[mode], + formats->pcm_chunks[mode]); } - snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n"); + snd_iprintf(buffer, "rx:\tmsg\tfixed\ttotal\n"); for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) { mode = i >> 1; @@ -75,8 +73,8 @@ static void proc_read_format(struct snd_info_entry *entry, "%u:\t%u\t%u\t%u\n", snd_motu_clock_rates[i], formats->msg_chunks, - formats->fixed_part_pcm_chunks[mode], - formats->differed_part_pcm_chunks[mode]); + motu->spec->rx_fixed_pcm_chunks[mode], + formats->pcm_chunks[mode]); } } diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c index 619b6ae73f62..e59e69ab1538 100644 --- a/sound/firewire/motu/motu-protocol-v2.c +++ b/sound/firewire/motu/motu-protocol-v2.c @@ -35,7 +35,8 @@ static int get_clock_rate(u32 data, unsigned int *rate) return 0; } -static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate) +int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, + unsigned int *rate) { __be32 reg; int err; @@ -48,7 +49,8 @@ static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate) return get_clock_rate(be32_to_cpu(reg), rate); } -static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate) +int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, + unsigned int rate) { __be32 reg; u32 data; @@ -76,14 +78,10 @@ static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate) sizeof(reg)); } -static int get_clock_source(struct snd_motu *motu, u32 data, - enum snd_motu_clock_source *src) +static int detect_clock_source_optical_model(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) { - unsigned int index = data & V2_CLOCK_SRC_MASK; - if (index > 5) - return -EIO; - - switch (index) { + switch (data) { case 0: *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; break; @@ -116,14 +114,50 @@ static int get_clock_source(struct snd_motu *motu, u32 data, *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; break; default: - return -EIO; + *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; } return 0; } -static int v2_get_clock_source(struct snd_motu *motu, - enum snd_motu_clock_source *src) +static int v2_detect_clock_source(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) +{ + switch (data) { + case 0: + *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; + break; + case 2: + *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; + break; + case 3: + *src = SND_MOTU_CLOCK_SOURCE_SPH; + break; + case 4: + *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; + break; + default: + *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; + } + + return 0; +} + +static int get_clock_source(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) +{ + data &= V2_CLOCK_SRC_MASK; + if (motu->spec == &snd_motu_spec_828mk2 || + motu->spec == &snd_motu_spec_traveler) + return detect_clock_source_optical_model(motu, data, src); + else + return v2_detect_clock_source(motu, data, src); +} + +int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src) { __be32 reg; int err; @@ -136,167 +170,189 @@ static int v2_get_clock_source(struct snd_motu *motu, return get_clock_source(motu, be32_to_cpu(reg), src); } -static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable) +// Expected for Traveler and 896HD, which implements Altera Cyclone EP1C3. +static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data, + bool enable) { - enum snd_motu_clock_source src; - __be32 reg; - u32 data; - int err = 0; + *data |= V2_CLOCK_MODEL_SPECIFIC; - // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. - if (motu->spec == &snd_motu_spec_828mk2) - return 0; + return 0; +} - err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, - sizeof(reg)); +// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200. +static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data, + bool enable) +{ + unsigned int rate; + enum snd_motu_clock_source src; + int err; + + err = get_clock_source(motu, *data, &src); if (err < 0) return err; - data = be32_to_cpu(reg); - err = get_clock_source(motu, data, &src); + err = get_clock_rate(*data, &rate); if (err < 0) return err; - data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); - if (enable) - data |= V2_CLOCK_FETCH_ENABLE; + if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) + *data |= V2_CLOCK_MODEL_SPECIFIC; + + return 0; +} - if (motu->spec->flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) { - // Expected for Traveler and 896HD, which implements Altera - // Cyclone EP1C3. - data |= V2_CLOCK_MODEL_SPECIFIC; +int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu, + bool enable) +{ + if (motu->spec == &snd_motu_spec_828mk2) { + // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. + return 0; } else { - // For UltraLite and 8pre, which implements Xilinx Spartan - // XC3S200. - unsigned int rate; + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, + ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); - err = get_clock_rate(data, &rate); + data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); + if (enable) + data |= V2_CLOCK_FETCH_ENABLE; + + if (motu->spec == &snd_motu_spec_traveler) + err = switch_fetching_mode_cyclone(motu, &data, enable); + else + err = switch_fetching_mode_spartan(motu, &data, enable); if (err < 0) return err; - if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) - data |= V2_CLOCK_MODEL_SPECIFIC; + reg = cpu_to_be32(data); + return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, + ®, sizeof(reg)); } +} - reg = cpu_to_be32(data); - return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®, - sizeof(reg)); +static int detect_packet_formats_828mk2(struct snd_motu *motu, u32 data) +{ + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 4; + } + + if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 4; + } + + return 0; } -static void calculate_fixed_part(struct snd_motu_packet_format *formats, - enum amdtp_stream_direction dir, - enum snd_motu_spec_flags flags, - unsigned char analog_ports) +static int detect_packet_formats_traveler(struct snd_motu *motu, u32 data) { - unsigned char pcm_chunks[3] = {0, 0, 0}; - - formats->msg_chunks = 2; - - pcm_chunks[0] = analog_ports; - pcm_chunks[1] = analog_ports; - if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) - pcm_chunks[2] = analog_ports; - - if (dir == AMDTP_IN_STREAM) { - if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } - if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } - } else { - if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } - - // Packets to v2 units include 2 chunks for phone 1/2, except - // for 176.4/192.0 kHz. - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 4; } - if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 4; } - /* - * All of v2 models have a pair of coaxial interfaces for digital in/out - * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these - * ports. - */ - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - - formats->fixed_part_pcm_chunks[0] = pcm_chunks[0]; - formats->fixed_part_pcm_chunks[1] = pcm_chunks[1]; - formats->fixed_part_pcm_chunks[2] = pcm_chunks[2]; + return 0; } -static void calculate_differed_part(struct snd_motu_packet_format *formats, - enum snd_motu_spec_flags flags, - u32 data, u32 mask, u32 shift) +static int detect_packet_formats_8pre(struct snd_motu *motu, u32 data) { - unsigned char pcm_chunks[2] = {0, 0}; - - /* - * When optical interfaces are configured for S/PDIF (TOSLINK), - * the above PCM frames come from them, instead of coaxial - * interfaces. - */ - data = (data & mask) >> shift; - if (data == V2_OPT_IFACE_MODE_ADAT) { - if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) { - pcm_chunks[0] += 8; - pcm_chunks[1] += 4; - } - // 8pre has two sets of optical interface and doesn't reduce - // chunks for ADAT signals. - if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) { - pcm_chunks[1] += 4; - } + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 8; } - /* At mode x4, no data chunks are supported in this part. */ - formats->differed_part_pcm_chunks[0] = pcm_chunks[0]; - formats->differed_part_pcm_chunks[1] = pcm_chunks[1]; + if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 8; + } + + return 0; } -static int v2_cache_packet_formats(struct snd_motu *motu) +int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu) { __be32 reg; u32 data; int err; + motu->tx_packet_formats.pcm_byte_offset = 10; + motu->rx_packet_formats.pcm_byte_offset = 10; + + motu->tx_packet_formats.msg_chunks = 2; + motu->rx_packet_formats.msg_chunks = 2; + err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®, sizeof(reg)); if (err < 0) return err; data = be32_to_cpu(reg); - calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM, - motu->spec->flags, motu->spec->analog_in_ports); - calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags, - data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT); + memcpy(motu->tx_packet_formats.pcm_chunks, + motu->spec->tx_fixed_pcm_chunks, + sizeof(motu->tx_packet_formats.pcm_chunks)); + memcpy(motu->rx_packet_formats.pcm_chunks, + motu->spec->rx_fixed_pcm_chunks, + sizeof(motu->rx_packet_formats.pcm_chunks)); - calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM, - motu->spec->flags, motu->spec->analog_out_ports); - calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags, - data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT); + if (motu->spec == &snd_motu_spec_828mk2) + return detect_packet_formats_828mk2(motu, data); + else if (motu->spec == &snd_motu_spec_traveler) + return detect_packet_formats_traveler(motu, data); + else if (motu->spec == &snd_motu_spec_8pre) + return detect_packet_formats_8pre(motu, data); + else + return 0; +} - motu->tx_packet_formats.pcm_byte_offset = 10; - motu->rx_packet_formats.pcm_byte_offset = 10; +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, + .tx_fixed_pcm_chunks = {14, 14, 0}, + .rx_fixed_pcm_chunks = {14, 14, 0}, +}; - return 0; -} +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, + .tx_fixed_pcm_chunks = {14, 14, 8}, + .rx_fixed_pcm_chunks = {14, 14, 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, + .tx_fixed_pcm_chunks = {14, 14, 0}, + .rx_fixed_pcm_chunks = {14, 14, 0}, +}; -const struct snd_motu_protocol snd_motu_protocol_v2 = { - .get_clock_rate = v2_get_clock_rate, - .set_clock_rate = v2_set_clock_rate, - .get_clock_source = v2_get_clock_source, - .switch_fetching_mode = v2_switch_fetching_mode, - .cache_packet_formats = v2_cache_packet_formats, +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, + .tx_fixed_pcm_chunks = {10, 6, 0}, + .rx_fixed_pcm_chunks = {10, 6, 0}, }; diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c index d1545e2b5caa..01a47ac7bb2d 100644 --- a/sound/firewire/motu/motu-protocol-v3.c +++ b/sound/firewire/motu/motu-protocol-v3.c @@ -24,7 +24,8 @@ #define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000 #define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000 -static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate) +int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu, + unsigned int *rate) { __be32 reg; u32 data; @@ -45,7 +46,8 @@ static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate) return 0; } -static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate) +int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu, + unsigned int rate) { __be32 reg; u32 data; @@ -85,55 +87,102 @@ static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate) return 0; } -static int v3_get_clock_source(struct snd_motu *motu, - enum snd_motu_clock_source *src) +static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) { - __be32 reg; - u32 data; - unsigned int val; - int err; - - err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, - sizeof(reg)); - if (err < 0) - return err; - data = be32_to_cpu(reg); - - val = data & V3_CLOCK_SOURCE_MASK; - if (val == 0x00) { + switch (data) { + case 0x00: *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; - } else if (val == 0x01) { + break; + case 0x01: *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; - } else if (val == 0x02) { + break; + case 0x02: *src = SND_MOTU_CLOCK_SOURCE_SPH; - } else if (val == 0x10) { + break; + case 0x10: *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; - } else if (val == 0x18 || val == 0x19) { - err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, - ®, sizeof(reg)); + break; + case 0x18: + case 0x19: + { + __be32 reg; + u32 options; + int err; + + err = snd_motu_transaction_read(motu, + V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg)); if (err < 0) return err; - data = be32_to_cpu(reg); + options = be32_to_cpu(reg); - if (val == 0x18) { - if (data & V3_NO_ADAT_OPT_IN_IFACE_A) + if (data == 0x18) { + if (options & V3_NO_ADAT_OPT_IN_IFACE_A) *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A; else *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A; } else { - if (data & V3_NO_ADAT_OPT_IN_IFACE_B) + if (options & V3_NO_ADAT_OPT_IN_IFACE_B) *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B; else *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B; } - } else { + + break; + } + default: *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; } return 0; } -static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable) +static int v3_detect_clock_source(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) +{ + switch (data) { + case 0x00: + *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; + break; + case 0x01: + *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; + break; + case 0x02: + *src = SND_MOTU_CLOCK_SOURCE_SPH; + break; + case 0x10: + *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; + break; + default: + *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; + } + + return 0; +} + +int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src) +{ + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, + sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK; + + if (motu->spec == &snd_motu_spec_828mk3) + return detect_clock_source_828mk3(motu, data, src); + else + return v3_detect_clock_source(motu, data, src); +} + +int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu, + bool enable) { __be32 reg; u32 data; @@ -155,162 +204,113 @@ static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable) sizeof(reg)); } -static void calculate_fixed_part(struct snd_motu_packet_format *formats, - enum amdtp_stream_direction dir, - enum snd_motu_spec_flags flags, - unsigned char analog_ports) +static int detect_packet_formats_828mk3(struct snd_motu *motu, u32 data) { - unsigned char pcm_chunks[3] = {0, 0, 0}; - - formats->msg_chunks = 2; - - pcm_chunks[0] = analog_ports; - pcm_chunks[1] = analog_ports; - if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) - pcm_chunks[2] = analog_ports; - - if (dir == AMDTP_IN_STREAM) { - if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) - pcm_chunks[2] += 2; - } - - if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) - pcm_chunks[2] += 2; - } - - if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } - } else { - if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (data & V3_ENABLE_OPT_IN_IFACE_A) { + if (data & V3_NO_ADAT_OPT_IN_IFACE_A) { + motu->tx_packet_formats.pcm_chunks[0] += 4; + motu->tx_packet_formats.pcm_chunks[1] += 4; + } else { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 4; } - - // Packets to v3 units include 2 chunks for phone 1/2, except - // for 176.4/192.0 kHz. - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; } - if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (data & V3_ENABLE_OPT_IN_IFACE_B) { + if (data & V3_NO_ADAT_OPT_IN_IFACE_B) { + motu->tx_packet_formats.pcm_chunks[0] += 4; + motu->tx_packet_formats.pcm_chunks[1] += 4; + } else { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 4; + } } - /* - * At least, packets have two data chunks for S/PDIF on coaxial - * interface. - */ - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - - /* - * Fixed part consists of PCM chunks multiple of 4, with msg chunks. As - * a result, this part can includes empty data chunks. - */ - formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2; - formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2; - if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) - formats->fixed_part_pcm_chunks[2] = - round_up(2 + pcm_chunks[2], 4) - 2; -} - -static void calculate_differed_part(struct snd_motu_packet_format *formats, - enum snd_motu_spec_flags flags, u32 data, - u32 a_enable_mask, u32 a_no_adat_mask, - u32 b_enable_mask, u32 b_no_adat_mask) -{ - unsigned char pcm_chunks[3] = {0, 0, 0}; - int i; - - if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) { - if (data & a_no_adat_mask) { - /* - * Additional two data chunks for S/PDIF on optical - * interface A. This includes empty data chunks. - */ - pcm_chunks[0] += 4; - pcm_chunks[1] += 4; + if (data & V3_ENABLE_OPT_OUT_IFACE_A) { + if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) { + motu->rx_packet_formats.pcm_chunks[0] += 4; + motu->rx_packet_formats.pcm_chunks[1] += 4; } else { - /* - * Additional data chunks for ADAT on optical interface - * A. - */ - pcm_chunks[0] += 8; - pcm_chunks[1] += 4; + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 4; } } - if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) { - if (data & b_no_adat_mask) { - /* - * Additional two data chunks for S/PDIF on optical - * interface B. This includes empty data chunks. - */ - pcm_chunks[0] += 4; - pcm_chunks[1] += 4; + if (data & V3_ENABLE_OPT_OUT_IFACE_B) { + if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) { + motu->rx_packet_formats.pcm_chunks[0] += 4; + motu->rx_packet_formats.pcm_chunks[1] += 4; } else { - /* - * Additional data chunks for ADAT on optical interface - * B. - */ - pcm_chunks[0] += 8; - pcm_chunks[1] += 4; + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 4; } } - for (i = 0; i < 3; ++i) { - if (pcm_chunks[i] > 0) - pcm_chunks[i] = round_up(pcm_chunks[i], 4); - - formats->differed_part_pcm_chunks[i] = pcm_chunks[i]; - } + return 0; } -static int v3_cache_packet_formats(struct snd_motu *motu) +int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) { __be32 reg; u32 data; int err; + motu->tx_packet_formats.pcm_byte_offset = 10; + motu->rx_packet_formats.pcm_byte_offset = 10; + + motu->tx_packet_formats.msg_chunks = 2; + motu->rx_packet_formats.msg_chunks = 2; + err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg)); if (err < 0) return err; data = be32_to_cpu(reg); - calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM, - motu->spec->flags, motu->spec->analog_in_ports); - calculate_differed_part(&motu->tx_packet_formats, - motu->spec->flags, data, - V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A, - V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B); + memcpy(motu->tx_packet_formats.pcm_chunks, + motu->spec->tx_fixed_pcm_chunks, + sizeof(motu->tx_packet_formats.pcm_chunks)); + memcpy(motu->rx_packet_formats.pcm_chunks, + motu->spec->rx_fixed_pcm_chunks, + sizeof(motu->rx_packet_formats.pcm_chunks)); - calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM, - motu->spec->flags, motu->spec->analog_out_ports); - calculate_differed_part(&motu->rx_packet_formats, - motu->spec->flags, data, - V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A, - V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B); + if (motu->spec == &snd_motu_spec_828mk3) + return detect_packet_formats_828mk3(motu, data); + else + return 0; +} - motu->tx_packet_formats.pcm_byte_offset = 10; - motu->rx_packet_formats.pcm_byte_offset = 10; - return 0; -} +const struct snd_motu_spec snd_motu_spec_828mk3 = { + .name = "828mk3", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | + SND_MOTU_SPEC_TX_MIDI_3RD_Q, + .tx_fixed_pcm_chunks = {18, 18, 14}, + .rx_fixed_pcm_chunks = {14, 14, 10}, +}; + +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, + .tx_fixed_pcm_chunks = {18, 14, 10}, + .rx_fixed_pcm_chunks = {14, 14, 14}, +}; + +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, + .tx_fixed_pcm_chunks = {10, 10, 0}, + .rx_fixed_pcm_chunks = {10, 10, 0}, +}; -const struct snd_motu_protocol snd_motu_protocol_v3 = { - .get_clock_rate = v3_get_clock_rate, - .set_clock_rate = v3_set_clock_rate, - .get_clock_source = v3_get_clock_source, - .switch_fetching_mode = v3_switch_fetching_mode, - .cache_packet_formats = v3_cache_packet_formats, +const struct snd_motu_spec snd_motu_spec_4pre = { + .name = "4pre", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .tx_fixed_pcm_chunks = {10, 10, 0}, + .rx_fixed_pcm_chunks = {10, 10, 0}, }; diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index a17ddceb1bec..2028c5419f6f 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -88,7 +88,7 @@ static void finish_session(struct snd_motu *motu) u32 data; int err; - err = motu->spec->protocol->switch_fetching_mode(motu, false); + err = snd_motu_protocol_switch_fetching_mode(motu, false); if (err < 0) return; @@ -110,7 +110,7 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) { int err; - err = motu->spec->protocol->cache_packet_formats(motu); + err = snd_motu_protocol_cache_packet_formats(motu); if (err < 0) return err; @@ -140,7 +140,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, unsigned int curr_rate; int err; - err = motu->spec->protocol->get_clock_rate(motu, &curr_rate); + err = snd_motu_protocol_get_clock_rate(motu, &curr_rate); if (err < 0) return err; if (rate == 0) @@ -153,7 +153,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, fw_iso_resources_free(&motu->tx_resources); fw_iso_resources_free(&motu->rx_resources); - err = motu->spec->protocol->set_clock_rate(motu, rate); + err = snd_motu_protocol_set_clock_rate(motu, rate); if (err < 0) { dev_err(&motu->unit->device, "fail to set sampling rate: %d\n", err); @@ -201,9 +201,9 @@ static int ensure_packet_formats(struct snd_motu *motu) data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS | RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS| TX_PACKET_TRANSMISSION_SPEED_MASK); - if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0) + if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0]) data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; - if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0) + if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0]) data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; data |= fw_parent_device(motu->unit)->max_speed; @@ -272,7 +272,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) goto stop_streams; } - err = motu->spec->protocol->switch_fetching_mode(motu, true); + err = snd_motu_protocol_switch_fetching_mode(motu, true); if (err < 0) { dev_err(&motu->unit->device, "fail to enable frame fetching: %d\n", err); @@ -317,7 +317,7 @@ static int init_stream(struct snd_motu *motu, struct amdtp_stream *s) if (err < 0) return err; - err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol); + err = amdtp_motu_init(s, motu->unit, dir, motu->spec); if (err < 0) fw_iso_resources_destroy(resources); diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index f2080d720aa9..a4929c1302dc 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -172,105 +172,6 @@ static void motu_bus_update(struct fw_unit *unit) snd_motu_transaction_reregister(motu); } -const struct snd_motu_spec snd_motu_spec_828mk2 = { - .name = "828mk2", - .protocol = &snd_motu_protocol_v2, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_RX_SEPARATED_MAIN | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, - - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_traveler = { - .name = "Traveler", - .protocol = &snd_motu_protocol_v2, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_SUPPORT_CLOCK_X4 | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_HAS_AESEBU_IFACE | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, - - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_ultralite = { - .name = "UltraLite", - .protocol = &snd_motu_protocol_v2, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | // padding. - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q | - SND_MOTU_SPEC_RX_SEPARATED_MAIN, - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_8pre = { - .name = "8pre", - .protocol = &snd_motu_protocol_v2, - // In tx, use coax chunks for mix-return 1/2. In rx, use coax chunks for - // dummy 1/2. - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_HAS_OPT_IFACE_B | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, - .analog_in_ports = 8, - .analog_out_ports = 2, -}; - -static const struct snd_motu_spec motu_828mk3 = { - .name = "828mk3", - .protocol = &snd_motu_protocol_v3, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_SUPPORT_CLOCK_X4 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_TX_REVERB_CHUNK | - SND_MOTU_SPEC_RX_SEPARATED_MAIN | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_HAS_OPT_IFACE_B | - SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, - - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_audio_express = { - .name = "AudioExpress", - .protocol = &snd_motu_protocol_v3, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_RX_SEPARATED_MAIN | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, - .analog_in_ports = 2, - .analog_out_ports = 4, -}; - -static const struct snd_motu_spec motu_4pre = { - .name = "4pre", - .protocol = &snd_motu_protocol_v3, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_RX_SEPARATED_MAIN, - .analog_in_ports = 2, - .analog_out_ports = 2, -}; - #define SND_MOTU_DEV_ENTRY(model, data) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ @@ -284,13 +185,14 @@ static const struct snd_motu_spec motu_4pre = { static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2), - SND_MOTU_DEV_ENTRY(0x000009, &motu_traveler), - SND_MOTU_DEV_ENTRY(0x00000d, &motu_ultralite), - SND_MOTU_DEV_ENTRY(0x00000f, &motu_8pre), - SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */ - SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */ - SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express), - SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre), + SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler), + SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite), + SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre), + SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3), // FireWire only. + SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only. + SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3), // Hybrid. + SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express), + SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre), { } }; MODULE_DEVICE_TABLE(ieee1394, motu_id_table); diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 6efbde405a0d..3d0236ee6716 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -36,8 +36,7 @@ struct snd_motu_packet_format { unsigned char pcm_byte_offset; unsigned char msg_chunks; - unsigned char fixed_part_pcm_chunks[3]; - unsigned char differed_part_pcm_chunks[3]; + unsigned char pcm_chunks[3]; }; struct snd_motu { @@ -74,19 +73,10 @@ struct snd_motu { }; enum snd_motu_spec_flags { - SND_MOTU_SPEC_SUPPORT_CLOCK_X2 = 0x0001, - SND_MOTU_SPEC_SUPPORT_CLOCK_X4 = 0x0002, - SND_MOTU_SPEC_TX_MICINST_CHUNK = 0x0004, - SND_MOTU_SPEC_TX_RETURN_CHUNK = 0x0008, - SND_MOTU_SPEC_TX_REVERB_CHUNK = 0x0010, - SND_MOTU_SPEC_HAS_AESEBU_IFACE = 0x0020, - SND_MOTU_SPEC_HAS_OPT_IFACE_A = 0x0040, - SND_MOTU_SPEC_HAS_OPT_IFACE_B = 0x0080, - SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0100, - SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0200, - SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0400, - SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0800, - SND_MOTU_SPEC_RX_SEPARATED_MAIN = 0x1000, + SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0001, + SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002, + SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004, + SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008, }; #define SND_MOTU_CLOCK_RATE_COUNT 6 @@ -108,33 +98,33 @@ enum snd_motu_clock_source { SND_MOTU_CLOCK_SOURCE_UNKNOWN, }; -struct snd_motu_protocol { - int (*get_clock_rate)(struct snd_motu *motu, unsigned int *rate); - int (*set_clock_rate)(struct snd_motu *motu, unsigned int rate); - int (*get_clock_source)(struct snd_motu *motu, - enum snd_motu_clock_source *source); - int (*switch_fetching_mode)(struct snd_motu *motu, bool enable); - int (*cache_packet_formats)(struct snd_motu *motu); +enum snd_motu_protocol_version { + SND_MOTU_PROTOCOL_V2, + SND_MOTU_PROTOCOL_V3, }; struct snd_motu_spec { const char *const name; + enum snd_motu_protocol_version protocol_version; enum snd_motu_spec_flags flags; - unsigned char analog_in_ports; - unsigned char analog_out_ports; - - const struct snd_motu_protocol *const protocol; + unsigned char tx_fixed_pcm_chunks[3]; + unsigned char rx_fixed_pcm_chunks[3]; }; -extern const struct snd_motu_protocol snd_motu_protocol_v2; -extern const struct snd_motu_protocol snd_motu_protocol_v3; - extern const struct snd_motu_spec snd_motu_spec_828mk2; +extern const struct snd_motu_spec snd_motu_spec_traveler; +extern const struct snd_motu_spec snd_motu_spec_ultralite; +extern const struct snd_motu_spec snd_motu_spec_8pre; + +extern const struct snd_motu_spec snd_motu_spec_828mk3; +extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3; +extern const struct snd_motu_spec snd_motu_spec_audio_express; +extern const struct snd_motu_spec snd_motu_spec_4pre; int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, - const struct snd_motu_protocol *const protocol); + const struct snd_motu_spec *spec); int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, struct snd_motu_packet_format *formats); @@ -169,4 +159,79 @@ int snd_motu_create_pcm_devices(struct snd_motu *motu); int snd_motu_create_midi_devices(struct snd_motu *motu); int snd_motu_create_hwdep_device(struct snd_motu *motu); + +int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, + unsigned int *rate); +int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, + unsigned int rate); +int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src); +int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu, + bool enable); +int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu); + +int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu, + unsigned int *rate); +int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu, + unsigned int rate); +int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src); +int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu, + bool enable); +int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu); + +static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu, + unsigned int *rate) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_get_clock_rate(motu, rate); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_get_clock_rate(motu, rate); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu, + unsigned int rate) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_set_clock_rate(motu, rate); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_set_clock_rate(motu, rate); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *source) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_get_clock_source(motu, source); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_get_clock_source(motu, source); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu, + bool enable) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_switch_fetching_mode(motu, enable); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_switch_fetching_mode(motu, enable); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_cache_packet_formats(motu); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_cache_packet_formats(motu); + else + return -ENXIO; +} + #endif diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index 4ca6b09056f3..3bc9224d5e4f 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -21,16 +21,17 @@ config SND_HDA_EXT_CORE select SND_HDA_CORE config SND_HDA_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" if !SND_DMA_SGBUF + int "Pre-allocated buffer size for HD-audio driver" range 0 32768 - default 0 if SND_DMA_SGBUF + default 2048 if SND_DMA_SGBUF default 64 if !SND_DMA_SGBUF help Specifies the default pre-allocated buffer-size in kB for the HD-audio driver. A larger buffer (e.g. 2048) is preferred for systems using PulseAudio. The default 64 is chosen just for compatibility reasons. - On x86 systems, the default is zero as we need no preallocation. + On x86 systems, the default is 2048 as a reasonable value for + most of modern systems. Note that the pre-allocation size can be changed dynamically via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 73bfa71845f6..d0a604c939df 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); static void default_release(struct device *dev) { - snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev)); + snd_hdac_ext_bus_device_exit(dev_to_hdac_dev(dev)); } /** diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 3fe62be1cbcc..09ddab5f5cae 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -81,7 +81,6 @@ int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, mutex_unlock(&bus->cmd_mutex); return err; } -EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb); /** * snd_hdac_bus_exec_verb_unlocked - unlocked version @@ -150,7 +149,6 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex) schedule_work(&bus->unsol_work); } -EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); /* * process queued unsolicited events @@ -162,6 +160,7 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work) struct hdac_driver *drv; unsigned int rp, caddr, res; + spin_lock_irq(&bus->reg_lock); while (bus->unsol_rp != bus->unsol_wp) { rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE; bus->unsol_rp = rp; @@ -173,10 +172,13 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work) codec = bus->caddr_tbl[caddr & 0x0f]; if (!codec || !codec->dev.driver) continue; + spin_unlock_irq(&bus->reg_lock); drv = drv_to_hdac_driver(codec->dev.driver); if (drv->unsol_event) drv->unsol_event(codec, res); + spin_lock_irq(&bus->reg_lock); } + spin_unlock_irq(&bus->reg_lock); } /** diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index bc4a8b606020..011b17cc1efa 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -9,6 +9,7 @@ #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_register.h> +#include "local.h" /* clear CORB read pointer properly */ static void azx_clear_corbrp(struct hdac_bus *bus) @@ -527,6 +528,18 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) } bus->chip_init = true; + + /* + * Default value of '8' is as per the HD audio specification (Rev 1.0a). + * Following relation is used to derive STRIPE control value. + * For sample rate <= 48K: + * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 } + * For sample rate > 48K: + * { ((num_channels * bits_per_sample * rate/48000) / + * number of SDOs) >= 8 } + */ + bus->sdo_limit = 8; + return true; } EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index e3119f5cb0d5..333220f0f8af 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -20,7 +20,7 @@ static int get_codec_vendor_name(struct hdac_device *codec); static void default_release(struct device *dev) { - snd_hdac_device_exit(container_of(dev, struct hdac_device, dev)); + snd_hdac_device_exit(dev_to_hdac_dev(dev)); } /** diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index a314b03b4a4c..a38a2af1654f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -38,7 +38,7 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus, else value = (channels * bits_per_sample) / sdo_line; - if (value >= 8) + if (value >= bus->sdo_limit) break; } diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index 5df0d2253844..99aec7349167 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -1,10 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz> +#include <linux/acpi.h> #include <linux/bits.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_intel.h> #include <sound/core.h> #include <sound/intel-dsp-config.h> #include <sound/intel-nhlt.h> @@ -14,9 +17,14 @@ static int dsp_driver; module_param(dsp_driver, int, 0444); MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)"); -#define FLAG_SST BIT(0) -#define FLAG_SOF BIT(1) -#define FLAG_SOF_ONLY_IF_DMIC BIT(16) +#define FLAG_SST BIT(0) +#define FLAG_SOF BIT(1) +#define FLAG_SST_ONLY_IF_DMIC BIT(15) +#define FLAG_SOF_ONLY_IF_DMIC BIT(16) +#define FLAG_SOF_ONLY_IF_SOUNDWIRE BIT(17) + +#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \ + FLAG_SOF_ONLY_IF_SOUNDWIRE) struct config_entry { u32 flags; @@ -100,6 +108,10 @@ static const struct config_entry config_table[] = { {} } }, + { + .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, + .device = 0x9d70, + }, #endif /* Kabylake-LP */ #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) @@ -116,6 +128,10 @@ static const struct config_entry config_table[] = { {} } }, + { + .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, + .device = 0x9d71, + }, #endif /* @@ -166,7 +182,7 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x9dc8, }, #endif @@ -187,7 +203,7 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0xa348, }, #endif @@ -204,16 +220,48 @@ static const struct config_entry config_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), } }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") + }, + }, + { + /* early version of SKU 09C6 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") + }, + }, {} } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x02c8, }, /* Cometlake-H */ { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF, + .device = 0x06c8, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), + }, + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x06c8, }, #endif @@ -234,7 +282,7 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x34c8, }, #endif @@ -254,9 +302,8 @@ static const struct config_entry config_table[] = { {} } }, - { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0xa0c8, }, #endif @@ -301,6 +348,28 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci) return ret; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) +static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) +{ + struct sdw_intel_acpi_info info; + acpi_handle handle; + int ret; + + handle = ACPI_HANDLE(&pci->dev); + + ret = sdw_intel_acpi_scan(handle, &info); + if (ret < 0) + return ret; + + return info.link_mask; +} +#else +static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) +{ + return 0; +} +#endif + int snd_intel_dsp_driver_probe(struct pci_dev *pci) { const struct config_entry *cfg; @@ -334,22 +403,36 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci) return SND_INTEL_DSP_DRIVER_ANY; if (cfg->flags & FLAG_SOF) { - if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC) { + if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE && + snd_intel_dsp_check_soundwire(pci) > 0) { + dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n"); + return SND_INTEL_DSP_DRIVER_SOF; + } + if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC && + snd_intel_dsp_check_dmic(pci)) { + dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); + return SND_INTEL_DSP_DRIVER_SOF; + } + if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE)) + return SND_INTEL_DSP_DRIVER_SOF; + } + + + if (cfg->flags & FLAG_SST) { + if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) { if (snd_intel_dsp_check_dmic(pci)) { - dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); - return SND_INTEL_DSP_DRIVER_SOF; + dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n"); + return SND_INTEL_DSP_DRIVER_SST; } } else { - return SND_INTEL_DSP_DRIVER_SOF; + return SND_INTEL_DSP_DRIVER_SST; } } - if (cfg->flags & FLAG_SST) - return SND_INTEL_DSP_DRIVER_SST; - return SND_INTEL_DSP_DRIVER_LEGACY; } EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel DSP config driver"); +MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index 73d4f8c4f4c7..059aaf04f536 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -4,58 +4,25 @@ #include <linux/acpi.h> #include <sound/intel-nhlt.h> -#define NHLT_ACPI_HEADER_SIG "NHLT" - -/* Unique identification for getting NHLT blobs */ -static const guid_t osc_guid = - GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, - 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); - struct nhlt_acpi_table *intel_nhlt_init(struct device *dev) { - acpi_handle handle; - union acpi_object *obj; - struct nhlt_resource_desc *nhlt_ptr; - struct nhlt_acpi_table *nhlt_table = NULL; - - handle = ACPI_HANDLE(dev); - if (!handle) { - dev_err(dev, "Didn't find ACPI_HANDLE\n"); - return NULL; - } + struct nhlt_acpi_table *nhlt; + acpi_status status; - obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL); - - if (!obj) - return NULL; - - if (obj->type != ACPI_TYPE_BUFFER) { - dev_dbg(dev, "No NHLT table found\n"); - ACPI_FREE(obj); + status = acpi_get_table(ACPI_SIG_NHLT, 0, + (struct acpi_table_header **)&nhlt); + if (ACPI_FAILURE(status)) { + dev_warn(dev, "NHLT table not found\n"); return NULL; } - nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - if (nhlt_ptr->length) - nhlt_table = (struct nhlt_acpi_table *) - memremap(nhlt_ptr->min_addr, nhlt_ptr->length, - MEMREMAP_WB); - ACPI_FREE(obj); - if (nhlt_table && - (strncmp(nhlt_table->header.signature, - NHLT_ACPI_HEADER_SIG, - strlen(NHLT_ACPI_HEADER_SIG)) != 0)) { - memunmap(nhlt_table); - dev_err(dev, "NHLT ACPI header signature incorrect\n"); - return NULL; - } - return nhlt_table; + return nhlt; } EXPORT_SYMBOL_GPL(intel_nhlt_init); void intel_nhlt_free(struct nhlt_acpi_table *nhlt) { - memunmap((void *)nhlt); + acpi_put_table((struct acpi_table_header *)nhlt); } EXPORT_SYMBOL_GPL(intel_nhlt_free); diff --git a/sound/hda/local.h b/sound/hda/local.h index 5b935219352f..896ba142e8bc 100644 --- a/sound/hda/local.h +++ b/sound/hda/local.h @@ -36,6 +36,9 @@ void hda_widget_sysfs_exit(struct hdac_device *codec); int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); +void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); +int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res); int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, unsigned int flags, unsigned int *res); diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index ce4c8ba2fa98..ca18fe3ff8a5 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -54,7 +54,7 @@ MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).") static const struct pnp_card_device_id snd_ad1816a_pnpids[] = { /* Analog Devices AD1815 */ { .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } }, - /* Analog Device AD1816? */ + /* Analog Devices AD1816? */ { .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, /* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */ { .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c index ff3a05ad99c0..64610571a5e1 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -267,8 +267,10 @@ static int snd_es968_pnp_detect(struct pnp_card_link *pcard, return error; } error = snd_es1688_probe(card, dev); - if (error < 0) + if (error < 0) { + snd_card_free(card); return error; + } pnp_set_card_drvdata(pcard, card); snd_es968_pnp_is_probed = 1; return 0; diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index e764816a8f7a..b039429e6871 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -867,10 +867,13 @@ static void snd_miro_write(struct snd_miro *chip, unsigned char reg, spin_unlock_irqrestore(&chip->lock, flags); } +static inline void snd_miro_write_mask(struct snd_miro *chip, + unsigned char reg, unsigned char value, unsigned char mask) +{ + unsigned char oldval = snd_miro_read(chip, reg); -#define snd_miro_write_mask(chip, reg, value, mask) \ - snd_miro_write(chip, reg, \ - (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask))) + snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask)); +} /* * Proc Interface diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index d06b29693c85..0e6d20e49158 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -317,10 +317,13 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, } -#define snd_opti9xx_write_mask(chip, reg, value, mask) \ - snd_opti9xx_write(chip, reg, \ - (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) +static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip, + unsigned char reg, unsigned char value, unsigned char mask) +{ + unsigned char oldval = snd_opti9xx_read(chip, reg); + snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask)); +} static int snd_opti9xx_configure(struct snd_opti9xx *chip, long port, diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index c5b1d5900eed..d6420d224d09 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -1171,7 +1171,10 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header) "alias for %d\n", header->number, header->hdr.a.OriginalSample); - + + if (header->number >= WF_MAX_SAMPLE) + return -EINVAL; + munge_int32 (header->number, &alias_hdr[0], 2); munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), @@ -1202,6 +1205,9 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header) int num_samples; unsigned char *msample_hdr; + if (header->number >= WF_MAX_SAMPLE) + return -EINVAL; + msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL); if (! msample_hdr) return -ENOMEM; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index ebf926728c5f..45ef0f52ec55 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -1356,7 +1356,7 @@ static int patch_cx20551(struct snd_ac97 *ac97) } /* - * Analog Device AD18xx, AD19xx codecs + * Analog Devices AD18xx, AD19xx codecs */ #ifdef CONFIG_PM static void ad18xx_resume(struct snd_ac97 *ac97) diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 6e3177bcc709..015c0d676897 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) static int src_put_rsc_ctrl_blk(void *blk) { - kfree((struct src_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) static int src_mgr_put_ctrl_blk(void *blk) { - kfree((struct src_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) static int srcimp_mgr_put_ctrl_blk(void *blk) { - kfree((struct srcimp_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) static int amixer_rsc_put_ctrl_blk(void *blk) { - kfree((struct amixer_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk) static int dai_put_ctrl_blk(void *blk) { - kfree((struct dai_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk) static int dao_put_ctrl_blk(void *blk) { - kfree((struct dao_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -1156,7 +1156,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) static int daio_mgr_put_ctrl_blk(void *blk) { - kfree((struct daio_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index ddb7c2ce3f7c..def8161cde4c 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1040,7 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2) + if (reg < 0x49 && channel_id <= 2) snd_emu10k1x_ptr_write(emu, reg, channel_id, val); } } diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index e1d3082a4fe9..7ba542e45a3d 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -99,10 +99,10 @@ comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m config SND_HDA_CODEC_ANALOG - tristate "Build Analog Device HD-audio codec support" + tristate "Build Analog Devices HD-audio codec support" select SND_HDA_GENERIC help - Say Y or M here to include Analog Device HD-audio codec support in + Say Y or M here to include Analog Devices HD-audio codec support in snd-hda-intel driver, such as AD1986A. comment "Set to Y if you want auto-loading the codec driver" diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 2c6d2becfe1a..824f4ac1a8ce 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -72,6 +72,12 @@ static int compare_input_type(const void *ap, const void *bp) if (a->type != b->type) return (int)(a->type - b->type); + /* If has both hs_mic and hp_mic, pick the hs_mic ahead of hp_mic. */ + if (a->is_headset_mic && b->is_headphone_mic) + return -1; /* don't swap */ + else if (a->is_headphone_mic && b->is_headset_mic) + return 1; /* swap */ + /* In case one has boost and the other one has not, pick the one with boost first. */ return (int)(b->has_boost_on_pin - a->has_boost_on_pin); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a34a2c9f4bcf..7e3ae4534df9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -641,8 +641,18 @@ static void hda_jackpoll_work(struct work_struct *work) struct hda_codec *codec = container_of(work, struct hda_codec, jackpoll_work.work); - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_poll_all(codec); + /* for non-polling trigger: we need nothing if already powered on */ + if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core)) + return; + + /* the power-up/down sequence triggers the runtime resume */ + snd_hda_power_up_pm(codec); + /* update jacks manually if polling is required, too */ + if (codec->jackpoll_interval) { + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_poll_all(codec); + } + snd_hda_power_down_pm(codec); if (!codec->jackpoll_interval) return; @@ -2951,18 +2961,14 @@ static int hda_codec_runtime_resume(struct device *dev) static int hda_codec_force_resume(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); - bool forced_resume = !codec->relaxed_resume && codec->jacktbl.used; int ret; - /* The get/put pair below enforces the runtime resume even if the - * device hasn't been used at suspend time. This trick is needed to - * update the jack state change during the sleep. - */ - if (forced_resume) - pm_runtime_get_noresume(dev); ret = pm_runtime_force_resume(dev); - if (forced_resume) - pm_runtime_put(dev); + /* schedule jackpoll work for jack detection update */ + if (codec->jackpoll_interval || + (pm_runtime_suspended(dev) && hda_codec_need_resume(codec))) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); return ret; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index bd093593f8fb..3565e2ab0965 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -36,10 +36,10 @@ #include <linux/time.h> #include <linux/completion.h> #include <linux/acpi.h> +#include <linux/pgtable.h> #ifdef CONFIG_X86 /* for snoop control */ -#include <asm/pgtable.h> #include <asm/set_memory.h> #include <asm/cpufeature.h> #endif @@ -1004,7 +1004,8 @@ static void __azx_runtime_resume(struct azx *chip, bool from_rt) if (status && from_rt) { list_for_each_codec(codec, &chip->bus) - if (status & (1 << codec->addr)) + if (!codec->relaxed_resume && + (status & (1 << codec->addr))) schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval); } @@ -1027,7 +1028,7 @@ static int azx_suspend(struct device *dev) chip = card->private_data; bus = azx_bus(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __azx_runtime_suspend(chip); + pm_runtime_force_suspend(dev); if (bus->irq >= 0) { free_irq(bus->irq, chip); bus->irq = -1; @@ -1055,7 +1056,8 @@ static int azx_resume(struct device *dev) chip->msi = 0; if (azx_acquire_irq(chip, 1) < 0) return -EIO; - __azx_runtime_resume(chip, false); + + pm_runtime_force_resume(dev); snd_power_change_state(card, SNDRV_CTL_POWER_D0); trace_azx_resume(chip); @@ -1071,6 +1073,8 @@ static int azx_freeze_noirq(struct device *dev) struct azx *chip = card->private_data; struct pci_dev *pci = to_pci_dev(dev); + if (!azx_is_pm_ready(card)) + return 0; if (chip->driver_type == AZX_DRIVER_SKL) pci_set_power_state(pci, PCI_D3hot); @@ -1083,6 +1087,8 @@ static int azx_thaw_noirq(struct device *dev) struct azx *chip = card->private_data; struct pci_dev *pci = to_pci_dev(dev); + if (!azx_is_pm_ready(card)) + return 0; if (chip->driver_type == AZX_DRIVER_SKL) pci_set_power_state(pci, PCI_D0); @@ -1098,12 +1104,12 @@ static int azx_runtime_suspend(struct device *dev) if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - if (!azx_has_pm_runtime(chip)) - return 0; /* enable controller wake up event */ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | - STATESTS_INT_MASK); + if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) { + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | + STATESTS_INT_MASK); + } __azx_runtime_suspend(chip); trace_azx_runtime_suspend(chip); @@ -1114,17 +1120,18 @@ static int azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; + bool from_rt = snd_power_get_state(card) == SNDRV_CTL_POWER_D0; if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - if (!azx_has_pm_runtime(chip)) - return 0; - __azx_runtime_resume(chip, true); + __azx_runtime_resume(chip, from_rt); /* disable controller Wake Up event*/ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & - ~STATESTS_INT_MASK); + if (from_rt) { + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & + ~STATESTS_INT_MASK); + } trace_azx_runtime_resume(chip); return 0; @@ -1199,10 +1206,8 @@ static void azx_vs_set_state(struct pci_dev *pci, if (!disabled) { dev_info(chip->card->dev, "Start delayed initialization\n"); - if (azx_probe_continue(chip) < 0) { + if (azx_probe_continue(chip) < 0) dev_err(chip->card->dev, "initialization error\n"); - hda->init_failed = true; - } } } else { dev_info(chip->card->dev, "%s via vga_switcheroo\n", @@ -1335,12 +1340,15 @@ static int register_vga_switcheroo(struct azx *chip) /* * destructor */ -static int azx_free(struct azx *chip) +static void azx_free(struct azx *chip) { struct pci_dev *pci = chip->pci; struct hda_intel *hda = container_of(chip, struct hda_intel, chip); struct hdac_bus *bus = azx_bus(chip); + if (hda->freed) + return; + if (azx_has_pm_runtime(chip) && chip->running) pm_runtime_get_noresume(&pci->dev); chip->running = 0; @@ -1384,9 +1392,8 @@ static int azx_free(struct azx *chip) if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) snd_hdac_i915_exit(bus); - kfree(hda); - return 0; + hda->freed = 1; } static int azx_dev_disconnect(struct snd_device *device) @@ -1402,7 +1409,8 @@ static int azx_dev_disconnect(struct snd_device *device) static int azx_dev_free(struct snd_device *device) { - return azx_free(device->device_data); + azx_free(device->device_data); + return 0; } #ifdef SUPPORT_VGA_SWITCHEROO @@ -1769,7 +1777,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, if (err < 0) return err; - hda = kzalloc(sizeof(*hda), GFP_KERNEL); + hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL); if (!hda) { pci_disable_device(pci); return -ENOMEM; @@ -1810,7 +1818,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, err = azx_bus_init(chip, model[dev]); if (err < 0) { - kfree(hda); pci_disable_device(pci); return err; } @@ -2005,7 +2012,7 @@ static int azx_first_init(struct azx *chip) /* codec detection */ if (!azx_bus(chip)->codec_mask) { dev_err(card->dev, "no codecs found!\n"); - return -ENODEV; + /* keep running the rest for the runtime PM */ } if (azx_acquire_irq(chip, 0) < 0) @@ -2027,24 +2034,15 @@ static void azx_firmware_cb(const struct firmware *fw, void *context) { struct snd_card *card = context; struct azx *chip = card->private_data; - struct pci_dev *pci = chip->pci; - - if (!fw) { - dev_err(card->dev, "Cannot load firmware, aborting\n"); - goto error; - } - chip->fw = fw; + if (fw) + chip->fw = fw; + else + dev_err(card->dev, "Cannot load firmware, continue without patching\n"); if (!chip->disabled) { /* continue probing */ - if (azx_probe_continue(chip)) - goto error; + azx_probe_continue(chip); } - return; /* OK */ - - error: - snd_card_free(card); - pci_set_drvdata(pci, NULL); } #endif @@ -2080,10 +2078,10 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream, * some HD-audio PCI entries are exposed without any codecs, and such devices * should be ignored from the beginning. */ -static const struct snd_pci_quirk driver_blacklist[] = { - SND_PCI_QUIRK(0x1043, 0x874f, "ASUS ROG Zenith II / Strix", 0), - SND_PCI_QUIRK(0x1462, 0xcb59, "MSI TRX40 Creator", 0), - SND_PCI_QUIRK(0x1462, 0xcb60, "MSI TRX40", 0), +static const struct pci_device_id driver_blacklist[] = { + { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */ + { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */ + { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */ {} }; @@ -2103,7 +2101,7 @@ static int azx_probe(struct pci_dev *pci, bool schedule_probe; int err; - if (snd_pci_quirk_lookup(pci, driver_blacklist)) { + if (pci_match_id(driver_blacklist, pci)) { dev_info(&pci->dev, "Skipping the blacklisted device\n"); return -ENODEV; } @@ -2308,9 +2306,11 @@ static int azx_probe_continue(struct azx *chip) #endif /* create codec instances */ - err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); - if (err < 0) - goto out_free; + if (bus->codec_mask) { + err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); + if (err < 0) + goto out_free; + } #ifdef CONFIG_SND_HDA_PATCH_LOADER if (chip->fw) { @@ -2324,7 +2324,7 @@ static int azx_probe_continue(struct azx *chip) #endif } #endif - if ((probe_only[dev] & 1) == 0) { + if (bus->codec_mask && !(probe_only[dev] & 1)) { err = azx_codec_configure(chip); if (err < 0) goto out_free; @@ -2341,17 +2341,23 @@ static int azx_probe_continue(struct azx *chip) set_default_power_save(chip); - if (azx_has_pm_runtime(chip)) + if (azx_has_pm_runtime(chip)) { + pm_runtime_use_autosuspend(&pci->dev); + pm_runtime_allow(&pci->dev); pm_runtime_put_autosuspend(&pci->dev); + } out_free: - if (err < 0 || !hda->need_i915_power) + if (err < 0) { + azx_free(chip); + return err; + } + + if (!hda->need_i915_power) display_power(chip, false); - if (err < 0) - hda->init_failed = 1; complete_all(&hda->probe_wait); to_hda_bus(bus)->bus_probing = 0; - return err; + return 0; } static void azx_remove(struct pci_dev *pci) @@ -2464,6 +2470,9 @@ static const struct pci_device_id azx_ids[] = { /* Icelake */ { PCI_DEVICE(0x8086, 0x34c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Icelake-H */ + { PCI_DEVICE(0x8086, 0x3dc8), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Jasperlake */ { PCI_DEVICE(0x8086, 0x38c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, @@ -2472,9 +2481,14 @@ static const struct pci_device_id azx_ids[] = { /* Tigerlake */ { PCI_DEVICE(0x8086, 0xa0c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Tigerlake-H */ + { PCI_DEVICE(0x8086, 0x43c8), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Elkhart Lake */ { PCI_DEVICE(0x8086, 0x4b55), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + { PCI_DEVICE(0x8086, 0x4b58), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Broxton-P(Apollolake) */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON }, @@ -2656,6 +2670,9 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x1002, 0xab20), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, + { PCI_DEVICE(0x1002, 0xab28), + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | + AZX_DCAPS_PM_RUNTIME }, { PCI_DEVICE(0x1002, 0xab38), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index 2acfff3da1a0..3fb119f09040 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -27,6 +27,7 @@ struct hda_intel { unsigned int use_vga_switcheroo:1; unsigned int vga_switcheroo_registered:1; unsigned int init_failed:1; /* delayed init failed */ + unsigned int freed:1; /* resources already released */ bool need_i915_power:1; /* the hda controller needs i915 power */ }; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 773992a07efa..0cc5fad1af8a 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -52,10 +52,21 @@ #define HDA_IPFS_INTR_MASK 0x188 #define HDA_IPFS_EN_INTR (1 << 16) +/* FPCI */ +#define FPCI_DBG_CFG_2 0x10F4 +#define FPCI_GCAP_NSDO_SHIFT 18 +#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT) + /* max number of SDs */ #define NUM_CAPTURE_SD 1 #define NUM_PLAYBACK_SD 1 +/* + * Tegra194 does not reflect correct number of SDO lines. Below macro + * is used to update the GCAP register to workaround the issue. + */ +#define TEGRA194_NUM_SDO_LINES 4 + struct hda_tegra { struct azx chip; struct device *dev; @@ -275,6 +286,7 @@ static int hda_tegra_init_clk(struct hda_tegra *hda) static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) { + struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); struct hdac_bus *bus = azx_bus(chip); struct snd_card *card = chip->card; int err; @@ -298,6 +310,26 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) bus->irq = irq_id; card->sync_irq = bus->irq; + /* + * Tegra194 has 4 SDO lines and the STRIPE can be used to + * indicate how many of the SDO lines the stream should be + * striped. But GCAP register does not reflect the true + * capability of HW. Below workaround helps to fix this. + * + * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2, + * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines. + */ + if (of_device_is_compatible(np, "nvidia,tegra194-hda")) { + u32 val; + + dev_info(card->dev, "Override SDO lines to %u\n", + TEGRA194_NUM_SDO_LINES); + + val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK; + val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT; + writel(val, hda->regs + FPCI_DBG_CFG_2); + } + gcap = azx_readw(chip, GCAP); dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); @@ -332,6 +364,23 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) /* initialize chip */ azx_init_chip(chip, 1); + /* + * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with + * 4 SDO lines due to legacy design limitation. Following + * is, from HD Audio Specification (Revision 1.0a), used to + * control striping of the stream across multiple SDO lines + * for sample rates <= 48K. + * + * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 } + * + * Due to legacy design issue it is recommended that above + * ratio must be greater than 8. Since number of SDO lines is + * in powers of 2, next available ratio is 16 which can be + * used as a limiting factor here. + */ + if (of_device_is_compatible(np, "nvidia,tegra194-hda")) + chip->bus.core.sdo_limit = 16; + /* codec detection */ if (!bus->codec_mask) { dev_err(card->dev, "no codecs found!\n"); @@ -408,6 +457,7 @@ static int hda_tegra_create(struct snd_card *card, static const struct of_device_id hda_tegra_match[] = { { .compatible = "nvidia,tegra30-hda" }, + { .compatible = "nvidia,tegra194-hda" }, {}, }; MODULE_DEVICE_TABLE(of, hda_tegra_match); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 403baca89452..41eaa89660c3 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -38,6 +38,10 @@ static bool static_hdmi_pcm; module_param(static_hdmi_pcm, bool, 0644); MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); +static bool enable_acomp = true; +module_param(enable_acomp, bool, 0444); +MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)"); + struct hdmi_spec_per_cvt { hda_nid_t cvt_nid; int assigned; @@ -255,7 +259,7 @@ static int hinfo_to_pcm_index(struct hda_codec *codec, if (get_pcm_rec(spec, pcm_idx)->stream == hinfo) return pcm_idx; - codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo); + codec_warn(codec, "HDMI: hinfo %p not tied to a PCM\n", hinfo); return -EINVAL; } @@ -273,7 +277,8 @@ static int hinfo_to_pin_index(struct hda_codec *codec, return pin_idx; } - codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo); + codec_dbg(codec, "HDMI: hinfo %p (pcm %d) not registered\n", hinfo, + hinfo_to_pcm_index(codec, hinfo)); return -EINVAL; } @@ -1800,33 +1805,43 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) static int hdmi_parse_codec(struct hda_codec *codec) { - hda_nid_t nid; + hda_nid_t start_nid; + unsigned int caps; int i, nodes; - nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid); - if (!nid || nodes < 0) { + nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &start_nid); + if (!start_nid || nodes < 0) { codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); return -EINVAL; } - for (i = 0; i < nodes; i++, nid++) { - unsigned int caps; - unsigned int type; + /* + * hdmi_add_pin() assumes total amount of converters to + * be known, so first discover all converters + */ + for (i = 0; i < nodes; i++) { + hda_nid_t nid = start_nid + i; caps = get_wcaps(codec, nid); - type = get_wcaps_type(caps); if (!(caps & AC_WCAP_DIGITAL)) continue; - switch (type) { - case AC_WID_AUD_OUT: + if (get_wcaps_type(caps) == AC_WID_AUD_OUT) hdmi_add_cvt(codec, nid); - break; - case AC_WID_PIN: + } + + /* discover audio pins */ + for (i = 0; i < nodes; i++) { + hda_nid_t nid = start_nid + i; + + caps = get_wcaps(codec, nid); + + if (!(caps & AC_WCAP_DIGITAL)) + continue; + + if (get_wcaps_type(caps) == AC_WID_PIN) hdmi_add_pin(codec, nid); - break; - } } return 0; @@ -1844,8 +1859,10 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) /* Add sanity check to pass klockwork check. * This should never happen. */ - if (WARN_ON(spdif == NULL)) + if (WARN_ON(spdif == NULL)) { + mutex_unlock(&codec->spdif_mutex); return true; + } non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO); mutex_unlock(&codec->spdif_mutex); return non_pcm; @@ -2018,7 +2035,7 @@ static const struct hda_pcm_ops generic_ops = { static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -2031,7 +2048,7 @@ static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -2045,7 +2062,7 @@ static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap, int prepared) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -2061,7 +2078,7 @@ static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -2194,7 +2211,9 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + struct hdmi_eld *pin_eld = &per_pin->sink_eld; + pin_eld->eld_valid = false; hdmi_present_sense(per_pin, 0); } @@ -2505,6 +2524,11 @@ static void generic_acomp_init(struct hda_codec *codec, { struct hdmi_spec *spec = codec->spec; + if (!enable_acomp) { + codec_info(codec, "audio component disabled by module option\n"); + return; + } + spec->port2pin = port2pin; setup_drm_audio_ops(codec, ops); if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops, @@ -3774,7 +3798,7 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, hda_nid_t pin_nid, int hdmi_slot, int stream_channel) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); int verb; int ati_channel_setup = 0; @@ -3810,7 +3834,7 @@ static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, hda_nid_t pin_nid, int asp_slot) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); bool was_odd = false; int ati_asp_slot = asp_slot; int verb; @@ -4132,6 +4156,11 @@ HDA_CODEC_ENTRY(0x10de0095, "GPU 95 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0097, "GPU 97 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0098, "GPU 98 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0099, "GPU 99 HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009a, "GPU 9a HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009d, "GPU 9d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009e, "GPU 9e HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de009f, "GPU 9f HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de00a0, "GPU a0 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x10de8067, "MCP67/68 HDMI", patch_nvhdmi_2ch), HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP", patch_via_hdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index de2826f90d34..1b2d8e56390a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -17,6 +17,7 @@ #include <linux/dmi.h> #include <linux/module.h> #include <linux/input.h> +#include <linux/leds.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/hda_codec.h> @@ -81,6 +82,7 @@ struct alc_spec { /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ int mute_led_polarity; + int micmute_led_polarity; hda_nid_t mute_led_nid; hda_nid_t cap_mute_led_nid; @@ -377,12 +379,14 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0233: case 0x10ec0235: case 0x10ec0236: + case 0x10ec0245: case 0x10ec0255: case 0x10ec0256: case 0x10ec0257: case 0x10ec0282: case 0x10ec0283: case 0x10ec0286: + case 0x10ec0287: case 0x10ec0288: case 0x10ec0285: case 0x10ec0298: @@ -797,9 +801,11 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) { if (!alc_subsystem_id(codec, ports)) { struct alc_spec *spec = codec->spec; - codec_dbg(codec, - "realtek: Enable default setup for auto mode as fallback\n"); - spec->init_amp = ALC_INIT_DEFAULT; + if (spec->init_amp == ALC_INIT_UNDEFINED) { + codec_dbg(codec, + "realtek: Enable default setup for auto mode as fallback\n"); + spec->init_amp = ALC_INIT_DEFAULT; + } } } @@ -2454,6 +2460,8 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), @@ -2469,6 +2477,9 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), @@ -4072,11 +4083,9 @@ static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec, /* update LED status via GPIO */ static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, - bool enabled) + int polarity, bool enabled) { - struct alc_spec *spec = codec->spec; - - if (spec->mute_led_polarity) + if (polarity) enabled = !enabled; alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ } @@ -4087,7 +4096,8 @@ static void alc_fixup_gpio_mute_hook(void *private_data, int enabled) struct hda_codec *codec = private_data; struct alc_spec *spec = codec->spec; - alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled); + alc_update_gpio_led(codec, spec->gpio_mute_led_mask, + spec->mute_led_polarity, enabled); } /* turn on/off mic-mute LED via GPIO per capture hook */ @@ -4096,9 +4106,30 @@ static void alc_gpio_micmute_update(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->micmute_led_polarity, spec->gen.micmute_led.led_value); } +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) +static int micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->micmute_led_polarity, !!brightness); + return 0; +} + +static struct led_classdev micmute_led_cdev = { + .name = "hda::micmute", + .max_brightness = 1, + .brightness_set_blocking = micmute_led_set, + .default_trigger = "audio-micmute", +}; +#endif + /* setup mute and mic-mute GPIO bits, add hooks appropriately */ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, int action, @@ -4106,6 +4137,9 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, unsigned int micmute_mask) { struct alc_spec *spec = codec->spec; +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + int err; +#endif alc_fixup_gpio(codec, action, mute_mask | micmute_mask); @@ -4118,6 +4152,13 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, if (micmute_mask) { spec->gpio_mic_led_mask = micmute_mask; snd_hda_gen_add_micmute_led(codec, alc_gpio_micmute_update); + +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); + err = devm_led_classdev_register(&codec->core.dev, &micmute_led_cdev); + if (err) + codec_warn(codec, "failed to register micmute LED\n"); +#endif } } @@ -4130,7 +4171,11 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - alc_fixup_hp_gpio_led(codec, action, 0x04, 0x00); + struct alc_spec *spec = codec->spec; + + spec->micmute_led_polarity = 1; + + alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01); } static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, @@ -5477,18 +5522,9 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec, { 0x19, 0x21a11010 }, /* dock mic */ { } }; - /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise - * the speaker output becomes too low by some reason on Thinkpads with - * ALC298 codec - */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x03, 0x17, 0x02, 0x21, 0x02, - 0 - }; struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.preferred_dacs = preferred_pairs; spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; snd_hda_apply_pincfgs(codec, pincfgs); } else if (action == HDA_FIXUP_ACT_INIT) { @@ -5501,6 +5537,23 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec, } } +static void alc_fixup_tpt470_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise + * the speaker output becomes too low by some reason on Thinkpads with + * ALC298 codec + */ + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x03, 0x17, 0x02, 0x21, 0x02, + 0 + }; + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gen.preferred_dacs = preferred_pairs; +} + static void alc_shutup_dell_xps13(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5792,7 +5845,8 @@ static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, snd_hda_gen_hp_automute(codec, jack); /* mute_led_polarity is set to 0, so we pass inverted value here */ - alc_update_gpio_led(codec, 0x10, !spec->gen.hp_jack_present); + alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity, + !spec->gen.hp_jack_present); } /* Manage GPIOs for HP EliteBook Folio 9480m. @@ -5853,6 +5907,15 @@ static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, } } +static void alc225_fixup_s3_pop_noise(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + codec->power_save_node = 1; +} + /* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */ static void alc274_fixup_bind_dacs(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -5957,6 +6020,7 @@ enum { ALC269_FIXUP_HP_LINE1_MIC1_LED, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, + ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, ALC269_FIXUP_NO_SHUTUP, ALC286_FIXUP_SONY_MIC_NO_PRESENCE, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, @@ -6042,9 +6106,11 @@ enum { ALC233_FIXUP_ACER_HEADSET_MIC, ALC294_FIXUP_LENOVO_MIC_LOCATION, ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, + ALC225_FIXUP_S3_POP_NOISE, ALC700_FIXUP_INTEL_REFERENCE, ALC274_FIXUP_DELL_BIND_DACS, ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + ALC298_FIXUP_TPT470_DOCK_FIX, ALC298_FIXUP_TPT470_DOCK, ALC255_FIXUP_DUMMY_LINEOUT_VERB, ALC255_FIXUP_DELL_HEADSET_MIC, @@ -6077,9 +6143,17 @@ enum { ALC294_FIXUP_ASUS_DUAL_SPK, ALC285_FIXUP_THINKPAD_HEADSET_JACK, ALC294_FIXUP_ASUS_HPE, + ALC294_FIXUP_ASUS_COEF_1B, ALC285_FIXUP_HP_GPIO_LED, ALC285_FIXUP_HP_MUTE_LED, ALC236_FIXUP_HP_MUTE_LED, + ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, + ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS, + ALC269VC_FIXUP_ACER_HEADSET_MIC, + ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE, + ALC289_FIXUP_ASUS_G401, + ALC256_FIXUP_ACER_MIC_NO_PRESENCE, }; static const struct hda_fixup alc269_fixups[] = { @@ -6277,6 +6351,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT }, + [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_LENOVO_DOCK, + }, [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_no_hp_to_lineout, @@ -6929,6 +7009,12 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, + .chain_id = ALC225_FIXUP_S3_POP_NOISE + }, + [ALC225_FIXUP_S3_POP_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc225_fixup_s3_pop_noise, + .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, [ALC700_FIXUP_INTEL_REFERENCE] = { @@ -6961,12 +7047,18 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC274_FIXUP_DELL_BIND_DACS }, - [ALC298_FIXUP_TPT470_DOCK] = { + [ALC298_FIXUP_TPT470_DOCK_FIX] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_tpt470_dock, .chained = true, .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE }, + [ALC298_FIXUP_TPT470_DOCK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_tpt470_dacs, + .chained = true, + .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX + }, [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -7027,7 +7119,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + .chain_id = ALC269_FIXUP_HEADSET_MIC }, [ALC294_FIXUP_ASUS_HEADSET_MIC] = { .type = HDA_FIXUP_PINS, @@ -7036,7 +7128,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, - .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC + .chain_id = ALC269_FIXUP_HEADSET_MIC }, [ALC294_FIXUP_ASUS_SPK] = { .type = HDA_FIXUP_VERBS, @@ -7044,6 +7136,8 @@ static const struct hda_fixup alc269_fixups[] = { /* Set EAPD high */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x40 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x8800 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7774 }, { } }, .chained = true, @@ -7201,6 +7295,17 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC }, + [ALC294_FIXUP_ASUS_COEF_1B] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Set bit 10 to correct noisy output after reboot from + * Windows 10 (due to pop noise reduction?) + */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b }, + { } + }, + }, [ALC285_FIXUP_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_hp_gpio_led, @@ -7213,6 +7318,67 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc236_fixup_hp_mute_led, }, + [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 }, + { } + }, + }, + [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, + [ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x90100120 }, /* use as internal speaker */ + { 0x18, 0x02a111f0 }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01011020 }, /* use as line out */ + { }, + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC269VC_FIXUP_ACER_HEADSET_MIC] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x02a11030 }, /* use as headset mic */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a11130 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MIC + }, + [ALC289_FIXUP_ASUS_G401] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x03a11020 }, /* headset mic with jack detect */ + { } + }, + }, + [ALC256_FIXUP_ACER_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x02a11120 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7228,16 +7394,20 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK), + SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), + SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x128f, "Acer Veriton Z6860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1290, "Acer Veriton Z4860G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1291, "Acer Veriton Z4660G", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS), SND_PCI_QUIRK(0x1028, 0x05bd, "Dell Latitude E6440", ALC292_FIXUP_DELL_E7X), @@ -7357,6 +7527,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), + SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), @@ -7378,13 +7550,18 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE), + SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_G401), SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC), @@ -7404,9 +7581,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC), SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), + SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC225_FIXUP_HEADSET_JACK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), + SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), + SND_PCI_QUIRK(0x144d, 0xc812, "Samsung Notebook Pen S (NT950SBE-X58)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), @@ -7416,12 +7597,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x8560, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x8561, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), + SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST), SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), @@ -7449,8 +7631,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x224c, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x224d, "Thinkpad", ALC298_FIXUP_TPT470_DOCK), SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), - SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Yoga 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x2293, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), @@ -7560,6 +7741,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"}, {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"}, {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, + {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"}, {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"}, {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, @@ -7571,6 +7753,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, + {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"}, {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"}, {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"}, @@ -7988,6 +8171,18 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60130}, {0x17, 0x90170110}, {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60120}, + {0x17, 0x90170110}, + {0x21, 0x04211030}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, {0x14, 0x90170110}, {0x21, 0x04211020}), @@ -8028,6 +8223,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { ALC225_STANDARD_PINS, {0x12, 0xb7a60130}, {0x17, 0x90170110}), + SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC, + {0x14, 0x01014010}, + {0x17, 0x90170120}, + {0x18, 0x02a11030}, + {0x19, 0x02a1103f}, + {0x21, 0x0221101f}), {} }; @@ -8195,7 +8396,9 @@ static int patch_alc269(struct hda_codec *codec) spec->gen.mixer_nid = 0; break; case 0x10ec0215: + case 0x10ec0245: case 0x10ec0285: + case 0x10ec0287: case 0x10ec0289: spec->codec_variant = ALC269_TYPE_ALC215; spec->shutup = alc225_shutup; @@ -8203,8 +8406,6 @@ static int patch_alc269(struct hda_codec *codec) spec->gen.mixer_nid = 0; break; case 0x10ec0225: - codec->power_save_node = 1; - /* fall through */ case 0x10ec0295: case 0x10ec0299: spec->codec_variant = ALC269_TYPE_ALC225; @@ -9456,6 +9657,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269), HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269), HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269), @@ -9475,6 +9677,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269), HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269), HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269), HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269), HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269), HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 884d0cdec08c..73e1e5400506 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2332,7 +2332,8 @@ static int snd_ice1712_chip_init(struct snd_ice1712 *ice) pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]); pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]); pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]); - if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) { + if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24 && + ice->eeprom.subvendor != ICE1712_SUBDEVICE_STAUDIO_ADCIII) { ice->gpio.write_mask = ice->eeprom.gpiomask; ice->gpio.direction = ice->eeprom.gpiodir; snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 6a0520c4fb5a..cf801a235df9 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -460,7 +460,7 @@ static void xonar_st_init(struct oxygen *chip) data->generic.anti_pop_delay = 100; data->h6 = chip->model.dac_channels_mixer > 2; - data->has_cs2000 = 1; + data->has_cs2000 = true; data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1; data->broken_i2c = true; @@ -502,7 +502,7 @@ static void xonar_xense_init(struct oxygen *chip) xonar_init_ext_power(chip); data->generic.anti_pop_delay = 100; - data->has_cs2000 = 1; + data->has_cs2000 = true; data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1; oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 592532c09a82..2e750b317be1 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -226,7 +226,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, offset += rec->period_size; } /* make loop */ - cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS); + cp->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS); cp->cmd_dep = cpu_to_le32(rec->cmd.addr); snd_pmac_dma_stop(rec); @@ -726,7 +726,7 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add chip->extra_dma.cmds->xfer_status = cpu_to_le16(0); chip->extra_dma.cmds->cmd_dep = cpu_to_le32(chip->extra_dma.addr); chip->extra_dma.cmds->phy_addr = cpu_to_le32(addr); - chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE + BR_ALWAYS); + chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS); out_le32(&chip->awacs->control, (in_le32(&chip->awacs->control) & ~0x1f00) | (speed << 8)); diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 6d2a33b8faa0..b8161a08f2ca 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -926,7 +926,7 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) PAGE_SHIFT, /* use system page size */ 0, /* dma type; not used */ NULL, - _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); + ALIGN(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); dev->d_region->ioid = PS3_AUDIO_IOID; ret = ps3_dma_region_create(dev->d_region); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index bdc305cece6e..71a6fe87d1a1 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -10,7 +10,7 @@ menuconfig SND_SOC select SND_JACK select REGMAP_I2C if I2C select REGMAP_SPI if SPI_MASTER - ---help--- + help If you want ASoC support, you should say Y here and also to the specific driver for your SoC platform below. diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h index 598b07d9b6fe..d0efc5e254ae 100644 --- a/sound/soc/codecs/hdac_hda.h +++ b/sound/soc/codecs/hdac_hda.h @@ -28,10 +28,6 @@ struct hdac_hda_priv { bool need_display_power; }; -#define hdac_to_hda_priv(_hdac) \ - container_of(_hdac, struct hdac_hda_priv, codec.core) -#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core) - struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void); #endif /* __HDAC_HDA_H__ */ diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index dc05c57db03a..c72cb2888c21 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -59,19 +59,19 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv, static const struct snd_kcontrol_new max9867_snd_controls[] = { SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL, - MAX9867_RIGHTVOL, 0, 41, 1, max9867_master_tlv), + MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv), SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv), SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN, MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv), SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, 5, 4, 0, max9867_micboost_tlv), + MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv), SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1), SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1, max9867_dac_tlv), SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0, max9867_dacboost_tlv), - SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 0, 4, 15, 1, + SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1, max9867_adc_tlv), SOC_ENUM("Speaker Mode", max9867_spkmode), SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index d27947aeb079..0594f89ea7f2 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -16,12 +16,12 @@ #include <linux/dmaengine.h> #include <linux/pci.h> #include <linux/acpi.h> +#include <linux/pgtable.h> /* supported DMA engine drivers */ #include <linux/dma/dw.h> #include <asm/page.h> -#include <asm/pgtable.h> #include "sst-dsp.h" #include "sst-dsp-priv.h" diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index c183f8e94ee4..16ac16f5a641 100644 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -10,8 +10,8 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/pm_runtime.h> +#include <linux/pgtable.h> #include <asm/page.h> -#include <asm/pgtable.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 112911dc271b..99a49248e966 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -4,7 +4,7 @@ menuconfig SND_SOC_SAMSUNG depends on PLAT_SAMSUNG || ARCH_EXYNOS || COMPILE_TEST depends on COMMON_CLK select SND_SOC_GENERIC_DMAENGINE_PCM - ---help--- + help Say Y or M if you want to add support for codecs attached to the Samsung SoCs' Audio interfaces. You will also need to select the audio interfaces to support below. diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 653a58c96e24..2491e1ce16d3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4338,16 +4338,16 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, codec = codec_dai->playback_widget; if (playback_cpu && codec) { - if (dai_link->params && !dai_link->playback_widget) { + if (dai_link->params && !rtd->playback_widget) { substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; dai = snd_soc_dapm_new_dai(card, substream, "playback"); if (IS_ERR(dai)) goto capture; - dai_link->playback_widget = dai; + rtd->playback_widget = dai; } dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu, - dai_link->playback_widget, + rtd->playback_widget, codec_dai, codec); } @@ -4356,16 +4356,16 @@ capture: codec = codec_dai->capture_widget; if (codec && capture_cpu) { - if (dai_link->params && !dai_link->capture_widget) { + if (dai_link->params && !rtd->capture_widget) { substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; dai = snd_soc_dapm_new_dai(card, substream, "capture"); if (IS_ERR(dai)) return; - dai_link->capture_widget = dai; + rtd->capture_widget = dai; } dapm_connect_dai_routes(&card->dapm, codec_dai, codec, - dai_link->capture_widget, + rtd->capture_widget, cpu_dai, capture_cpu); } } diff --git a/sound/soc/sof/probe.h b/sound/soc/sof/probe.h index b04b728c7224..5e159ab239fa 100644 --- a/sound/soc/sof/probe.h +++ b/sound/soc/sof/probe.h @@ -36,7 +36,7 @@ struct sof_probe_point_desc { struct sof_ipc_probe_dma_add_params { struct sof_ipc_cmd_hdr hdr; unsigned int num_elems; - struct sof_probe_dma dma[0]; + struct sof_probe_dma dma[]; } __packed; struct sof_ipc_probe_info_params { @@ -51,19 +51,19 @@ struct sof_ipc_probe_info_params { struct sof_ipc_probe_dma_remove_params { struct sof_ipc_cmd_hdr hdr; unsigned int num_elems; - unsigned int stream_tag[0]; + unsigned int stream_tag[]; } __packed; struct sof_ipc_probe_point_add_params { struct sof_ipc_cmd_hdr hdr; unsigned int num_elems; - struct sof_probe_point_desc desc[0]; + struct sof_probe_point_desc desc[]; } __packed; struct sof_ipc_probe_point_remove_params { struct sof_ipc_cmd_hdr hdr; unsigned int num_elems; - unsigned int buffer_id[0]; + unsigned int buffer_id[]; } __packed; int sof_ipc_probe_init(struct snd_sof_dev *sdev, diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c index b1d9615f2375..d9e348444bd0 100644 --- a/sound/soc/txx9/txx9aclc-ac97.c +++ b/sound/soc/txx9/txx9aclc-ac97.c @@ -14,6 +14,7 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/gfp.h> +#include <asm/mach-tx39xx/ioremap.h> /* for TXX9_DIRECTMAP_BASE */ #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> diff --git a/sound/usb/card.c b/sound/usb/card.c index fd6fd1726ea0..162bdd6eb4d4 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -634,7 +634,6 @@ static int usb_audio_probe(struct usb_interface *intf, id, &chip); if (err < 0) goto __error; - chip->pm_intf = intf; break; } else if (vid[i] != -1 || pid[i] != -1) { dev_info(&dev->dev, @@ -651,6 +650,13 @@ static int usb_audio_probe(struct usb_interface *intf, goto __error; } } + + if (chip->num_interfaces >= MAX_CARD_INTERFACES) { + dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n"); + err = -EINVAL; + goto __error; + } + dev_set_drvdata(&dev->dev, chip); /* @@ -703,6 +709,7 @@ static int usb_audio_probe(struct usb_interface *intf, } usb_chip[chip->index] = chip; + chip->intf[chip->num_interfaces] = intf; chip->num_interfaces++; usb_set_intfdata(intf, chip); atomic_dec(&chip->active); @@ -818,19 +825,37 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) int snd_usb_autoresume(struct snd_usb_audio *chip) { + int i, err; + if (atomic_read(&chip->shutdown)) return -EIO; - if (atomic_inc_return(&chip->active) == 1) - return usb_autopm_get_interface(chip->pm_intf); + if (atomic_inc_return(&chip->active) != 1) + return 0; + + for (i = 0; i < chip->num_interfaces; i++) { + err = usb_autopm_get_interface(chip->intf[i]); + if (err < 0) { + /* rollback */ + while (--i >= 0) + usb_autopm_put_interface(chip->intf[i]); + atomic_dec(&chip->active); + return err; + } + } return 0; } void snd_usb_autosuspend(struct snd_usb_audio *chip) { + int i; + if (atomic_read(&chip->shutdown)) return; - if (atomic_dec_and_test(&chip->active)) - usb_autopm_put_interface(chip->pm_intf); + if (!atomic_dec_and_test(&chip->active)) + return; + + for (i = 0; i < chip->num_interfaces; i++) + usb_autopm_put_interface(chip->intf[i]); } static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) @@ -843,9 +868,6 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) if (chip == (void *)-1L) return 0; - chip->autosuspended = !!PMSG_IS_AUTO(message); - if (!chip->autosuspended) - snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); if (!chip->num_suspended_intf++) { list_for_each_entry(as, &chip->pcm_list, list) { snd_usb_pcm_suspend(as); @@ -858,6 +880,11 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) snd_usb_mixer_suspend(mixer); } + if (!PMSG_IS_AUTO(message) && !chip->system_suspend) { + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); + chip->system_suspend = chip->num_suspended_intf; + } + return 0; } @@ -871,10 +898,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) if (chip == (void *)-1L) return 0; - if (--chip->num_suspended_intf) - return 0; atomic_inc(&chip->active); /* avoid autopm */ + if (chip->num_suspended_intf > 1) + goto out; list_for_each_entry(as, &chip->pcm_list, list) { err = snd_usb_pcm_resume(as); @@ -896,9 +923,12 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) snd_usbmidi_resume(p); } - if (!chip->autosuspended) + out: + if (chip->num_suspended_intf == chip->system_suspend) { snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); - chip->autosuspended = 0; + chip->system_suspend = 0; + } + chip->num_suspended_intf--; err_out: atomic_dec(&chip->active); /* allow autopm after this point */ diff --git a/sound/usb/card.h b/sound/usb/card.h index 395403a2d33f..de43267b9c8a 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -84,6 +84,10 @@ struct snd_usb_endpoint { dma_addr_t sync_dma; /* DMA address of syncbuf */ unsigned int pipe; /* the data i/o pipe */ + unsigned int packsize[2]; /* small/large packet sizes in samples */ + unsigned int sample_rem; /* remainder from division fs/pps */ + unsigned int sample_accum; /* sample accumulator */ + unsigned int pps; /* packets per second */ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */ int freqshift; /* how much to shift the feedback value to get Q16.16 */ @@ -104,6 +108,7 @@ struct snd_usb_endpoint { int iface, altsetting; int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ + bool is_implicit_feedback; /* This endpoint is used as implicit feedback */ spinlock_t lock; struct list_head list; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 4a9a2f6ef5a4..88760268fb55 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -124,12 +124,12 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep) /* * For streaming based on information derived from sync endpoints, - * prepare_outbound_urb_sizes() will call next_packet_size() to + * prepare_outbound_urb_sizes() will call slave_next_packet_size() to * determine the number of samples to be sent in the next packet. * - * For implicit feedback, next_packet_size() is unused. + * For implicit feedback, slave_next_packet_size() is unused. */ -int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep) { unsigned long flags; int ret; @@ -146,6 +146,29 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) return ret; } +/* + * For adaptive and synchronous endpoints, prepare_outbound_urb_sizes() + * will call next_packet_size() to determine the number of samples to be + * sent in the next packet. + */ +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) +{ + 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; + ret = ep->packsize[1]; + } else { + ret = ep->packsize[0]; + } + + return ret; +} + static void retire_outbound_urb(struct snd_usb_endpoint *ep, struct snd_urb_ctx *urb_ctx) { @@ -190,6 +213,8 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, if (ctx->packet_size[i]) counts = ctx->packet_size[i]; + else if (ep->sync_master) + counts = snd_usb_endpoint_slave_next_packet_size(ep); else counts = snd_usb_endpoint_next_packet_size(ep); @@ -321,17 +346,17 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) ep->next_packet_read_pos %= MAX_URBS; /* take URB out of FIFO */ - if (!list_empty(&ep->ready_playback_urbs)) + if (!list_empty(&ep->ready_playback_urbs)) { ctx = list_first_entry(&ep->ready_playback_urbs, struct snd_urb_ctx, ready_list); + list_del_init(&ctx->ready_list); + } } spin_unlock_irqrestore(&ep->lock, flags); if (ctx == NULL) return; - list_del_init(&ctx->ready_list); - /* copy over the length information */ for (i = 0; i < packet->packets; i++) ctx->packet_size[i] = packet->packet_size[i]; @@ -497,6 +522,8 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, list_add_tail(&ep->list, &chip->ep_list); + ep->is_implicit_feedback = 0; + __exit_unlock: mutex_unlock(&chip->mutex); @@ -597,6 +624,178 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) } /* + * Check data endpoint for format differences + */ +static bool check_ep_params(struct snd_usb_endpoint *ep, + snd_pcm_format_t pcm_format, + unsigned int channels, + unsigned int period_bytes, + unsigned int frames_per_period, + unsigned int periods_per_buffer, + struct audioformat *fmt, + struct snd_usb_endpoint *sync_ep) +{ + unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb; + unsigned int max_packs_per_period, urbs_per_period, urb_packs; + unsigned int max_urbs; + int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; + int tx_length_quirk = (ep->chip->tx_length_quirk && + usb_pipeout(ep->pipe)); + bool ret = 1; + + if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { + /* + * When operating in DSD DOP mode, the size of a sample frame + * in hardware differs from the actual physical format width + * because we need to make room for the DOP markers. + */ + frame_bits += channels << 3; + } + + ret = ret && (ep->datainterval == fmt->datainterval); + ret = ret && (ep->stride == frame_bits >> 3); + + switch (pcm_format) { + case SNDRV_PCM_FORMAT_U8: + ret = ret && (ep->silence_value == 0x80); + break; + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + ret = ret && (ep->silence_value == 0x69); + break; + default: + ret = ret && (ep->silence_value == 0); + } + + /* assume max. frequency is 50% higher than nominal */ + ret = ret && (ep->freqmax == ep->freqn + (ep->freqn >> 1)); + /* Round up freqmax to nearest integer in order to calculate maximum + * packet size, which must represent a whole number of frames. + * This is accomplished by adding 0x0.ffff before converting the + * Q16.16 format into integer. + * In order to accurately calculate the maximum packet size when + * the data interval is more than 1 (i.e. ep->datainterval > 0), + * multiply by the data interval prior to rounding. For instance, + * a freqmax of 41 kHz will result in a max packet size of 6 (5.125) + * frames with a data interval of 1, but 11 (10.25) frames with a + * data interval of 2. + * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the + * maximum datainterval value of 3, at USB full speed, higher for + * USB high speed, noting that ep->freqmax is in units of + * frames per packet in Q16.16 format.) + */ + maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) * + (frame_bits >> 3); + if (tx_length_quirk) + maxsize += sizeof(__le32); /* Space for length descriptor */ + /* but wMaxPacketSize might reduce this */ + if (ep->maxpacksize && ep->maxpacksize < maxsize) { + /* whatever fits into a max. size packet */ + unsigned int data_maxsize = maxsize = ep->maxpacksize; + + if (tx_length_quirk) + /* Need to remove the length descriptor to calc freq */ + data_maxsize -= sizeof(__le32); + ret = ret && (ep->freqmax == (data_maxsize / (frame_bits >> 3)) + << (16 - ep->datainterval)); + } + + if (ep->fill_max) + ret = ret && (ep->curpacksize == ep->maxpacksize); + else + ret = ret && (ep->curpacksize == maxsize); + + if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) { + packs_per_ms = 8 >> ep->datainterval; + max_packs_per_urb = MAX_PACKS_HS; + } else { + packs_per_ms = 1; + max_packs_per_urb = MAX_PACKS; + } + if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) + max_packs_per_urb = min(max_packs_per_urb, + 1U << sync_ep->syncinterval); + max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); + + /* + * Capture endpoints need to use small URBs because there's no way + * to tell in advance where the next period will end, and we don't + * want the next URB to complete much after the period ends. + * + * Playback endpoints with implicit sync much use the same parameters + * as their corresponding capture endpoint. + */ + if (usb_pipein(ep->pipe) || + snd_usb_endpoint_implicit_feedback_sink(ep)) { + + urb_packs = packs_per_ms; + /* + * Wireless devices can poll at a max rate of once per 4ms. + * For dataintervals less than 5, increase the packet count to + * allow the host controller to use bursting to fill in the + * gaps. + */ + if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_WIRELESS) { + int interval = ep->datainterval; + + while (interval < 5) { + urb_packs <<= 1; + ++interval; + } + } + /* make capture URBs <= 1 ms and smaller than a period */ + urb_packs = min(max_packs_per_urb, urb_packs); + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; + ret = ret && (ep->nurbs == MAX_URBS); + + /* + * Playback endpoints without implicit sync are adjusted so that + * a period fits as evenly as possible in the smallest number of + * URBs. The total number of URBs is adjusted to the size of the + * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits. + */ + } else { + /* determine how small a packet can be */ + minsize = (ep->freqn >> (16 - ep->datainterval)) * + (frame_bits >> 3); + /* with sync from device, assume it can be 12% lower */ + if (sync_ep) + minsize -= minsize >> 3; + minsize = max(minsize, 1u); + + /* how many packets will contain an entire ALSA period? */ + max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize); + + /* how many URBs will contain a period? */ + urbs_per_period = DIV_ROUND_UP(max_packs_per_period, + max_packs_per_urb); + /* how many packets are needed in each URB? */ + urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period); + + /* limit the number of frames in a single URB */ + ret = ret && (ep->max_urb_frames == + DIV_ROUND_UP(frames_per_period, urbs_per_period)); + + /* try to use enough URBs to contain an entire ALSA buffer */ + max_urbs = min((unsigned) MAX_URBS, + MAX_QUEUE * packs_per_ms / urb_packs); + ret = ret && (ep->nurbs == min(max_urbs, + urbs_per_period * periods_per_buffer)); + } + + ret = ret && (ep->datainterval == fmt->datainterval); + ret = ret && (ep->maxpacksize == fmt->maxpacksize); + ret = ret && + (ep->fill_max == !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)); + + return ret; +} + +/* * configure a data endpoint */ static int data_ep_set_params(struct snd_usb_endpoint *ep, @@ -861,10 +1060,23 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, int err; if (ep->use_count != 0) { - usb_audio_warn(ep->chip, - "Unable to change format on ep #%x: already in use\n", - ep->ep_num); - return -EBUSY; + bool check = ep->is_implicit_feedback && + check_ep_params(ep, pcm_format, + channels, period_bytes, + period_frames, buffer_periods, + fmt, sync_ep); + + if (!check) { + usb_audio_warn(ep->chip, + "Unable to change format on ep #%x: already in use\n", + ep->ep_num); + return -EBUSY; + } + + usb_audio_dbg(ep->chip, + "Ep #%x already in use as implicit feedback but format not changed\n", + ep->ep_num); + return 0; } /* release old buffers, if any */ @@ -874,10 +1086,17 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, ep->maxpacksize = fmt->maxpacksize; ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX); - if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) + if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) { ep->freqn = get_usb_full_speed_rate(rate); - else + ep->pps = 1000 >> ep->datainterval; + } else { ep->freqn = get_usb_high_speed_rate(rate); + ep->pps = 8000 >> ep->datainterval; + } + + ep->sample_rem = rate % ep->pps; + ep->packsize[0] = rate / ep->pps; + ep->packsize[1] = (rate + (ep->pps - 1)) / ep->pps; /* calculate the frequency in 16.16 format */ ep->freqm = ep->freqn; @@ -936,6 +1155,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) ep->active_mask = 0; ep->unlink_mask = 0; ep->phase = 0; + ep->sample_accum = 0; snd_usb_endpoint_start_quirk(ep); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 63a39d4fa8d8..d23fa0a8c11b 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -28,6 +28,7 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free(struct snd_usb_endpoint *ep); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep); int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, diff --git a/sound/usb/format.c b/sound/usb/format.c index 50e1874c847c..1b28d01d1f4c 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -278,6 +278,52 @@ static bool s1810c_valid_sample_rate(struct audioformat *fp, } /* + * Many Focusrite devices supports a limited set of sampling rates per + * altsetting. Maximum rate is exposed in the last 4 bytes of Format Type + * descriptor which has a non-standard bLength = 10. + */ +static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip, + struct audioformat *fp, + unsigned int rate) +{ + struct usb_interface *iface; + struct usb_host_interface *alts; + unsigned char *fmt; + unsigned int max_rate; + + iface = usb_ifnum_to_if(chip->dev, fp->iface); + if (!iface) + return true; + + alts = &iface->altsetting[fp->altset_idx]; + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_FORMAT_TYPE); + if (!fmt) + return true; + + if (fmt[0] == 10) { /* bLength */ + max_rate = combine_quad(&fmt[6]); + + /* Validate max rate */ + if (max_rate != 48000 && + max_rate != 96000 && + max_rate != 192000 && + max_rate != 384000) { + + usb_audio_info(chip, + "%u:%d : unexpected max rate: %u\n", + fp->iface, fp->altsetting, max_rate); + + return true; + } + + return rate <= max_rate; + } + + return true; +} + +/* * Helper function to walk the array of sample rate triplets reported by * the device. The problem is that we need to parse whole array first to * get to know how many sample rates we have to expect. @@ -319,6 +365,11 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, !s1810c_valid_sample_rate(fp, rate)) goto skip_rate; + /* Filter out invalid rates on Focusrite devices */ + if (USB_ID_VENDOR(chip->usb_id) == 0x1235 && + !focusrite_valid_sample_rate(chip, fp, rate)) + goto skip_rate; + if (fp->rate_table) fp->rate_table[nr_rates] = rate; if (!fp->rate_min || rate < fp->rate_min) @@ -343,8 +394,9 @@ skip_rate: return nr_rates; } -/* Line6 Helix series don't support the UAC2_CS_RANGE usb function - * call. Return a static table of known clock rates. +/* Line6 Helix series and the Rode Rodecaster Pro don't support the + * UAC2_CS_RANGE usb function call. Return a static table of known + * clock rates. */ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip, struct audioformat *fp) @@ -357,6 +409,7 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip, case USB_ID(0x0e41, 0x4248): /* Line6 Helix >= fw 2.82 */ case USB_ID(0x0e41, 0x4249): /* Line6 Helix Rack >= fw 2.82 */ case USB_ID(0x0e41, 0x424a): /* Line6 Helix LT >= fw 2.82 */ + case USB_ID(0x19f7, 0x0011): /* Rode Rodecaster Pro */ return set_fixed_rate(fp, 48000, SNDRV_PCM_RATE_48000); } diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c index 663d608c4287..970c9bdce0b2 100644 --- a/sound/usb/line6/capture.c +++ b/sound/usb/line6/capture.c @@ -286,6 +286,8 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) urb->interval = LINE6_ISO_INTERVAL; urb->error_count = 0; urb->complete = audio_in_callback; + if (usb_urb_ep_type_check(urb)) + return -EINVAL; } return 0; diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index 4f096685ed65..2746d9698180 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -550,6 +550,7 @@ static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file) /* NOTE: hwdep layer provides atomicity here */ line6->messages.active = 1; + line6->messages.nonblock = file->f_flags & O_NONBLOCK ? 1 : 0; return 0; } @@ -579,6 +580,9 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, while (kfifo_len(&line6->messages.fifo) == 0) { mutex_unlock(&line6->messages.read_lock); + if (line6->messages.nonblock) + return -EAGAIN; + rv = wait_event_interruptible( line6->messages.wait_queue, kfifo_len(&line6->messages.fifo) != 0); @@ -626,11 +630,27 @@ line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count, return rv; } +static __poll_t +line6_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) +{ + __poll_t rv; + struct usb_line6 *line6 = hwdep->private_data; + + poll_wait(file, &line6->messages.wait_queue, wait); + + mutex_lock(&line6->messages.read_lock); + rv = kfifo_len(&line6->messages.fifo) == 0 ? 0 : EPOLLIN | EPOLLRDNORM; + mutex_unlock(&line6->messages.read_lock); + + return rv; +} + static const struct snd_hwdep_ops hwdep_ops = { .open = line6_hwdep_open, .release = line6_hwdep_release, .read = line6_hwdep_read, .write = line6_hwdep_write, + .poll = line6_hwdep_poll, }; /* Insert into circular buffer */ @@ -820,7 +840,7 @@ void line6_disconnect(struct usb_interface *interface) if (WARN_ON(usbdev != line6->usbdev)) return; - cancel_delayed_work(&line6->startup_work); + cancel_delayed_work_sync(&line6->startup_work); if (line6->urb_listen != NULL) line6_stop_listen(line6); diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index e5e572ed5f30..1a4e3700c80c 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -163,6 +163,7 @@ struct usb_line6 { struct mutex read_lock; wait_queue_head_t wait_queue; unsigned int active:1; + unsigned int nonblock:1; STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT) fifo; } messages; diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c index 01930ce7bd75..8233c61e23f1 100644 --- a/sound/usb/line6/playback.c +++ b/sound/usb/line6/playback.c @@ -431,6 +431,8 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) urb->interval = LINE6_ISO_INTERVAL; urb->error_count = 0; urb->complete = audio_out_callback; + if (usb_urb_ep_type_check(urb)) + return -EINVAL; } return 0; diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index d37db32ecd3b..e39dc85c355a 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -21,8 +21,7 @@ enum { LINE6_PODHD300, LINE6_PODHD400, - LINE6_PODHD500_0, - LINE6_PODHD500_1, + LINE6_PODHD500, LINE6_PODX3, LINE6_PODX3LIVE, LINE6_PODHD500X, @@ -318,8 +317,7 @@ static const struct usb_device_id podhd_id_table[] = { /* TODO: no need to alloc data interfaces when only audio is used */ { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, - { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, - { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, + { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500 }, { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 }, { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X }, @@ -352,23 +350,13 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x82, .ep_audio_w = 0x01, }, - [LINE6_PODHD500_0] = { + [LINE6_PODHD500] = { .id = "PODHD500", .name = "POD HD500", - .capabilities = LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_CONTROL | LINE6_CAP_HWMON, .altsetting = 1, - .ep_ctrl_r = 0x81, - .ep_ctrl_w = 0x01, - .ep_audio_r = 0x86, - .ep_audio_w = 0x02, - }, - [LINE6_PODHD500_1] = { - .id = "PODHD500", - .name = "POD HD500", - .capabilities = LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 0, + .ctrl_if = 1, .ep_ctrl_r = 0x81, .ep_ctrl_w = 0x01, .ep_audio_r = 0x86, diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 047b90595d65..354f57692938 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1499,6 +1499,8 @@ void snd_usbmidi_disconnect(struct list_head *p) spin_unlock_irq(&umidi->disc_lock); up_write(&umidi->disc_rwsem); + del_timer_sync(&umidi->error_timer); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i]; if (ep->out) @@ -1525,7 +1527,6 @@ void snd_usbmidi_disconnect(struct list_head *p) ep->in = NULL; } } - del_timer_sync(&umidi->error_timer); } EXPORT_SYMBOL(snd_usbmidi_disconnect); @@ -2301,16 +2302,22 @@ void snd_usbmidi_input_stop(struct list_head *p) } EXPORT_SYMBOL(snd_usbmidi_input_stop); -static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint *ep) +static void snd_usbmidi_input_start_ep(struct snd_usb_midi *umidi, + struct snd_usb_midi_in_endpoint *ep) { unsigned int i; + unsigned long flags; if (!ep) return; for (i = 0; i < INPUT_URBS; ++i) { struct urb *urb = ep->urbs[i]; - urb->dev = ep->umidi->dev; - snd_usbmidi_submit_urb(urb, GFP_KERNEL); + spin_lock_irqsave(&umidi->disc_lock, flags); + if (!atomic_read(&urb->use_count)) { + urb->dev = ep->umidi->dev; + snd_usbmidi_submit_urb(urb, GFP_ATOMIC); + } + spin_unlock_irqrestore(&umidi->disc_lock, flags); } } @@ -2326,7 +2333,7 @@ void snd_usbmidi_input_start(struct list_head *p) if (umidi->input_running || !umidi->opened[1]) return; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) - snd_usbmidi_input_start_ep(umidi->endpoints[i].in); + snd_usbmidi_input_start_ep(umidi, umidi->endpoints[i].in); umidi->input_running = 1; } EXPORT_SYMBOL(snd_usbmidi_input_start); diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 721d12130d0c..eab0fd4fd7c3 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -581,8 +581,9 @@ static int check_matrix_bitmap(unsigned char *bmap, * if failed, give up and free the control instance. */ -int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, - struct snd_kcontrol *kctl) +int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list, + struct snd_kcontrol *kctl, + bool is_std_info) { struct usb_mixer_interface *mixer = list->mixer; int err; @@ -596,6 +597,7 @@ int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, return err; } list->kctl = kctl; + list->is_std_info = is_std_info; list->next_id_elem = mixer->id_elems[list->id]; mixer->id_elems[list->id] = list; return 0; @@ -1182,6 +1184,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, cval->res = 384; } break; + case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */ + if ((strstr(kctl->id.name, "Playback Volume") != NULL) || + strstr(kctl->id.name, "Capture Volume") != NULL) { + cval->min >>= 8; + cval->max = 0; + cval->res = 1; + } + break; } } @@ -1457,7 +1467,7 @@ error: usb_audio_err(chip, "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", UAC_GET_CUR, validx, idx, cval->val_type); - return ret; + return filter_error(cval, ret); } ucontrol->value.integer.value[0] = val; @@ -1771,10 +1781,16 @@ static void get_connector_control_name(struct usb_mixer_interface *mixer, /* Build a mixer control for a UAC connector control (jack-detect) */ static void build_connector_control(struct usb_mixer_interface *mixer, + const struct usbmix_name_map *imap, struct usb_audio_term *term, bool is_input) { struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; + const struct usbmix_name_map *map; + + map = find_map(imap, term->id, 0); + if (check_ignored_ctl(map)) + return; cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) @@ -1805,8 +1821,12 @@ static void build_connector_control(struct usb_mixer_interface *mixer, usb_mixer_elem_info_free(cval); return; } - get_connector_control_name(mixer, term, is_input, kctl->id.name, - sizeof(kctl->id.name)); + + if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) + strlcat(kctl->id.name, " Jack", sizeof(kctl->id.name)); + else + get_connector_control_name(mixer, term, is_input, kctl->id.name, + sizeof(kctl->id.name)); kctl->private_free = snd_usb_mixer_elem_free; snd_usb_mixer_add_control(&cval->head, kctl); } @@ -2109,8 +2129,9 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid, check_input_term(state, term_id, &iterm); /* Check for jack detection. */ - if (uac_v2v3_control_is_readable(bmctls, control)) - build_connector_control(state->mixer, &iterm, true); + if ((iterm.type & 0xff00) != 0x0100 && + uac_v2v3_control_is_readable(bmctls, control)) + build_connector_control(state->mixer, state->map, &iterm, true); return 0; } @@ -3071,13 +3092,13 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, memset(&iterm, 0, sizeof(iterm)); iterm.id = UAC3_BADD_IT_ID4; iterm.type = UAC_BIDIR_TERMINAL_HEADSET; - build_connector_control(mixer, &iterm, true); + build_connector_control(mixer, map->map, &iterm, true); /* Output Term - Insertion control */ memset(&oterm, 0, sizeof(oterm)); oterm.id = UAC3_BADD_OT_ID3; oterm.type = UAC_BIDIR_TERMINAL_HEADSET; - build_connector_control(mixer, &oterm, false); + build_connector_control(mixer, map->map, &oterm, false); } return 0; @@ -3106,7 +3127,8 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) if (map->id == state.chip->usb_id) { state.map = map->map; state.selector_map = map->selector_map; - mixer->ignore_ctl_error = map->ignore_ctl_error; + mixer->connector_map = map->connector_map; + mixer->ignore_ctl_error |= map->ignore_ctl_error; break; } } @@ -3149,10 +3171,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) if (err < 0 && err != -EINVAL) return err; - if (uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls), + if ((state.oterm.type & 0xff00) != 0x0100 && + uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls), UAC2_TE_CONNECTOR)) { - build_connector_control(state.mixer, &state.oterm, - false); + build_connector_control(state.mixer, state.map, + &state.oterm, false); } } else { /* UAC_VERSION_3 */ struct uac3_output_terminal_descriptor *desc = p; @@ -3174,10 +3197,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) if (err < 0 && err != -EINVAL) return err; - if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls), + if ((state.oterm.type & 0xff00) != 0x0100 && + uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls), UAC3_TE_INSERTION)) { - build_connector_control(state.mixer, &state.oterm, - false); + build_connector_control(state.mixer, state.map, + &state.oterm, false); } } } @@ -3185,13 +3209,38 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) return 0; } +static int delegate_notify(struct usb_mixer_interface *mixer, int unitid, + u8 *control, u8 *channel) +{ + const struct usbmix_connector_map *map = mixer->connector_map; + + if (!map) + return unitid; + + for (; map->id; map++) { + if (map->id == unitid) { + if (control && map->control) + *control = map->control; + if (channel && map->channel) + *channel = map->channel; + return map->delegated_id; + } + } + return unitid; +} + void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { struct usb_mixer_elem_list *list; + unitid = delegate_notify(mixer, unitid, NULL, NULL); + for_each_mixer_elem(list, mixer, unitid) { - struct usb_mixer_elem_info *info = - mixer_elem_list_to_info(list); + struct usb_mixer_elem_info *info; + + if (!list->is_std_info) + continue; + info = mixer_elem_list_to_info(list); /* invalidate cache, so the value is read from the device */ info->cached = 0; snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE, @@ -3258,6 +3307,8 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; } + unitid = delegate_notify(mixer, unitid, &control, &channel); + for_each_mixer_elem(list, mixer, unitid) count++; @@ -3269,6 +3320,8 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, if (!list->kctl) continue; + if (!list->is_std_info) + continue; info = mixer_elem_list_to_info(list); if (count > 1 && info->control != control) diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 65d6d08c96f5..c29e27ac43a7 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -6,6 +6,13 @@ struct media_mixer_ctl; +struct usbmix_connector_map { + u8 id; + u8 delegated_id; + u8 control; + u8 channel; +}; + struct usb_mixer_interface { struct snd_usb_audio *chip; struct usb_host_interface *hostif; @@ -18,6 +25,9 @@ struct usb_mixer_interface { /* the usb audio specification version this interface complies to */ int protocol; + /* optional connector delegation map */ + const struct usbmix_connector_map *connector_map; + /* Sound Blaster remote control stuff */ const struct rc_config *rc_cfg; u32 rc_code; @@ -56,6 +66,7 @@ struct usb_mixer_elem_list { struct usb_mixer_elem_list *next_id_elem; /* list of controls with same id */ struct snd_kcontrol *kctl; unsigned int id; + bool is_std_info; usb_mixer_elem_dump_func_t dump; usb_mixer_elem_resume_func_t resume; }; @@ -93,8 +104,12 @@ void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid); int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int value_set); -int snd_usb_mixer_add_control(struct usb_mixer_elem_list *list, - struct snd_kcontrol *kctl); +int snd_usb_mixer_add_list(struct usb_mixer_elem_list *list, + struct snd_kcontrol *kctl, + bool is_std_info); + +#define snd_usb_mixer_add_control(list, kctl) \ + snd_usb_mixer_add_list(list, kctl, true) void snd_usb_mixer_elem_init_std(struct usb_mixer_elem_list *list, struct usb_mixer_interface *mixer, diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 72b575c34860..9af7aa93f6fa 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -27,6 +27,7 @@ struct usbmix_ctl_map { u32 id; const struct usbmix_name_map *map; const struct usbmix_selector_map *selector_map; + const struct usbmix_connector_map *connector_map; int ignore_ctl_error; }; @@ -360,13 +361,57 @@ static const struct usbmix_name_map corsair_virtuoso_map[] = { }; /* Some mobos shipped with a dummy HD-audio show the invalid GET_MIN/GET_MAX - * response for Input Gain Pad (id=19, control=12). Skip it. + * response for Input Gain Pad (id=19, control=12) and the connector status + * for SPDIF terminal (id=18). Skip them. */ static const struct usbmix_name_map asus_rog_map[] = { + { 18, NULL }, /* OT, connector control */ { 19, NULL, 12 }, /* FU, Input Gain Pad */ {} }; +/* TRX40 mobos with Realtek ALC1220-VB */ +static const struct usbmix_name_map trx40_mobo_map[] = { + { 18, NULL }, /* OT, IEC958 - broken response, disabled */ + { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */ + { 16, "Speaker" }, /* OT */ + { 22, "Speaker Playback" }, /* FU */ + { 7, "Line" }, /* IT */ + { 19, "Line Capture" }, /* FU */ + { 17, "Front Headphone" }, /* OT */ + { 23, "Front Headphone Playback" }, /* FU */ + { 8, "Mic" }, /* IT */ + { 20, "Mic Capture" }, /* FU */ + { 9, "Front Mic" }, /* IT */ + { 21, "Front Mic Capture" }, /* FU */ + { 24, "IEC958 Playback" }, /* FU */ + {} +}; + +static const struct usbmix_connector_map trx40_mobo_connector_map[] = { + { 10, 16 }, /* (Back) Speaker */ + { 11, 17 }, /* Front Headphone */ + { 13, 7 }, /* Line */ + { 14, 8 }, /* Mic */ + { 15, 9 }, /* Front Mic */ + {} +}; + +/* Rear panel + front mic on Gigabyte TRX40 Aorus Master with ALC1220-VB */ +static const struct usbmix_name_map aorus_master_alc1220vb_map[] = { + { 17, NULL }, /* OT, IEC958?, disabled */ + { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */ + { 16, "Line Out" }, /* OT */ + { 22, "Line Out Playback" }, /* FU */ + { 7, "Line" }, /* IT */ + { 19, "Line Capture" }, /* FU */ + { 8, "Mic" }, /* IT */ + { 20, "Mic Capture" }, /* FU */ + { 9, "Front Mic" }, /* IT */ + { 21, "Front Mic Capture" }, /* FU */ + {} +}; + /* * Control map entries */ @@ -496,9 +541,14 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x1b1c, 0x0a42), .map = corsair_virtuoso_map, }, + { /* Gigabyte TRX40 Aorus Master (rear panel + front mic) */ + .id = USB_ID(0x0414, 0xa001), + .map = aorus_master_alc1220vb_map, + }, { /* Gigabyte TRX40 Aorus Pro WiFi */ .id = USB_ID(0x0414, 0xa002), - .map = asus_rog_map, + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, }, { /* ASUS ROG Zenith II */ .id = USB_ID(0x0b05, 0x1916), @@ -510,11 +560,18 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { }, { /* MSI TRX40 Creator */ .id = USB_ID(0x0db0, 0x0d64), - .map = asus_rog_map, + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, }, { /* MSI TRX40 */ .id = USB_ID(0x0db0, 0x543d), - .map = asus_rog_map, + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, + }, + { /* Asrock TRX40 Creator */ + .id = USB_ID(0x26ce, 0x0a01), + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, }, { 0 } /* terminator */ }; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 02b036b2aefb..cec1cfd7edb7 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -158,7 +158,8 @@ static int add_single_ctl_with_resume(struct usb_mixer_interface *mixer, return -ENOMEM; } kctl->private_free = snd_usb_mixer_elem_free; - return snd_usb_mixer_add_control(list, kctl); + /* don't use snd_usb_mixer_add_control() here, this is a special list element */ + return snd_usb_mixer_add_list(list, kctl, false); } /* @@ -1509,11 +1510,15 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, /* use known values for that card: interface#1 altsetting#1 */ iface = usb_ifnum_to_if(chip->dev, 1); - if (!iface || iface->num_altsetting < 2) - return -EINVAL; + if (!iface || iface->num_altsetting < 2) { + err = -EINVAL; + goto end; + } alts = &iface->altsetting[1]; - if (get_iface_desc(alts)->bNumEndpoints < 1) - return -EINVAL; + if (get_iface_desc(alts)->bNumEndpoints < 1) { + err = -EINVAL; + goto end; + } ep = get_endpoint(alts, 0)->bEndpointAddress; err = snd_usb_ctl_msg(chip->dev, @@ -2181,6 +2186,421 @@ static int snd_rme_controls_create(struct usb_mixer_interface *mixer) return 0; } +/* + * RME Babyface Pro (FS) + * + * These devices exposes a couple of DSP functions via request to EP0. + * Switches are available via control registers, while routing is controlled + * by controlling the volume on each possible crossing point. + * Volume control is linear, from -inf (dec. 0) to +6dB (dec. 65536) with + * 0dB being at dec. 32768. + */ +enum { + SND_BBFPRO_CTL_REG1 = 0, + SND_BBFPRO_CTL_REG2 +}; + +#define SND_BBFPRO_CTL_REG_MASK 1 +#define SND_BBFPRO_CTL_IDX_MASK 0xff +#define SND_BBFPRO_CTL_IDX_SHIFT 1 +#define SND_BBFPRO_CTL_VAL_MASK 1 +#define SND_BBFPRO_CTL_VAL_SHIFT 9 +#define SND_BBFPRO_CTL_REG1_CLK_MASTER 0 +#define SND_BBFPRO_CTL_REG1_CLK_OPTICAL 1 +#define SND_BBFPRO_CTL_REG1_SPDIF_PRO 7 +#define SND_BBFPRO_CTL_REG1_SPDIF_EMPH 8 +#define SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL 10 +#define SND_BBFPRO_CTL_REG2_48V_AN1 0 +#define SND_BBFPRO_CTL_REG2_48V_AN2 1 +#define SND_BBFPRO_CTL_REG2_SENS_IN3 2 +#define SND_BBFPRO_CTL_REG2_SENS_IN4 3 +#define SND_BBFPRO_CTL_REG2_PAD_AN1 4 +#define SND_BBFPRO_CTL_REG2_PAD_AN2 5 + +#define SND_BBFPRO_MIXER_IDX_MASK 0x1ff +#define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff +#define SND_BBFPRO_MIXER_VAL_SHIFT 9 +#define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf +#define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB + +#define SND_BBFPRO_USBREQ_CTL_REG1 0x10 +#define SND_BBFPRO_USBREQ_CTL_REG2 0x17 +#define SND_BBFPRO_USBREQ_MIXER 0x12 + +static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg, + u8 index, u8 value) +{ + int err; + u16 usb_req, usb_idx, usb_val; + struct snd_usb_audio *chip = mixer->chip; + + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + + if (reg == SND_BBFPRO_CTL_REG1) { + usb_req = SND_BBFPRO_USBREQ_CTL_REG1; + if (index == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) { + usb_idx = 3; + usb_val = value ? 3 : 0; + } else { + usb_idx = 1 << index; + usb_val = value ? usb_idx : 0; + } + } else { + usb_req = SND_BBFPRO_USBREQ_CTL_REG2; + usb_idx = 1 << index; + usb_val = value ? usb_idx : 0; + } + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), usb_req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + usb_val, usb_idx, NULL, 0); + + snd_usb_unlock_shutdown(chip); + return err; +} + +static int snd_bbfpro_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 reg, idx, val; + int pv; + + pv = kcontrol->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + val = kcontrol->private_value >> SND_BBFPRO_CTL_VAL_SHIFT; + + if ((reg == SND_BBFPRO_CTL_REG1 && + idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) || + (reg == SND_BBFPRO_CTL_REG2 && + (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 || + idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) { + ucontrol->value.enumerated.item[0] = val; + } else { + ucontrol->value.integer.value[0] = val; + } + return 0; +} + +static int snd_bbfpro_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u8 reg, idx; + int pv; + + pv = kcontrol->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + + if (reg == SND_BBFPRO_CTL_REG1 && + idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) { + static const char * const texts[2] = { + "AutoSync", + "Internal" + }; + return snd_ctl_enum_info(uinfo, 1, 2, texts); + } else if (reg == SND_BBFPRO_CTL_REG2 && + (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 || + idx == SND_BBFPRO_CTL_REG2_SENS_IN4)) { + static const char * const texts[2] = { + "-10dBV", + "+4dBu" + }; + return snd_ctl_enum_info(uinfo, 1, 2, texts); + } + + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + return 0; +} + +static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int err; + u8 reg, idx; + int old_value, pv, val; + + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + + pv = kcontrol->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + old_value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK; + + if ((reg == SND_BBFPRO_CTL_REG1 && + idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) || + (reg == SND_BBFPRO_CTL_REG2 && + (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 || + idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) { + val = ucontrol->value.enumerated.item[0]; + } else { + val = ucontrol->value.integer.value[0]; + } + + if (val > 1) + return -EINVAL; + + if (val == old_value) + return 0; + + kcontrol->private_value = reg + | ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT) + | ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT); + + err = snd_bbfpro_ctl_update(mixer, reg, idx, val); + return err < 0 ? err : 1; +} + +static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list) +{ + u8 reg, idx; + int value, pv; + + pv = list->kctl->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK; + + return snd_bbfpro_ctl_update(list->mixer, reg, idx, value); +} + +static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index, + u32 value) +{ + struct snd_usb_audio *chip = mixer->chip; + int err; + u16 idx; + u16 usb_idx, usb_val; + u32 v; + + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + + idx = index & SND_BBFPRO_MIXER_IDX_MASK; + // 18 bit linear volume, split so 2 bits end up in index. + v = value & SND_BBFPRO_MIXER_VAL_MASK; + usb_idx = idx | (v & 0x3) << 14; + usb_val = (v >> 2) & 0xffff; + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), + SND_BBFPRO_USBREQ_MIXER, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, + usb_val, usb_idx, NULL, 0); + + snd_usb_unlock_shutdown(chip); + return err; +} + +static int snd_bbfpro_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT; + return 0; +} + +static int snd_bbfpro_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = SND_BBFPRO_MIXER_VAL_MIN; + uinfo->value.integer.max = SND_BBFPRO_MIXER_VAL_MAX; + return 0; +} + +static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int err; + u16 idx; + u32 new_val, old_value, uvalue; + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + + uvalue = ucontrol->value.integer.value[0]; + idx = kcontrol->private_value & SND_BBFPRO_MIXER_IDX_MASK; + old_value = kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT; + + if (uvalue > SND_BBFPRO_MIXER_VAL_MAX) + return -EINVAL; + + if (uvalue == old_value) + return 0; + + new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK; + + kcontrol->private_value = idx + | (new_val << SND_BBFPRO_MIXER_VAL_SHIFT); + + err = snd_bbfpro_vol_update(mixer, idx, new_val); + return err < 0 ? err : 1; +} + +static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list) +{ + int pv = list->kctl->private_value; + u16 idx = pv & SND_BBFPRO_MIXER_IDX_MASK; + u32 val = (pv >> SND_BBFPRO_MIXER_VAL_SHIFT) + & SND_BBFPRO_MIXER_VAL_MASK; + return snd_bbfpro_vol_update(list->mixer, idx, val); +} + +// Predfine elements +static const struct snd_kcontrol_new snd_bbfpro_ctl_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .index = 0, + .info = snd_bbfpro_ctl_info, + .get = snd_bbfpro_ctl_get, + .put = snd_bbfpro_ctl_put +}; + +static const struct snd_kcontrol_new snd_bbfpro_vol_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .index = 0, + .info = snd_bbfpro_vol_info, + .get = snd_bbfpro_vol_get, + .put = snd_bbfpro_vol_put +}; + +static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg, + u8 index, char *name) +{ + struct snd_kcontrol_new knew = snd_bbfpro_ctl_control; + + knew.name = name; + knew.private_value = (reg & SND_BBFPRO_CTL_REG_MASK) + | ((index & SND_BBFPRO_CTL_IDX_MASK) + << SND_BBFPRO_CTL_IDX_SHIFT); + + return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_ctl_resume, + &knew, NULL); +} + +static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index, + char *name) +{ + struct snd_kcontrol_new knew = snd_bbfpro_vol_control; + + knew.name = name; + knew.private_value = index & SND_BBFPRO_MIXER_IDX_MASK; + + return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_vol_resume, + &knew, NULL); +} + +static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i, o; + char name[48]; + + static const char * const input[] = { + "AN1", "AN2", "IN3", "IN4", "AS1", "AS2", "ADAT3", + "ADAT4", "ADAT5", "ADAT6", "ADAT7", "ADAT8"}; + + static const char * const output[] = { + "AN1", "AN2", "PH3", "PH4", "AS1", "AS2", "ADAT3", "ADAT4", + "ADAT5", "ADAT6", "ADAT7", "ADAT8"}; + + for (o = 0 ; o < 12 ; ++o) { + for (i = 0 ; i < 12 ; ++i) { + // Line routing + snprintf(name, sizeof(name), + "%s-%s-%s Playback Volume", + (i < 2 ? "Mic" : "Line"), + input[i], output[o]); + err = snd_bbfpro_vol_add(mixer, (26 * o + i), name); + if (err < 0) + return err; + + // PCM routing... yes, it is output remapping + snprintf(name, sizeof(name), + "PCM-%s-%s Playback Volume", + output[i], output[o]); + err = snd_bbfpro_vol_add(mixer, (26 * o + 12 + i), + name); + if (err < 0) + return err; + } + } + + // Control Reg 1 + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_CLK_OPTICAL, + "Sample Clock Source"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_SPDIF_PRO, + "IEC958 Pro Mask"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_SPDIF_EMPH, + "IEC958 Emphasis"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL, + "IEC958 Switch"); + if (err < 0) + return err; + + // Control Reg 2 + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_48V_AN1, + "Mic-AN1 48V"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_48V_AN2, + "Mic-AN2 48V"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_SENS_IN3, + "Line-IN3 Sens."); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_SENS_IN4, + "Line-IN4 Sens."); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_PAD_AN1, + "Mic-AN1 PAD"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_PAD_AN2, + "Mic-AN2 PAD"); + if (err < 0) + return err; + + return 0; +} + int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { int err = 0; @@ -2282,6 +2702,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */ err = snd_sc1810_init_mixer(mixer); break; + case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */ + err = snd_bbfpro_controls_create(mixer); + break; } return err; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index a4e4064f9aee..40b7cd13fed9 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -367,9 +367,15 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ifnum = 0; goto add_sync_ep_from_ifnum; case USB_ID(0x07fd, 0x0008): /* MOTU M Series */ + case USB_ID(0x31e9, 0x0002): /* Solid State Logic SSL2+ */ + case USB_ID(0x0d9a, 0x00df): /* RTX6001 */ ep = 0x81; ifnum = 2; goto add_sync_ep_from_ifnum; + case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */ + ep = 0x82; + ifnum = 0; + goto add_sync_ep_from_ifnum; case USB_ID(0x0582, 0x01d8): /* BOSS Katana */ /* BOSS Katana amplifiers do not need quirks */ return 0; @@ -404,6 +410,8 @@ add_sync_ep: if (!subs->sync_endpoint) return -EINVAL; + subs->sync_endpoint->is_implicit_feedback = 1; + subs->data_endpoint->sync_master = subs->sync_endpoint; return 1; @@ -502,12 +510,15 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, implicit_fb ? SND_USB_ENDPOINT_TYPE_DATA : SND_USB_ENDPOINT_TYPE_SYNC); + if (!subs->sync_endpoint) { if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) return 0; return -EINVAL; } + subs->sync_endpoint->is_implicit_feedback = implicit_fb; + subs->data_endpoint->sync_master = subs->sync_endpoint; return 0; @@ -1579,6 +1590,8 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, for (i = 0; i < ctx->packets; i++) { if (ctx->packet_size[i]) counts = ctx->packet_size[i]; + else if (ep->sync_master) + counts = snd_usb_endpoint_slave_next_packet_size(ep); else counts = snd_usb_endpoint_next_packet_size(ep); @@ -1775,6 +1788,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream return 0; case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs); + subs->data_endpoint->retire_data_urb = NULL; subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 4174ad11fca6..889c550c9f29 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -54,6 +54,38 @@ void snd_usb_audio_create_proc(struct snd_usb_audio *chip) proc_audio_usbid_read); } +static const char * const channel_labels[] = { + [SNDRV_CHMAP_NA] = "N/A", + [SNDRV_CHMAP_MONO] = "MONO", + [SNDRV_CHMAP_FL] = "FL", + [SNDRV_CHMAP_FR] = "FR", + [SNDRV_CHMAP_FC] = "FC", + [SNDRV_CHMAP_LFE] = "LFE", + [SNDRV_CHMAP_RL] = "RL", + [SNDRV_CHMAP_RR] = "RR", + [SNDRV_CHMAP_FLC] = "FLC", + [SNDRV_CHMAP_FRC] = "FRC", + [SNDRV_CHMAP_RC] = "RC", + [SNDRV_CHMAP_SL] = "SL", + [SNDRV_CHMAP_SR] = "SR", + [SNDRV_CHMAP_TC] = "TC", + [SNDRV_CHMAP_TFL] = "TFL", + [SNDRV_CHMAP_TFC] = "TFC", + [SNDRV_CHMAP_TFR] = "TFR", + [SNDRV_CHMAP_TRL] = "TRL", + [SNDRV_CHMAP_TRC] = "TRC", + [SNDRV_CHMAP_TRR] = "TRR", + [SNDRV_CHMAP_TFLC] = "TFLC", + [SNDRV_CHMAP_TFRC] = "TFRC", + [SNDRV_CHMAP_LLFE] = "LLFE", + [SNDRV_CHMAP_RLFE] = "RLFE", + [SNDRV_CHMAP_TSL] = "TSL", + [SNDRV_CHMAP_TSR] = "TSR", + [SNDRV_CHMAP_BC] = "BC", + [SNDRV_CHMAP_RLC] = "RLC", + [SNDRV_CHMAP_RRC] = "RRC", +}; + /* * proc interface for list the supported pcm formats */ @@ -97,6 +129,27 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s snd_iprintf(buffer, " Data packet interval: %d us\n", 125 * (1 << fp->datainterval)); snd_iprintf(buffer, " Bits: %d\n", fp->fmt_bits); + + if (fp->dsd_raw) + snd_iprintf(buffer, " DSD raw: DOP=%d, bitrev=%d\n", + fp->dsd_dop, fp->dsd_bitrev); + + if (fp->chmap) { + const struct snd_pcm_chmap_elem *map = fp->chmap; + int c; + + snd_iprintf(buffer, " Channel map:"); + for (c = 0; c < map->channels; c++) { + if (map->map[c] >= ARRAY_SIZE(channel_labels) || + !channel_labels[map->map[c]]) + snd_iprintf(buffer, " --"); + else + snd_iprintf(buffer, " %s", + channel_labels[map->map[c]]); + } + snd_iprintf(buffer, "\n"); + } + // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); // snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes); } diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index e009d584e7d0..9092cc0aa807 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -25,6 +25,33 @@ .idProduct = prod, \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC +#define QUIRK_RENAME_DEVICE(_vendor, _device) \ + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ + .vendor_name = _vendor, \ + .product_name = _device, \ + .ifnum = QUIRK_NO_INTERFACE \ + } + +#define QUIRK_DEVICE_PROFILE(_vendor, _device, _profile) \ + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ + .vendor_name = _vendor, \ + .product_name = _device, \ + .profile_name = _profile, \ + .ifnum = QUIRK_NO_INTERFACE \ + } + +/* HP Thunderbolt Dock Audio Headset */ +{ + USB_DEVICE(0x03f0, 0x0269), + QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Headset", + "HP-Thunderbolt-Dock-Audio-Headset"), +}, +/* HP Thunderbolt Dock Audio Module */ +{ + USB_DEVICE(0x03f0, 0x0567), + QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Module", + "HP-Thunderbolt-Dock-Audio-Module"), +}, /* FTDI devices */ { USB_DEVICE(0x0403, 0xb8d8), @@ -61,20 +88,12 @@ /* Creative/E-Mu devices */ { USB_DEVICE(0x041e, 0x3010), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Creative Labs", - .product_name = "Sound Blaster MP3+", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Creative Labs", "Sound Blaster MP3+") }, /* Creative/Toshiba Multimedia Center SB-0500 */ { USB_DEVICE(0x041e, 0x3048), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Toshiba", - .product_name = "SB-0500", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Toshiba", "SB-0500") }, { /* E-Mu 0202 USB */ @@ -207,11 +226,7 @@ .idProduct = 0x0990, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Logitech, Inc.", - .product_name = "QuickCam Pro 9000", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Logitech, Inc.", "QuickCam Pro 9000") }, /* @@ -2596,11 +2611,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, { USB_DEVICE(0x0ccd, 0x0028), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "TerraTec", - .product_name = "Aureon5.1MkII", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("TerraTec", "Aureon5.1MkII") }, { USB_DEVICE(0x0ccd, 0x0035), @@ -2615,19 +2626,11 @@ YAMAHA_DEVICE(0x7010, "UB99"), /* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ { USB_DEVICE(0x103d, 0x0100), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Stanton", - .product_name = "ScratchAmp", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp") }, { USB_DEVICE(0x103d, 0x0101), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Stanton", - .product_name = "ScratchAmp", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp") }, /* Novation EMS devices */ @@ -2756,90 +2759,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_MIDI_NOVATION } }, -{ - /* - * Focusrite Scarlett Solo 2nd generation - * Reports that playback should use Synch: Synchronous - * while still providing a feedback endpoint. Synchronous causes - * snapping on some sample rates. - * Force it to use Synch: Asynchronous. - */ - USB_DEVICE(0x1235, 0x8205), - .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 = 1, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .channels = 2, - .iface = 1, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x01, - .ep_attr = USB_ENDPOINT_XFER_ISOC | - USB_ENDPOINT_SYNC_ASYNC, - .protocol = UAC_VERSION_2, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000, - .rate_min = 44100, - .rate_max = 192000, - .nr_rates = 6, - .rate_table = (unsigned int[]) { - 44100, 48000, 88200, - 96000, 176400, 192000 - }, - .clock = 41 - } - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .channels = 2, - .iface = 2, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x82, - .ep_attr = USB_ENDPOINT_XFER_ISOC | - USB_ENDPOINT_SYNC_ASYNC | - USB_ENDPOINT_USAGE_IMPLICIT_FB, - .protocol = UAC_VERSION_2, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000, - .rate_min = 44100, - .rate_max = 192000, - .nr_rates = 6, - .rate_table = (unsigned int[]) { - 44100, 48000, 88200, - 96000, 176400, 192000 - }, - .clock = 41 - } - }, - { - .ifnum = 3, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, /* Access Music devices */ { @@ -2872,11 +2791,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), { /* aka. Serato Scratch Live DJ Box */ USB_DEVICE(0x13e5, 0x0001), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Rane", - .product_name = "SL-1", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Rane", "SL-1") }, /* Native Instruments MK2 series */ @@ -3343,10 +3258,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), * is also used by the CM106 based cards, so make it unique. */ USB_DEVICE(0x0d8c, 0x0103), - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .product_name = "Audio Advantage MicroII", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE(NULL, "Audio Advantage MicroII") }, /* disabled due to regression for other devices; @@ -3452,12 +3364,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), /* Dell WD15 Dock */ { USB_DEVICE(0x0bda, 0x4014), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Dell", - .product_name = "WD15 Dock", - .profile_name = "Dell-WD15-Dock", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_DEVICE_PROFILE("Dell", "WD15 Dock", "Dell-WD15-Dock") }, /* Dell WD19 Dock */ { @@ -3635,4 +3542,147 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } }, +#define ALC1220_VB_DESKTOP(vend, prod) { \ + USB_DEVICE(vend, prod), \ + QUIRK_DEVICE_PROFILE("Realtek", "ALC1220-VB-DT", \ + "Realtek-ALC1220-VB-Desktop") \ +} +ALC1220_VB_DESKTOP(0x0414, 0xa002), /* Gigabyte TRX40 Aorus Pro WiFi */ +ALC1220_VB_DESKTOP(0x0db0, 0x0d64), /* MSI TRX40 Creator */ +ALC1220_VB_DESKTOP(0x0db0, 0x543d), /* MSI TRX40 */ +ALC1220_VB_DESKTOP(0x26ce, 0x0a01), /* Asrock TRX40 Creator */ +#undef ALC1220_VB_DESKTOP + +/* Two entries for Gigabyte TRX40 Aorus Master: + * TRX40 Aorus Master has two USB-audio devices, one for the front headphone + * with ESS SABRE9218 DAC chip, while another for the rest I/O (the rear + * panel and the front mic) with Realtek ALC1220-VB. + * Here we provide two distinct names for making UCM profiles easier. + */ +{ + USB_DEVICE(0x0414, 0xa000), + QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Front Headphone", + "Gigabyte-Aorus-Master-Front-Headphone") +}, +{ + USB_DEVICE(0x0414, 0xa001), + QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Main Audio", + "Gigabyte-Aorus-Master-Main-Audio") +}, +{ + /* + * Pioneer DJ DJM-900NXS2 + * 10 channels playback & 12 channels capture @ 44.1/48/96kHz S24LE + */ + USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000a), + .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_44100| + SNDRV_PCM_RATE_48000| + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 3, + .rate_table = (unsigned int[]) { + 44100, 48000, 96000 + } + } + }, + { + .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_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC| + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_44100| + SNDRV_PCM_RATE_48000| + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 3, + .rate_table = (unsigned int[]) { + 44100, 48000, 96000 + } + } + }, + { + .ifnum = -1 + } + } + } +}, + +/* + * MacroSilicon MS2109 based HDMI capture cards + * + * These claim 96kHz 1ch in the descriptors, but are actually 48kHz 2ch. + * They also need QUIRK_AUDIO_ALIGN_TRANSFER, which makes one wonder if + * they pretend to be 96kHz mono as a workaround for stereo being broken + * by that... + * + * They also have swapped L-R channels, but that's for userspace to deal + * with. + */ +{ + USB_DEVICE(0x534d, 0x2109), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .vendor_name = "MacroSilicon", + .product_name = "MS2109", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = &(const struct snd_usb_audio_quirk[]) { + { + .ifnum = 2, + .type = QUIRK_AUDIO_ALIGN_TRANSFER, + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_MIXER, + }, + { + .ifnum = 3, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .channels = 2, + .iface = 3, + .altsetting = 1, + .altset_idx = 1, + .attributes = 0, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC | + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 48000, + .rate_max = 48000, + } + }, + { + .ifnum = -1 + } + } + } +}, + #undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a8ece1701068..fca72730a802 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1458,6 +1458,30 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs, subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0; } + +/* + * Pioneer DJ DJM-900NXS2 + * Device needs to know the sample rate each time substream is started + */ +static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs) +{ + + /* Convert sample rate value to little endian */ + u8 sr[3]; + + sr[0] = subs->cur_rate & 0xff; + sr[1] = (subs->cur_rate >> 8) & 0xff; + sr[2] = (subs->cur_rate >> 16) & 0xff; + + /* Configure device */ + usb_set_interface(subs->dev, 0, 1); + snd_usb_ctl_msg(subs->stream->chip->dev, + usb_rcvctrlpipe(subs->stream->chip->dev, 0), + 0x01, 0x22, 0x0100, 0x0082, &sr, 0x0003); + + return 0; +} + void snd_usb_set_format_quirk(struct snd_usb_substream *subs, struct audioformat *fmt) { @@ -1468,6 +1492,9 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */ set_format_emu_quirk(subs, fmt); break; + case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */ + pioneer_djm_set_format_quirk(subs); + break; } } @@ -1505,6 +1532,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) static bool is_itf_usb_dsd_dac(unsigned int id) { switch (id) { + case USB_ID(0x154e, 0x1002): /* Denon DCD-1500RE */ case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */ case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ @@ -1636,15 +1664,24 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) msleep(20); - /* Zoom R16/24, Logitech H650e, Jabra 550a needs a tiny delay here, - * otherwise requests like get/set frequency return as failed despite - * actually succeeding. + /* Zoom R16/24, Logitech H650e, Jabra 550a, Kingston HyperX needs a tiny + * delay here, otherwise requests like get/set frequency return as + * failed despite actually succeeding. */ if ((chip->usb_id == USB_ID(0x1686, 0x00dd) || chip->usb_id == USB_ID(0x046d, 0x0a46) || - chip->usb_id == USB_ID(0x0b0e, 0x0349)) && + chip->usb_id == USB_ID(0x0b0e, 0x0349) || + chip->usb_id == USB_ID(0x0951, 0x16ad)) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) usleep_range(1000, 2000); + + /* + * Samsung USBC Headset (AKG) need a tiny delay after each + * class compliant request. (Model number: AAM625R or AAM627R) + */ + if (chip->usb_id == USB_ID(0x04e8, 0xa051) && + (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) + usleep_range(5000, 6000); } /* @@ -1687,7 +1724,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */ case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */ - case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */ + case USB_ID(0x16d0, 0x06b2): /* NuPrime DAC-10 */ case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */ case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */ case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */ @@ -1828,6 +1865,7 @@ struct registration_quirk { static const struct registration_quirk registration_quirks[] = { REG_QUIRK_ENTRY(0x0951, 0x16d8, 2), /* Kingston HyperX AMP */ REG_QUIRK_ENTRY(0x0951, 0x16ed, 2), /* Kingston HyperX Cloud Alpha S */ + REG_QUIRK_ENTRY(0x0951, 0x16ea, 2), /* Kingston HyperX Cloud Flight S */ { 0 } /* terminator */ }; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 1c892c7f14d7..b91c4c0807ec 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -19,14 +19,16 @@ struct media_device; struct media_intf_devnode; +#define MAX_CARD_INTERFACES 16 + struct snd_usb_audio { int index; struct usb_device *dev; struct snd_card *card; - struct usb_interface *pm_intf; + struct usb_interface *intf[MAX_CARD_INTERFACES]; u32 usb_id; struct mutex mutex; - unsigned int autosuspended:1; + unsigned int system_suspend; atomic_t active; atomic_t shutdown; atomic_t usage_count; diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h index e0f77172ce8f..144b85f57bd2 100644 --- a/sound/usb/usx2y/usbusx2y.h +++ b/sound/usb/usx2y/usbusx2y.h @@ -18,7 +18,7 @@ struct snd_usX2Y_AsyncSeq { struct snd_usX2Y_urbSeq { int submitted; int len; - struct urb *urb[0]; + struct urb *urb[]; }; #include "usx2yhwdeppcm.h" diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 37d290fe9d43..ecaf41265dcd 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -681,6 +681,8 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate) us->submitted = 2*NOOF_SETRATE_URBS; for (i = 0; i < NOOF_SETRATE_URBS; ++i) { struct urb *urb = us->urb[i]; + if (!urb) + continue; if (urb->status) { if (!err) err = -ENODEV; diff --git a/sound/x86/Kconfig b/sound/x86/Kconfig index 21f8919678fb..77777192f650 100644 --- a/sound/x86/Kconfig +++ b/sound/x86/Kconfig @@ -3,7 +3,7 @@ menuconfig SND_X86 bool "X86 sound devices" depends on X86 default y - ---help--- + help X86 sound devices that don't fall under SoC or PCI categories if SND_X86 |