Commit 8beea313 authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'for-next' into for-linus

Merge 5.16-devel branch for upstreaming
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parents 3ab79920 2672e197
......@@ -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);
......
......@@ -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 */
......@@ -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)
......
......@@ -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 */
......@@ -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
......
......@@ -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)
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 */
......@@ -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",
......
......@@ -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);
}
......
// 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,
};
......@@ -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
......@@ -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);
}
......
// 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);
}
......@@ -16,6 +16,14 @@
#include "motu.h"
static bool has_dsp_event(struct snd_motu *motu)
{
if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
return (snd_motu_register_dsp_message_parser_count_event(motu) > 0);
else
return false;
}
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
......@@ -25,7 +33,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&motu->lock);
while (!motu->dev_lock_changed && motu->msg == 0) {
while (!motu->dev_lock_changed && motu->msg == 0 && !has_dsp_event(motu)) {
prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&motu->lock);
schedule();
......@@ -40,20 +48,46 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (motu->dev_lock_count > 0);
motu->dev_lock_changed = false;
spin_unlock_irq(&motu->lock);
count = min_t(long, count, sizeof(event.lock_status));
} else {
count = min_t(long, count, sizeof(event));
if (copy_to_user(buf, &event, count))
return -EFAULT;
} else if (motu->msg > 0) {
event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
event.motu_notification.message = motu->msg;
motu->msg = 0;
spin_unlock_irq(&motu->lock);
count = min_t(long, count, sizeof(event.motu_notification));
}
count = min_t(long, count, sizeof(event));
if (copy_to_user(buf, &event, count))
return -EFAULT;
} else if (has_dsp_event(motu)) {
size_t consumed = 0;
u32 __user *ptr;
u32 ev;
spin_unlock_irq(&motu->lock);
spin_unlock_irq(&motu->lock);
if (copy_to_user(buf, &event, count))
return -EFAULT;
// Header is filled later.
consumed += sizeof(event.motu_register_dsp_change);
while (consumed < count &&
snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) {
ptr = (u32 __user *)(buf + consumed);
if (put_user(ev, ptr))
return -EFAULT;
consumed += sizeof(ev);
}
event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE;
event.motu_register_dsp_change.count =
(consumed - sizeof(event.motu_register_dsp_change)) / 4;
if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change)))
return -EFAULT;
count = consumed;
}
return count;
}
......@@ -67,7 +101,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_wait(file, &motu->hwdep_wait, wait);
spin_lock_irq(&motu->lock);
if (motu->dev_lock_changed || motu->msg)
if (motu->dev_lock_changed || motu->msg || has_dsp_event(motu))
events = EPOLLIN | EPOLLRDNORM;
else
events = 0;
......@@ -155,6 +189,71 @@ static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
return hwdep_lock(motu);
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
return hwdep_unlock(motu);
case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_METER:
{
struct snd_firewire_motu_register_dsp_meter *meter;
int err;
if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
return -ENXIO;
meter = kzalloc(sizeof(*meter), GFP_KERNEL);
if (!meter)
return -ENOMEM;
snd_motu_register_dsp_message_parser_copy_meter(motu, meter);
err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
kfree(meter);
if (err)
return -EFAULT;
return 0;
}
case SNDRV_FIREWIRE_IOCTL_MOTU_COMMAND_DSP_METER:
{
struct snd_firewire_motu_command_dsp_meter *meter;
int err;
if (!(motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP))
return -ENXIO;
meter = kzalloc(sizeof(*meter), GFP_KERNEL);
if (!meter)
return -ENOMEM;
snd_motu_command_dsp_message_parser_copy_meter(motu, meter);
err = copy_to_user((void __user *)arg, meter, sizeof(*meter));
kfree(meter);
if (err)
return -EFAULT;
return 0;
}
case SNDRV_FIREWIRE_IOCTL_MOTU_REGISTER_DSP_PARAMETER:
{
struct snd_firewire_motu_register_dsp_parameter *param;
int err;
if (!(motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP))
return -ENXIO;
param = kzalloc(sizeof(*param), GFP_KERNEL);
if (!param)
return -ENOMEM;
snd_motu_register_dsp_message_parser_copy_parameter(motu, param);
err = copy_to_user((void __user *)arg, param, sizeof(*param));
kfree(param);
if (err)
return -EFAULT;
return 0;
}
default:
return -ENOIOCTLCMD;
}
......@@ -193,5 +292,7 @@ int snd_motu_create_hwdep_device(struct snd_motu *motu)
hwdep->private_data = motu;
hwdep->exclusive = true;
motu->hwdep = hwdep;
return 0;
}
......@@ -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},
......
......@@ -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},
};
This diff is collapsed.
......@@ -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,
......
......@@ -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;
......
......@@ -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
......@@ -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;
}
......
......@@ -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;
......
......@@ -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. */
......
......@@ -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);
......
......@@ -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);
......
......@@ -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.
......
......@@ -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
......
......@@ -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.
......
......@@ -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,
};
......
......@@ -8634,6 +8634,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation",
ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87e5, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87e7, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
......
......@@ -68,6 +68,7 @@ static struct hdac_ext_stream *
return NULL;
}
spin_lock_irq(&bus->reg_lock);
list_for_each_entry(stream, &bus->stream_list, list) {
struct hdac_ext_stream *hstream =
stream_to_hdac_ext_stream(stream);
......@@ -107,12 +108,12 @@ static struct hdac_ext_stream *
* is updated in snd_hdac_ext_stream_decouple().
*/
if (!res->decoupled)
snd_hdac_ext_stream_decouple(bus, res, true);
spin_lock_irq(&bus->reg_lock);
snd_hdac_ext_stream_decouple_locked(bus, res, true);
res->link_locked = 1;
res->link_substream = substream;
spin_unlock_irq(&bus->reg_lock);
}
spin_unlock_irq(&bus->reg_lock);
return res;
}
......
......@@ -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)
......
......@@ -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)
......
......@@ -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;
};
......
......@@ -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
......
This diff is collapsed.
......@@ -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 */
......@@ -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+ */
......
......@@ -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,
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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) {
......
......@@ -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;
}
......
......@@ -361,9 +361,8 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
memset(buf, 0, sizeof(buf));
ret = snd_usb_lock_shutdown(chip) ? -EIO : 0;
if (ret)
goto error;
if (snd_usb_lock_shutdown(chip))
return -EIO;
idx = mixer_ctrl_intf(cval->head.mixer) | (cval->head.id << 8);
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
......@@ -372,8 +371,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
snd_usb_unlock_shutdown(chip);
if (ret < 0) {
error:
usb_audio_err(chip,
usb_audio_dbg(chip,
"cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
request, validx, idx, cval->val_type);
return ret;
......@@ -1208,12 +1206,32 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
}
}
/* forcibly initialize the current mixer value; if GET_CUR fails, set to
* the minimum as default
*/
static void init_cur_mix_raw(struct usb_mixer_elem_info *cval, int ch, int idx)
{
int val, err;
err = snd_usb_get_cur_mix_value(cval, ch, idx, &val);
if (!err)
return;
if (!cval->head.mixer->ignore_ctl_error)
usb_audio_warn(cval->head.mixer->chip,
"%d:%d: failed to get current value for ch %d (%d)\n",
cval->head.id, mixer_ctrl_intf(cval->head.mixer),
ch, err);
snd_usb_set_cur_mix_value(cval, ch, idx, cval->min);
}
/*
* retrieve the minimum and maximum values for the specified control
*/
static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
int default_min, struct snd_kcontrol *kctl)
{
int i, idx;
/* for failsafe */
cval->min = default_min;
cval->max = cval->min + 1;
......@@ -1226,7 +1244,6 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
} else {
int minchn = 0;
if (cval->cmask) {
int i;
for (i = 0; i < MAX_CHANNELS; i++)
if (cval->cmask & (1 << i)) {
minchn = i + 1;
......@@ -1327,6 +1344,19 @@ static int get_min_max_with_quirks(struct usb_mixer_elem_info *cval,
}
}
/* 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;
}
......
......@@ -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;
......
This diff is collapsed.
......@@ -3892,6 +3892,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
......
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment