OSDN Git Service

Merge tag 'asoc-v5.16' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorTakashi Iwai <tiwai@suse.de>
Mon, 1 Nov 2021 15:58:27 +0000 (16:58 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 1 Nov 2021 15:58:27 +0000 (16:58 +0100)
ASoC: Updates for v5.16

This is an unusually large set of updates, mostly a large crop of
unusually big drivers coupled with extensive overhauls of existing code.
There's a SH change here for the DAI format terminology, the change is
straightforward and the SH maintainers don't seem very active.

 - A new version of the audio graph card which supports a wider range of
   systems.
 - Move of the Cirrus DSP framework into drivers/firmware to allow for
   future use by non-audio DSPs.
 - Several conversions to YAML DT bindings.
 - Continuing cleanups to the SOF and Intel code.
 - A very big overhaul of the cs42l42 driver, correcting many problems.
 - Support for AMD Vangogh and Yelow Cap, Cirrus CS35L41, Maxim
   MAX98520 and MAX98360A, Mediatek MT8195, Nuvoton NAU8821, nVidia
   Tegra210, NXP i.MX8ULP, Qualcomm AudioReach, Realtek ALC5682I-VS,
   RT5682S, and RT9120 and Rockchip RV1126 and RK3568

55 files changed:
include/sound/hdaudio_ext.h
include/sound/memalloc.h
include/uapi/sound/asound.h
include/uapi/sound/firewire.h
sound/core/Makefile
sound/core/memalloc.c
sound/core/memalloc_local.h
sound/core/oss/mixer_oss.c
sound/core/pcm_compat.c
sound/core/pcm_lib.c
sound/core/pcm_local.h
sound/core/pcm_memory.c
sound/core/pcm_native.c
sound/core/sgbuf.c [deleted file]
sound/firewire/motu/Makefile
sound/firewire/motu/amdtp-motu.c
sound/firewire/motu/motu-command-dsp-message-parser.c [new file with mode: 0644]
sound/firewire/motu/motu-hwdep.c
sound/firewire/motu/motu-protocol-v2.c
sound/firewire/motu/motu-protocol-v3.c
sound/firewire/motu/motu-register-dsp-message-parser.c [new file with mode: 0644]
sound/firewire/motu/motu-stream.c
sound/firewire/motu/motu.c
sound/firewire/motu/motu.h
sound/firewire/oxfw/oxfw-stream.c
sound/firewire/oxfw/oxfw.c
sound/firewire/oxfw/oxfw.h
sound/hda/ext/hdac_ext_stream.c
sound/hda/hdac_stream.c
sound/isa/Kconfig
sound/isa/gus/gus_dma.c
sound/pci/Kconfig
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_realtek.c
sound/soc/sof/intel/hda-dai.c
sound/usb/6fire/comm.c
sound/usb/6fire/firmware.c
sound/usb/card.h
sound/usb/clock.c
sound/usb/endpoint.c
sound/usb/endpoint.h
sound/usb/format.c
sound/usb/implicit.c
sound/usb/line6/driver.c
sound/usb/line6/driver.h
sound/usb/line6/podhd.c
sound/usb/line6/toneport.c
sound/usb/misc/ua101.c
sound/usb/mixer.c
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/usb/usx2y/usbusx2yaudio.c
sound/virtio/virtio_pcm_msg.c

index 3755816..d4e31ea 100644 (file)
@@ -88,6 +88,8 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
                                           struct snd_pcm_substream *substream,
                                           int type);
 void snd_hdac_ext_stream_release(struct hdac_ext_stream *azx_dev, int type);
+void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
+                                 struct hdac_ext_stream *azx_dev, bool decouple);
 void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
                                struct hdac_ext_stream *azx_dev, bool decouple);
 void snd_hdac_ext_stop_streams(struct hdac_bus *bus);
index b197e3f..653dfff 100644 (file)
@@ -9,16 +9,20 @@
 #ifndef __SOUND_MEMALLOC_H
 #define __SOUND_MEMALLOC_H
 
+#include <linux/dma-direction.h>
 #include <asm/page.h>
 
 struct device;
 struct vm_area_struct;
+struct sg_table;
 
 /*
  * buffer device info
  */
 struct snd_dma_device {
        int type;                       /* SNDRV_DMA_TYPE_XXX */
+       enum dma_data_direction dir;    /* DMA direction */
+       bool need_sync;                 /* explicit sync needed? */
        struct device *dev;             /* generic device */
 };
 
@@ -32,19 +36,21 @@ struct snd_dma_device {
 #define SNDRV_DMA_TYPE_CONTINUOUS      1       /* continuous no-DMA memory */
 #define SNDRV_DMA_TYPE_DEV             2       /* generic device continuous */
 #define SNDRV_DMA_TYPE_DEV_WC          5       /* continuous write-combined */
-#ifdef CONFIG_SND_DMA_SGBUF
-#define SNDRV_DMA_TYPE_DEV_SG          3       /* generic device SG-buffer */
-#define SNDRV_DMA_TYPE_DEV_WC_SG       6       /* SG write-combined */
-#else
-#define SNDRV_DMA_TYPE_DEV_SG  SNDRV_DMA_TYPE_DEV /* no SG-buf support */
-#define SNDRV_DMA_TYPE_DEV_WC_SG       SNDRV_DMA_TYPE_DEV_WC
-#endif
 #ifdef CONFIG_GENERIC_ALLOCATOR
 #define SNDRV_DMA_TYPE_DEV_IRAM                4       /* generic device iram-buffer */
 #else
 #define SNDRV_DMA_TYPE_DEV_IRAM        SNDRV_DMA_TYPE_DEV
 #endif
 #define SNDRV_DMA_TYPE_VMALLOC         7       /* vmalloc'ed buffer */
+#define SNDRV_DMA_TYPE_NONCONTIG       8       /* non-coherent SG buffer */
+#define SNDRV_DMA_TYPE_NONCOHERENT     9       /* non-coherent buffer */
+#ifdef CONFIG_SND_DMA_SGBUF
+#define SNDRV_DMA_TYPE_DEV_SG          SNDRV_DMA_TYPE_NONCONTIG
+#define SNDRV_DMA_TYPE_DEV_WC_SG       6       /* SG write-combined */
+#else
+#define SNDRV_DMA_TYPE_DEV_SG  SNDRV_DMA_TYPE_DEV /* no SG-buf support */
+#define SNDRV_DMA_TYPE_DEV_WC_SG       SNDRV_DMA_TYPE_DEV_WC
+#endif
 
 /*
  * info for buffer allocation
@@ -66,22 +72,52 @@ static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
 }
 
 /* allocate/release a buffer */
-int snd_dma_alloc_pages(int type, struct device *dev, size_t size,
-                       struct snd_dma_buffer *dmab);
+int snd_dma_alloc_dir_pages(int type, struct device *dev,
+                           enum dma_data_direction dir, size_t size,
+                           struct snd_dma_buffer *dmab);
+
+static inline int snd_dma_alloc_pages(int type, struct device *dev,
+                                     size_t size, struct snd_dma_buffer *dmab)
+{
+       return snd_dma_alloc_dir_pages(type, dev, DMA_BIDIRECTIONAL, size, dmab);
+}
+
 int snd_dma_alloc_pages_fallback(int type, struct device *dev, size_t size,
                                  struct snd_dma_buffer *dmab);
 void snd_dma_free_pages(struct snd_dma_buffer *dmab);
 int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
                        struct vm_area_struct *area);
 
+enum snd_dma_sync_mode { SNDRV_DMA_SYNC_CPU, SNDRV_DMA_SYNC_DEVICE };
+#ifdef CONFIG_HAS_DMA
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+                        enum snd_dma_sync_mode mode);
+#else
+static inline void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+                                      enum snd_dma_sync_mode mode) {}
+#endif
+
 dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset);
 struct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset);
 unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
                                      unsigned int ofs, unsigned int size);
 
 /* device-managed memory allocator */
-struct snd_dma_buffer *snd_devm_alloc_pages(struct device *dev, int type,
-                                           size_t size);
+struct snd_dma_buffer *snd_devm_alloc_dir_pages(struct device *dev, int type,
+                                               enum dma_data_direction dir,
+                                               size_t size);
+
+static inline struct snd_dma_buffer *
+snd_devm_alloc_pages(struct device *dev, int type, size_t size)
+{
+       return snd_devm_alloc_dir_pages(dev, type, DMA_BIDIRECTIONAL, size);
+}
+
+static inline struct sg_table *
+snd_dma_noncontig_sg_table(struct snd_dma_buffer *dmab)
+{
+       return dmab->private_data;
+}
 
 #endif /* __SOUND_MEMALLOC_H */
 
index 5859ca0..5fbb79e 100644 (file)
@@ -1002,7 +1002,7 @@ typedef int __bitwise snd_ctl_elem_iface_t;
 #define SNDRV_CTL_ELEM_ACCESS_WRITE            (1<<1)
 #define SNDRV_CTL_ELEM_ACCESS_READWRITE                (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
 #define SNDRV_CTL_ELEM_ACCESS_VOLATILE         (1<<2)  /* control value may be changed without a notification */
-// (1 << 3) is unused.
+/* (1 << 3) is unused. */
 #define SNDRV_CTL_ELEM_ACCESS_TLV_READ         (1<<4)  /* TLV read is possible */
 #define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE                (1<<5)  /* TLV write is possible */
 #define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE    (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
index ae12826..39cf6eb 100644 (file)
@@ -13,6 +13,7 @@
 #define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE   0x746e736c
 #define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION 0x64776479
 #define SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL    0x7473636d
+#define SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE  0x4d545244
 
 struct snd_firewire_event_common {
        unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -65,6 +66,12 @@ struct snd_firewire_event_tascam_control {
        struct snd_firewire_tascam_change changes[0];
 };
 
+struct snd_firewire_event_motu_register_dsp_change {
+       unsigned int type;
+       __u32 count;            /* The number of changes. */
+       __u32 changes[];        /* Encoded event for change of register DSP. */
+};
+
 union snd_firewire_event {
        struct snd_firewire_event_common            common;
        struct snd_firewire_event_lock_status       lock_status;
@@ -73,6 +80,7 @@ union snd_firewire_event {
        struct snd_firewire_event_digi00x_message   digi00x_message;
        struct snd_firewire_event_tascam_control    tascam_control;
        struct snd_firewire_event_motu_notification motu_notification;
+       struct snd_firewire_event_motu_register_dsp_change motu_register_dsp_change;
 };
 
 
@@ -80,6 +88,9 @@ union snd_firewire_event {
 #define SNDRV_FIREWIRE_IOCTL_LOCK      _IO('H', 0xf9)
 #define SNDRV_FIREWIRE_IOCTL_UNLOCK    _IO('H', 0xfa)
 #define SNDRV_FIREWIRE_IOCTL_TASCAM_STATE _IOR('H', 0xfb, struct snd_firewire_tascam_state)
+#define SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER   _IOR('H', 0xfc, struct snd_firewire_motu_register_dsp_meter)
+#define SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER    _IOR('H', 0xfd, struct snd_firewire_motu_command_dsp_meter)
+#define SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER       _IOR('H', 0xfe, struct snd_firewire_motu_register_dsp_parameter)
 
 #define SNDRV_FIREWIRE_TYPE_DICE       1
 #define SNDRV_FIREWIRE_TYPE_FIREWORKS  2
@@ -108,4 +119,143 @@ struct snd_firewire_tascam_state {
        __be32 data[SNDRV_FIREWIRE_TASCAM_STATE_COUNT];
 };
 
+/*
+ * In below MOTU models, software is allowed to control their DSP by accessing to registers.
+ *  - 828mk2
+ *  - 896hd
+ *  - Traveler
+ *  - 8 pre
+ *  - Ultralite
+ *  - 4 pre
+ *  - Audio Express
+ *
+ * On the other hand, the status of DSP is split into specific messages included in the sequence of
+ * isochronous packet. ALSA firewire-motu driver gathers the messages and allow userspace applications
+ * to read it via ioctl. In 828mk2, 896hd, and Traveler, hardware meter for all of physical inputs
+ * are put into the message, while one pair of physical outputs is selected. The selection is done by
+ * LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c.
+ *
+ * I note that V3HD/V4HD uses asynchronous transaction for the purpose. The destination address is
+ * registered to 0x'ffff'f000'0b38 and '0b3c by asynchronous write quadlet request. The size of
+ * message differs between 23 and 51 quadlets. For the case, the number of mixer bus can be extended
+ * up to 12.
+ */
+
+#define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT     24
+#define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_OUTPUT_COUNT    24
+#define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT \
+       (SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT + SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_OUTPUT_COUNT)
+
+/**
+ * struct snd_firewire_motu_register_dsp_meter - the container for meter information in DSP
+ *                                              controlled by register access
+ * @data: Signal level meters. The mapping between position and input/output channel is
+ *       model-dependent.
+ *
+ * The structure expresses the part of DSP status for hardware meter. The u8 storage includes linear
+ * value for audio signal level between 0x00 and 0x7f.
+ */
+struct snd_firewire_motu_register_dsp_meter {
+       __u8 data[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT];
+};
+
+#define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT           4
+#define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT       20
+#define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT           10
+#define SNDRV_FIREWIRE_MOTU_REGISTER_DSP_ALIGNED_INPUT_COUNT   (SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT + 2)
+
+/**
+ * snd_firewire_motu_register_dsp_parameter - the container for parameters of DSP controlled
+ *                                           by register access.
+ * @mixer.source.gain: The gain of source to mixer.
+ * @mixer.source.pan: The L/R balance of source to mixer.
+ * @mixer.source.flag: The flag of source to mixer, including mute, solo.
+ * @mixer.source.paired_balance: The L/R balance of paired source to mixer, only for 4 pre and
+ *                              Audio Express.
+ * @mixer.source.paired_width: The width of paired source to mixer, only for 4 pre and
+ *                            Audio Express.
+ * @mixer.output.paired_volume: The volume of paired output from mixer.
+ * @mixer.output.paired_flag: The flag of paired output from mixer.
+ * @output.main_paired_volume: The volume of paired main output.
+ * @output.hp_paired_volume: The volume of paired hp output.
+ * @output.hp_paired_assignment: The source assigned to paired hp output.
+ * @output.reserved: Padding for 32 bit alignment for future extension.
+ * @line_input.boost_flag: The flags of boost for line inputs, only for 828mk2 and Traveler.
+ * @line_input.nominal_level_flag: The flags of nominal level for line inputs, only for 828mk2 and
+ *                                Traveler.
+ * @line_input.reserved: Padding for 32 bit alignment for future extension.
+ * @input.gain_and_invert: The value including gain and invert for input, only for Ultralite, 4 pre
+ *                        and Audio Express.
+ * @input.flag: The flag of input; e.g. jack detection, phantom power, and pad, only for Ultralite,
+ *             4 pre and Audio express.
+ * @reserved: Padding so that the size of structure is kept to 512 byte, but for future extension.
+ *
+ * The structure expresses the set of parameters for DSP controlled by register access.
+ */
+struct snd_firewire_motu_register_dsp_parameter {
+       struct {
+               struct {
+                       __u8 gain[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT];
+                       __u8 pan[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT];
+                       __u8 flag[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT];
+                       __u8 paired_balance[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT];
+                       __u8 paired_width[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT];
+               } source[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT];
+               struct {
+                       __u8 paired_volume[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT];
+                       __u8 paired_flag[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT];
+               } output;
+       } mixer;
+       struct {
+               __u8 main_paired_volume;
+               __u8 hp_paired_volume;
+               __u8 hp_paired_assignment;
+               __u8 reserved[5];
+       } output;
+       struct {
+               __u8 boost_flag;
+               __u8 nominal_level_flag;
+               __u8 reserved[6];
+       } line_input;
+       struct {
+               __u8 gain_and_invert[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_ALIGNED_INPUT_COUNT];
+               __u8 flag[SNDRV_FIREWIRE_MOTU_REGISTER_DSP_ALIGNED_INPUT_COUNT];
+       } input;
+       __u8 reserved[64];
+};
+
+/*
+ * In below MOTU models, software is allowed to control their DSP by command in frame of
+ * asynchronous transaction to 0x'ffff'0001'0000:
+ *
+ *  - 828 mk3 (FireWire only and Hybrid)
+ *  - 896 mk3 (FireWire only and Hybrid)
+ *  - Ultralite mk3 (FireWire only and Hybrid)
+ *  - Traveler mk3
+ *  - Track 16
+ *
+ * On the other hand, the states of hardware meter is split into specific messages included in the
+ * sequence of isochronous packet. ALSA firewire-motu driver gathers the message and allow userspace
+ * application to read it via ioctl.
+ */
+
+#define SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT    400
+
+/**
+ * struct snd_firewire_motu_command_dsp_meter - the container for meter information in DSP
+ *                                             controlled by command
+ * @data: Signal level meters. The mapping between position and signal channel is model-dependent.
+ *
+ * The structure expresses the part of DSP status for hardware meter. The 32 bit storage is
+ * estimated to include IEEE 764 32 bit single precision floating point (binary32) value. It is
+ * expected to be linear value (not logarithm) for audio signal level between 0.0 and +1.0.
+ */
+struct snd_firewire_motu_command_dsp_meter {
+#ifdef __KERNEL__
+       __u32 data[SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT];
+#else
+       float data[SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT];
+#endif
+};
+
 #endif /* _UAPI_SOUND_FIREWIRE_H_INCLUDED */
index d774792..350d704 100644 (file)
@@ -9,7 +9,9 @@ ifneq ($(CONFIG_SND_PROC_FS),)
 snd-y += info.o
 snd-$(CONFIG_SND_OSSEMUL) += info_oss.o
 endif
+ifneq ($(CONFIG_M68K),y)
 snd-$(CONFIG_ISA_DMA_API) += isadma.o
+endif
 snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
 snd-$(CONFIG_SND_VMASTER) += vmaster.o
 snd-$(CONFIG_SND_JACK)   += ctljack.o jack.o
@@ -17,7 +19,6 @@ snd-$(CONFIG_SND_JACK)          += ctljack.o jack.o
 snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
                pcm_memory.o memalloc.o
 snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
-snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
 snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
 snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
 
index c7c943c..99cd0f6 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
 #include <linux/genalloc.h>
+#include <linux/highmem.h>
 #include <linux/vmalloc.h>
 #ifdef CONFIG_X86
 #include <asm/set_memory.h>
@@ -39,9 +40,11 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
 }
 
 /**
- * snd_dma_alloc_pages - allocate the buffer area according to the given type
+ * snd_dma_alloc_dir_pages - allocate the buffer area according to the given
+ *     type and direction
  * @type: the DMA buffer type
  * @device: the device pointer
+ * @dir: DMA direction
  * @size: the buffer size to allocate
  * @dmab: buffer allocation record to store the allocated data
  *
@@ -51,8 +54,9 @@ static void *__snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
  * Return: Zero if the buffer with the given size is allocated successfully,
  * otherwise a negative value on error.
  */
-int snd_dma_alloc_pages(int type, struct device *device, size_t size,
-                       struct snd_dma_buffer *dmab)
+int snd_dma_alloc_dir_pages(int type, struct device *device,
+                           enum dma_data_direction dir, size_t size,
+                           struct snd_dma_buffer *dmab)
 {
        if (WARN_ON(!size))
                return -ENXIO;
@@ -62,6 +66,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
        size = PAGE_ALIGN(size);
        dmab->dev.type = type;
        dmab->dev.dev = device;
+       dmab->dev.dir = dir;
        dmab->bytes = 0;
        dmab->addr = 0;
        dmab->private_data = NULL;
@@ -71,7 +76,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
        dmab->bytes = size;
        return 0;
 }
-EXPORT_SYMBOL(snd_dma_alloc_pages);
+EXPORT_SYMBOL(snd_dma_alloc_dir_pages);
 
 /**
  * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback
@@ -129,9 +134,10 @@ static void __snd_release_pages(struct device *dev, void *res)
 }
 
 /**
- * snd_devm_alloc_pages - allocate the buffer and manage with devres
+ * snd_devm_alloc_dir_pages - allocate the buffer and manage with devres
  * @dev: the device pointer
  * @type: the DMA buffer type
+ * @dir: DMA direction
  * @size: the buffer size to allocate
  *
  * Allocate buffer pages depending on the given type and manage using devres.
@@ -144,7 +150,8 @@ static void __snd_release_pages(struct device *dev, void *res)
  * The function returns the snd_dma_buffer object at success, or NULL if failed.
  */
 struct snd_dma_buffer *
-snd_devm_alloc_pages(struct device *dev, int type, size_t size)
+snd_devm_alloc_dir_pages(struct device *dev, int type,
+                        enum dma_data_direction dir, size_t size)
 {
        struct snd_dma_buffer *dmab;
        int err;
@@ -157,7 +164,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size)
        if (!dmab)
                return NULL;
 
-       err = snd_dma_alloc_pages(type, dev, size, dmab);
+       err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
        if (err < 0) {
                devres_free(dmab);
                return NULL;
@@ -166,7 +173,7 @@ snd_devm_alloc_pages(struct device *dev, int type, size_t size)
        devres_add(dev, dmab);
        return dmab;
 }
-EXPORT_SYMBOL_GPL(snd_devm_alloc_pages);
+EXPORT_SYMBOL_GPL(snd_devm_alloc_dir_pages);
 
 /**
  * snd_dma_buffer_mmap - perform mmap of the given DMA buffer
@@ -185,6 +192,26 @@ int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
 }
 EXPORT_SYMBOL(snd_dma_buffer_mmap);
 
+#ifdef CONFIG_HAS_DMA
+/**
+ * snd_dma_buffer_sync - sync DMA buffer between CPU and device
+ * @dmab: buffer allocation information
+ * @mode: sync mode
+ */
+void snd_dma_buffer_sync(struct snd_dma_buffer *dmab,
+                        enum snd_dma_sync_mode mode)
+{
+       const struct snd_malloc_ops *ops;
+
+       if (!dmab || !dmab->dev.need_sync)
+               return;
+       ops = snd_dma_get_ops(dmab);
+       if (ops && ops->sync)
+               ops->sync(dmab, mode);
+}
+EXPORT_SYMBOL_GPL(snd_dma_buffer_sync);
+#endif /* CONFIG_HAS_DMA */
+
 /**
  * snd_sgbuf_get_addr - return the physical address at the corresponding offset
  * @dmab: buffer allocation information
@@ -468,6 +495,161 @@ static const struct snd_malloc_ops snd_dma_wc_ops = {
        .mmap = snd_dma_wc_mmap,
 };
 #endif /* CONFIG_X86 */
+
+/*
+ * Non-contiguous pages allocator
+ */
+static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+       struct sg_table *sgt;
+       void *p;
+
+       sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir,
+                                     DEFAULT_GFP, 0);
+       if (!sgt)
+               return NULL;
+       dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir);
+       p = dma_vmap_noncontiguous(dmab->dev.dev, size, sgt);
+       if (p)
+               dmab->private_data = sgt;
+       else
+               dma_free_noncontiguous(dmab->dev.dev, size, sgt, dmab->dev.dir);
+       return p;
+}
+
+static void snd_dma_noncontig_free(struct snd_dma_buffer *dmab)
+{
+       dma_vunmap_noncontiguous(dmab->dev.dev, dmab->area);
+       dma_free_noncontiguous(dmab->dev.dev, dmab->bytes, dmab->private_data,
+                              dmab->dev.dir);
+}
+
+static int snd_dma_noncontig_mmap(struct snd_dma_buffer *dmab,
+                                 struct vm_area_struct *area)
+{
+       return dma_mmap_noncontiguous(dmab->dev.dev, area,
+                                     dmab->bytes, dmab->private_data);
+}
+
+static void snd_dma_noncontig_sync(struct snd_dma_buffer *dmab,
+                                  enum snd_dma_sync_mode mode)
+{
+       if (mode == SNDRV_DMA_SYNC_CPU) {
+               if (dmab->dev.dir == DMA_TO_DEVICE)
+                       return;
+               dma_sync_sgtable_for_cpu(dmab->dev.dev, dmab->private_data,
+                                        dmab->dev.dir);
+               invalidate_kernel_vmap_range(dmab->area, dmab->bytes);
+       } else {
+               if (dmab->dev.dir == DMA_FROM_DEVICE)
+                       return;
+               flush_kernel_vmap_range(dmab->area, dmab->bytes);
+               dma_sync_sgtable_for_device(dmab->dev.dev, dmab->private_data,
+                                           dmab->dev.dir);
+       }
+}
+
+static const struct snd_malloc_ops snd_dma_noncontig_ops = {
+       .alloc = snd_dma_noncontig_alloc,
+       .free = snd_dma_noncontig_free,
+       .mmap = snd_dma_noncontig_mmap,
+       .sync = snd_dma_noncontig_sync,
+       /* re-use vmalloc helpers for get_* ops */
+       .get_addr = snd_dma_vmalloc_get_addr,
+       .get_page = snd_dma_vmalloc_get_page,
+       .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+
+/* x86-specific SG-buffer with WC pages */
+#ifdef CONFIG_SND_DMA_SGBUF
+#define vmalloc_to_virt(v) (unsigned long)page_to_virt(vmalloc_to_page(v))
+
+static void *snd_dma_sg_wc_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+       void *p = snd_dma_noncontig_alloc(dmab, size);
+       size_t ofs;
+
+       if (!p)
+               return NULL;
+       for (ofs = 0; ofs < size; ofs += PAGE_SIZE)
+               set_memory_uc(vmalloc_to_virt(p + ofs), 1);
+       return p;
+}
+
+static void snd_dma_sg_wc_free(struct snd_dma_buffer *dmab)
+{
+       size_t ofs;
+
+       for (ofs = 0; ofs < dmab->bytes; ofs += PAGE_SIZE)
+               set_memory_wb(vmalloc_to_virt(dmab->area + ofs), 1);
+       snd_dma_noncontig_free(dmab);
+}
+
+static int snd_dma_sg_wc_mmap(struct snd_dma_buffer *dmab,
+                             struct vm_area_struct *area)
+{
+       area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
+       /* FIXME: dma_mmap_noncontiguous() works? */
+       return -ENOENT; /* continue with the default mmap handler */
+}
+
+const struct snd_malloc_ops snd_dma_sg_wc_ops = {
+       .alloc = snd_dma_sg_wc_alloc,
+       .free = snd_dma_sg_wc_free,
+       .mmap = snd_dma_sg_wc_mmap,
+       .sync = snd_dma_noncontig_sync,
+       .get_addr = snd_dma_vmalloc_get_addr,
+       .get_page = snd_dma_vmalloc_get_page,
+       .get_chunk_size = snd_dma_vmalloc_get_chunk_size,
+};
+#endif /* CONFIG_SND_DMA_SGBUF */
+
+/*
+ * Non-coherent pages allocator
+ */
+static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size)
+{
+       dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->dev.dir);
+       return dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr,
+                                    dmab->dev.dir, DEFAULT_GFP);
+}
+
+static void snd_dma_noncoherent_free(struct snd_dma_buffer *dmab)
+{
+       dma_free_noncoherent(dmab->dev.dev, dmab->bytes, dmab->area,
+                            dmab->addr, dmab->dev.dir);
+}
+
+static int snd_dma_noncoherent_mmap(struct snd_dma_buffer *dmab,
+                                   struct vm_area_struct *area)
+{
+       area->vm_page_prot = vm_get_page_prot(area->vm_flags);
+       return dma_mmap_pages(dmab->dev.dev, area,
+                             area->vm_end - area->vm_start,
+                             virt_to_page(dmab->area));
+}
+
+static void snd_dma_noncoherent_sync(struct snd_dma_buffer *dmab,
+                                    enum snd_dma_sync_mode mode)
+{
+       if (mode == SNDRV_DMA_SYNC_CPU) {
+               if (dmab->dev.dir != DMA_TO_DEVICE)
+                       dma_sync_single_for_cpu(dmab->dev.dev, dmab->addr,
+                                               dmab->bytes, dmab->dev.dir);
+       } else {
+               if (dmab->dev.dir != DMA_FROM_DEVICE)
+                       dma_sync_single_for_device(dmab->dev.dev, dmab->addr,
+                                                  dmab->bytes, dmab->dev.dir);
+       }
+}
+
+static const struct snd_malloc_ops snd_dma_noncoherent_ops = {
+       .alloc = snd_dma_noncoherent_alloc,
+       .free = snd_dma_noncoherent_free,
+       .mmap = snd_dma_noncoherent_mmap,
+       .sync = snd_dma_noncoherent_sync,
+};
+
 #endif /* CONFIG_HAS_DMA */
 
 /*
@@ -479,14 +661,15 @@ static const struct snd_malloc_ops *dma_ops[] = {
 #ifdef CONFIG_HAS_DMA
        [SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
        [SNDRV_DMA_TYPE_DEV_WC] = &snd_dma_wc_ops,
+       [SNDRV_DMA_TYPE_NONCONTIG] = &snd_dma_noncontig_ops,
+       [SNDRV_DMA_TYPE_NONCOHERENT] = &snd_dma_noncoherent_ops,
+#ifdef CONFIG_SND_DMA_SGBUF
+       [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_wc_ops,
+#endif
 #ifdef CONFIG_GENERIC_ALLOCATOR
        [SNDRV_DMA_TYPE_DEV_IRAM] = &snd_dma_iram_ops,
 #endif /* CONFIG_GENERIC_ALLOCATOR */
 #endif /* CONFIG_HAS_DMA */
