mirror of
https://github.com/t2linux/kernel.git
synced 2026-04-30 13:48:59 -07:00
Merge branch 'for-next' into for-linus
Merge 5.16-devel branch for upstreaming Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
@@ -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);
|
||||
|
||||
+47
-11
@@ -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 */
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
|
||||
+195
-12
@@ -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 */
|
||||
|
||||
+10
-3
@@ -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",
|
||||
|
||||
+46
-20
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,21 +48,47 @@ 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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&motu->lock);
|
||||
|
||||
if (copy_to_user(buf, &event, count))
|
||||
return -EFAULT;
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user