-#ifdef CONFIG_SND_DMA_SGBUF
-       [SNDRV_DMA_TYPE_DEV_SG] = &snd_dma_sg_ops,
-       [SNDRV_DMA_TYPE_DEV_WC_SG] = &snd_dma_sg_ops,
-#endif
 };
 
 static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab)
index 9f2e0a6..a6f3a87 100644 (file)
@@ -10,6 +10,7 @@ struct snd_malloc_ops {
        unsigned int (*get_chunk_size)(struct snd_dma_buffer *dmab,
                                       unsigned int ofs, unsigned int size);
        int (*mmap)(struct snd_dma_buffer *dmab, struct vm_area_struct *area);
+       void (*sync)(struct snd_dma_buffer *dmab, enum snd_dma_sync_mode mode);
 };
 
 #ifdef CONFIG_SND_DMA_SGBUF
index 6a5abdd..9620115 100644 (file)
@@ -130,11 +130,13 @@ static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
+       mutex_lock(&mixer->reg_mutex);
        for (chn = 0; chn < 31; chn++) {
                pslot = &mixer->slots[chn];
                if (pslot->put_volume || pslot->put_recsrc)
                        result |= 1 << chn;
        }
+       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -146,11 +148,13 @@ static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
+       mutex_lock(&mixer->reg_mutex);
        for (chn = 0; chn < 31; chn++) {
                pslot = &mixer->slots[chn];
                if (pslot->put_volume && pslot->stereo)
                        result |= 1 << chn;
        }
+       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -161,6 +165,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
+       mutex_lock(&mixer->reg_mutex);
        if (mixer->put_recsrc && mixer->get_recsrc) {   /* exclusive */
                result = mixer->mask_recsrc;
        } else {
@@ -172,6 +177,7 @@ static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)
                                result |= 1 << chn;
                }
        }
+       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -182,12 +188,12 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
 
        if (mixer == NULL)
                return -EIO;
+       mutex_lock(&mixer->reg_mutex);
        if (mixer->put_recsrc && mixer->get_recsrc) {   /* exclusive */
-               int err;
                unsigned int index;
-               err = mixer->get_recsrc(fmixer, &index);
-               if (err < 0)
-                       return err;
+               result = mixer->get_recsrc(fmixer, &index);
+               if (result < 0)
+                       goto unlock;
                result = 1 << index;
        } else {
                struct snd_mixer_oss_slot *pslot;
@@ -202,7 +208,10 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
                        }
                }
        }
-       return mixer->oss_recsrc = result;
+       mixer->oss_recsrc = result;
+ unlock:
+       mutex_unlock(&mixer->reg_mutex);
+       return result;
 }
 
 static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc)
@@ -215,6 +224,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
 
        if (mixer == NULL)
                return -EIO;
+       mutex_lock(&mixer->reg_mutex);
        if (mixer->get_recsrc && mixer->put_recsrc) {   /* exclusive input */
                if (recsrc & ~mixer->oss_recsrc)
                        recsrc &= ~mixer->oss_recsrc;
@@ -240,6 +250,7 @@ static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsr
                        }
                }
        }
+       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -251,6 +262,7 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
 
        if (mixer == NULL || slot > 30)
                return -EIO;
+       mutex_lock(&mixer->reg_mutex);
        pslot = &mixer->slots[slot];
        left = pslot->volume[0];
        right = pslot->volume[1];
@@ -258,15 +270,21 @@ static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)
                result = pslot->get_volume(fmixer, pslot, &left, &right);
        if (!pslot->stereo)
                right = left;
-       if (snd_BUG_ON(left < 0 || left > 100))
-               return -EIO;
-       if (snd_BUG_ON(right < 0 || right > 100))
-               return -EIO;
+       if (snd_BUG_ON(left < 0 || left > 100)) {
+               result = -EIO;
+               goto unlock;
+       }
+       if (snd_BUG_ON(right < 0 || right > 100)) {
+               result = -EIO;
+               goto unlock;
+       }
        if (result >= 0) {
                pslot->volume[0] = left;
                pslot->volume[1] = right;
                result = (left & 0xff) | ((right & 0xff) << 8);
        }
+ unlock:
+       mutex_unlock(&mixer->reg_mutex);
        return result;
 }
 
@@ -279,6 +297,7 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
 
        if (mixer == NULL || slot > 30)
                return -EIO;
+       mutex_lock(&mixer->reg_mutex);
        pslot = &mixer->slots[slot];
        if (left > 100)
                left = 100;
@@ -289,10 +308,13 @@ static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,
        if (pslot->put_volume)
                result = pslot->put_volume(fmixer, pslot, left, right);
        if (result < 0)
-               return result;
+               goto unlock;
        pslot->volume[0] = left;
        pslot->volume[1] = right;
-       return (left & 0xff) | ((right & 0xff) << 8);
+       result = (left & 0xff) | ((right & 0xff) << 8);
+ unlock:
+       mutex_unlock(&mixer->reg_mutex);
+       return result;
 }
 
 static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg)
index dfe5a64..e4e1768 100644 (file)
@@ -453,6 +453,8 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream,
        sstatus.suspended_state = status->suspended_state;
        sstatus.audio_tstamp = status->audio_tstamp;
        snd_pcm_stream_unlock_irq(substream);
+       if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+               snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (put_user(sstatus.state, &src->s.status.state) ||
            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
            put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
@@ -533,6 +535,8 @@ static int snd_pcm_ioctl_sync_ptr_buggy(struct snd_pcm_substream *substream,
        sync_ptr.s.status.suspended_state = status->suspended_state;
        sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
        snd_pcm_stream_unlock_irq(substream);
+       if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+               snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
                return -EFAULT;
        return 0;
index a144a3f..4f4b473 100644 (file)
@@ -106,6 +106,7 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
                frames -= transfer;
                ofs = 0;
        }
+       snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
 }
 
 #ifdef CONFIG_SND_DEBUG
@@ -2256,8 +2257,12 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
                        goto _end_unlock;
                }
                snd_pcm_stream_unlock_irq(substream);
+               if (!is_playback)
+                       snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
                err = writer(substream, appl_ofs, data, offset, frames,
                             transfer);
+               if (is_playback)
+                       snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
                snd_pcm_stream_lock_irq(substream);
                if (err < 0)
                        goto _end_unlock;
index fe9689b..ecb2169 100644 (file)
@@ -73,4 +73,11 @@ void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq);
                for ((subs) = (pcm)->streams[str].substream; (subs); \
                     (subs) = (subs)->next)
 
+static inline void snd_pcm_dma_buffer_sync(struct snd_pcm_substream *substream,
+                                          enum snd_dma_sync_mode mode)
+{
+       if (substream->runtime->info & SNDRV_PCM_INFO_EXPLICIT_SYNC)
+               snd_dma_buffer_sync(snd_pcm_get_dma_buf(substream), mode);
+}
+
 #endif /* __SOUND_CORE_PCM_LOCAL_H */
index 7fbd1cc..b70ce3b 100644 (file)
@@ -32,15 +32,20 @@ module_param(max_alloc_per_card, ulong, 0644);
 MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
 
 static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
-                         size_t size, struct snd_dma_buffer *dmab)
+                         int str, size_t size, struct snd_dma_buffer *dmab)
 {
+       enum dma_data_direction dir;
        int err;
 
        if (max_alloc_per_card &&
            card->total_pcm_alloc_bytes + size > max_alloc_per_card)
                return -ENOMEM;
 
-       err = snd_dma_alloc_pages(type, dev, size, dmab);
+       if (str == SNDRV_PCM_STREAM_PLAYBACK)
+               dir = DMA_TO_DEVICE;
+       else
+               dir = DMA_FROM_DEVICE;
+       err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
        if (!err) {
                mutex_lock(&card->memory_mutex);
                card->total_pcm_alloc_bytes += dmab->bytes;
@@ -77,7 +82,7 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream,
 
        do {
                err = do_alloc_pages(card, dmab->dev.type, dmab->dev.dev,
-                                    size, dmab);
+                                    substream->stream, size, dmab);
                if (err != -ENOMEM)
                        return err;
                if (no_fallback)
@@ -177,6 +182,7 @@ static void snd_pcm_lib_preallocate_proc_write(struct snd_info_entry *entry,
                        if (do_alloc_pages(card,
                                           substream->dma_buffer.dev.type,
                                           substream->dma_buffer.dev.dev,
+                                          substream->stream,
                                           size, &new_dmab) < 0) {
                                buffer->error = -ENOMEM;
                                pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
@@ -418,6 +424,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size)
                if (do_alloc_pages(card,
                                   substream->dma_buffer.dev.type,
                                   substream->dma_buffer.dev.dev,
+                                  substream->stream,
                                   size, dmab) < 0) {
                        kfree(dmab);
                        pr_debug("ALSA pcmC%dD%d%c,%d:%s: cannot preallocate for size %zu\n",
index d233cb3..621883e 100644 (file)
@@ -2685,6 +2685,13 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
                goto error;
        }
 
+       /* automatically set EXPLICIT_SYNC flag in the managed mode whenever
+        * the DMA buffer requires it
+        */
+       if (substream->managed_buffer_alloc &&
+           substream->dma_buffer.dev.need_sync)
+               substream->runtime->hw.info |= SNDRV_PCM_INFO_EXPLICIT_SYNC;
+
        *rsubstream = substream;
        return 0;
 
@@ -2912,6 +2919,8 @@ static snd_pcm_sframes_t snd_pcm_rewind(struct snd_pcm_substream *substream,
                ret = rewind_appl_ptr(substream, frames,
                                      snd_pcm_hw_avail(substream));
        snd_pcm_stream_unlock_irq(substream);
+       if (ret >= 0)
+               snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        return ret;
 }
 
@@ -2929,35 +2938,31 @@ static snd_pcm_sframes_t snd_pcm_forward(struct snd_pcm_substream *substream,
                ret = forward_appl_ptr(substream, frames,
                                       snd_pcm_avail(substream));
        snd_pcm_stream_unlock_irq(substream);
+       if (ret >= 0)
+               snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        return ret;
 }
 
-static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
-{
-       int err;
-
-       snd_pcm_stream_lock_irq(substream);
-       err = do_pcm_hwsync(substream);
-       snd_pcm_stream_unlock_irq(substream);
-       return err;
-}
-               
 static int snd_pcm_delay(struct snd_pcm_substream *substream,
                         snd_pcm_sframes_t *delay)
 {
        int err;
-       snd_pcm_sframes_t n = 0;
 
        snd_pcm_stream_lock_irq(substream);
        err = do_pcm_hwsync(substream);
-       if (!err)
-               n = snd_pcm_calc_delay(substream);
+       if (delay && !err)
+               *delay = snd_pcm_calc_delay(substream);
        snd_pcm_stream_unlock_irq(substream);
-       if (!err)
-               *delay = n;
+       snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_CPU);
+
        return err;
 }
                
+static inline int snd_pcm_hwsync(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_delay(substream, NULL);
+}
+
 static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
                            struct snd_pcm_sync_ptr __user *_sync_ptr)
 {
@@ -3000,6 +3005,8 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
        sync_ptr.s.status.suspended_state = status->suspended_state;
        sync_ptr.s.status.audio_tstamp = status->audio_tstamp;
        snd_pcm_stream_unlock_irq(substream);
+       if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL))
+               snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (copy_to_user(_sync_ptr, &sync_ptr, sizeof(sync_ptr)))
                return -EFAULT;
        return 0;
@@ -3096,6 +3103,8 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream,
        sstatus.suspended_state = status->suspended_state;
        sstatus.audio_tstamp = status->audio_tstamp;
        snd_pcm_stream_unlock_irq(substream);
+       if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL))
+               snd_pcm_dma_buffer_sync(substream, SNDRV_DMA_SYNC_DEVICE);
        if (put_user(sstatus.state, &src->s.status.state) ||
            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
            put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp_sec) ||
@@ -3218,6 +3227,9 @@ static int snd_pcm_common_ioctl(struct file *file,
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
 
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+               return -EBADFD;
+
        res = snd_power_wait(substream->pcm->card);
        if (res < 0)
                return res;
@@ -3272,7 +3284,7 @@ static int snd_pcm_common_ioctl(struct file *file,
                return snd_pcm_hwsync(substream);
        case SNDRV_PCM_IOCTL_DELAY:
        {
-               snd_pcm_sframes_t delay;
+               snd_pcm_sframes_t delay = 0;
                snd_pcm_sframes_t __user *res = arg;
                int err;
 
@@ -3344,6 +3356,9 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
        snd_pcm_uframes_t *frames = arg;
        snd_pcm_sframes_t result;
        
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+               return -EBADFD;
+
        switch (cmd) {
        case SNDRV_PCM_IOCTL_FORWARD:
        {
@@ -3386,7 +3401,8 @@ static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
        runtime = substream->runtime;
-       if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+       if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+           runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
                return -EBADFD;
        if (!frame_aligned(runtime, count))
                return -EINVAL;
@@ -3410,7 +3426,8 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf,
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
        runtime = substream->runtime;
-       if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+       if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+           runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
                return -EBADFD;
        if (!frame_aligned(runtime, count))
                return -EINVAL;
@@ -3436,7 +3453,8 @@ static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to)
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
        runtime = substream->runtime;
-       if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+       if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+           runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
                return -EBADFD;
        if (!iter_is_iovec(to))
                return -EINVAL;
@@ -3472,7 +3490,8 @@ static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from)
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
        runtime = substream->runtime;
-       if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
+       if (runtime->status->state == SNDRV_PCM_STATE_OPEN ||
+           runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
                return -EBADFD;
        if (!iter_is_iovec(from))
                return -EINVAL;
@@ -3511,6 +3530,9 @@ static __poll_t snd_pcm_poll(struct file *file, poll_table *wait)
                return ok | EPOLLERR;
 
        runtime = substream->runtime;
+       if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+               return ok | EPOLLERR;
+
        poll_wait(file, &runtime->sleep, wait);
 
        mask = 0;
@@ -3820,6 +3842,8 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
        substream = pcm_file->substream;
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+               return -EBADFD;
 
        offset = area->vm_pgoff << PAGE_SHIFT;
        switch (offset) {
@@ -3856,6 +3880,8 @@ static int snd_pcm_fasync(int fd, struct file * file, int on)
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
        runtime = substream->runtime;
+       if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED)
+               return -EBADFD;
        return fasync_helper(fd, file, on, &runtime->fasync);
 }
 
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
deleted file mode 100644 (file)
index 8352a5c..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Scatter-Gather buffer
- *
- *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
- */
-
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/export.h>
-#include <sound/memalloc.h>
-#include "memalloc_local.h"
-
-struct snd_sg_page {
-       void *buf;
-       dma_addr_t addr;
-};
-
-struct snd_sg_buf {
-       int size;       /* allocated byte size */
-       int pages;      /* allocated pages */
-       int tblsize;    /* allocated table size */
-       struct snd_sg_page *table;      /* address table */
-       struct page **page_table;       /* page table (for vmap/vunmap) */
-       struct device *dev;
-};
-
-/* table entries are align to 32 */
-#define SGBUF_TBL_ALIGN                32
-#define sgbuf_align_table(tbl) ALIGN((tbl), SGBUF_TBL_ALIGN)
-
-static void snd_dma_sg_free(struct snd_dma_buffer *dmab)
-{
-       struct snd_sg_buf *sgbuf = dmab->private_data;
-       struct snd_dma_buffer tmpb;
-       int i;
-
-       if (!sgbuf)
-               return;
-
-       vunmap(dmab->area);
-       dmab->area = NULL;
-
-       tmpb.dev.type = SNDRV_DMA_TYPE_DEV;
-       if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
-               tmpb.dev.type = SNDRV_DMA_TYPE_DEV_WC;
-       tmpb.dev.dev = sgbuf->dev;
-       for (i = 0; i < sgbuf->pages; i++) {
-               if (!(sgbuf->table[i].addr & ~PAGE_MASK))
-                       continue; /* continuous pages */
-               tmpb.area = sgbuf->table[i].buf;
-               tmpb.addr = sgbuf->table[i].addr & PAGE_MASK;
-               tmpb.bytes = (sgbuf->table[i].addr & ~PAGE_MASK) << PAGE_SHIFT;
-               snd_dma_free_pages(&tmpb);
-       }
-
-       kfree(sgbuf->table);
-       kfree(sgbuf->page_table);
-       kfree(sgbuf);
-       dmab->private_data = NULL;
-}
-
-#define MAX_ALLOC_PAGES                32
-
-static void *snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size)
-{
-       struct snd_sg_buf *sgbuf;
-       unsigned int i, pages, chunk, maxpages;
-       struct snd_dma_buffer tmpb;
-       struct snd_sg_page *table;
-       struct page **pgtable;
-       int type = SNDRV_DMA_TYPE_DEV;
-       pgprot_t prot = PAGE_KERNEL;
-       void *area;
-
-       dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
-       if (!sgbuf)
-               return NULL;
-       if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) {
-               type = SNDRV_DMA_TYPE_DEV_WC;
-#ifdef pgprot_noncached
-               prot = pgprot_noncached(PAGE_KERNEL);
-#endif
-       }
-       sgbuf->dev = dmab->dev.dev;
-       pages = snd_sgbuf_aligned_pages(size);
-       sgbuf->tblsize = sgbuf_align_table(pages);
-       table = kcalloc(sgbuf->tblsize, sizeof(*table), GFP_KERNEL);
-       if (!table)
-               goto _failed;
-       sgbuf->table = table;
-       pgtable = kcalloc(sgbuf->tblsize, sizeof(*pgtable), GFP_KERNEL);
-       if (!pgtable)
-               goto _failed;
-       sgbuf->page_table = pgtable;
-
-       /* allocate pages */
-       maxpages = MAX_ALLOC_PAGES;
-       while (pages > 0) {
-               chunk = pages;
-               /* don't be too eager to take a huge chunk */
-               if (chunk > maxpages)
-                       chunk = maxpages;
-               chunk <<= PAGE_SHIFT;
-               if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev,
-                                                chunk, &tmpb) < 0) {
-                       if (!sgbuf->pages)
-                               goto _failed;
-                       size = sgbuf->pages * PAGE_SIZE;
-                       break;
-               }
-               chunk = tmpb.bytes >> PAGE_SHIFT;
-               for (i = 0; i < chunk; i++) {
-                       table->buf = tmpb.area;
-                       table->addr = tmpb.addr;
-                       if (!i)
-                               table->addr |= chunk; /* mark head */
-                       table++;
-                       *pgtable++ = virt_to_page(tmpb.area);
-                       tmpb.area += PAGE_SIZE;
-                       tmpb.addr += PAGE_SIZE;
-               }
-               sgbuf->pages += chunk;
-               pages -= chunk;
-               if (chunk < maxpages)
-                       maxpages = chunk;
-       }
-
-       sgbuf->size = size;
-       area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot);
-       if (!area)
-               goto _failed;
-       return area;
-
- _failed:
-       snd_dma_sg_free(dmab); /* free the table */
-       return NULL;
-}
-
-static dma_addr_t snd_dma_sg_get_addr(struct snd_dma_buffer *dmab,
-                                     size_t offset)
-{
-       struct snd_sg_buf *sgbuf = dmab->private_data;
-       dma_addr_t addr;
-
-       addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
-       addr &= ~((dma_addr_t)PAGE_SIZE - 1);
-       return addr + offset % PAGE_SIZE;
-}
-
-static struct page *snd_dma_sg_get_page(struct snd_dma_buffer *dmab,
-                                       size_t offset)
-{
-       struct snd_sg_buf *sgbuf = dmab->private_data;
-       unsigned int idx = offset >> PAGE_SHIFT;
-
-       if (idx >= (unsigned int)sgbuf->pages)
-               return NULL;
-       return sgbuf->page_table[idx];
-}
-
-static unsigned int snd_dma_sg_get_chunk_size(struct snd_dma_buffer *dmab,
-                                             unsigned int ofs,
-                                             unsigned int size)
-{
-       struct snd_sg_buf *sg = dmab->private_data;
-       unsigned int start, end, pg;
-
-       start = ofs >> PAGE_SHIFT;
-       end = (ofs + size - 1) >> PAGE_SHIFT;
-       /* check page continuity */
-       pg = sg->table[start].addr >> PAGE_SHIFT;
-       for (;;) {
-               start++;
-               if (start > end)
-                       break;
-               pg++;
-               if ((sg->table[start].addr >> PAGE_SHIFT) != pg)
-                       return (start << PAGE_SHIFT) - ofs;
-       }
-       /* ok, all on continuous pages */
-       return size;
-}
-
-static int snd_dma_sg_mmap(struct snd_dma_buffer *dmab,
-                          struct vm_area_struct *area)
-{
-       if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG)
-               area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
-       return -ENOENT; /* continue with the default mmap handler */
-}
-
-const struct snd_malloc_ops snd_dma_sg_ops = {
-       .alloc = snd_dma_sg_alloc,
-       .free = snd_dma_sg_free,
-       .get_addr = snd_dma_sg_get_addr,
-       .get_page = snd_dma_sg_get_page,
-       .get_chunk_size = snd_dma_sg_get_chunk_size,
-       .mmap = snd_dma_sg_mmap,
-};
index acdf665..3bef2a0 100644 (file)
@@ -4,5 +4,6 @@ CFLAGS_amdtp-motu.o     := -I$(src)
 snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
                          motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
                          motu-protocol-v2.o motu-protocol-v3.o \
-                         motu-protocol-v1.o
+                         motu-protocol-v1.o motu-register-dsp-message-parser.o \
+                         motu-command-dsp-message-parser.o
 obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
index a18c2c0..2fb52f4 100644 (file)
@@ -333,6 +333,7 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
                                            unsigned int packets,
                                            struct snd_pcm_substream *pcm)
 {
+       struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
        struct amdtp_motu *p = s->protocol;
        unsigned int pcm_frames = 0;
        int i;
@@ -357,6 +358,14 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
                        read_midi_messages(s, buf, data_blocks);
        }
 
+       if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+               snd_motu_register_dsp_message_parser_parse(motu, descs, packets,
+                                                          s->data_block_quadlets);
+       } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+               snd_motu_command_dsp_message_parser_parse(motu, descs, packets,
+                                                         s->data_block_quadlets);
+       }
+
        // For tracepoints.
        if (trace_data_block_sph_enabled() ||
            trace_data_block_message_enabled())
@@ -415,8 +424,6 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
                if (p->midi_ports)
                        write_midi_messages(s, buf, data_blocks);
 
-               // TODO: how to interact control messages between userspace?
-
                write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
        }
 
diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c
new file mode 100644 (file)
index 0000000..9efe4d3
--- /dev/null
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-command-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP function by command transferred in
+// asynchronous transaction:
+//  * 828 mk3 (FireWire only and Hybrid)
+//  * 896 mk3 (FireWire only and Hybrid)
+//  * Ultralite mk3 (FireWire only and Hybrid)
+//  * Traveler mk3
+//  * Track 16
+//
+// Isochronous packets from the above models includes messages to report state of hardware meter.
+
+#include "motu.h"
+
+enum msg_parser_state {
+       INITIALIZED,
+       FRAGMENT_DETECTED,
+       AVAILABLE,
+};
+
+struct msg_parser {
+       spinlock_t lock;
+       enum msg_parser_state state;
+       unsigned int interval;
+       unsigned int message_count;
+       unsigned int fragment_pos;
+       unsigned int value_index;
+       u64 value;
+       struct snd_firewire_motu_command_dsp_meter meter;
+};
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu)
+{
+       struct msg_parser *parser;
+
+       parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+       if (!parser)
+               return -ENOMEM;
+       spin_lock_init(&parser->lock);
+       motu->message_parser = parser;
+
+       return 0;
+}
+
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc)
+{
+       struct msg_parser *parser = motu->message_parser;
+
+       parser->state = INITIALIZED;
+
+       // All of data blocks don't have messages with meaningful information.
+       switch (sfc) {
+       case CIP_SFC_176400:
+       case CIP_SFC_192000:
+               parser->interval = 4;
+               break;
+       case CIP_SFC_88200:
+       case CIP_SFC_96000:
+               parser->interval = 2;
+               break;
+       case CIP_SFC_32000:
+       case CIP_SFC_44100:
+       case CIP_SFC_48000:
+       default:
+               parser->interval = 1;
+               break;
+       }
+
+       return 0;
+}
+
+#define FRAGMENT_POS                   6
+#define MIDI_BYTE_POS                  7
+#define MIDI_FLAG_POS                  8
+// One value of hardware meter consists of 4 messages.
+#define FRAGMENTS_PER_VALUE            4
+#define VALUES_AT_IMAGE_END            0xffffffffffffffff
+
+void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+                                       unsigned int desc_count, unsigned int data_block_quadlets)
+{
+       struct msg_parser *parser = motu->message_parser;
+       unsigned int interval = parser->interval;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&parser->lock, flags);
+
+       for (i = 0; i < desc_count; ++i) {
+               const struct pkt_desc *desc = descs + i;
+               __be32 *buffer = desc->ctx_payload;
+               unsigned int data_blocks = desc->data_blocks;
+               int j;
+
+               for (j = 0; j < data_blocks; ++j) {
+                       u8 *b = (u8 *)buffer;
+                       buffer += data_block_quadlets;
+
+                       switch (parser->state) {
+                       case INITIALIZED:
+                       {
+                               u8 fragment = b[FRAGMENT_POS];
+
+                               if (fragment > 0) {
+                                       parser->value = fragment;
+                                       parser->message_count = 1;
+                                       parser->state = FRAGMENT_DETECTED;
+                               }
+                               break;
+                       }
+                       case FRAGMENT_DETECTED:
+                       {
+                               if (parser->message_count % interval == 0) {
+                                       u8 fragment = b[FRAGMENT_POS];
+
+                                       parser->value >>= 8;
+                                       parser->value |= (u64)fragment << 56;
+
+                                       if (parser->value == VALUES_AT_IMAGE_END) {
+                                               parser->state = AVAILABLE;
+                                               parser->fragment_pos = 0;
+                                               parser->value_index = 0;
+                                               parser->message_count = 0;
+                                       }
+                               }
+                               ++parser->message_count;
+                               break;
+                       }
+                       case AVAILABLE:
+                       default:
+                       {
+                               if (parser->message_count % interval == 0) {
+                                       u8 fragment = b[FRAGMENT_POS];
+
+                                       parser->value >>= 8;
+                                       parser->value |= (u64)fragment << 56;
+                                       ++parser->fragment_pos;
+
+                                       if (parser->fragment_pos == 4) {
+                                               // Skip the last two quadlets since they could be
+                                               // invalid value (0xffffffff) as floating point
+                                               // number.
+                                               if (parser->value_index <
+                                                   SNDRV_FIREWIRE_MOTU_COMMAND_DSP_METER_COUNT - 2) {
+                                                       u32 val = (u32)(parser->value >> 32);
+                                                       parser->meter.data[parser->value_index] = val;
+                                               }
+                                               ++parser->value_index;
+                                               parser->fragment_pos = 0;
+                                       }
+
+                                       if (parser->value == VALUES_AT_IMAGE_END) {
+                                               parser->value_index = 0;
+                                               parser->fragment_pos = 0;
+                                               parser->message_count = 0;
+                                       }
+                               }
+                               ++parser->message_count;
+                               break;
+                       }
+                       }
+               }
+       }
+
+       spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+                                       struct snd_firewire_motu_command_dsp_meter *meter)
+{
+       struct msg_parser *parser = motu->message_parser;
+       unsigned long flags;
+
+       spin_lock_irqsave(&parser->lock, flags);
+       memcpy(meter, &parser->meter, sizeof(*meter));
+       spin_unlock_irqrestore(&parser->lock, flags);
+}
index b5ced5d..a900fc0 100644 (file)
 
 #include "motu.h"
 
+static bool has_dsp_event(struct snd_motu *motu)
+{
+       if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+               return (snd_motu_register_dsp_message_parser_count_event(motu) > 0);
+       else
+               return false;
+}
+
 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
                       loff_t *offset)
 {
@@ -25,7 +33,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 
        spin_lock_irq(&motu->lock);
 
-       while (!motu->dev_lock_changed && motu->msg == 0) {
+       while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) {
                prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
                spin_unlock_irq(&motu->lock);
                schedule();
@@ -40,20 +48,46 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
                event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
                event.lock_status.status = (motu->dev_lock_count > 0);
                motu->dev_lock_changed = false;
+               spin_unlock_irq(&motu->lock);
 
-               count = min_t(long, count, sizeof(event.lock_status));
-       } else {
+               count = min_t(long, count, sizeof(event));
+               if (copy_to_user(buf, &event, count))
+                       return -EFAULT;
+       } else if (motu->msg > 0) {
                event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
                event.motu_notification.message = motu->msg;
                motu->msg = 0;
+               spin_unlock_irq(&motu->lock);
 
-               count = min_t(long, count, sizeof(event.motu_notification));
-       }
+               count = min_t(long, count, sizeof(event));
+               if (copy_to_user(buf, &event, count))
+                       return -EFAULT;
+       } else if (has_dsp_event(motu)) {
+               size_t consumed = 0;
+               u32 __user *ptr;
+               u32 ev;
 
-       spin_unlock_irq(&motu->lock);
+               spin_unlock_irq(&motu->lock);
 
-       if (copy_to_user(buf, &event, count))
-               return -EFAULT;
+               // Header is filled later.
+               consumed += sizeof(event.motu_register_dsp_change);
+
+               while (consumed < count &&
+                      snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
+                       ptr = (u32 __user *)(buf + consumed);
+                       if (put_user(ev, ptr))
+                               return -EFAULT;
+                       consumed += sizeof(ev);
+               }
+
+               event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
+               event.motu_register_dsp_change.count =
+                       (consumed - sizeof(event.motu_register_dsp_change)) / 4;
+               if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
+                       return -EFAULT;
+
+               count = consumed;
+       }
 
        return count;
 }
@@ -67,7 +101,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
        poll_wait(file, &motu->hwdep_wait, wait);
 
        spin_lock_irq(&motu->lock);
-       if (motu->dev_lock_changed || motu->msg)
+       if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
                events = EPOLLIN | EPOLLRDNORM;
        else
                events = 0;
@@ -155,6 +189,71 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
                return hwdep_lock(motu);
        case SNDRV_FIREWIRE_IOCTL_UNLOCK:
                return hwdep_unlock(motu);
+       case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER:
+       {
+               struct snd_firewire_motu_register_dsp_meter *meter;
+               int err;
+
+               if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+                       return -ENXIO;
+
+               meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+               if (!meter)
+                       return -ENOMEM;
+
+               snd_motu_register_dsp_message_parser_copy_meter(motu, meter);
+
+               err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+               kfree(meter);
+
+               if (err)
+                       return -EFAULT;
+
+               return 0;
+       }
+       case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER:
+       {
+               struct snd_firewire_motu_command_dsp_meter *meter;
+               int err;
+
+               if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP))
+                       return -ENXIO;
+
+               meter = kzalloc(sizeof(*meter), GFP_KERNEL);
+               if (!meter)
+                       return -ENOMEM;
+
+               snd_motu_command_dsp_message_parser_copy_meter(motu, meter);
+
+               err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
+               kfree(meter);
+
+               if (err)
+                       return -EFAULT;
+
+               return 0;
+       }
+       case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER:
+       {
+               struct snd_firewire_motu_register_dsp_parameter *param;
+               int err;
+
+               if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
+                       return -ENXIO;
+
+               param = kzalloc(sizeof(*param), GFP_KERNEL);
+               if (!param)
+                       return -ENOMEM;
+
+               snd_motu_register_dsp_message_parser_copy_parameter(motu, param);
+
+               err = copy_to_user((void __user *)arg, param, sizeof(*param));
+               kfree(param);
+               if (err)
+                       return -EFAULT;
+
+               return 0;
+       }
        default:
                return -ENOIOCTLCMD;
        }
@@ -193,5 +292,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu)
        hwdep->private_data = motu;
        hwdep->exclusive = true;
 
+       motu->hwdep = hwdep;
+
        return 0;
 }
index 2bd4485..a5f70ef 100644 (file)
@@ -275,7 +275,8 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = {
        .name = "828mk2",
        .protocol_version = SND_MOTU_PROTOCOL_V2,
        .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
-                SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+                SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+                SND_MOTU_SPEC_REGISTER_DSP,
        .tx_fixed_pcm_chunks = {14, 14, 0},
        .rx_fixed_pcm_chunks = {14, 14, 0},
 };
@@ -283,7 +284,7 @@ const struct snd_motu_spec snd_motu_spec_828mk2 = {
 const struct snd_motu_spec snd_motu_spec_896hd = {
        .name = "896HD",
        .protocol_version = SND_MOTU_PROTOCOL_V2,
-       // No support for MIDI.
+       .flags = SND_MOTU_SPEC_REGISTER_DSP,
        .tx_fixed_pcm_chunks = {14, 14, 8},
        .rx_fixed_pcm_chunks = {14, 14, 8},
 };
@@ -292,7 +293,8 @@ const struct snd_motu_spec snd_motu_spec_traveler = {
        .name = "Traveler",
        .protocol_version = SND_MOTU_PROTOCOL_V2,
        .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
-                SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+                SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+                SND_MOTU_SPEC_REGISTER_DSP,
        .tx_fixed_pcm_chunks = {14, 14, 8},
        .rx_fixed_pcm_chunks = {14, 14, 8},
 };
@@ -301,7 +303,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite = {
        .name = "UltraLite",
        .protocol_version = SND_MOTU_PROTOCOL_V2,
        .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
-                SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+                SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+                SND_MOTU_SPEC_REGISTER_DSP,
        .tx_fixed_pcm_chunks = {14, 14, 0},
        .rx_fixed_pcm_chunks = {14, 14, 0},
 };
@@ -310,7 +313,8 @@ const struct snd_motu_spec snd_motu_spec_8pre = {
        .name = "8pre",
        .protocol_version = SND_MOTU_PROTOCOL_V2,
        .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
-                SND_MOTU_SPEC_TX_MIDI_2ND_Q,
+                SND_MOTU_SPEC_TX_MIDI_2ND_Q |
+                SND_MOTU_SPEC_REGISTER_DSP,
        // Two dummy chunks always in the end of data block.
        .tx_fixed_pcm_chunks = {10, 10, 0},
        .rx_fixed_pcm_chunks = {6, 6, 0},
index 56e4504..05608e8 100644 (file)
@@ -261,12 +261,12 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
                return 0;
 }
 
-
 const struct snd_motu_spec snd_motu_spec_828mk3_fw = {
        .name = "828mk3",
        .protocol_version = SND_MOTU_PROTOCOL_V3,
        .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
-                SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+                SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+                SND_MOTU_SPEC_COMMAND_DSP,
        .tx_fixed_pcm_chunks = {18, 18, 14},
        .rx_fixed_pcm_chunks = {14, 14, 10},
 };
@@ -275,7 +275,8 @@ const struct snd_motu_spec snd_motu_spec_828mk3_hybrid = {
        .name = "828mk3",
        .protocol_version = SND_MOTU_PROTOCOL_V3,
        .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
-                SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+                SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+                SND_MOTU_SPEC_COMMAND_DSP,
        .tx_fixed_pcm_chunks = {18, 18, 14},
        .rx_fixed_pcm_chunks = {14, 14, 14},    // Additional 4 dummy chunks at higher rate.
 };
@@ -284,7 +285,8 @@ const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
        .name = "UltraLiteMk3",
        .protocol_version = SND_MOTU_PROTOCOL_V3,
        .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q |
-                SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+                SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+                SND_MOTU_SPEC_COMMAND_DSP,
        .tx_fixed_pcm_chunks = {18, 14, 10},
        .rx_fixed_pcm_chunks = {14, 14, 14},
 };
@@ -293,7 +295,8 @@ const struct snd_motu_spec snd_motu_spec_audio_express = {
        .name = "AudioExpress",
        .protocol_version = SND_MOTU_PROTOCOL_V3,
        .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
-                SND_MOTU_SPEC_TX_MIDI_3RD_Q,
+                SND_MOTU_SPEC_TX_MIDI_3RD_Q |
+                SND_MOTU_SPEC_REGISTER_DSP,
        .tx_fixed_pcm_chunks = {10, 10, 0},
        .rx_fixed_pcm_chunks = {10, 10, 0},
 };
@@ -301,6 +304,7 @@ const struct snd_motu_spec snd_motu_spec_audio_express = {
 const struct snd_motu_spec snd_motu_spec_4pre = {
        .name = "4pre",
        .protocol_version = SND_MOTU_PROTOCOL_V3,
+       .flags = SND_MOTU_SPEC_REGISTER_DSP,
        .tx_fixed_pcm_chunks = {10, 10, 0},
        .rx_fixed_pcm_chunks = {10, 10, 0},
 };
diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c
new file mode 100644 (file)
index 0000000..0c58756
--- /dev/null
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// motu-register-dsp-message-parser.c - a part of driver for MOTU FireWire series
+//
+// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+
+// Below models allow software to configure their DSP functions by asynchronous transaction
+// to access their internal registers.
+// * 828 mk2
+// * 896hd
+// * Traveler
+// * 8 pre
+// * Ultralite
+// * 4 pre
+// * Audio Express
+//
+// Additionally, isochronous packets from the above models include messages to notify state of
+// DSP. The messages are two set of 3 byte data in 2nd and 3rd quadlet of data block. When user
+// operates hardware components such as dial and switch, corresponding messages are transferred.
+// The messages include Hardware metering and MIDI messages as well.
+
+#include "motu.h"
+
+#define MSG_FLAG_POS                    4
+#define MSG_FLAG_TYPE_MASK              0xf8
+#define MSG_FLAG_MIDI_MASK              0x01
+#define MSG_FLAG_MODEL_SPECIFIC_MASK    0x06
+#define   MSG_FLAG_8PRE                 0x00
+#define   MSG_FLAG_ULTRALITE            0x04
+#define   MSG_FLAG_TRAVELER             0x04
+#define   MSG_FLAG_828MK2               0x04
+#define   MSG_FLAG_896HD                0x04
+#define   MSG_FLAG_4PRE                 0x05 // MIDI mask is in 8th byte.
+#define   MSG_FLAG_AUDIOEXPRESS         0x05 // MIDI mask is in 8th byte.
+#define MSG_FLAG_TYPE_SHIFT             3
+#define MSG_VALUE_POS                   5
+#define MSG_MIDI_BYTE_POS              6
+#define MSG_METER_IDX_POS               7
+
+// In 4 pre and Audio express, meter index is in 6th byte. MIDI flag is in 8th byte and MIDI byte
+// is in 7th byte.
+#define MSG_METER_IDX_POS_4PRE_AE      6
+#define MSG_MIDI_BYTE_POS_4PRE_AE      7
+#define MSG_FLAG_MIDI_POS_4PRE_AE      8
+
+enum register_dsp_msg_type {
+       // Used for messages with no information.
+       INVALID = 0x00,
+       MIXER_SELECT = 0x01,
+       MIXER_SRC_GAIN = 0x02,
+       MIXER_SRC_PAN = 0x03,
+       MIXER_SRC_FLAG = 0x04,
+       MIXER_OUTPUT_PAIRED_VOLUME = 0x05,
+       MIXER_OUTPUT_PAIRED_FLAG = 0x06,
+       MAIN_OUTPUT_PAIRED_VOLUME = 0x07,
+       HP_OUTPUT_PAIRED_VOLUME = 0x08,
+       HP_OUTPUT_PAIRED_ASSIGNMENT = 0x09,
+       // Transferred by all models but the purpose is still unknown.
+       UNKNOWN_0 = 0x0a,
+       // Specific to 828mk2, 896hd, Traveler.
+       UNKNOWN_2 = 0x0c,
+       // Specific to 828mk2, Traveler, and 896hd (not functional).
+       LINE_INPUT_BOOST = 0x0d,
+       // Specific to 828mk2, Traveler, and 896hd (not functional).
+       LINE_INPUT_NOMINAL_LEVEL = 0x0e,
+       // Specific to Ultralite, 4 pre, Audio express, and 8 pre (not functional).
+       INPUT_GAIN_AND_INVERT = 0x15,
+       // Specific to 4 pre, and Audio express.
+       INPUT_FLAG = 0x16,
+       // Specific to 4 pre, and Audio express.
+       MIXER_SRC_PAIRED_BALANCE = 0x17,
+       // Specific to 4 pre, and Audio express.
+       MIXER_SRC_PAIRED_WIDTH = 0x18,
+       // Transferred by all models. This type of message interposes the series of the other
+       // messages. The message delivers signal level up to 96.0 kHz. In 828mk2, 896hd, and
+       // Traveler, one of physical outputs is selected for the message. The selection is done
+       // by LSB one byte in asynchronous write quadlet transaction to 0x'ffff'f000'0b2c.
+       METER = 0x1f,
+};
+
+#define EVENT_QUEUE_SIZE       16
+
+struct msg_parser {
+       spinlock_t lock;
+       struct snd_firewire_motu_register_dsp_meter meter;
+       bool meter_pos_quirk;
+
+       struct snd_firewire_motu_register_dsp_parameter param;
+       u8 prev_mixer_src_type;
+       u8 mixer_ch;
+       u8 mixer_src_ch;
+
+       u8 input_ch;
+       u8 prev_msg_type;
+
+       u32 event_queue[EVENT_QUEUE_SIZE];
+       unsigned int push_pos;
+       unsigned int pull_pos;
+};
+
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu)
+{
+       struct msg_parser *parser;
+       parser = devm_kzalloc(&motu->card->card_dev, sizeof(*parser), GFP_KERNEL);
+       if (!parser)
+               return -ENOMEM;
+       spin_lock_init(&parser->lock);
+       if (motu->spec == &snd_motu_spec_4pre || motu->spec == &snd_motu_spec_audio_express)
+               parser->meter_pos_quirk = true;
+       motu->message_parser = parser;
+       return 0;
+}
+
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu)
+{
+       struct msg_parser *parser = motu->message_parser;
+
+       parser->prev_mixer_src_type = INVALID;
+       parser->mixer_ch = 0xff;
+       parser->mixer_src_ch = 0xff;
+       parser->prev_msg_type = INVALID;
+
+       return 0;
+}
+
+// Rough implementaion of queue without overrun check.
+static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 identifier1, u8 val)
+{
+       struct msg_parser *parser = motu->message_parser;
+       unsigned int pos = parser->push_pos;
+       u32 entry;
+
+       if (!motu->hwdep || motu->hwdep->used == 0)
+               return;
+
+       entry = (msg_type << 24) | (identifier0 << 16) | (identifier1 << 8) | val;
+       parser->event_queue[pos] = entry;
+
+       ++pos;
+       if (pos >= EVENT_QUEUE_SIZE)
+               pos = 0;
+       parser->push_pos = pos;
+}
+
+void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+                                       unsigned int desc_count, unsigned int data_block_quadlets)
+{
+       struct msg_parser *parser = motu->message_parser;
+       bool meter_pos_quirk = parser->meter_pos_quirk;
+       unsigned int pos = parser->push_pos;
+       unsigned long flags;
+       int i;
+
+       spin_lock_irqsave(&parser->lock, flags);
+
+       for (i = 0; i < desc_count; ++i) {
+               const struct pkt_desc *desc = descs + i;
+               __be32 *buffer = desc->ctx_payload;
+               unsigned int data_blocks = desc->data_blocks;
+               int j;
+
+               for (j = 0; j < data_blocks; ++j) {
+                       u8 *b = (u8 *)buffer;
+                       u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT;
+                       u8 val = b[MSG_VALUE_POS];
+
+                       buffer += data_block_quadlets;
+
+                       switch (msg_type) {
+                       case MIXER_SELECT:
+                       {
+                               u8 mixer_ch = val / 0x20;
+                               if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+                                       parser->mixer_src_ch = 0;
+                                       parser->mixer_ch = mixer_ch;
+                               }
+                               break;
+                       }
+                       case MIXER_SRC_GAIN:
+                       case MIXER_SRC_PAN:
+                       case MIXER_SRC_FLAG:
+                       case MIXER_SRC_PAIRED_BALANCE:
+                       case MIXER_SRC_PAIRED_WIDTH:
+                       {
+                               struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+                               u8 mixer_ch = parser->mixer_ch;
+                               u8 mixer_src_ch = parser->mixer_src_ch;
+
+                               if (msg_type != parser->prev_mixer_src_type)
+                                       mixer_src_ch = 0;
+                               else
+                                       ++mixer_src_ch;
+                               parser->prev_mixer_src_type = msg_type;
+
+                               if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT &&
+                                   mixer_src_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_SRC_COUNT) {
+                                       u8 mixer_ch = parser->mixer_ch;
+
+                                       switch (msg_type) {
+                                       case MIXER_SRC_GAIN:
+                                               if (param->mixer.source[mixer_ch].gain[mixer_src_ch] != val) {
+                                                       queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+                                                       param->mixer.source[mixer_ch].gain[mixer_src_ch] = val;
+                                               }
+                                               break;
+                                       case MIXER_SRC_PAN:
+                                               if (param->mixer.source[mixer_ch].pan[mixer_src_ch] != val) {
+                                                       queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+                                                       param->mixer.source[mixer_ch].pan[mixer_src_ch] = val;
+                                               }
+                                               break;
+                                       case MIXER_SRC_FLAG:
+                                               if (param->mixer.source[mixer_ch].flag[mixer_src_ch] != val) {
+                                                       queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+                                                       param->mixer.source[mixer_ch].flag[mixer_src_ch] = val;
+                                               }
+                                               break;
+                                       case MIXER_SRC_PAIRED_BALANCE:
+                                               if (param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] != val) {
+                                                       queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+                                                       param->mixer.source[mixer_ch].paired_balance[mixer_src_ch] = val;
+                                               }
+                                               break;
+                                       case MIXER_SRC_PAIRED_WIDTH:
+                                               if (param->mixer.source[mixer_ch].paired_width[mixer_src_ch] != val) {
+                                                       queue_event(motu, msg_type, mixer_ch, mixer_src_ch, val);
+                                                       param->mixer.source[mixer_ch].paired_width[mixer_src_ch] = val;
+                                               }
+                                               break;
+                                       default:
+                                               break;
+                                       }
+
+                                       parser->mixer_src_ch = mixer_src_ch;
+                               }
+                               break;
+                       }
+                       case MIXER_OUTPUT_PAIRED_VOLUME:
+                       case MIXER_OUTPUT_PAIRED_FLAG:
+                       {
+                               struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+                               u8 mixer_ch = parser->mixer_ch;
+
+                               if (mixer_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_MIXER_COUNT) {
+                                       switch (msg_type) {
+                                       case MIXER_OUTPUT_PAIRED_VOLUME:
+                                               if (param->mixer.output.paired_volume[mixer_ch] != val) {
+                                                       queue_event(motu, msg_type, mixer_ch, 0, val);
+                                                       param->mixer.output.paired_volume[mixer_ch] = val;
+                                               }
+                                               break;
+                                       case MIXER_OUTPUT_PAIRED_FLAG:
+                                               if (param->mixer.output.paired_flag[mixer_ch] != val) {
+                                                       queue_event(motu, msg_type, mixer_ch, 0, val);
+                                                       param->mixer.output.paired_flag[mixer_ch] = val;
+                                               }
+                                               break;
+                                       default:
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+                       case MAIN_OUTPUT_PAIRED_VOLUME:
+                               if (parser->param.output.main_paired_volume != val) {
+                                       queue_event(motu, msg_type, 0, 0, val);
+                                       parser->param.output.main_paired_volume = val;
+                               }
+                               break;
+                       case HP_OUTPUT_PAIRED_VOLUME:
+                               if (parser->param.output.hp_paired_volume != val) {
+                                       queue_event(motu, msg_type, 0, 0, val);
+                                       parser->param.output.hp_paired_volume = val;
+                               }
+                               break;
+                       case HP_OUTPUT_PAIRED_ASSIGNMENT:
+                               if (parser->param.output.hp_paired_assignment != val) {
+                                       queue_event(motu, msg_type, 0, 0, val);
+                                       parser->param.output.hp_paired_assignment = val;
+                               }
+                               break;
+                       case LINE_INPUT_BOOST:
+                               if (parser->param.line_input.boost_flag != val) {
+                                       queue_event(motu, msg_type, 0, 0, val);
+                                       parser->param.line_input.boost_flag = val;
+                               }
+                               break;
+                       case LINE_INPUT_NOMINAL_LEVEL:
+                               if (parser->param.line_input.nominal_level_flag != val) {
+                                       queue_event(motu, msg_type, 0, 0, val);
+                                       parser->param.line_input.nominal_level_flag = val;
+                               }
+                               break;
+                       case INPUT_GAIN_AND_INVERT:
+                       case INPUT_FLAG:
+                       {
+                               struct snd_firewire_motu_register_dsp_parameter *param = &parser->param;
+                               u8 input_ch = parser->input_ch;
+
+                               if (parser->prev_msg_type != msg_type)
+                                       input_ch = 0;
+                               else
+                                       ++input_ch;
+
+                               if (input_ch < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_INPUT_COUNT) {
+                                       switch (msg_type) {
+                                       case INPUT_GAIN_AND_INVERT:
+                                               if (param->input.gain_and_invert[input_ch] != val) {
+                                                       queue_event(motu, msg_type, input_ch, 0, val);
+                                                       param->input.gain_and_invert[input_ch] = val;
+                                               }
+                                               break;
+                                       case INPUT_FLAG:
+                                               if (param->input.flag[input_ch] != val) {
+                                                       queue_event(motu, msg_type, input_ch, 0, val);
+                                                       param->input.flag[input_ch] = val;
+                                               }
+                                               break;
+                                       default:
+                                               break;
+                                       }
+                                       parser->input_ch = input_ch;
+                               }
+                               break;
+                       }
+                       case UNKNOWN_0:
+                       case UNKNOWN_2:
+                               break;
+                       case METER:
+                       {
+                               u8 pos;
+
+                               if (!meter_pos_quirk)
+                                       pos = b[MSG_METER_IDX_POS];
+                               else
+                                       pos = b[MSG_METER_IDX_POS_4PRE_AE];
+
+                               if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT) {
+                                       parser->meter.data[pos] = val;
+                               } else if (pos >= 0x80) {
+                                       pos -= (0x80 - SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_INPUT_COUNT);
+
+                                       if (pos < SNDRV_FIREWIRE_MOTU_REGISTER_DSP_METER_COUNT)
+                                               parser->meter.data[pos] = val;
+                               }
+
+                               // The message for meter is interruptible to the series of other
+                               // types of messages. Don't cache it.
+                               fallthrough;
+                       }
+                       case INVALID:
+                       default:
+                               // Don't cache it.
+                               continue;
+                       }
+
+                       parser->prev_msg_type = msg_type;
+               }
+       }
+
+       if (pos != parser->push_pos)
+               wake_up(&motu->hwdep_wait);
+
+       spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+                                               struct snd_firewire_motu_register_dsp_meter *meter)
+{
+       struct msg_parser *parser = motu->message_parser;
+       unsigned long flags;
+
+       spin_lock_irqsave(&parser->lock, flags);
+       memcpy(meter, &parser->meter, sizeof(*meter));
+       spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+                                       struct snd_firewire_motu_register_dsp_parameter *param)
+{
+       struct msg_parser *parser = motu->message_parser;
+       unsigned long flags;
+
+       spin_lock_irqsave(&parser->lock, flags);
+       memcpy(param, &parser->param, sizeof(*param));
+       spin_unlock_irqrestore(&parser->lock, flags);
+}
+
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu)
+{
+       struct msg_parser *parser = motu->message_parser;
+
+       if (parser->pull_pos > parser->push_pos)
+               return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos;
+       else
+               return parser->push_pos - parser->pull_pos;
+}
+
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event)
+{
+       struct msg_parser *parser = motu->message_parser;
+       unsigned int pos = parser->pull_pos;
+       unsigned long flags;
+
+       if (pos == parser->push_pos)
+               return false;
+
+       spin_lock_irqsave(&parser->lock, flags);
+
+       *event = parser->event_queue[pos];
+
+       ++pos;
+       if (pos >= EVENT_QUEUE_SIZE)
+               pos = 0;
+       parser->pull_pos = pos;
+
+       spin_unlock_irqrestore(&parser->lock, flags);
+
+       return true;
+}
index 9e6ca39..64aec9c 100644 (file)
@@ -255,6 +255,16 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
                if (err < 0)
                        return err;
 
+               if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+                       err = snd_motu_register_dsp_message_parser_init(motu);
+                       if (err < 0)
+                               return err;
+               } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+                       err = snd_motu_command_dsp_message_parser_init(motu, motu->tx_stream.sfc);
+                       if (err < 0)
+                               return err;
+               }
+
                err = begin_session(motu);
                if (err < 0) {
                        dev_err(&motu->unit->device,
index f654262..5fc7ae4 100644 (file)
@@ -112,6 +112,16 @@ static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *ent
        if (err < 0)
                goto error;
 
+       if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
+               err = snd_motu_register_dsp_message_parser_new(motu);
+               if (err < 0)
+                       goto error;
+       } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
+               err = snd_motu_command_dsp_message_parser_new(motu);
+               if (err < 0)
+                       goto error;
+       }
+
        err = snd_card_register(card);
        if (err < 0)
                goto error;
index f1a830b..79704ae 100644 (file)
@@ -74,10 +74,13 @@ struct snd_motu {
        int dev_lock_count;
        bool dev_lock_changed;
        wait_queue_head_t hwdep_wait;
+       struct snd_hwdep *hwdep;
 
        struct amdtp_domain domain;
 
        struct amdtp_motu_cache cache;
+
+       void *message_parser;
 };
 
 enum snd_motu_spec_flags {
@@ -85,6 +88,8 @@ enum snd_motu_spec_flags {
        SND_MOTU_SPEC_RX_MIDI_3RD_Q     = 0x0002,
        SND_MOTU_SPEC_TX_MIDI_2ND_Q     = 0x0004,
        SND_MOTU_SPEC_TX_MIDI_3RD_Q     = 0x0008,
+       SND_MOTU_SPEC_REGISTER_DSP      = 0x0010,
+       SND_MOTU_SPEC_COMMAND_DSP       = 0x0020,
 };
 
 #define SND_MOTU_CLOCK_RATE_COUNT      6
@@ -270,4 +275,22 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
                return -ENXIO;
 }
 
+int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu);
+void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+                                       unsigned int desc_count, unsigned int data_block_quadlets);
+void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
+                                       struct snd_firewire_motu_register_dsp_meter *meter);
+void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
+                                       struct snd_firewire_motu_register_dsp_parameter *params);
+unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *motu);
+bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event);
+
+int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu);
+int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc);
+void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
+                                       unsigned int desc_count, unsigned int data_block_quadlets);
+void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
+                                       struct snd_firewire_motu_command_dsp_meter *meter);
+
 #endif
index fff18b5..f4a702d 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/delay.h>
 
 #define AVC_GENERIC_FRAME_MAXIMUM_BYTES        512
-#define READY_TIMEOUT_MS       200
+#define READY_TIMEOUT_MS       600
 
 /*
  * According to datasheet of Oxford Semiconductor:
@@ -367,6 +367,11 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
                                // Just after changing sampling transfer frequency, many cycles are
                                // skipped for packet transmission.
                                tx_init_skip_cycles = 400;
+                       } else if (oxfw->quirks & SND_OXFW_QUIRK_VOLUNTARY_RECOVERY) {
+                               // It takes a bit time for target device to adjust event frequency
+                               // according to nominal event frequency in isochronous packets from
+                               // ALSA oxfw driver.
+                               tx_init_skip_cycles = 4000;
                        } else {
                                replay_seq = true;
                        }
index daf7313..b496f87 100644 (file)
@@ -25,6 +25,7 @@
 #define MODEL_SATELLITE                0x00200f
 #define MODEL_SCS1M            0x001000
 #define MODEL_DUET_FW          0x01dddd
+#define MODEL_ONYX_1640I       0x001640
 
 #define SPECIFIER_1394TA       0x00a02d
 #define VERSION_AVC            0x010001
@@ -192,6 +193,13 @@ static int detect_quirks(struct snd_oxfw *oxfw, const struct ieee1394_device_id
                // OXFW971-based models may transfer events by blocking method.
                if (!(oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD))
                        oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
+
+               if (model == MODEL_ONYX_1640I) {
+                       //Unless receiving packets without NOINFO packet, the device transfers
+                       //mostly half of events in packets than expected.
+                       oxfw->quirks |= SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET |
+                                       SND_OXFW_QUIRK_VOLUNTARY_RECOVERY;
+               }
        }
 
        return 0;
index c13034f..d728e45 100644 (file)
@@ -47,6 +47,11 @@ enum snd_oxfw_quirk {
        // the device to process audio data even if the value is invalid in a point of
        // IEC 61883-1/6.
        SND_OXFW_QUIRK_IGNORE_NO_INFO_PACKET = 0x10,
+       // Loud Technologies Mackie Onyx 1640i seems to configure OXFW971 ASIC so that it decides
+       // event frequency according to events in received isochronous packets. The device looks to
+       // performs media clock recovery voluntarily. In the recovery, the packets with NO_INFO
+       // are ignored, thus driver should transfer packets with timestamp.
+       SND_OXFW_QUIRK_VOLUNTARY_RECOVERY = 0x20,
 };
 
 /* This is an arbitrary number for convinience. */
index 0c005d6..37154ed 100644 (file)
@@ -106,20 +106,14 @@ void snd_hdac_stream_free_all(struct hdac_bus *bus)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
 
-/**
- * snd_hdac_ext_stream_decouple - decouple the hdac stream
- * @bus: HD-audio core bus
- * @stream: HD-audio ext core stream object to initialize
- * @decouple: flag to decouple
- */
-void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
-                               struct hdac_ext_stream *stream, bool decouple)
+void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
+                                        struct hdac_ext_stream *stream,
+                                        bool decouple)
 {
        struct hdac_stream *hstream = &stream->hstream;
        u32 val;
        int mask = AZX_PPCTL_PROCEN(hstream->index);
 
-       spin_lock_irq(&bus->reg_lock);
        val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
 
        if (decouple && !val)
@@ -128,6 +122,20 @@ void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
                snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
 
        stream->decoupled = decouple;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
+
+/**
+ * snd_hdac_ext_stream_decouple - decouple the hdac stream
+ * @bus: HD-audio core bus
+ * @stream: HD-audio ext core stream object to initialize
+ * @decouple: flag to decouple
+ */
+void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
+                                 struct hdac_ext_stream *stream, bool decouple)
+{
+       spin_lock_irq(&bus->reg_lock);
+       snd_hdac_ext_stream_decouple_locked(bus, stream, decouple);
        spin_unlock_irq(&bus->reg_lock);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
@@ -252,6 +260,7 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
                return NULL;
        }
 
+       spin_lock_irq(&bus->reg_lock);
        list_for_each_entry(stream, &bus->stream_list, list) {
                struct hdac_ext_stream *hstream = container_of(stream,
                                                struct hdac_ext_stream,
@@ -266,17 +275,16 @@ hdac_ext_link_stream_assign(struct hdac_bus *bus,
                }
 
                if (!hstream->link_locked) {
-                       snd_hdac_ext_stream_decouple(bus, hstream, true);
+                       snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
                        res = hstream;
                        break;
                }
        }
        if (res) {
-               spin_lock_irq(&bus->reg_lock);
                res->link_locked = 1;
                res->link_substream = substream;
-               spin_unlock_irq(&bus->reg_lock);
        }
+       spin_unlock_irq(&bus->reg_lock);
        return res;
 }
 
@@ -292,6 +300,7 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
                return NULL;
        }
 
+       spin_lock_irq(&bus->reg_lock);
        list_for_each_entry(stream, &bus->stream_list, list) {
                struct hdac_ext_stream *hstream = container_of(stream,
                                                struct hdac_ext_stream,
@@ -301,18 +310,17 @@ hdac_ext_host_stream_assign(struct hdac_bus *bus,
 
                if (!stream->opened) {
                        if (!hstream->decoupled)
-                               snd_hdac_ext_stream_decouple(bus, hstream, true);
+                               snd_hdac_ext_stream_decouple_locked(bus, hstream, true);
                        res = hstream;
                        break;
                }
        }
        if (res) {
-               spin_lock_irq(&bus->reg_lock);
                res->hstream.opened = 1;
                res->hstream.running = 0;
                res->hstream.substream = substream;
-               spin_unlock_irq(&bus->reg_lock);
        }
+       spin_unlock_irq(&bus->reg_lock);
 
        return res;
 }
@@ -378,15 +386,17 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
                break;
 
        case HDAC_EXT_STREAM_TYPE_HOST:
+               spin_lock_irq(&bus->reg_lock);
                if (stream->decoupled && !stream->link_locked)
-                       snd_hdac_ext_stream_decouple(bus, stream, false);
+                       snd_hdac_ext_stream_decouple_locked(bus, stream, false);
+               spin_unlock_irq(&bus->reg_lock);
                snd_hdac_stream_release(&stream->hstream);
                break;
 
        case HDAC_EXT_STREAM_TYPE_LINK:
-               if (stream->decoupled && !stream->hstream.opened)
-                       snd_hdac_ext_stream_decouple(bus, stream, false);
                spin_lock_irq(&bus->reg_lock);
+               if (stream->decoupled && !stream->hstream.opened)
+                       snd_hdac_ext_stream_decouple_locked(bus, stream, false);
                stream->link_locked = 0;
                stream->link_substream = NULL;
                spin_unlock_irq(&bus->reg_lock);
index 1eb8563..9867555 100644 (file)
@@ -296,6 +296,7 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
        int key = (substream->pcm->device << 16) | (substream->number << 2) |
                (substream->stream + 1);
 
+       spin_lock_irq(&bus->reg_lock);
        list_for_each_entry(azx_dev, &bus->stream_list, list) {
                if (azx_dev->direction != substream->stream)
                        continue;
@@ -309,13 +310,12 @@ struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus,
                        res = azx_dev;
        }
        if (res) {
-               spin_lock_irq(&bus->reg_lock);
                res->opened = 1;
                res->running = 0;
                res->assigned_key = key;
                res->substream = substream;
-               spin_unlock_irq(&bus->reg_lock);
        }
+       spin_unlock_irq(&bus->reg_lock);
        return res;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_stream_assign);
index 6ffa48d..570b88e 100644 (file)
@@ -22,7 +22,7 @@ config SND_SB16_DSP
 menuconfig SND_ISA
        bool "ISA sound devices"
        depends on ISA || COMPILE_TEST
-       depends on ISA_DMA_API
+       depends on ISA_DMA_API && !M68K
        default y
        help
          Support for sound devices connected via the ISA bus.
index a1c770d..6d664dd 100644 (file)
@@ -126,6 +126,8 @@ static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
        }
        block = snd_gf1_dma_next_block(gus);
        spin_unlock(&gus->dma_lock);
+       if (!block)
+               return;
        snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
        kfree(block);
 #if 0
index 93bc9be..41ce125 100644 (file)
@@ -279,6 +279,7 @@ config SND_CS46XX_NEW_DSP
 config SND_CS5530
        tristate "CS5530 Audio"
        depends on ISA_DMA_API && (X86_32 || COMPILE_TEST)
+       depends on !M68K
        select SND_SB16_DSP
        help
          Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
index 4d22e7a..7762718 100644 (file)
@@ -638,13 +638,17 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
  * the update-IRQ timing.  The IRQ is issued before actually the
  * data is processed.  So, we need to process it afterwords in a
  * workqueue.
+ *
+ * Returns 1 if OK to proceed, 0 for delay handling, -1 for skipping update
  */
 static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
 {
        struct snd_pcm_substream *substream = azx_dev->core.substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        int stream = substream->stream;
        u32 wallclk;
        unsigned int pos;
+       snd_pcm_uframes_t hwptr, target;
 
        wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
        if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
@@ -681,6 +685,24 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
                /* NG - it's below the first next period boundary */
                return chip->bdl_pos_adj ? 0 : -1;
        azx_dev->core.start_wallclk += wallclk;
+
+       if (azx_dev->core.no_period_wakeup)
+               return 1; /* OK, no need to check period boundary */
+
+       if (runtime->hw_ptr_base != runtime->hw_ptr_interrupt)
+               return 1; /* OK, already in hwptr updating process */
+
+       /* check whether the period gets really elapsed */
+       pos = bytes_to_frames(runtime, pos);
+       hwptr = runtime->hw_ptr_base + pos;
+       if (hwptr < runtime->status->hw_ptr)
+               hwptr += runtime->buffer_size;
+       target = runtime->hw_ptr_interrupt + runtime->period_size;
+       if (hwptr < target) {
+               /* too early wakeup, process it later */
+               return chip->bdl_pos_adj ? 0 : -1;
+       }
+
        return 1; /* OK, it's fine */
 }
 
@@ -859,31 +881,6 @@ static int azx_get_delay_from_fifo(struct azx *chip, struct azx_dev *azx_dev,
        return substream->runtime->delay;
 }
 
-static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
-                                        struct azx_dev *azx_dev)
-{
-       return _snd_hdac_chip_readl(azx_bus(chip),
-                                   AZX_REG_VS_SDXDPIB_XBASE +
-                                   (AZX_REG_VS_SDXDPIB_XINTERVAL *
-                                    azx_dev->core.index));
-}
-
-/* get the current DMA position with correction on SKL+ chips */
-static unsigned int azx_get_pos_skl(struct azx *chip, struct azx_dev *azx_dev)
-{
-       /* DPIB register gives a more accurate position for playback */
-       if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               return azx_skl_get_dpib_pos(chip, azx_dev);
-
-       /* For capture, we need to read posbuf, but it requires a delay
-        * for the possible boundary overlap; the read of DPIB fetches the
-        * actual posbuf
-        */
-       udelay(20);
-       azx_skl_get_dpib_pos(chip, azx_dev);
-       return azx_get_pos_posbuf(chip, azx_dev);
-}
-
 static void __azx_shutdown_chip(struct azx *chip, bool skip_link_reset)
 {
        azx_stop_chip(chip);
@@ -1573,7 +1570,7 @@ static void assign_position_fix(struct azx *chip, int fix)
                [POS_FIX_POSBUF] = azx_get_pos_posbuf,
                [POS_FIX_VIACOMBO] = azx_via_get_position,
                [POS_FIX_COMBO] = azx_get_pos_lpib,
-               [POS_FIX_SKL] = azx_get_pos_skl,
+               [POS_FIX_SKL] = azx_get_pos_posbuf,
                [POS_FIX_FIFO] = azx_get_pos_fifo,
        };
 
index 22d27b1..6322fac 100644 (file)
@@ -2535,6 +2535,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
        SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
        SND_PCI_QUIRK(0x1558, 0x65e5, "Clevo PC50D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
+       SND_PCI_QUIRK(0x1558, 0x65f1, "Clevo PC50HS", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
        SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
        SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
        SND_PCI_QUIRK(0x1558, 0x67e5, "Clevo PC70D[PRS](?:-D|-G)?", ALC1220_FIXUP_CLEVO_PB51ED_PINS),
@@ -4354,6 +4355,16 @@ static void alc287_fixup_hp_gpio_led(struct hda_codec *codec,
        alc_fixup_hp_gpio_led(codec, action, 0x10, 0);
 }
 
+static void alc245_fixup_hp_gpio_led(struct hda_codec *codec,
+                               const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               spec->micmute_led_polarity = 1;
+       alc_fixup_hp_gpio_led(codec, action, 0, 0x04);
+}
+
 /* turn on/off mic-mute LED per capture hook via VREF change */
 static int vref_micmute_led_set(struct led_classdev *led_cdev,
                                enum led_brightness brightness)
@@ -6405,6 +6416,44 @@ static void alc_fixup_no_int_mic(struct hda_codec *codec,
        }
 }
 
+/* GPIO1 = amplifier on/off
+ * GPIO3 = mic mute LED
+ */
+static void alc285_fixup_hp_spectre_x360_eb1(struct hda_codec *codec,
+                                         const struct hda_fixup *fix, int action)
+{
+       static const hda_nid_t conn[] = { 0x02 };
+
+       struct alc_spec *spec = codec->spec;
+       static const struct hda_pintbl pincfgs[] = {
+               { 0x14, 0x90170110 },  /* front/high speakers */
+               { 0x17, 0x90170130 },  /* back/bass speakers */
+               { }
+       };
+
+       //enable micmute led
+       alc_fixup_hp_gpio_led(codec, action, 0x00, 0x04);
+
+       switch (action) {
+       case HDA_FIXUP_ACT_PRE_PROBE:
+               spec->micmute_led_polarity = 1;
+               /* needed for amp of back speakers */
+               spec->gpio_mask |= 0x01;
+               spec->gpio_dir |= 0x01;
+               snd_hda_apply_pincfgs(codec, pincfgs);
+               /* share DAC to have unified volume control */
+               snd_hda_override_conn_list(codec, 0x14, ARRAY_SIZE(conn), conn);
+               snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn);
+               break;
+       case HDA_FIXUP_ACT_INIT:
+               /* need to toggle GPIO to enable the amp of back speakers */
+               alc_update_gpio_data(codec, 0x01, true);
+               msleep(100);
+               alc_update_gpio_data(codec, 0x01, false);
+               break;
+       }
+}
+
 static void alc285_fixup_hp_spectre_x360(struct hda_codec *codec,
                                          const struct hda_fixup *fix, int action)
 {
@@ -6557,6 +6606,7 @@ enum {
        ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED,
        ALC280_FIXUP_HP_9480M,
        ALC245_FIXUP_HP_X360_AMP,
+       ALC285_FIXUP_HP_SPECTRE_X360_EB1,
        ALC288_FIXUP_DELL_HEADSET_MODE,
        ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
        ALC288_FIXUP_DELL_XPS_13,
@@ -6669,6 +6719,7 @@ enum {
        ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK,
        ALC287_FIXUP_HP_GPIO_LED,
        ALC256_FIXUP_HP_HEADSET_MIC,
+       ALC245_FIXUP_HP_GPIO_LED,
        ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
        ALC282_FIXUP_ACER_DISABLE_LINEOUT,
        ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST,
@@ -7293,6 +7344,8 @@ static const struct hda_fixup alc269_fixups[] = {
        [ALC245_FIXUP_HP_X360_AMP] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc245_fixup_hp_x360_amp,
+               .chained = true,
+               .chain_id = ALC245_FIXUP_HP_GPIO_LED
        },
        [ALC288_FIXUP_DELL_HEADSET_MODE] = {
                .type = HDA_FIXUP_FUNC,
@@ -8250,6 +8303,10 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc285_fixup_hp_spectre_x360,
        },
+       [ALC285_FIXUP_HP_SPECTRE_X360_EB1] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc285_fixup_hp_spectre_x360_eb1
+       },
        [ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc285_fixup_ideapad_s740_coef,
@@ -8388,6 +8445,10 @@ static const struct hda_fixup alc269_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc256_fixup_tongfang_reset_persistent_settings,
        },
+       [ALC245_FIXUP_HP_GPIO_LED] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc245_fixup_hp_gpio_led,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -8573,6 +8634,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
                      ALC285_FIXUP_HP_GPIO_AMP_INIT),
        SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation",
                      ALC285_FIXUP_HP_GPIO_AMP_INIT),
+       SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
@@ -8584,6 +8646,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x87f7, "HP Spectre x360 14", ALC245_FIXUP_HP_X360_AMP),
        SND_PCI_QUIRK(0x103c, 0x8805, "HP ProBook 650 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x880d, "HP EliteBook 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
+       SND_PCI_QUIRK(0x103c, 0x8811, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
+       SND_PCI_QUIRK(0x103c, 0x8812, "HP Spectre x360 15-eb1xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
        SND_PCI_QUIRK(0x103c, 0x8846, "HP EliteBook 850 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8847, "HP EliteBook x360 830 G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x884b, "HP EliteBook 840 Aero G8 Notebook PC", ALC285_FIXUP_HP_GPIO_LED),
@@ -9005,6 +9069,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
        {.id = ALC245_FIXUP_HP_X360_AMP, .name = "alc245-hp-x360-amp"},
        {.id = ALC295_FIXUP_HP_OMEN, .name = "alc295-hp-omen"},
        {.id = ALC285_FIXUP_HP_SPECTRE_X360, .name = "alc285-hp-spectre-x360"},
+       {.id = ALC285_FIXUP_HP_SPECTRE_X360_EB1, .name = "alc285-hp-spectre-x360-eb1"},
        {.id = ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP, .name = "alc287-ideapad-bass-spk-amp"},
        {.id = ALC623_FIXUP_LENOVO_THINKSTATION_P340, .name = "alc623-lenovo-thinkstation-p340"},
        {.id = ALC255_FIXUP_ACER_HEADPHONE_AND_MIC, .name = "alc255-acer-headphone-and-mic"},
index dfd2df0..7657938 100644 (file)
@@ -72,6 +72,7 @@ static struct hdac_ext_stream *
                return NULL;
        }
 
+       spin_lock_irq(&bus->reg_lock);
        list_for_each_entry(stream, &bus->stream_list, list) {
                struct hdac_ext_stream *hstream =
                        stream_to_hdac_ext_stream(stream);
@@ -111,12 +112,12 @@ static struct hdac_ext_stream *
                 * is updated in snd_hdac_ext_stream_decouple().
                 */
                if (!res->decoupled)
-                       snd_hdac_ext_stream_decouple(bus, res, true);
-               spin_lock_irq(&bus->reg_lock);
+                       snd_hdac_ext_stream_decouple_locked(bus, res, true);
+
                res->link_locked = 1;
                res->link_substream = substream;
-               spin_unlock_irq(&bus->reg_lock);
        }
+       spin_unlock_irq(&bus->reg_lock);
 
        return res;
 }
index 43a2a62..49629d4 100644 (file)
@@ -95,7 +95,7 @@ static int usb6fire_comm_send_buffer(u8 *buffer, struct usb_device *dev)
        int actual_len;
 
        ret = usb_interrupt_msg(dev, usb_sndintpipe(dev, COMM_EP),
-                       buffer, buffer[1] + 2, &actual_len, HZ);
+                       buffer, buffer[1] + 2, &actual_len, 1000);
        if (ret < 0)
                return ret;
        else if (actual_len != buffer[1] + 2)
index 8981e61..c51abc5 100644 (file)
@@ -160,7 +160,7 @@ static int usb6fire_fw_ezusb_write(struct usb_device *device,
 {
        return usb_control_msg_send(device, 0, type,
                                    USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                                   value, 0, data, len, HZ, GFP_KERNEL);
+                                   value, 0, data, len, 1000, GFP_KERNEL);
 }
 
 static int usb6fire_fw_ezusb_read(struct usb_device *device,
@@ -168,7 +168,7 @@ static int usb6fire_fw_ezusb_read(struct usb_device *device,
 {
        return usb_control_msg_recv(device, 0, type,
                                    USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-                                   value, 0, data, len, HZ, GFP_KERNEL);
+                                   value, 0, data, len, 1000, GFP_KERNEL);
 }
 
 static int usb6fire_fw_fpga_write(struct usb_device *device,
@@ -178,7 +178,7 @@ static int usb6fire_fw_fpga_write(struct usb_device *device,
        int ret;
 
        ret = usb_bulk_msg(device, usb_sndbulkpipe(device, FPGA_EP), data, len,
-                       &actual_len, HZ);
+                       &actual_len, 1000);
        if (ret < 0)
                return ret;
        else if (actual_len != len)
index 5b19901..87f042d 100644 (file)
@@ -74,8 +74,9 @@ struct snd_usb_endpoint {
 
        atomic_t state;         /* running state */
 
-       void (*prepare_data_urb) (struct snd_usb_substream *subs,
-                                 struct urb *urb);
+       int (*prepare_data_urb) (struct snd_usb_substream *subs,
+                                struct urb *urb,
+                                bool in_stream_lock);
        void (*retire_data_urb) (struct snd_usb_substream *subs,
                                 struct urb *urb);
 
@@ -94,9 +95,9 @@ struct snd_usb_endpoint {
        struct list_head ready_playback_urbs; /* playback URB FIFO for implicit fb */
 
        unsigned int nurbs;             /* # urbs */
-       unsigned int nominal_queue_size; /* total buffer sizes in URBs */
        unsigned long active_mask;      /* bitmask of active urbs */
        unsigned long unlink_mask;      /* bitmask of unlinked urbs */
+       atomic_t submitted_urbs;        /* currently submitted urbs */
        char *syncbuf;                  /* sync buffer for all sync URBs */
        dma_addr_t sync_dma;            /* DMA address of syncbuf */
 
@@ -125,6 +126,7 @@ struct snd_usb_endpoint {
        int skip_packets;               /* quirks for devices to ignore the first n packets
                                           in a stream */
        bool implicit_fb_sync;          /* syncs with implicit feedback */
+       bool lowlatency_playback;       /* low-latency playback mode */
        bool need_setup;                /* (re-)need for configure? */
 
        /* for hw constraints */
@@ -136,6 +138,7 @@ struct snd_usb_endpoint {
        unsigned int cur_period_frames;
        unsigned int cur_period_bytes;
        unsigned int cur_buffer_periods;
+       unsigned char cur_clock;
 
        spinlock_t lock;
        struct list_head list;
@@ -188,7 +191,7 @@ struct snd_usb_substream {
        } dsd_dop;
 
        bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
-       bool early_playback_start;      /* early start needed for playback? */
+       bool lowlatency_playback;       /* low-latency playback mode */
        struct media_ctl *media_ctl;
 };
 
index 81d5ce0..4dfe764 100644 (file)
@@ -271,7 +271,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
                return -EINVAL;
        }
 
-       /* first, see if the ID we're looking for is a clock source already */
+       /* first, see if the ID we're looking at is a clock source already */
        source = snd_usb_find_clock_source(chip, entity_id, proto);
        if (source) {
                entity_id = GET_VAL(source, proto, bClockID);
@@ -297,7 +297,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip,
                        goto find_source;
                }
 
-               /* the entity ID we are looking for is a selector.
+               /* the entity ID we are looking at is a selector.
                 * find out what it currently selects */
                ret = uac_clock_selector_get_val(chip, clock_id);
                if (ret < 0) {
@@ -496,6 +496,10 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip,
        union uac23_clock_source_desc *cs_desc;
 
        cs_desc = snd_usb_find_clock_source(chip, clock, fmt->protocol);
+
+       if (!cs_desc)
+               return 0;
+
        if (fmt->protocol == UAC_VERSION_3)
                bmControls = le32_to_cpu(cs_desc->v3.bmControls);
        else
index 533919a..743b828 100644 (file)
@@ -148,18 +148,23 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep)
  * This won't be used for implicit feedback which takes the packet size
  * returned from the sync source
  */
-static int slave_next_packet_size(struct snd_usb_endpoint *ep)
+static int slave_next_packet_size(struct snd_usb_endpoint *ep,
+                                 unsigned int avail)
 {
        unsigned long flags;
+       unsigned int phase;
        int ret;
 
        if (ep->fill_max)
                return ep->maxframesize;
 
        spin_lock_irqsave(&ep->lock, flags);
-       ep->phase = (ep->phase & 0xffff)
-               + (ep->freqm << ep->datainterval);
-       ret = min(ep->phase >> 16, ep->maxframesize);
+       phase = (ep->phase & 0xffff) + (ep->freqm << ep->datainterval);
+       ret = min(phase >> 16, ep->maxframesize);
+       if (avail && ret >= avail)
+               ret = -EAGAIN;
+       else
+               ep->phase = phase;
        spin_unlock_irqrestore(&ep->lock, flags);
 
        return ret;
@@ -169,20 +174,25 @@ static int slave_next_packet_size(struct snd_usb_endpoint *ep)
  * Return the number of samples to be sent in the next packet
  * for adaptive and synchronous endpoints
  */
-static int next_packet_size(struct snd_usb_endpoint *ep)
+static int next_packet_size(struct snd_usb_endpoint *ep, unsigned int avail)
 {
+       unsigned int sample_accum;
        int ret;
 
        if (ep->fill_max)
                return ep->maxframesize;
 
-       ep->sample_accum += ep->sample_rem;
-       if (ep->sample_accum >= ep->pps) {
-               ep->sample_accum -= ep->pps;
+       sample_accum = ep->sample_accum + ep->sample_rem;
+       if (sample_accum >= ep->pps) {
+               sample_accum -= ep->pps;
                ret = ep->packsize[1];
        } else {
                ret = ep->packsize[0];
        }
+       if (avail && ret >= avail)
+               ret = -EAGAIN;
+       else
+               ep->sample_accum = sample_accum;
 
        return ret;
 }
@@ -190,16 +200,27 @@ static int next_packet_size(struct snd_usb_endpoint *ep)
 /*
  * snd_usb_endpoint_next_packet_size: Return the number of samples to be sent
  * in the next packet
+ *
+ * If the size is equal or exceeds @avail, don't proceed but return -EAGAIN
+ * Exception: @avail = 0 for skipping the check.
  */
 int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
-                                     struct snd_urb_ctx *ctx, int idx)
+                                     struct snd_urb_ctx *ctx, int idx,
+                                     unsigned int avail)
 {
-       if (ctx->packet_size[idx])
-               return ctx->packet_size[idx];
-       else if (ep->sync_source)
-               return slave_next_packet_size(ep);
+       unsigned int packet;
+
+       packet = ctx->packet_size[idx];
+       if (packet) {
+               if (avail && packet >= avail)
+                       return -EAGAIN;
+               return packet;
+       }
+
+       if (ep->sync_source)
+               return slave_next_packet_size(ep, avail);
        else
-               return next_packet_size(ep);
+               return next_packet_size(ep, avail);
 }
 
 static void call_retire_callback(struct snd_usb_endpoint *ep,
@@ -263,7 +284,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
                unsigned int length;
                int counts;
 
-               counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
+               counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, 0);
                length = counts * ep->stride; /* number of silent bytes */
                offset = offs * ep->stride + extra * i;
                urb->iso_frame_desc[i].offset = offset;
@@ -286,8 +307,9 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep,
 /*
  * Prepare a PLAYBACK urb for submission to the bus.
  */
-static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
-                                struct snd_urb_ctx *ctx)
+static int prepare_outbound_urb(struct snd_usb_endpoint *ep,
+                               struct snd_urb_ctx *ctx,
+                               bool in_stream_lock)
 {
        struct urb *urb = ctx->urb;
        unsigned char *cp = urb->transfer_buffer;
@@ -299,9 +321,9 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
        case SND_USB_ENDPOINT_TYPE_DATA:
                data_subs = READ_ONCE(ep->data_subs);
                if (data_subs && ep->prepare_data_urb)
-                       ep->prepare_data_urb(data_subs, urb);
-               else /* no data provider, so send silence */
-                       prepare_silent_urb(ep, ctx);
+                       return ep->prepare_data_urb(data_subs, urb, in_stream_lock);
+               /* no data provider, so send silence */
+               prepare_silent_urb(ep, ctx);
                break;
 
        case SND_USB_ENDPOINT_TYPE_SYNC:
@@ -330,13 +352,14 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
 
                break;
        }
+       return 0;
 }
 
 /*
  * Prepare a CAPTURE or SYNC urb for submission to the bus.
  */
-static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
-                                      struct snd_urb_ctx *urb_ctx)
+static int prepare_inbound_urb(struct snd_usb_endpoint *ep,
+                              struct snd_urb_ctx *urb_ctx)
 {
        int i, offs;
        struct urb *urb = urb_ctx->urb;
@@ -361,6 +384,7 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
                urb->iso_frame_desc[0].offset = 0;
                break;
        }
+       return 0;
 }
 
 /* notify an error as XRUN to the assigned PCM data substream */
@@ -396,6 +420,16 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep)
        return p;
 }
 
+static void push_back_to_ready_list(struct snd_usb_endpoint *ep,
+                                   struct snd_urb_ctx *ctx)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+       spin_unlock_irqrestore(&ep->lock, flags);
+}
+
 /*
  * Send output urbs that have been prepared previously. URBs are dequeued
  * from ep->ready_playback_urbs and in case there aren't any available
@@ -406,12 +440,14 @@ next_packet_fifo_dequeue(struct snd_usb_endpoint *ep)
  * is that host controllers don't guarantee the order in which they return
  * inbound and outbound packets to their submitters.
  *
- * This function is only used for implicit feedback endpoints. For endpoints
- * driven by dedicated sync endpoints, URBs are immediately re-submitted
- * from their completion handler.
+ * This function is used both for implicit feedback endpoints and in low-
+ * latency playback mode.
  */
-static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
+void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
+                                      bool in_stream_lock)
 {
+       bool implicit_fb = snd_usb_endpoint_implicit_feedback_sink(ep);
+
        while (ep_state_running(ep)) {
 
                unsigned long flags;
@@ -420,14 +456,14 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
                int err, i;
 
                spin_lock_irqsave(&ep->lock, flags);
-               if (ep->next_packet_queued > 0 &&
+               if ((!implicit_fb || ep->next_packet_queued > 0) &&
                    !list_empty(&ep->ready_playback_urbs)) {
                        /* take URB out of FIFO */
                        ctx = list_first_entry(&ep->ready_playback_urbs,
                                               struct snd_urb_ctx, ready_list);
                        list_del_init(&ctx->ready_list);
-
-                       packet = next_packet_fifo_dequeue(ep);
+                       if (implicit_fb)
+                               packet = next_packet_fifo_dequeue(ep);
                }
                spin_unlock_irqrestore(&ep->lock, flags);
 
@@ -435,11 +471,24 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
                        return;
 
                /* copy over the length information */
-               for (i = 0; i < packet->packets; i++)
-                       ctx->packet_size[i] = packet->packet_size[i];
+               if (implicit_fb) {
+                       for (i = 0; i < packet->packets; i++)
+                               ctx->packet_size[i] = packet->packet_size[i];
+               }
 
                /* call the data handler to fill in playback data */
-               prepare_outbound_urb(ep, ctx);
+               err = prepare_outbound_urb(ep, ctx, in_stream_lock);
+               /* can be stopped during prepare callback */
+               if (unlikely(!ep_state_running(ep)))
+                       break;
+               if (err < 0) {
+                       /* push back to ready list again for -EAGAIN */
+                       if (err == -EAGAIN)
+                               push_back_to_ready_list(ep, ctx);
+                       else
+                               notify_xrun(ep);
+                       return;
+               }
 
                err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
                if (err < 0) {
@@ -451,6 +500,7 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
                }
 
                set_bit(ctx->index, &ep->active_mask);
+               atomic_inc(&ep->submitted_urbs);
        }
 }
 
@@ -461,7 +511,6 @@ static void snd_complete_urb(struct urb *urb)
 {
        struct snd_urb_ctx *ctx = urb->context;
        struct snd_usb_endpoint *ep = ctx->ep;
-       unsigned long flags;
        int err;
 
        if (unlikely(urb->status == -ENOENT ||          /* unlinked */
@@ -482,16 +531,20 @@ static void snd_complete_urb(struct urb *urb)
                if (unlikely(!ep_state_running(ep)))
                        goto exit_clear;
 
-               if (snd_usb_endpoint_implicit_feedback_sink(ep)) {
-                       spin_lock_irqsave(&ep->lock, flags);
-                       list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+               /* in low-latency and implicit-feedback modes, push back the
+                * URB to ready list at first, then process as much as possible
+                */
+               if (ep->lowlatency_playback ||
+                    snd_usb_endpoint_implicit_feedback_sink(ep)) {
+                       push_back_to_ready_list(ep, ctx);
                        clear_bit(ctx->index, &ep->active_mask);
-                       spin_unlock_irqrestore(&ep->lock, flags);
-                       queue_pending_output_urbs(ep);
+                       snd_usb_queue_pending_output_urbs(ep, false);
+                       atomic_dec(&ep->submitted_urbs); /* decrement at last */
                        return;
                }
 
-               prepare_outbound_urb(ep, ctx);
+               /* in non-lowlatency mode, no error handling for prepare */
+               prepare_outbound_urb(ep, ctx, false);
                /* can be stopped during prepare callback */
                if (unlikely(!ep_state_running(ep)))
                        goto exit_clear;
@@ -513,6 +566,7 @@ static void snd_complete_urb(struct urb *urb)
 
 exit_clear:
        clear_bit(ctx->index, &ep->active_mask);
+       atomic_dec(&ep->submitted_urbs);
 }
 
 /*
@@ -596,6 +650,7 @@ int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type)
        ep->type = type;
        ep->ep_num = ep_num;
        INIT_LIST_HEAD(&ep->ready_playback_urbs);
+       atomic_set(&ep->submitted_urbs, 0);
 
        is_playback = ((ep_num & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
        ep_num &= USB_ENDPOINT_NUMBER_MASK;
@@ -722,6 +777,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
                ep->cur_period_frames = params_period_size(params);
                ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes;
                ep->cur_buffer_periods = params_periods(params);
+               ep->cur_clock = fp->clock;
 
                if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
                        endpoint_set_syncinterval(chip, ep);
@@ -781,14 +837,19 @@ void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip,
  * Pass NULL to deactivate each callback.
  */
 void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep,
-                                  void (*prepare)(struct snd_usb_substream *subs,
-                                                  struct urb *urb),
+                                  int (*prepare)(struct snd_usb_substream *subs,
+                                                 struct urb *urb,
+                                                 bool in_stream_lock),
                                   void (*retire)(struct snd_usb_substream *subs,
                                                  struct urb *urb),
                                   struct snd_usb_substream *data_subs)
 {
        ep->prepare_data_urb = prepare;
        ep->retire_data_urb = retire;
+       if (data_subs)
+               ep->lowlatency_playback = data_subs->lowlatency_playback;
+       else
+               ep->lowlatency_playback = false;
        WRITE_ONCE(ep->data_subs, data_subs);
 }
 
@@ -833,6 +894,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
                ep->altsetting = 0;
                ep->cur_audiofmt = NULL;
                ep->cur_rate = 0;
+               ep->cur_clock = 0;
                ep->iface_ref = NULL;
                usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
        }
@@ -859,7 +921,7 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep)
                return 0;
 
        do {
-               alive = bitmap_weight(&ep->active_mask, ep->nurbs);
+               alive = atomic_read(&ep->submitted_urbs);
                if (!alive)
                        break;
 
@@ -893,9 +955,10 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
  *
  * This function moves the EP to STOPPING state if it's being RUNNING.
  */
-static int stop_urbs(struct snd_usb_endpoint *ep, bool force)
+static int stop_urbs(struct snd_usb_endpoint *ep, bool force, bool keep_pending)
 {
        unsigned int i;
+       unsigned long flags;
 
        if (!force && atomic_read(&ep->running))
                return -EBUSY;
@@ -903,9 +966,14 @@ static int stop_urbs(struct snd_usb_endpoint *ep, bool force)
        if (!ep_state_update(ep, EP_STATE_RUNNING, EP_STATE_STOPPING))
                return 0;
 
+       spin_lock_irqsave(&ep->lock, flags);
        INIT_LIST_HEAD(&ep->ready_playback_urbs);
        ep->next_packet_head = 0;
        ep->next_packet_queued = 0;
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       if (keep_pending)
+               return 0;
 
        for (i = 0; i < ep->nurbs; i++) {
                if (test_bit(i, &ep->active_mask)) {
@@ -930,7 +998,7 @@ static int release_urbs(struct snd_usb_endpoint *ep, bool force)
        snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL);
 
        /* stop and unlink urbs */
-       err = stop_urbs(ep, force);
+       err = stop_urbs(ep, force, false);
        if (err)
                return err;
 
@@ -1132,10 +1200,6 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
                INIT_LIST_HEAD(&u->ready_list);
        }
 
-       /* total buffer bytes of all URBs plus the next queue;
-        * referred in pcm.c
-        */
-       ep->nominal_queue_size = maxsize * urb_packs * (ep->nurbs + 1);
        return 0;
 
 out_of_memory:
@@ -1340,6 +1404,25 @@ unlock:
        return err;
 }
 
+/* get the current rate set to the given clock by any endpoint */
+int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock)
+{
+       struct snd_usb_endpoint *ep;
+       int rate = 0;
+
+       if (!clock)
+               return 0;
+       mutex_lock(&chip->mutex);
+       list_for_each_entry(ep, &chip->ep_list, list) {
+               if (ep->cur_clock == clock && ep->cur_rate) {
+                       rate = ep->cur_rate;
+                       break;
+               }
+       }
+       mutex_unlock(&chip->mutex);
+       return rate;
+}
+
 /**
  * snd_usb_endpoint_start: start an snd_usb_endpoint
  *
@@ -1355,6 +1438,7 @@ unlock:
  */
 int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
 {
+       bool is_playback = usb_pipeout(ep->pipe);
        int err;
        unsigned int i;
 
@@ -1391,13 +1475,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
 
        if (snd_usb_endpoint_implicit_feedback_sink(ep) &&
            !(ep->chip->quirk_flags & QUIRK_FLAG_PLAYBACK_FIRST)) {
-               for (i = 0; i < ep->nurbs; i++) {
-                       struct snd_urb_ctx *ctx = ep->urb + i;
-                       list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
-               }
-
                usb_audio_dbg(ep->chip, "No URB submission due to implicit fb sync\n");
-               return 0;
+               i = 0;
+               goto fill_rest;
        }
 
        for (i = 0; i < ep->nurbs; i++) {
@@ -1406,10 +1486,18 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
                if (snd_BUG_ON(!urb))
                        goto __error;
 
-               if (usb_pipeout(ep->pipe)) {
-                       prepare_outbound_urb(ep, urb->context);
-               } else {
-                       prepare_inbound_urb(ep, urb->context);
+               if (is_playback)
+                       err = prepare_outbound_urb(ep, urb->context, true);
+               else
+                       err = prepare_inbound_urb(ep, urb->context);
+               if (err < 0) {
+                       /* stop filling at applptr */
+                       if (err == -EAGAIN)
+                               break;
+                       usb_audio_dbg(ep->chip,
+                                     "EP 0x%x: failed to prepare urb: %d\n",
+                                     ep->ep_num, err);
+                       goto __error;
                }
 
                err = usb_submit_urb(urb, GFP_ATOMIC);
@@ -1420,14 +1508,29 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
                        goto __error;
                }
                set_bit(i, &ep->active_mask);
+               atomic_inc(&ep->submitted_urbs);
+       }
+
+       if (!i) {
+               usb_audio_dbg(ep->chip, "XRUN at starting EP 0x%x\n",
+                             ep->ep_num);
+               goto __error;
        }
 
        usb_audio_dbg(ep->chip, "%d URBs submitted for EP 0x%x\n",
-                     ep->nurbs, ep->ep_num);
+                     i, ep->ep_num);
+
+ fill_rest:
+       /* put the remaining URBs to ready list */
+       if (is_playback) {
+               for (; i < ep->nurbs; i++)
+                       push_back_to_ready_list(ep, ep->urb + i);
+       }
+
        return 0;
 
 __error:
-       snd_usb_endpoint_stop(ep);
+       snd_usb_endpoint_stop(ep, false);
        return -EPIPE;
 }
 
@@ -1435,6 +1538,7 @@ __error:
  * snd_usb_endpoint_stop: stop an snd_usb_endpoint
  *
  * @ep: the endpoint to stop (may be NULL)
+ * @keep_pending: keep in-flight URBs
  *
  * A call to this function will decrement the running count of the endpoint.
  * In case the last user has requested the endpoint stop, the URBs will
@@ -1445,7 +1549,7 @@ __error:
  * The caller needs to synchronize the pending stop operation via
  * snd_usb_endpoint_sync_pending_stop().
  */
-void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending)
 {
        if (!ep)
                return;
@@ -1460,7 +1564,7 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep)
        if (!atomic_dec_return(&ep->running)) {
                if (ep->sync_source)
                        WRITE_ONCE(ep->sync_source->sync_sink, NULL);
-               stop_urbs(ep, false);
+               stop_urbs(ep, false, keep_pending);
        }
 }
 
@@ -1575,7 +1679,7 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
                }
 
                spin_unlock_irqrestore(&ep->lock, flags);
-               queue_pending_output_urbs(ep);
+               snd_usb_queue_pending_output_urbs(ep, false);
 
                return;
        }
index a668f67..6a9af04 100644 (file)
@@ -19,6 +19,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
                            struct snd_usb_endpoint *ep);
 int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
                               struct snd_usb_endpoint *ep);
+int snd_usb_endpoint_get_clock_rate(struct snd_usb_audio *chip, int clock);
 
 bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip,
                                 struct snd_usb_endpoint *ep,
@@ -29,14 +30,15 @@ void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip,
                               struct snd_usb_endpoint *data_ep,
                               struct snd_usb_endpoint *sync_ep);
 void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep,
-                                  void (*prepare)(struct snd_usb_substream *subs,
-                                                  struct urb *urb),
+                                  int (*prepare)(struct snd_usb_substream *subs,
+                                                 struct urb *urb,
+                                                 bool in_stream_lock),
                                   void (*retire)(struct snd_usb_substream *subs,
                                                  struct urb *urb),
                                   struct snd_usb_substream *data_subs);
 
 int snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
-void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, bool keep_pending);
 void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
 void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep);
 int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
@@ -45,6 +47,9 @@ void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
 
 int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
 int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
-                                     struct snd_urb_ctx *ctx, int idx);
+                                     struct snd_urb_ctx *ctx, int idx,
+                                     unsigned int avail);
+void snd_usb_queue_pending_output_urbs(struct snd_usb_endpoint *ep,
+                                      bool in_stream_lock);
 
 #endif /* __USBAUDIO_ENDPOINT_H */
index 50efccb..f5e676a 100644 (file)
@@ -414,6 +414,7 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip,
        case USB_ID(0x0e41, 0x4242): /* Line6 Helix Rack */
        case USB_ID(0x0e41, 0x4244): /* Line6 Helix LT */
        case USB_ID(0x0e41, 0x4246): /* Line6 HX-Stomp */
+       case USB_ID(0x0e41, 0x4253): /* Line6 HX-Stomp XL */
        case USB_ID(0x0e41, 0x4247): /* Line6 Pod Go */
        case USB_ID(0x0e41, 0x4248): /* Line6 Helix >= fw 2.82 */
        case USB_ID(0x0e41, 0x4249): /* Line6 Helix Rack >= fw 2.82 */
index 23767a1..70319c8 100644 (file)
@@ -54,8 +54,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
 
        /* Fixed EP */
        /* FIXME: check the availability of generic matching */
-       IMPLICIT_FB_FIXED_DEV(0x1397, 0x0001, 0x81, 1), /* Behringer UFX1604 */
-       IMPLICIT_FB_FIXED_DEV(0x1397, 0x0002, 0x81, 1), /* Behringer UFX1204 */
        IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */
        IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0001, 0x81, 2), /* Solid State Logic SSL2 */
        IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0002, 0x81, 2), /* Solid State Logic SSL2+ */
index 9602929..59faa5a 100644 (file)
@@ -113,12 +113,12 @@ int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
                        retval = usb_interrupt_msg(line6->usbdev,
                                                usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
                                                (char *)frag_buf, frag_size,
-                                               &partial, LINE6_TIMEOUT * HZ);
+                                               &partial, LINE6_TIMEOUT);
                } else {
                        retval = usb_bulk_msg(line6->usbdev,
                                                usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
                                                (char *)frag_buf, frag_size,
-                                               &partial, LINE6_TIMEOUT * HZ);
+                                               &partial, LINE6_TIMEOUT);
                }
 
                if (retval) {
@@ -347,7 +347,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
        ret = usb_control_msg_send(usbdev, 0, 0x67,
                                   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
                                   (datalen << 8) | 0x21, address, NULL, 0,
-                                  LINE6_TIMEOUT * HZ, GFP_KERNEL);
+                                  LINE6_TIMEOUT, GFP_KERNEL);
        if (ret) {
                dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
                goto exit;
@@ -360,7 +360,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
                ret = usb_control_msg_recv(usbdev, 0, 0x67,
                                           USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
                                           0x0012, 0x0000, &len, 1,
-                                          LINE6_TIMEOUT * HZ, GFP_KERNEL);
+                                          LINE6_TIMEOUT, GFP_KERNEL);
                if (ret) {
                        dev_err(line6->ifcdev,
                                "receive length failed (error %d)\n", ret);
@@ -387,7 +387,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
        /* receive the result: */
        ret = usb_control_msg_recv(usbdev, 0, 0x67,
                                   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                                  0x0013, 0x0000, data, datalen, LINE6_TIMEOUT * HZ,
+                                  0x0013, 0x0000, data, datalen, LINE6_TIMEOUT,
                                   GFP_KERNEL);
        if (ret)
                dev_err(line6->ifcdev, "read failed (error %d)\n", ret);
@@ -417,7 +417,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
 
        ret = usb_control_msg_send(usbdev, 0, 0x67,
                                   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
-                                  0x0022, address, data, datalen, LINE6_TIMEOUT * HZ,
+                                  0x0022, address, data, datalen, LINE6_TIMEOUT,
                                   GFP_KERNEL);
        if (ret) {
                dev_err(line6->ifcdev,
@@ -430,7 +430,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
 
                ret = usb_control_msg_recv(usbdev, 0, 0x67,
                                           USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
-                                          0x0012, 0x0000, status, 1, LINE6_TIMEOUT * HZ,
+                                          0x0012, 0x0000, status, 1, LINE6_TIMEOUT,
                                           GFP_KERNEL);
                if (ret) {
                        dev_err(line6->ifcdev,
index 71d3da1..ecf3a2b 100644 (file)
@@ -27,7 +27,7 @@
 #define LINE6_FALLBACK_INTERVAL 10
 #define LINE6_FALLBACK_MAXPACKETSIZE 16
 
-#define LINE6_TIMEOUT 1
+#define LINE6_TIMEOUT 1000
 #define LINE6_BUFSIZE_LISTEN 64
 #define LINE6_MIDI_MESSAGE_MAXLEN 256
 
index 28794a3..b24bc82 100644 (file)
@@ -190,7 +190,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
        ret = usb_control_msg_send(usbdev, 0,
                                        0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
                                        0x11, 0,
-                                       NULL, 0, LINE6_TIMEOUT * HZ, GFP_KERNEL);
+                                       NULL, 0, LINE6_TIMEOUT, GFP_KERNEL);
        if (ret) {
                dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
                goto exit;
@@ -200,7 +200,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
        ret = usb_control_msg_recv(usbdev, 0, 0x67,
                                        USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
                                        0x11, 0x0,
-                                       init_bytes, 3, LINE6_TIMEOUT * HZ, GFP_KERNEL);
+                                       init_bytes, 3, LINE6_TIMEOUT, GFP_KERNEL);
        if (ret) {
                dev_err(pod->line6.ifcdev,
                        "receive length failed (error %d)\n", ret);
@@ -220,7 +220,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
                                        USB_REQ_SET_FEATURE,
                                        USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
                                        1, 0,
-                                       NULL, 0, LINE6_TIMEOUT * HZ, GFP_KERNEL);
+                                       NULL, 0, LINE6_TIMEOUT, GFP_KERNEL);
 exit:
        return ret;
 }
index 4e5693c..e33df58 100644 (file)
@@ -128,7 +128,7 @@ static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
 
        ret = usb_control_msg_send(usbdev, 0, 0x67,
                                   USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
-                                  cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ,
+                                  cmd1, cmd2, NULL, 0, LINE6_TIMEOUT,
                                   GFP_KERNEL);
 
        if (ret) {
index 5834d1d..4f6b20e 100644 (file)
@@ -1000,7 +1000,7 @@ static int detect_usb_format(struct ua101 *ua)
                fmt_playback->bSubframeSize * ua->playback.channels;
 
        epd = &ua->intf[INTF_CAPTURE]->altsetting[1].endpoint[0].desc;
-       if (!usb_endpoint_is_isoc_in(epd)) {
+       if (!usb_endpoint_is_isoc_in(epd) || usb_endpoint_maxp(epd) == 0) {
                dev_err(&ua->dev->dev, "invalid capture endpoint\n");
                return -ENXIO;
        }
@@ -1008,7 +1008,7 @@ static int detect_usb_format(struct ua101 *ua)
        ua->capture.max_packet_bytes = usb_endpoint_maxp(epd);
 
        epd = &ua->intf[INTF_PLAYBACK]->altsetting[1].endpoint[0].desc;
-       if (!usb_endpoint_is_isoc_out(epd)) {
+       if (!usb_endpoint_is_isoc_out(epd) || usb_endpoint_maxp(epd) == 0) {
                dev_err(&ua->dev->dev, "invalid playback endpoint\n");
                return -ENXIO;
        }
index a2ce535..6e7bac8 100644 (file)
@@ -361,9 +361,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
 
        memset(buf, 0, sizeof(buf));
 
-       ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
-       if (ret)
-               goto error;
+       if (snd_usb_lock_shutdown(chip))
+               return -EIO;
 
        idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
        ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
@@ -372,8 +371,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
        snd_usb_unlock_shutdown(chip);
 
        if (ret < 0) {
-error:
-               usb_audio_err(chip,
+               usb_audio_dbg(chip,
                        "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
                        request, validx, idx, cval->val_type);
                return ret;
@@ -1198,15 +1196,42 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
                        cval->res = 1;
                }
                break;
+       case USB_ID(0x1224, 0x2a25): /* Jieli Technology USB PHY 2.0 */
+               if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
+                       usb_audio_info(chip,
+                               "set resolution quirk: cval->res = 16\n");
+                       cval->res = 16;
+               }
+               break;
        }
 }
 
+/* forcibly initialize the current mixer value; if GET_CUR fails, set to
+ * the minimum as default
+ */
+static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
+{
+       int val, err;
+
+       err = snd_usb_get_cur_mix_value(cval, ch, idx, &val);
+       if (!err)
+               return;
+       if (!cval->head.mixer->ignore_ctl_error)
+               usb_audio_warn(cval->head.mixer->chip,
+                              "%d:%d: failed to get current value for ch %d (%d)\n",
+                              cval->head.id, mixer_ctrl_intf(cval->head.mixer),
+                              ch, err);
+       snd_usb_set_cur_mix_value(cval, ch, idx, cval->min);
+}
+
 /*
  * retrieve the minimum and maximum values for the specified control
  */
 static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
                                   int default_min, struct snd_kcontrol *kctl)
 {
+       int i, idx;
+
        /* for failsafe */
        cval->min = default_min;
        cval->max = cval->min + 1;
@@ -1219,7 +1244,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
        } else {
                int minchn = 0;
                if (cval->cmask) {
-                       int i;
                        for (i = 0; i < MAX_CHANNELS; i++)
                                if (cval->cmask & (1 << i)) {
                                        minchn = i + 1;
@@ -1320,6 +1344,19 @@ no_res_check:
                }
        }
 
+       /* initialize all elements */
+       if (!cval->cmask) {
+               init_cur_mix_raw(cval, 0, 0);
+       } else {
+               idx = 0;
+               for (i = 0; i < MAX_CHANNELS; i++) {
+                       if (cval->cmask & (1 << i)) {
+                               init_cur_mix_raw(cval, i + 1, idx);
+                               idx++;
+                       }
+               }
+       }
+
        return 0;
 }
 
index 46082dc..d489c1d 100644 (file)
@@ -2795,6 +2795,7 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer)
 #define SND_DJM_750_IDX                0x1
 #define SND_DJM_850_IDX                0x2
 #define SND_DJM_900NXS2_IDX    0x3
+#define SND_DJM_750MK2_IDX     0x4
 
 
 #define SND_DJM_CTL(_name, suffix, _default_value, _windex) { \
@@ -2984,10 +2985,40 @@ static const struct snd_djm_ctl snd_djm_ctls_900nxs2[] = {
        SND_DJM_CTL("Ch5 Input",   900nxs2_cap5, 3, SND_DJM_WINDEX_CAP)
 };
 
+// DJM-750MK2
+static const u16 snd_djm_opts_750mk2_cap1[] = {
+       0x0100, 0x0102, 0x0103, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a };
+static const u16 snd_djm_opts_750mk2_cap2[] = {
+       0x0200, 0x0202, 0x0203, 0x0206, 0x0207, 0x0208, 0x0209, 0x020a };
+static const u16 snd_djm_opts_750mk2_cap3[] = {
+       0x0300, 0x0302, 0x0303, 0x0306, 0x0307, 0x0308, 0x0309, 0x030a };
+static const u16 snd_djm_opts_750mk2_cap4[] = {
+       0x0400, 0x0402, 0x0403, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a };
+static const u16 snd_djm_opts_750mk2_cap5[] = {
+       0x0507, 0x0508, 0x0509, 0x050a, 0x0511, 0x0512, 0x0513, 0x0514 };
+
+static const u16 snd_djm_opts_750mk2_pb1[] = { 0x0100, 0x0101, 0x0104 };
+static const u16 snd_djm_opts_750mk2_pb2[] = { 0x0200, 0x0201, 0x0204 };
+static const u16 snd_djm_opts_750mk2_pb3[] = { 0x0300, 0x0301, 0x0304 };
+
+
+static const struct snd_djm_ctl snd_djm_ctls_750mk2[] = {
+       SND_DJM_CTL("Capture Level", cap_level, 0, SND_DJM_WINDEX_CAPLVL),
+       SND_DJM_CTL("Ch1 Input",   750mk2_cap1, 2, SND_DJM_WINDEX_CAP),
+       SND_DJM_CTL("Ch2 Input",   750mk2_cap2, 2, SND_DJM_WINDEX_CAP),
+       SND_DJM_CTL("Ch3 Input",   750mk2_cap3, 2, SND_DJM_WINDEX_CAP),
+       SND_DJM_CTL("Ch4 Input",   750mk2_cap4, 2, SND_DJM_WINDEX_CAP),
+       SND_DJM_CTL("Ch5 Input",   750mk2_cap5, 3, SND_DJM_WINDEX_CAP),
+       SND_DJM_CTL("Ch1 Output",   750mk2_pb1, 0, SND_DJM_WINDEX_PB),
+       SND_DJM_CTL("Ch2 Output",   750mk2_pb2, 1, SND_DJM_WINDEX_PB),
+       SND_DJM_CTL("Ch3 Output",   750mk2_pb3, 2, SND_DJM_WINDEX_PB)
+};
+
 
 static const struct snd_djm_device snd_djm_devices[] = {
        SND_DJM_DEVICE(250mk2),
        SND_DJM_DEVICE(750),
+       SND_DJM_DEVICE(750mk2),
        SND_DJM_DEVICE(850),
        SND_DJM_DEVICE(900nxs2)
 };
@@ -3235,6 +3266,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
        case USB_ID(0x08e4, 0x017f): /* Pioneer DJ DJM-750 */
                err = snd_djm_controls_create(mixer, SND_DJM_750_IDX);
                break;
+       case USB_ID(0x2b73, 0x001b): /* Pioneer DJ DJM-750MK2 */
+               err = snd_djm_controls_create(mixer, SND_DJM_750MK2_IDX);
+               break;
        case USB_ID(0x08e4, 0x0163): /* Pioneer DJ DJM-850 */
                err = snd_djm_controls_create(mixer, SND_DJM_850_IDX);
                break;
index 5dc9266..95ec8ee 100644 (file)
@@ -219,16 +219,16 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip,
        return 0;
 }
 
-static bool stop_endpoints(struct snd_usb_substream *subs)
+static bool stop_endpoints(struct snd_usb_substream *subs, bool keep_pending)
 {
        bool stopped = 0;
 
        if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
-               snd_usb_endpoint_stop(subs->sync_endpoint);
+               snd_usb_endpoint_stop(subs->sync_endpoint, keep_pending);
                stopped = true;
        }
        if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
-               snd_usb_endpoint_stop(subs->data_endpoint);
+               snd_usb_endpoint_stop(subs->data_endpoint, keep_pending);
                stopped = true;
        }
        return stopped;
@@ -261,7 +261,7 @@ static int start_endpoints(struct snd_usb_substream *subs)
        return 0;
 
  error:
-       stop_endpoints(subs);
+       stop_endpoints(subs, false);
        return err;
 }
 
@@ -437,7 +437,7 @@ static int configure_endpoints(struct snd_usb_audio *chip,
 
        if (subs->data_endpoint->need_setup) {
                /* stop any running stream beforehand */
-               if (stop_endpoints(subs))
+               if (stop_endpoints(subs, false))
                        sync_pending_stops(subs);
                err = snd_usb_endpoint_configure(chip, subs->data_endpoint);
                if (err < 0)
@@ -572,7 +572,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
        subs->cur_audiofmt = NULL;
        mutex_unlock(&chip->mutex);
        if (!snd_usb_lock_shutdown(chip)) {
-               if (stop_endpoints(subs))
+               if (stop_endpoints(subs, false))
                        sync_pending_stops(subs);
                close_endpoints(chip, subs);
                snd_usb_unlock_shutdown(chip);
@@ -581,6 +581,26 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+/* check whether early start is needed for playback stream */
+static int lowlatency_playback_available(struct snd_pcm_runtime *runtime,
+                                        struct snd_usb_substream *subs)
+{
+       struct snd_usb_audio *chip = subs->stream->chip;
+
+       if (subs->direction == SNDRV_PCM_STREAM_CAPTURE)
+               return false;
+       /* disabled via module option? */
+       if (!chip->lowlatency)
+               return false;
+       /* free-wheeling mode? (e.g. dmix) */
+       if (runtime->stop_threshold > runtime->buffer_size)
+               return false;
+       /* implicit feedback mode has own operation mode */
+       if (snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint))
+               return false;
+       return true;
+}
+
 /*
  * prepare callback
  *
@@ -614,13 +634,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
        subs->period_elapsed_pending = 0;
        runtime->delay = 0;
 
-       /* check whether early start is needed for playback stream */
-       subs->early_playback_start =
-               subs->direction == SNDRV_PCM_STREAM_PLAYBACK &&
-               (!chip->lowlatency ||
-                (subs->data_endpoint->nominal_queue_size >= subs->buffer_bytes));
-
-       if (subs->early_playback_start)
+       subs->lowlatency_playback = lowlatency_playback_available(runtime, subs);
+       if (!subs->lowlatency_playback)
                ret = start_endpoints(subs);
 
  unlock:
@@ -734,6 +749,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
                        struct snd_pcm_hw_rule *rule)
 {
        struct snd_usb_substream *subs = rule->private;
+       struct snd_usb_audio *chip = subs->stream->chip;
        const struct audioformat *fp;
        struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
        unsigned int rmin, rmax, r;
@@ -745,6 +761,14 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params,
        list_for_each_entry(fp, &subs->fmt_list, list) {
                if (!hw_check_valid_format(subs, params, fp))
                        continue;
+               r = snd_usb_endpoint_get_clock_rate(chip, fp->clock);
+               if (r > 0) {
+                       if (!snd_interval_test(it, r))
+                               continue;
+                       rmin = min(rmin, r);
+                       rmax = max(rmax, r);
+                       continue;
+               }
                if (fp->rate_table && fp->nr_rates) {
                        for (i = 0; i < fp->nr_rates; i++) {
                                r = fp->rate_table[i];
@@ -1056,6 +1080,13 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
        if (err < 0)
                return err;
 
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (fp->implicit_fb) {
+                       runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+                       break;
+               }
+       }
+
        return 0;
 }
 
@@ -1068,6 +1099,10 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
        int ret;
 
        runtime->hw = snd_usb_hardware;
+       /* need an explicit sync to catch applptr update in low-latency mode */
+       if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
+           as->chip->lowlatency)
+               runtime->hw.info |= SNDRV_PCM_INFO_SYNC_APPLPTR;
        runtime->private_data = subs;
        subs->pcm_substream = substream;
        /* runtime PM is also done there */
@@ -1320,44 +1355,66 @@ static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
        return bytes;
 }
 
-static void prepare_playback_urb(struct snd_usb_substream *subs,
-                                struct urb *urb)
+static int prepare_playback_urb(struct snd_usb_substream *subs,
+                               struct urb *urb,
+                               bool in_stream_lock)
 {
        struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
        struct snd_usb_endpoint *ep = subs->data_endpoint;
        struct snd_urb_ctx *ctx = urb->context;
-       unsigned int counts, frames, bytes;
+       unsigned int frames, bytes;
+       int counts;
+       unsigned int transfer_done, frame_limit, avail = 0;
        int i, stride, period_elapsed = 0;
        unsigned long flags;
+       int err = 0;
 
        stride = ep->stride;
 
        frames = 0;
        ctx->queued = 0;
        urb->number_of_packets = 0;
+
        spin_lock_irqsave(&subs->lock, flags);
-       subs->frame_limit += ep->max_urb_frames;
+       frame_limit = subs->frame_limit + ep->max_urb_frames;
+       transfer_done = subs->transfer_done;
+
+       if (subs->lowlatency_playback &&
+           runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
+               unsigned int hwptr = subs->hwptr_done / stride;
+
+               /* calculate the byte offset-in-buffer of the appl_ptr */
+               avail = (runtime->control->appl_ptr - runtime->hw_ptr_base)
+                       % runtime->buffer_size;
+               if (avail <= hwptr)
+                       avail += runtime->buffer_size;
+               avail -= hwptr;
+       }
+
        for (i = 0; i < ctx->packets; i++) {
-               counts = snd_usb_endpoint_next_packet_size(ep, ctx, i);
+               counts = snd_usb_endpoint_next_packet_size(ep, ctx, i, avail);
+               if (counts < 0)
+                       break;
                /* set up descriptor */
                urb->iso_frame_desc[i].offset = frames * stride;
                urb->iso_frame_desc[i].length = counts * stride;
                frames += counts;
+               avail -= counts;
                urb->number_of_packets++;
-               subs->transfer_done += counts;
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       subs->frame_limit = 0;
+               transfer_done += counts;
+               if (transfer_done >= runtime->period_size) {
+                       transfer_done -= runtime->period_size;
+                       frame_limit = 0;
                        period_elapsed = 1;
                        if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
-                               if (subs->transfer_done > 0) {
+                               if (transfer_done > 0) {
                                        /* FIXME: fill-max mode is not
                                         * supported yet */
-                                       frames -= subs->transfer_done;
-                                       counts -= subs->transfer_done;
+                                       frames -= transfer_done;
+                                       counts -= transfer_done;
                                        urb->iso_frame_desc[i].length =
                                                counts * stride;
-                                       subs->transfer_done = 0;
+                                       transfer_done = 0;
                                }
                                i++;
                                if (i < ctx->packets) {
@@ -1371,13 +1428,19 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
                        }
                }
                /* finish at the period boundary or after enough frames */
-               if ((period_elapsed ||
-                               subs->transfer_done >= subs->frame_limit) &&
+               if ((period_elapsed || transfer_done >= frame_limit) &&
                    !snd_usb_endpoint_implicit_feedback_sink(ep))
                        break;
        }
-       bytes = frames * stride;
 
+       if (!frames) {
+               err = -EAGAIN;
+               goto unlock;
+       }
+
+       bytes = frames * stride;
+       subs->transfer_done = transfer_done;
+       subs->frame_limit = frame_limit;
        if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE &&
                     subs->cur_audiofmt->dsd_dop)) {
                fill_playback_urb_dsd_dop(subs, urb, bytes);
@@ -1403,14 +1466,23 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
                subs->trigger_tstamp_pending_update = false;
        }
 
-       if (period_elapsed && !subs->running && !subs->early_playback_start) {
+       if (period_elapsed && !subs->running && subs->lowlatency_playback) {
                subs->period_elapsed_pending = 1;
                period_elapsed = 0;
        }
+
+ unlock:
        spin_unlock_irqrestore(&subs->lock, flags);
+       if (err < 0)
+               return err;
        urb->transfer_buffer_length = bytes;
-       if (period_elapsed)
-               snd_pcm_period_elapsed(subs->pcm_substream);
+       if (period_elapsed) {
+               if (in_stream_lock)
+                       snd_pcm_period_elapsed_under_stream_lock(subs->pcm_substream);
+               else
+                       snd_pcm_period_elapsed(subs->pcm_substream);
+       }
+       return 0;
 }
 
 /*
@@ -1442,6 +1514,27 @@ static void retire_playback_urb(struct snd_usb_substream *subs,
                snd_pcm_period_elapsed(subs->pcm_substream);
 }
 
+/* PCM ack callback for the playback stream;
+ * this plays a role only when the stream is running in low-latency mode.
+ */
+static int snd_usb_pcm_playback_ack(struct snd_pcm_substream *substream)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+       struct snd_usb_endpoint *ep;
+
+       if (!subs->lowlatency_playback || !subs->running)
+               return 0;
+       ep = subs->data_endpoint;
+       if (!ep)
+               return 0;
+       /* When no more in-flight URBs available, try to process the pending
+        * outputs here
+        */
+       if (!ep->active_mask)
+               snd_usb_queue_pending_output_urbs(ep, true);
+       return 0;
+}
+
 static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
                                              int cmd)
 {
@@ -1457,7 +1550,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
                                              prepare_playback_urb,
                                              retire_playback_urb,
                                              subs);
-               if (!subs->early_playback_start &&
+               if (subs->lowlatency_playback &&
                    cmd == SNDRV_PCM_TRIGGER_START) {
                        err = start_endpoints(subs);
                        if (err < 0) {
@@ -1473,7 +1566,7 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea
                return 0;
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
-               stop_endpoints(subs);
+               stop_endpoints(subs, substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING);
                snd_usb_endpoint_set_callback(subs->data_endpoint,
                                              NULL, NULL, NULL);
                subs->running = 0;
@@ -1521,7 +1614,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream
                return 0;
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
-               stop_endpoints(subs);
+               stop_endpoints(subs, false);
                fallthrough;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                snd_usb_endpoint_set_callback(subs->data_endpoint,
@@ -1545,6 +1638,7 @@ static const struct snd_pcm_ops snd_usb_playback_ops = {
        .trigger =      snd_usb_substream_playback_trigger,
        .sync_stop =    snd_usb_pcm_sync_stop,
        .pointer =      snd_usb_pcm_pointer,
+       .ack =          snd_usb_pcm_playback_ack,
 };
 
 static const struct snd_pcm_ops snd_usb_capture_ops = {
index de18fff..b1522e4 100644 (file)
@@ -3894,6 +3894,64 @@ YAMAHA_DEVICE(0x7010, "UB99"),
 },
 {
        /*
+        * Pioneer DJ DJM-750MK2
+        * 10 channels playback & 12 channels capture @ 48kHz S24LE
+        */
+       USB_DEVICE_VENDOR_SPEC(0x2b73, 0x001b),
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const struct snd_usb_audio_quirk[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+                               .data = &(const struct audioformat) {
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+                                       .channels = 10,
+                                       .iface = 0,
+                                       .altsetting = 1,
+                                       .altset_idx = 1,
+                                       .endpoint = 0x01,
+                                       .ep_attr = USB_ENDPOINT_XFER_ISOC|
+                                           USB_ENDPOINT_SYNC_ASYNC,
+                                       .rates = SNDRV_PCM_RATE_48000,
+                                       .rate_min = 48000,
+                                       .rate_max = 48000,
+                                       .nr_rates = 1,
+                                       .rate_table = (unsigned int[]) {
+                                               48000
+                                       }
+                               }
+                       },
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+                               .data = &(const struct audioformat) {
+                                       .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+                                       .channels = 12,
+                                       .iface = 0,
+                                       .altsetting = 1,
+                                       .altset_idx = 1,
+                                       .endpoint = 0x82,
+                                       .ep_idx = 1,
+                                       .ep_attr = USB_ENDPOINT_XFER_ISOC|
+                                               USB_ENDPOINT_SYNC_ASYNC|
+                                               USB_ENDPOINT_USAGE_IMPLICIT_FB,
+                                       .rates = SNDRV_PCM_RATE_48000,
+                                       .rate_min = 48000,
+                                       .rate_max = 48000,
+                                       .nr_rates = 1,
+                                       .rate_table = (unsigned int[]) { 48000 }
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
+{
+       /*
         * Pioneer DJ DJM-850
         * 8 channels playback and 8 channels capture @ 44.1/48/96kHz S24LE
         * Playback on EP 0x05
@@ -4012,6 +4070,38 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                }
        }
 },
+{
+       /*
+        * Sennheiser GSP670
+        * Change order of interfaces loaded
+        */
+       USB_DEVICE(0x1395, 0x0300),
+       .bInterfaceClass = USB_CLASS_PER_INTERFACE,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = &(const struct snd_usb_audio_quirk[]) {
+                       // Communication
+                       {
+                               .ifnum = 3,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       // Recording
+                       {
+                               .ifnum = 4,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       // Main
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
 
 #undef USB_DEVICE_VENDOR_SPEC
 #undef USB_AUDIO_DEVICE
index 889c855..1d418c3 100644 (file)
@@ -1719,6 +1719,11 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip,
                 */
                fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX;
                break;
+       case USB_ID(0x1224, 0x2a25):  /* Jieli Technology USB PHY 2.0 */
+               /* mic works only when ep packet size is set to wMaxPacketSize */
+               fp->attributes |= UAC_EP_CS_ATTR_FILL_MAX;
+               break;
+
        }
 }
 
@@ -1744,6 +1749,7 @@ static const struct registration_quirk registration_quirks[] = {
        REG_QUIRK_ENTRY(0x0951, 0x16ea, 2),     /* Kingston HyperX Cloud Flight S */
        REG_QUIRK_ENTRY(0x0ecb, 0x1f46, 2),     /* JBL Quantum 600 */
        REG_QUIRK_ENTRY(0x0ecb, 0x1f47, 2),     /* JBL Quantum 800 */
+       REG_QUIRK_ENTRY(0x0ecb, 0x1f4c, 2),     /* JBL Quantum 400 */
        REG_QUIRK_ENTRY(0x0ecb, 0x2039, 2),     /* JBL Quantum 400 */
        REG_QUIRK_ENTRY(0x0ecb, 0x203c, 2),     /* JBL Quantum 600 */
        REG_QUIRK_ENTRY(0x0ecb, 0x203e, 2),     /* JBL Quantum 800 */
@@ -1884,10 +1890,14 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
                   QUIRK_FLAG_GET_SAMPLE_RATE),
        DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
                   QUIRK_FLAG_GET_SAMPLE_RATE),
+       DEVICE_FLG(0x30be, 0x0101, /* Schiit Hel */
+                  QUIRK_FLAG_IGNORE_CTL_ERROR),
        DEVICE_FLG(0x413c, 0xa506, /* Dell AE515 sound bar */
                   QUIRK_FLAG_GET_SAMPLE_RATE),
        DEVICE_FLG(0x534d, 0x2109, /* MacroSilicon MS2109 */
                   QUIRK_FLAG_ALIGN_TRANSFER),
+       DEVICE_FLG(0x1224, 0x2a25, /* Jieli Technology USB PHY 2.0 */
+                  QUIRK_FLAG_GET_SAMPLE_RATE),
 
        /* Vendor matches */
        VENDOR_FLG(0x045e, /* MS Lifecam */
index c39cc68..cfc1ea5 100644 (file)
@@ -668,14 +668,15 @@ static void i_usx2y_04int(struct urb *urb)
 
 static int usx2y_rate_set(struct usx2ydev *usx2y, int rate)
 {
-       int                     err = 0, i;
-       struct snd_usx2y_urb_seq        *us = NULL;
-       int                     *usbdata = NULL;
-       const struct s_c2       *ra = rate == 48000 ? setrate_48000 : setrate_44100;
+       int err = 0, i;
+       struct snd_usx2y_urb_seq *us = NULL;
+       int *usbdata = NULL;
+       const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100;
        struct urb *urb;
 
        if (usx2y->rate != rate) {
-               us = kzalloc(sizeof(*us) + sizeof(struct urb *) * NOOF_SETRATE_URBS, GFP_KERNEL);
+               us = kzalloc(struct_size(us, urb, NOOF_SETRATE_URBS),
+                            GFP_KERNEL);
                if (!us) {
                        err = -ENOMEM;
                        goto cleanup;
index f88c8f2..aca2dc1 100644 (file)
@@ -20,7 +20,7 @@ struct virtio_pcm_msg {
        struct virtio_snd_pcm_xfer xfer;
        struct virtio_snd_pcm_status status;
        size_t length;
-       struct scatterlist sgs[0];
+       struct scatterlist sgs[];
 };
 
 /**
@@ -146,8 +146,7 @@ int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss,
                int sg_num = virtsnd_pcm_sg_num(data, period_bytes);
                struct virtio_pcm_msg *msg;
 
-               msg = kzalloc(sizeof(*msg) + sizeof(*msg->sgs) * (sg_num + 2),
-                             GFP_KERNEL);
+               msg = kzalloc(struct_size(msg, sgs, sg_num + 2), GFP_KERNEL);
                if (!msg)
                        return -ENOMEM;