mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
Merge branch 'for-next' into for-linus
This commit is contained in:
commit
498386d1c4
@ -75,6 +75,10 @@ properties:
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
enum: [ 0, 1, 2, 3 ]
|
||||
|
||||
port:
|
||||
$ref: audio-graph-port.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -102,7 +102,7 @@ Conexant codecs
|
||||
---------------
|
||||
|
||||
Auto-Mute Mode
|
||||
See Reatek codecs.
|
||||
See Realtek codecs.
|
||||
|
||||
|
||||
Analog codecs
|
||||
|
@ -7112,6 +7112,13 @@ L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/input/joystick/fsia6b.c
|
||||
|
||||
FOCUSRITE SCARLETT GEN 2/3 MIXER DRIVER
|
||||
M: Geoffrey D. Bennett <g@b4.vu>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
F: sound/usb/mixer_scarlett_gen2.c
|
||||
|
||||
FORCEDETH GIGABIT ETHERNET DRIVER
|
||||
M: Rain River <rain.1986.08.12@gmail.com>
|
||||
M: Zhu Yanjun <zyjzyj2000@gmail.com>
|
||||
|
@ -492,7 +492,7 @@ int sdw_read_no_pm(struct sdw_slave *slave, u32 addr)
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_read_no_pm);
|
||||
|
||||
static int sdw_update_no_pm(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
|
||||
int sdw_update_no_pm(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
@ -503,6 +503,21 @@ static int sdw_update_no_pm(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
|
||||
tmp = (tmp & ~mask) | val;
|
||||
return sdw_write_no_pm(slave, addr, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_update_no_pm);
|
||||
|
||||
/* Read-Modify-Write Slave register */
|
||||
int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
tmp = sdw_read(slave, addr);
|
||||
if (tmp < 0)
|
||||
return tmp;
|
||||
|
||||
tmp = (tmp & ~mask) | val;
|
||||
return sdw_write(slave, addr, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL(sdw_update);
|
||||
|
||||
/**
|
||||
* sdw_nread() - Read "n" contiguous SDW Slave registers
|
||||
|
@ -201,19 +201,6 @@ static inline void sdw_fill_port_params(struct sdw_port_params *params,
|
||||
params->data_mode = data_mode;
|
||||
}
|
||||
|
||||
/* Read-Modify-Write Slave register */
|
||||
static inline int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
tmp = sdw_read(slave, addr);
|
||||
if (tmp < 0)
|
||||
return tmp;
|
||||
|
||||
tmp = (tmp & ~mask) | val;
|
||||
return sdw_write(slave, addr, tmp);
|
||||
}
|
||||
|
||||
/* broadcast read/write for tests */
|
||||
int sdw_bread_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr);
|
||||
int sdw_bwrite_no_pm_unlocked(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 value);
|
||||
|
@ -1041,6 +1041,9 @@ int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value);
|
||||
int sdw_read_no_pm(struct sdw_slave *slave, u32 addr);
|
||||
int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val);
|
||||
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val);
|
||||
int sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val);
|
||||
int sdw_update_no_pm(struct sdw_slave *slave, u32 addr, u8 mask, u8 val);
|
||||
|
||||
int sdw_compare_devid(struct sdw_slave *slave, struct sdw_slave_id id);
|
||||
void sdw_extract_slave_id(struct sdw_bus *bus, u64 addr, struct sdw_slave_id *id);
|
||||
|
||||
|
@ -128,7 +128,9 @@ struct snd_card {
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
unsigned int power_state; /* power state */
|
||||
atomic_t power_ref;
|
||||
wait_queue_head_t power_sleep;
|
||||
wait_queue_head_t power_ref_sleep;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
|
||||
@ -142,21 +144,61 @@ struct snd_card {
|
||||
#ifdef CONFIG_PM
|
||||
static inline unsigned int snd_power_get_state(struct snd_card *card)
|
||||
{
|
||||
return card->power_state;
|
||||
return READ_ONCE(card->power_state);
|
||||
}
|
||||
|
||||
static inline void snd_power_change_state(struct snd_card *card, unsigned int state)
|
||||
{
|
||||
card->power_state = state;
|
||||
WRITE_ONCE(card->power_state, state);
|
||||
wake_up(&card->power_sleep);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_power_ref - Take the reference count for power control
|
||||
* @card: sound card object
|
||||
*
|
||||
* The power_ref reference of the card is used for managing to block
|
||||
* the snd_power_sync_ref() operation. This function increments the reference.
|
||||
* The counterpart snd_power_unref() has to be called appropriately later.
|
||||
*/
|
||||
static inline void snd_power_ref(struct snd_card *card)
|
||||
{
|
||||
atomic_inc(&card->power_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_power_unref - Release the reference count for power control
|
||||
* @card: sound card object
|
||||
*/
|
||||
static inline void snd_power_unref(struct snd_card *card)
|
||||
{
|
||||
if (atomic_dec_and_test(&card->power_ref))
|
||||
wake_up(&card->power_ref_sleep);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_power_sync_ref - wait until the card power_ref is freed
|
||||
* @card: sound card object
|
||||
*
|
||||
* This function is used to synchronize with the pending power_ref being
|
||||
* released.
|
||||
*/
|
||||
static inline void snd_power_sync_ref(struct snd_card *card)
|
||||
{
|
||||
wait_event(card->power_ref_sleep, !atomic_read(&card->power_ref));
|
||||
}
|
||||
|
||||
/* init.c */
|
||||
int snd_power_wait(struct snd_card *card, unsigned int power_state);
|
||||
int snd_power_wait(struct snd_card *card);
|
||||
int snd_power_ref_and_wait(struct snd_card *card);
|
||||
|
||||
#else /* ! CONFIG_PM */
|
||||
|
||||
static inline int snd_power_wait(struct snd_card *card, unsigned int state) { return 0; }
|
||||
static inline int snd_power_wait(struct snd_card *card) { return 0; }
|
||||
static inline void snd_power_ref(struct snd_card *card) {}
|
||||
static inline void snd_power_unref(struct snd_card *card) {}
|
||||
static inline int snd_power_ref_and_wait(struct snd_card *card) { return 0; }
|
||||
static inline void snd_power_sync_ref(struct snd_card *card) {}
|
||||
#define snd_power_get_state(card) ({ (void)(card); SNDRV_CTL_POWER_D0; })
|
||||
#define snd_power_change_state(card, state) do { (void)(card); } while (0)
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <asm/page.h>
|
||||
|
||||
struct device;
|
||||
struct vm_area_struct;
|
||||
|
||||
/*
|
||||
* buffer device info
|
||||
@ -64,84 +65,19 @@ static inline unsigned int snd_sgbuf_aligned_pages(size_t size)
|
||||
return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
/*
|
||||
* Scatter-Gather generic device pages
|
||||
*/
|
||||
void *snd_malloc_sgbuf_pages(struct device *device,
|
||||
size_t size, struct snd_dma_buffer *dmab,
|
||||
size_t *res_size);
|
||||
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
/*
|
||||
* return the physical address at the corresponding offset
|
||||
*/
|
||||
static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
|
||||
size_t offset)
|
||||
{
|
||||
struct snd_sg_buf *sgbuf = dmab->private_data;
|
||||
dma_addr_t addr;
|
||||
|
||||
if (!sgbuf)
|
||||
return dmab->addr + offset;
|
||||
addr = sgbuf->table[offset >> PAGE_SHIFT].addr;
|
||||
addr &= ~((dma_addr_t)PAGE_SIZE - 1);
|
||||
return addr + offset % PAGE_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the virtual address at the corresponding offset
|
||||
*/
|
||||
static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
|
||||
size_t offset)
|
||||
{
|
||||
struct snd_sg_buf *sgbuf = dmab->private_data;
|
||||
|
||||
if (!sgbuf)
|
||||
return dmab->area + offset;
|
||||
return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
|
||||
}
|
||||
|
||||
unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
|
||||
unsigned int ofs, unsigned int size);
|
||||
#else
|
||||
/* non-SG versions */
|
||||
static inline dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab,
|
||||
size_t offset)
|
||||
{
|
||||
return dmab->addr + offset;
|
||||
}
|
||||
|
||||
static inline void *snd_sgbuf_get_ptr(struct snd_dma_buffer *dmab,
|
||||
size_t offset)
|
||||
{
|
||||
return dmab->area + offset;
|
||||
}
|
||||
|
||||
#define snd_sgbuf_get_chunk_size(dmab, ofs, size) (size)
|
||||
|
||||
#endif /* CONFIG_SND_DMA_SGBUF */
|
||||
|
||||
/* 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_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);
|
||||
|
||||
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);
|
||||
|
||||
#endif /* __SOUND_MEMALLOC_H */
|
||||
|
||||
|
@ -1066,6 +1066,7 @@ void snd_pcm_set_ops(struct snd_pcm * pcm, int direction,
|
||||
void snd_pcm_set_sync(struct snd_pcm_substream *substream);
|
||||
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
|
||||
unsigned int cmd, void *arg);
|
||||
void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substream);
|
||||
void snd_pcm_period_elapsed(struct snd_pcm_substream *substream);
|
||||
snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
|
||||
void *buf, bool interleaved,
|
||||
@ -1253,14 +1254,6 @@ static inline int snd_pcm_lib_alloc_vmalloc_32_buffer
|
||||
|
||||
#define snd_pcm_get_dma_buf(substream) ((substream)->runtime->dma_buffer_p)
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
/*
|
||||
* SG-buffer handling
|
||||
*/
|
||||
#define snd_pcm_substream_sgbuf(substream) \
|
||||
snd_pcm_get_dma_buf(substream)->private_data
|
||||
#endif /* SND_DMA_SGBUF */
|
||||
|
||||
/**
|
||||
* snd_pcm_sgbuf_get_addr - Get the DMA address at the corresponding offset
|
||||
* @substream: PCM substream
|
||||
@ -1272,17 +1265,6 @@ snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
|
||||
return snd_sgbuf_get_addr(snd_pcm_get_dma_buf(substream), ofs);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_sgbuf_get_ptr - Get the virtual address at the corresponding offset
|
||||
* @substream: PCM substream
|
||||
* @ofs: byte offset
|
||||
*/
|
||||
static inline void *
|
||||
snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
|
||||
{
|
||||
return snd_sgbuf_get_ptr(snd_pcm_get_dma_buf(substream), ofs);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_sgbuf_get_chunk_size - Compute the max size that fits within the
|
||||
* contig. page from the given size
|
||||
|
@ -81,6 +81,8 @@ struct snd_rawmidi_substream {
|
||||
bool opened; /* open flag */
|
||||
bool append; /* append flag (merge more streams) */
|
||||
bool active_sensing; /* send active sensing when close */
|
||||
unsigned int framing; /* whether to frame input data */
|
||||
unsigned int clock_type; /* clock source to use for input framing */
|
||||
int use_count; /* use counter (for output) */
|
||||
size_t bytes;
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
@ -710,7 +710,7 @@ enum {
|
||||
* Raw MIDI section - /dev/snd/midi??
|
||||
*/
|
||||
|
||||
#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1)
|
||||
#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2)
|
||||
|
||||
enum {
|
||||
SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
|
||||
@ -736,12 +736,38 @@ struct snd_rawmidi_info {
|
||||
unsigned char reserved[64]; /* reserved for future use */
|
||||
};
|
||||
|
||||
#define SNDRV_RAWMIDI_MODE_FRAMING_MASK (7<<0)
|
||||
#define SNDRV_RAWMIDI_MODE_FRAMING_SHIFT 0
|
||||
#define SNDRV_RAWMIDI_MODE_FRAMING_NONE (0<<0)
|
||||
#define SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP (1<<0)
|
||||
#define SNDRV_RAWMIDI_MODE_CLOCK_MASK (7<<3)
|
||||
#define SNDRV_RAWMIDI_MODE_CLOCK_SHIFT 3
|
||||
#define SNDRV_RAWMIDI_MODE_CLOCK_NONE (0<<3)
|
||||
#define SNDRV_RAWMIDI_MODE_CLOCK_REALTIME (1<<3)
|
||||
#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC (2<<3)
|
||||
#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW (3<<3)
|
||||
|
||||
#define SNDRV_RAWMIDI_FRAMING_DATA_LENGTH 16
|
||||
|
||||
struct snd_rawmidi_framing_tstamp {
|
||||
/* For now, frame_type is always 0. Midi 2.0 is expected to add new
|
||||
* types here. Applications are expected to skip unknown frame types.
|
||||
*/
|
||||
__u8 frame_type;
|
||||
__u8 length; /* number of valid bytes in data field */
|
||||
__u8 reserved[2];
|
||||
__u32 tv_nsec; /* nanoseconds */
|
||||
__u64 tv_sec; /* seconds */
|
||||
__u8 data[SNDRV_RAWMIDI_FRAMING_DATA_LENGTH];
|
||||
} __packed;
|
||||
|
||||
struct snd_rawmidi_params {
|
||||
int stream;
|
||||
size_t buffer_size; /* queue size in bytes */
|
||||
size_t avail_min; /* minimum avail bytes for wakeup */
|
||||
unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */
|
||||
unsigned char reserved[16]; /* reserved for future use */
|
||||
unsigned int mode; /* For input data only, frame incoming data */
|
||||
unsigned char reserved[12]; /* reserved for future use */
|
||||
};
|
||||
|
||||
#ifndef __KERNEL__
|
||||
|
@ -520,7 +520,7 @@ static int ac97_bus_remove(struct device *dev)
|
||||
struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -918,10 +918,8 @@ i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card,
|
||||
}
|
||||
|
||||
cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL);
|
||||
if (!cii) {
|
||||
printk(KERN_DEBUG "i2sbus: failed to allocate cii\n");
|
||||
if (!cii)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* use the private data to point to the codec info */
|
||||
cii->sdev = soundbus_dev_get(dev);
|
||||
|
@ -47,9 +47,7 @@ static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97,
|
||||
static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97,
|
||||
unsigned short reg, unsigned short val)
|
||||
{
|
||||
int __always_unused ret;
|
||||
|
||||
ret = pxa2xx_ac97_write(ac97->num, reg, val);
|
||||
pxa2xx_ac97_write(ac97->num, reg, val);
|
||||
}
|
||||
|
||||
static const struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
|
||||
|
@ -995,7 +995,10 @@ static int __snd_ctl_elem_info(struct snd_card *card,
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
info->access = 0;
|
||||
#endif
|
||||
result = kctl->info(kctl, info);
|
||||
result = snd_power_ref_and_wait(card);
|
||||
if (!result)
|
||||
result = kctl->info(kctl, info);
|
||||
snd_power_unref(card);
|
||||
if (result >= 0) {
|
||||
snd_BUG_ON(info->access);
|
||||
index_offset = snd_ctl_get_ioff(kctl, &info->id);
|
||||
@ -1042,9 +1045,6 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
|
||||
|
||||
if (copy_from_user(&info, _info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
|
||||
if (result < 0)
|
||||
return result;
|
||||
result = snd_ctl_elem_info(ctl, &info);
|
||||
if (result < 0)
|
||||
return result;
|
||||
@ -1088,7 +1088,10 @@ static int snd_ctl_elem_read(struct snd_card *card,
|
||||
|
||||
if (!snd_ctl_skip_validation(&info))
|
||||
fill_remaining_elem_value(control, &info, pattern);
|
||||
ret = kctl->get(kctl, control);
|
||||
ret = snd_power_ref_and_wait(card);
|
||||
if (!ret)
|
||||
ret = kctl->get(kctl, control);
|
||||
snd_power_unref(card);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!snd_ctl_skip_validation(&info) &&
|
||||
@ -1113,10 +1116,6 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
|
||||
if (IS_ERR(control))
|
||||
return PTR_ERR(control);
|
||||
|
||||
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
|
||||
down_read(&card->controls_rwsem);
|
||||
result = snd_ctl_elem_read(card, control);
|
||||
up_read(&card->controls_rwsem);
|
||||
@ -1154,7 +1153,10 @@ static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
|
||||
}
|
||||
|
||||
snd_ctl_build_ioff(&control->id, kctl, index_offset);
|
||||
result = kctl->put(kctl, control);
|
||||
result = snd_power_ref_and_wait(card);
|
||||
if (!result)
|
||||
result = kctl->put(kctl, control);
|
||||
snd_power_unref(card);
|
||||
if (result < 0) {
|
||||
up_write(&card->controls_rwsem);
|
||||
return result;
|
||||
@ -1183,10 +1185,6 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
|
||||
return PTR_ERR(control);
|
||||
|
||||
card = file->card;
|
||||
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
|
||||
result = snd_ctl_elem_write(card, file, control);
|
||||
if (result < 0)
|
||||
goto error;
|
||||
@ -1669,7 +1667,7 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
|
||||
{SNDRV_CTL_TLV_OP_CMD, SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND},
|
||||
};
|
||||
struct snd_kcontrol_volatile *vd = &kctl->vd[snd_ctl_get_ioff(kctl, id)];
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
/* Check support of the request for this element. */
|
||||
for (i = 0; i < ARRAY_SIZE(pairs); ++i) {
|
||||
@ -1687,7 +1685,11 @@ static int call_tlv_handler(struct snd_ctl_file *file, int op_flag,
|
||||
vd->owner != NULL && vd->owner != file)
|
||||
return -EPERM;
|
||||
|
||||
return kctl->tlv.c(kctl, op_flag, size, buf);
|
||||
ret = snd_power_ref_and_wait(file->card);
|
||||
if (!ret)
|
||||
ret = kctl->tlv.c(kctl, op_flag, size, buf);
|
||||
snd_power_unref(file->card);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_tlv_buf(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id,
|
||||
@ -1815,11 +1817,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
|
||||
case SNDRV_CTL_IOCTL_POWER:
|
||||
return -ENOPROTOOPT;
|
||||
case SNDRV_CTL_IOCTL_POWER_STATE:
|
||||
#ifdef CONFIG_PM
|
||||
return put_user(card->power_state, ip) ? -EFAULT : 0;
|
||||
#else
|
||||
return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
|
||||
#endif
|
||||
}
|
||||
down_read(&snd_ioctl_rwsem);
|
||||
list_for_each_entry(p, &snd_control_ioctls, list) {
|
||||
|
@ -96,9 +96,6 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
|
||||
if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
|
||||
goto error;
|
||||
|
||||
err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = snd_ctl_elem_info(ctl, data);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@ -187,7 +184,10 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
|
||||
return -ENOMEM;
|
||||
}
|
||||
info->id = *id;
|
||||
err = kctl->info(kctl, info);
|
||||
err = snd_power_ref_and_wait(card);
|
||||
if (!err)
|
||||
err = kctl->info(kctl, info);
|
||||
snd_power_unref(card);
|
||||
up_read(&card->controls_rwsem);
|
||||
if (err >= 0) {
|
||||
err = info->type;
|
||||
@ -298,9 +298,6 @@ static int ctl_elem_read_user(struct snd_card *card,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = snd_ctl_elem_read(card, data);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@ -326,9 +323,6 @@ static int ctl_elem_write_user(struct snd_ctl_file *file,
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = snd_ctl_elem_write(card, file, data);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
@ -393,11 +393,11 @@ static void snd_ctl_led_dev_release(struct device *dev)
|
||||
* sysfs
|
||||
*/
|
||||
|
||||
static ssize_t show_mode(struct device *dev,
|
||||
static ssize_t mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
|
||||
const char *str;
|
||||
const char *str = NULL;
|
||||
|
||||
switch (led->mode) {
|
||||
case MODE_FOLLOW_MUTE: str = "follow-mute"; break;
|
||||
@ -408,7 +408,8 @@ static ssize_t show_mode(struct device *dev,
|
||||
return sprintf(buf, "%s\n", str);
|
||||
}
|
||||
|
||||
static ssize_t store_mode(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t mode_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
|
||||
@ -437,7 +438,7 @@ static ssize_t store_mode(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_brightness(struct device *dev,
|
||||
static ssize_t brightness_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
|
||||
@ -445,8 +446,8 @@ static ssize_t show_brightness(struct device *dev,
|
||||
return sprintf(buf, "%u\n", ledtrig_audio_get(led->trigger_type));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(mode, 0644, show_mode, store_mode);
|
||||
static DEVICE_ATTR(brightness, 0444, show_brightness, NULL);
|
||||
static DEVICE_ATTR_RW(mode);
|
||||
static DEVICE_ATTR_RO(brightness);
|
||||
|
||||
static struct attribute *snd_ctl_led_dev_attrs[] = {
|
||||
&dev_attr_mode.attr,
|
||||
@ -580,22 +581,25 @@ static ssize_t set_led_id(struct snd_ctl_led_card *led_card, const char *buf, si
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t parse_attach(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t attach_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
|
||||
return set_led_id(led_card, buf, count, true);
|
||||
}
|
||||
|
||||
static ssize_t parse_detach(struct device *dev, struct device_attribute *attr,
|
||||
static ssize_t detach_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
|
||||
return set_led_id(led_card, buf, count, false);
|
||||
}
|
||||
|
||||
static ssize_t ctl_reset(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t reset_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
|
||||
int err;
|
||||
@ -608,8 +612,8 @@ static ssize_t ctl_reset(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ctl_list(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t list_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
|
||||
struct snd_card *card;
|
||||
@ -642,10 +646,10 @@ static ssize_t ctl_list(struct device *dev,
|
||||
return buf2 - buf;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(attach, 0200, NULL, parse_attach);
|
||||
static DEVICE_ATTR(detach, 0200, NULL, parse_detach);
|
||||
static DEVICE_ATTR(reset, 0200, NULL, ctl_reset);
|
||||
static DEVICE_ATTR(list, 0444, ctl_list, NULL);
|
||||
static DEVICE_ATTR_WO(attach);
|
||||
static DEVICE_ATTR_WO(detach);
|
||||
static DEVICE_ATTR_WO(reset);
|
||||
static DEVICE_ATTR_RO(list);
|
||||
|
||||
static struct attribute *snd_ctl_led_card_attrs[] = {
|
||||
&dev_attr_attach.attr,
|
||||
|
@ -195,7 +195,8 @@ static int snd_hwdep_dsp_status(struct snd_hwdep *hw,
|
||||
return -ENXIO;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.dsp_loaded = hw->dsp_loaded;
|
||||
if ((err = hw->ops.dsp_status(hw, &info)) < 0)
|
||||
err = hw->ops.dsp_status(hw, &info);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (copy_to_user(_info, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
@ -500,7 +501,8 @@ static void __init snd_hwdep_proc_init(void)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
|
||||
if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) {
|
||||
entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL);
|
||||
if (entry) {
|
||||
entry->c.text.read = snd_hwdep_proc_read;
|
||||
if (snd_info_register(entry) < 0) {
|
||||
snd_info_free_entry(entry);
|
||||
|
@ -31,7 +31,8 @@ int snd_oss_info_register(int dev, int num, char *string)
|
||||
return -ENXIO;
|
||||
mutex_lock(&strings);
|
||||
if (string == NULL) {
|
||||
if ((x = snd_sndstat_strings[num][dev]) != NULL) {
|
||||
x = snd_sndstat_strings[num][dev];
|
||||
if (x) {
|
||||
kfree(x);
|
||||
x = NULL;
|
||||
}
|
||||
|
@ -220,6 +220,8 @@ int snd_card_new(struct device *parent, int idx, const char *xid,
|
||||
mutex_init(&card->memory_mutex);
|
||||
#ifdef CONFIG_PM
|
||||
init_waitqueue_head(&card->power_sleep);
|
||||
init_waitqueue_head(&card->power_ref_sleep);
|
||||
atomic_set(&card->power_ref, 0);
|
||||
#endif
|
||||
init_waitqueue_head(&card->remove_sleep);
|
||||
card->sync_irq = -1;
|
||||
@ -442,6 +444,7 @@ int snd_card_disconnect(struct snd_card *card)
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
wake_up(&card->power_sleep);
|
||||
snd_power_sync_ref(card);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
@ -662,17 +665,15 @@ void snd_card_set_id(struct snd_card *card, const char *nid)
|
||||
}
|
||||
EXPORT_SYMBOL(snd_card_set_id);
|
||||
|
||||
static ssize_t
|
||||
card_id_show_attr(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_card *card = container_of(dev, struct snd_card, card_dev);
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", card->id);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
card_id_store_attr(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t id_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct snd_card *card = container_of(dev, struct snd_card, card_dev);
|
||||
char buf1[sizeof(card->id)];
|
||||
@ -700,17 +701,16 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr,
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(id, 0644, card_id_show_attr, card_id_store_attr);
|
||||
static DEVICE_ATTR_RW(id);
|
||||
|
||||
static ssize_t
|
||||
card_number_show_attr(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t number_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_card *card = container_of(dev, struct snd_card, card_dev);
|
||||
return scnprintf(buf, PAGE_SIZE, "%i\n", card->number);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(number, 0444, card_number_show_attr, NULL);
|
||||
static DEVICE_ATTR_RO(number);
|
||||
|
||||
static struct attribute *card_dev_attrs[] = {
|
||||
&dev_attr_id.attr,
|
||||
@ -770,7 +770,8 @@ int snd_card_register(struct snd_card *card)
|
||||
card->registered = true;
|
||||
}
|
||||
|
||||
if ((err = snd_device_register_all(card)) < 0)
|
||||
err = snd_device_register_all(card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mutex_lock(&snd_card_mutex);
|
||||
if (snd_cards[card->number]) {
|
||||
@ -813,7 +814,8 @@ static void snd_card_info_read(struct snd_info_entry *entry,
|
||||
|
||||
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
|
||||
mutex_lock(&snd_card_mutex);
|
||||
if ((card = snd_cards[idx]) != NULL) {
|
||||
card = snd_cards[idx];
|
||||
if (card) {
|
||||
count++;
|
||||
snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n",
|
||||
idx,
|
||||
@ -837,7 +839,8 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer)
|
||||
|
||||
for (idx = count = 0; idx < SNDRV_CARDS; idx++) {
|
||||
mutex_lock(&snd_card_mutex);
|
||||
if ((card = snd_cards[idx]) != NULL) {
|
||||
card = snd_cards[idx];
|
||||
if (card) {
|
||||
count++;
|
||||
snd_iprintf(buffer, "%s\n", card->longname);
|
||||
}
|
||||
@ -859,7 +862,8 @@ static void snd_card_module_info_read(struct snd_info_entry *entry,
|
||||
|
||||
for (idx = 0; idx < SNDRV_CARDS; idx++) {
|
||||
mutex_lock(&snd_card_mutex);
|
||||
if ((card = snd_cards[idx]) != NULL)
|
||||
card = snd_cards[idx];
|
||||
if (card)
|
||||
snd_iprintf(buffer, "%2i %s\n",
|
||||
idx, card->module->name);
|
||||
mutex_unlock(&snd_card_mutex);
|
||||
@ -1002,21 +1006,28 @@ EXPORT_SYMBOL(snd_card_file_remove);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
* snd_power_wait - wait until the power-state is changed.
|
||||
* @card: soundcard structure
|
||||
* @power_state: expected power state
|
||||
* snd_power_ref_and_wait - wait until the card gets powered up
|
||||
* @card: soundcard structure
|
||||
*
|
||||
* Waits until the power-state is changed.
|
||||
* Take the power_ref reference count of the given card, and
|
||||
* wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
|
||||
* The refcount is down again while sleeping until power-up, hence this
|
||||
* function can be used for syncing the floating control ops accesses,
|
||||
* typically around calling control ops.
|
||||
*
|
||||
* Return: Zero if successful, or a negative error code.
|
||||
* The caller needs to pull down the refcount via snd_power_unref() later
|
||||
* no matter whether the error is returned from this function or not.
|
||||
*
|
||||
* Return: Zero if successful, or a negative error code.
|
||||
*/
|
||||
int snd_power_wait(struct snd_card *card, unsigned int power_state)
|
||||
int snd_power_ref_and_wait(struct snd_card *card)
|
||||
{
|
||||
wait_queue_entry_t wait;
|
||||
int result = 0;
|
||||
|
||||
snd_power_ref(card);
|
||||
/* fastpath */
|
||||
if (snd_power_get_state(card) == power_state)
|
||||
if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
|
||||
return 0;
|
||||
init_waitqueue_entry(&wait, current);
|
||||
add_wait_queue(&card->power_sleep, &wait);
|
||||
@ -1025,13 +1036,33 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state)
|
||||
result = -ENODEV;
|
||||
break;
|
||||
}
|
||||
if (snd_power_get_state(card) == power_state)
|
||||
if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0)
|
||||
break;
|
||||
snd_power_unref(card);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(30 * HZ);
|
||||
snd_power_ref(card);
|
||||
}
|
||||
remove_wait_queue(&card->power_sleep, &wait);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_power_ref_and_wait);
|
||||
|
||||
/**
|
||||
* snd_power_wait - wait until the card gets powered up (old form)
|
||||
* @card: soundcard structure
|
||||
*
|
||||
* Wait until the card gets powered up to SNDRV_CTL_POWER_D0 state.
|
||||
*
|
||||
* Return: Zero if successful, or a negative error code.
|
||||
*/
|
||||
int snd_power_wait(struct snd_card *card)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snd_power_ref_and_wait(card);
|
||||
snd_power_unref(card);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_power_wait);
|
||||
#endif /* CONFIG_PM */
|
||||
|
@ -15,99 +15,27 @@
|
||||
#include <asm/set_memory.h>
|
||||
#endif
|
||||
#include <sound/memalloc.h>
|
||||
#include "memalloc_local.h"
|
||||
|
||||
/*
|
||||
*
|
||||
* Bus-specific memory allocators
|
||||
*
|
||||
*/
|
||||
static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab);
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
/* allocate the coherent DMA pages */
|
||||
static void snd_malloc_dev_pages(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
gfp_t gfp_flags;
|
||||
|
||||
gfp_flags = GFP_KERNEL
|
||||
| __GFP_COMP /* compound page lets parts be mapped */
|
||||
| __GFP_NORETRY /* don't trigger OOM-killer */
|
||||
| __GFP_NOWARN; /* no stack trace print - this call is non-critical */
|
||||
dmab->area = dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr,
|
||||
gfp_flags);
|
||||
#ifdef CONFIG_X86
|
||||
if (dmab->area && dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC)
|
||||
set_memory_wc((unsigned long)dmab->area,
|
||||
PAGE_ALIGN(size) >> PAGE_SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* free the coherent DMA pages */
|
||||
static void snd_free_dev_pages(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
#ifdef CONFIG_X86
|
||||
if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC)
|
||||
set_memory_wb((unsigned long)dmab->area,
|
||||
PAGE_ALIGN(dmab->bytes) >> PAGE_SHIFT);
|
||||
#endif
|
||||
dma_free_coherent(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_ALLOCATOR
|
||||
/**
|
||||
* snd_malloc_dev_iram - allocate memory from on-chip internal ram
|
||||
* @dmab: buffer allocation record to store the allocated data
|
||||
* @size: number of bytes to allocate from the iram
|
||||
*
|
||||
* This function requires iram phandle provided via of_node
|
||||
*/
|
||||
static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
struct device *dev = dmab->dev.dev;
|
||||
struct gen_pool *pool = NULL;
|
||||
|
||||
dmab->area = NULL;
|
||||
dmab->addr = 0;
|
||||
|
||||
if (dev->of_node)
|
||||
pool = of_gen_pool_get(dev->of_node, "iram", 0);
|
||||
|
||||
if (!pool)
|
||||
return;
|
||||
|
||||
/* Assign the pool into private_data field */
|
||||
dmab->private_data = pool;
|
||||
|
||||
dmab->area = gen_pool_dma_alloc_align(pool, size, &dmab->addr,
|
||||
PAGE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_free_dev_iram - free allocated specific memory from on-chip internal ram
|
||||
* @dmab: buffer allocation record to store the allocated data
|
||||
*/
|
||||
static void snd_free_dev_iram(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
struct gen_pool *pool = dmab->private_data;
|
||||
|
||||
if (pool && dmab->area)
|
||||
gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
|
||||
}
|
||||
#endif /* CONFIG_GENERIC_ALLOCATOR */
|
||||
#endif /* CONFIG_HAS_DMA */
|
||||
|
||||
/*
|
||||
*
|
||||
* ALSA generic memory management
|
||||
*
|
||||
*/
|
||||
|
||||
static inline gfp_t snd_mem_get_gfp_flags(const struct device *dev,
|
||||
/* a cast to gfp flag from the dev pointer; for CONTINUOUS and VMALLOC types */
|
||||
static inline gfp_t snd_mem_get_gfp_flags(const struct snd_dma_buffer *dmab,
|
||||
gfp_t default_gfp)
|
||||
{
|
||||
if (!dev)
|
||||
if (!dmab->dev.dev)
|
||||
return default_gfp;
|
||||
else
|
||||
return (__force gfp_t)(unsigned long)dev;
|
||||
return (__force gfp_t)(unsigned long)dmab->dev.dev;
|
||||
}
|
||||
|
||||
static int __snd_dma_alloc_pages(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
|
||||
|
||||
if (WARN_ON_ONCE(!ops || !ops->alloc))
|
||||
return -EINVAL;
|
||||
return ops->alloc(dmab, size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,7 +54,7 @@ static inline gfp_t snd_mem_get_gfp_flags(const struct device *dev,
|
||||
int snd_dma_alloc_pages(int type, struct device *device, size_t size,
|
||||
struct snd_dma_buffer *dmab)
|
||||
{
|
||||
gfp_t gfp;
|
||||
int err;
|
||||
|
||||
if (WARN_ON(!size))
|
||||
return -ENXIO;
|
||||
@ -140,43 +68,10 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
|
||||
dmab->area = NULL;
|
||||
dmab->addr = 0;
|
||||
dmab->private_data = NULL;
|
||||
switch (type) {
|
||||
case SNDRV_DMA_TYPE_CONTINUOUS:
|
||||
gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL);
|
||||
dmab->area = alloc_pages_exact(size, gfp);
|
||||
break;
|
||||
case SNDRV_DMA_TYPE_VMALLOC:
|
||||
gfp = snd_mem_get_gfp_flags(device, GFP_KERNEL | __GFP_HIGHMEM);
|
||||
dmab->area = __vmalloc(size, gfp);
|
||||
break;
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
#ifdef CONFIG_GENERIC_ALLOCATOR
|
||||
case SNDRV_DMA_TYPE_DEV_IRAM:
|
||||
snd_malloc_dev_iram(dmab, size);
|
||||
if (dmab->area)
|
||||
break;
|
||||
/* Internal memory might have limited size and no enough space,
|
||||
* so if we fail to malloc, try to fetch memory traditionally.
|
||||
*/
|
||||
dmab->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
fallthrough;
|
||||
#endif /* CONFIG_GENERIC_ALLOCATOR */
|
||||
case SNDRV_DMA_TYPE_DEV:
|
||||
case SNDRV_DMA_TYPE_DEV_UC:
|
||||
snd_malloc_dev_pages(dmab, size);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
case SNDRV_DMA_TYPE_DEV_SG:
|
||||
case SNDRV_DMA_TYPE_DEV_UC_SG:
|
||||
snd_malloc_sgbuf_pages(device, size, dmab, NULL);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
pr_err("snd-malloc: invalid device type %d\n", type);
|
||||
return -ENXIO;
|
||||
}
|
||||
if (! dmab->area)
|
||||
err = __snd_dma_alloc_pages(dmab, size);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!dmab->area)
|
||||
return -ENOMEM;
|
||||
dmab->bytes = size;
|
||||
return 0;
|
||||
@ -217,7 +112,6 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size,
|
||||
}
|
||||
EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
|
||||
|
||||
|
||||
/**
|
||||
* snd_dma_free_pages - release the allocated buffer
|
||||
* @dmab: the buffer allocation record to release
|
||||
@ -226,32 +120,288 @@ EXPORT_SYMBOL(snd_dma_alloc_pages_fallback);
|
||||
*/
|
||||
void snd_dma_free_pages(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
switch (dmab->dev.type) {
|
||||
case SNDRV_DMA_TYPE_CONTINUOUS:
|
||||
free_pages_exact(dmab->area, dmab->bytes);
|
||||
break;
|
||||
case SNDRV_DMA_TYPE_VMALLOC:
|
||||
vfree(dmab->area);
|
||||
break;
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
#ifdef CONFIG_GENERIC_ALLOCATOR
|
||||
case SNDRV_DMA_TYPE_DEV_IRAM:
|
||||
snd_free_dev_iram(dmab);
|
||||
break;
|
||||
#endif /* CONFIG_GENERIC_ALLOCATOR */
|
||||
case SNDRV_DMA_TYPE_DEV:
|
||||
case SNDRV_DMA_TYPE_DEV_UC:
|
||||
snd_free_dev_pages(dmab);
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
case SNDRV_DMA_TYPE_DEV_SG:
|
||||
case SNDRV_DMA_TYPE_DEV_UC_SG:
|
||||
snd_free_sgbuf_pages(dmab);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
pr_err("snd-malloc: invalid device type %d\n", dmab->dev.type);
|
||||
}
|
||||
const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
|
||||
|
||||
if (ops && ops->free)
|
||||
ops->free(dmab);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_dma_free_pages);
|
||||
|
||||
/**
|
||||
* snd_dma_buffer_mmap - perform mmap of the given DMA buffer
|
||||
* @dmab: buffer allocation information
|
||||
* @area: VM area information
|
||||
*/
|
||||
int snd_dma_buffer_mmap(struct snd_dma_buffer *dmab,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
|
||||
|
||||
if (ops && ops->mmap)
|
||||
return ops->mmap(dmab, area);
|
||||
else
|
||||
return -ENOENT;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_dma_buffer_mmap);
|
||||
|
||||
/**
|
||||
* snd_sgbuf_get_addr - return the physical address at the corresponding offset
|
||||
* @dmab: buffer allocation information
|
||||
* @offset: offset in the ring buffer
|
||||
*/
|
||||
dma_addr_t snd_sgbuf_get_addr(struct snd_dma_buffer *dmab, size_t offset)
|
||||
{
|
||||
const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
|
||||
|
||||
if (ops && ops->get_addr)
|
||||
return ops->get_addr(dmab, offset);
|
||||
else
|
||||
return dmab->addr + offset;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_sgbuf_get_addr);
|
||||
|
||||
/**
|
||||
* snd_sgbuf_get_page - return the physical page at the corresponding offset
|
||||
* @dmab: buffer allocation information
|
||||
* @offset: offset in the ring buffer
|
||||
*/
|
||||
struct page *snd_sgbuf_get_page(struct snd_dma_buffer *dmab, size_t offset)
|
||||
{
|
||||
const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
|
||||
|
||||
if (ops && ops->get_page)
|
||||
return ops->get_page(dmab, offset);
|
||||
else
|
||||
return virt_to_page(dmab->area + offset);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_sgbuf_get_page);
|
||||
|
||||
/**
|
||||
* snd_sgbuf_get_chunk_size - compute the max chunk size with continuous pages
|
||||
* on sg-buffer
|
||||
* @dmab: buffer allocation information
|
||||
* @ofs: offset in the ring buffer
|
||||
* @size: the requested size
|
||||
*/
|
||||
unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
|
||||
unsigned int ofs, unsigned int size)
|
||||
{
|
||||
const struct snd_malloc_ops *ops = snd_dma_get_ops(dmab);
|
||||
|
||||
if (ops && ops->get_chunk_size)
|
||||
return ops->get_chunk_size(dmab, ofs, size);
|
||||
else
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
|
||||
|
||||
/*
|
||||
* Continuous pages allocator
|
||||
*/
|
||||
static int snd_dma_continuous_alloc(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
gfp_t gfp = snd_mem_get_gfp_flags(dmab, GFP_KERNEL);
|
||||
|
||||
dmab->area = alloc_pages_exact(size, gfp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_dma_continuous_free(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
free_pages_exact(dmab->area, dmab->bytes);
|
||||
}
|
||||
|
||||
static int snd_dma_continuous_mmap(struct snd_dma_buffer *dmab,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
return remap_pfn_range(area, area->vm_start,
|
||||
dmab->addr >> PAGE_SHIFT,
|
||||
area->vm_end - area->vm_start,
|
||||
area->vm_page_prot);
|
||||
}
|
||||
|
||||
static const struct snd_malloc_ops snd_dma_continuous_ops = {
|
||||
.alloc = snd_dma_continuous_alloc,
|
||||
.free = snd_dma_continuous_free,
|
||||
.mmap = snd_dma_continuous_mmap,
|
||||
};
|
||||
|
||||
/*
|
||||
* VMALLOC allocator
|
||||
*/
|
||||
static int snd_dma_vmalloc_alloc(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
gfp_t gfp = snd_mem_get_gfp_flags(dmab, GFP_KERNEL | __GFP_HIGHMEM);
|
||||
|
||||
dmab->area = __vmalloc(size, gfp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_dma_vmalloc_free(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
vfree(dmab->area);
|
||||
}
|
||||
|
||||
static int snd_dma_vmalloc_mmap(struct snd_dma_buffer *dmab,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
return remap_vmalloc_range(area, dmab->area, 0);
|
||||
}
|
||||
|
||||
static dma_addr_t snd_dma_vmalloc_get_addr(struct snd_dma_buffer *dmab,
|
||||
size_t offset)
|
||||
{
|
||||
return page_to_phys(vmalloc_to_page(dmab->area + offset)) +
|
||||
offset % PAGE_SIZE;
|
||||
}
|
||||
|
||||
static struct page *snd_dma_vmalloc_get_page(struct snd_dma_buffer *dmab,
|
||||
size_t offset)
|
||||
{
|
||||
return vmalloc_to_page(dmab->area + offset);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
snd_dma_vmalloc_get_chunk_size(struct snd_dma_buffer *dmab,
|
||||
unsigned int ofs, unsigned int size)
|
||||
{
|
||||
ofs %= PAGE_SIZE;
|
||||
size += ofs;
|
||||
if (size > PAGE_SIZE)
|
||||
size = PAGE_SIZE;
|
||||
return size - ofs;
|
||||
}
|
||||
|
||||
static const struct snd_malloc_ops snd_dma_vmalloc_ops = {
|
||||
.alloc = snd_dma_vmalloc_alloc,
|
||||
.free = snd_dma_vmalloc_free,
|
||||
.mmap = snd_dma_vmalloc_mmap,
|
||||
.get_addr = snd_dma_vmalloc_get_addr,
|
||||
.get_page = snd_dma_vmalloc_get_page,
|
||||
.get_chunk_size = snd_dma_vmalloc_get_chunk_size,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
/*
|
||||
* IRAM allocator
|
||||
*/
|
||||
#ifdef CONFIG_GENERIC_ALLOCATOR
|
||||
static int snd_dma_iram_alloc(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
struct device *dev = dmab->dev.dev;
|
||||
struct gen_pool *pool;
|
||||
|
||||
if (dev->of_node) {
|
||||
pool = of_gen_pool_get(dev->of_node, "iram", 0);
|
||||
/* Assign the pool into private_data field */
|
||||
dmab->private_data = pool;
|
||||
|
||||
dmab->area = gen_pool_dma_alloc_align(pool, size, &dmab->addr,
|
||||
PAGE_SIZE);
|
||||
if (dmab->area)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Internal memory might have limited size and no enough space,
|
||||
* so if we fail to malloc, try to fetch memory traditionally.
|
||||
*/
|
||||
dmab->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||
return __snd_dma_alloc_pages(dmab, size);
|
||||
}
|
||||
|
||||
static void snd_dma_iram_free(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
struct gen_pool *pool = dmab->private_data;
|
||||
|
||||
if (pool && dmab->area)
|
||||
gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes);
|
||||
}
|
||||
|
||||
static int snd_dma_iram_mmap(struct snd_dma_buffer *dmab,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
|
||||
return remap_pfn_range(area, area->vm_start,
|
||||
dmab->addr >> PAGE_SHIFT,
|
||||
area->vm_end - area->vm_start,
|
||||
area->vm_page_prot);
|
||||
}
|
||||
|
||||
static const struct snd_malloc_ops snd_dma_iram_ops = {
|
||||
.alloc = snd_dma_iram_alloc,
|
||||
.free = snd_dma_iram_free,
|
||||
.mmap = snd_dma_iram_mmap,
|
||||
};
|
||||
#endif /* CONFIG_GENERIC_ALLOCATOR */
|
||||
|
||||
/*
|
||||
* Coherent device pages allocator
|
||||
*/
|
||||
static int snd_dma_dev_alloc(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
gfp_t gfp_flags;
|
||||
|
||||
gfp_flags = GFP_KERNEL
|
||||
| __GFP_COMP /* compound page lets parts be mapped */
|
||||
| __GFP_NORETRY /* don't trigger OOM-killer */
|
||||
| __GFP_NOWARN; /* no stack trace print - this call is non-critical */
|
||||
dmab->area = dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr,
|
||||
gfp_flags);
|
||||
#ifdef CONFIG_X86
|
||||
if (dmab->area && dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC)
|
||||
set_memory_wc((unsigned long)dmab->area,
|
||||
PAGE_ALIGN(size) >> PAGE_SHIFT);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_dma_dev_free(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
#ifdef CONFIG_X86
|
||||
if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC)
|
||||
set_memory_wb((unsigned long)dmab->area,
|
||||
PAGE_ALIGN(dmab->bytes) >> PAGE_SHIFT);
|
||||
#endif
|
||||
dma_free_coherent(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
|
||||
}
|
||||
|
||||
static int snd_dma_dev_mmap(struct snd_dma_buffer *dmab,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
return dma_mmap_coherent(dmab->dev.dev, area,
|
||||
dmab->area, dmab->addr, dmab->bytes);
|
||||
}
|
||||
|
||||
static const struct snd_malloc_ops snd_dma_dev_ops = {
|
||||
.alloc = snd_dma_dev_alloc,
|
||||
.free = snd_dma_dev_free,
|
||||
.mmap = snd_dma_dev_mmap,
|
||||
};
|
||||
#endif /* CONFIG_HAS_DMA */
|
||||
|
||||
/*
|
||||
* Entry points
|
||||
*/
|
||||
static const struct snd_malloc_ops *dma_ops[] = {
|
||||
[SNDRV_DMA_TYPE_CONTINUOUS] = &snd_dma_continuous_ops,
|
||||
[SNDRV_DMA_TYPE_VMALLOC] = &snd_dma_vmalloc_ops,
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
[SNDRV_DMA_TYPE_DEV] = &snd_dma_dev_ops,
|
||||
[SNDRV_DMA_TYPE_DEV_UC] = &snd_dma_dev_ops,
|
||||
#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_UC_SG] = &snd_dma_sg_ops,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct snd_malloc_ops *snd_dma_get_ops(struct snd_dma_buffer *dmab)
|
||||
{
|
||||
if (WARN_ON_ONCE(dmab->dev.type <= SNDRV_DMA_TYPE_UNKNOWN ||
|
||||
dmab->dev.type >= ARRAY_SIZE(dma_ops)))
|
||||
return NULL;
|
||||
return dma_ops[dmab->dev.type];
|
||||
}
|
||||
|
19
sound/core/memalloc_local.h
Normal file
19
sound/core/memalloc_local.h
Normal file
@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#ifndef __MEMALLOC_LOCAL_H
|
||||
#define __MEMALLOC_LOCAL_H
|
||||
|
||||
struct snd_malloc_ops {
|
||||
int (*alloc)(struct snd_dma_buffer *dmab, size_t size);
|
||||
void (*free)(struct snd_dma_buffer *dmab);
|
||||
dma_addr_t (*get_addr)(struct snd_dma_buffer *dmab, size_t offset);
|
||||
struct page *(*get_page)(struct snd_dma_buffer *dmab, size_t offset);
|
||||
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);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
extern const struct snd_malloc_ops snd_dma_sg_ops;
|
||||
#endif
|
||||
|
||||
#endif /* __MEMALLOC_LOCAL_H */
|
@ -185,7 +185,8 @@ static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)
|
||||
if (mixer->put_recsrc && mixer->get_recsrc) { /* exclusive */
|
||||
int err;
|
||||
unsigned int index;
|
||||
if ((err = mixer->get_recsrc(fmixer, &index)) < 0)
|
||||
err = mixer->get_recsrc(fmixer, &index);
|
||||
if (err < 0)
|
||||
return err;
|
||||
result = 1 << index;
|
||||
} else {
|
||||
@ -517,7 +518,8 @@ static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,
|
||||
if (numid == ID_UNKNOWN)
|
||||
return;
|
||||
down_read(&card->controls_rwsem);
|
||||
if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
|
||||
kctl = snd_ctl_find_numid(card, numid);
|
||||
if (!kctl) {
|
||||
up_read(&card->controls_rwsem);
|
||||
return;
|
||||
}
|
||||
@ -555,7 +557,8 @@ static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
|
||||
if (numid == ID_UNKNOWN)
|
||||
return;
|
||||
down_read(&card->controls_rwsem);
|
||||
if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
|
||||
kctl = snd_ctl_find_numid(card, numid);
|
||||
if (!kctl) {
|
||||
up_read(&card->controls_rwsem);
|
||||
return;
|
||||
}
|
||||
@ -620,7 +623,8 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
|
||||
if (numid == ID_UNKNOWN)
|
||||
return;
|
||||
down_read(&card->controls_rwsem);
|
||||
if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
|
||||
kctl = snd_ctl_find_numid(card, numid);
|
||||
if (!kctl) {
|
||||
up_read(&card->controls_rwsem);
|
||||
return;
|
||||
}
|
||||
@ -636,7 +640,8 @@ static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
|
||||
uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
|
||||
if (uinfo->count > 1)
|
||||
uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
|
||||
if ((res = kctl->put(kctl, uctl)) < 0)
|
||||
res = kctl->put(kctl, uctl);
|
||||
if (res < 0)
|
||||
goto __unalloc;
|
||||
if (res > 0)
|
||||
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
|
||||
@ -661,7 +666,8 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
|
||||
if (numid == ID_UNKNOWN)
|
||||
return;
|
||||
down_read(&card->controls_rwsem);
|
||||
if ((kctl = snd_ctl_find_numid(card, numid)) == NULL) {
|
||||
kctl = snd_ctl_find_numid(card, numid);
|
||||
if (!kctl) {
|
||||
up_read(&card->controls_rwsem);
|
||||
return;
|
||||
}
|
||||
@ -681,7 +687,8 @@ static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
|
||||
} else {
|
||||
uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
|
||||
}
|
||||
if ((res = kctl->put(kctl, uctl)) < 0)
|
||||
res = kctl->put(kctl, uctl);
|
||||
if (res < 0)
|
||||
goto __unalloc;
|
||||
if (res > 0)
|
||||
snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
|
||||
@ -809,9 +816,11 @@ static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
|
||||
err = -ENOENT;
|
||||
goto __unlock;
|
||||
}
|
||||
if ((err = kctl->info(kctl, uinfo)) < 0)
|
||||
err = kctl->info(kctl, uinfo);
|
||||
if (err < 0)
|
||||
goto __unlock;
|
||||
if ((err = kctl->get(kctl, uctl)) < 0)
|
||||
err = kctl->get(kctl, uctl);
|
||||
if (err < 0)
|
||||
goto __unlock;
|
||||
for (idx = 0; idx < 32; idx++) {
|
||||
if (!(mixer->mask_recsrc & (1 << idx)))
|
||||
@ -860,7 +869,8 @@ static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned
|
||||
err = -ENOENT;
|
||||
goto __unlock;
|
||||
}
|
||||
if ((err = kctl->info(kctl, uinfo)) < 0)
|
||||
err = kctl->info(kctl, uinfo);
|
||||
if (err < 0)
|
||||
goto __unlock;
|
||||
for (idx = 0; idx < 32; idx++) {
|
||||
if (!(mixer->mask_recsrc & (1 << idx)))
|
||||
@ -915,7 +925,8 @@ static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *sl
|
||||
up_read(&card->controls_rwsem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if ((err = kcontrol->info(kcontrol, info)) < 0) {
|
||||
err = kcontrol->info(kcontrol, info);
|
||||
if (err < 0) {
|
||||
up_read(&card->controls_rwsem);
|
||||
kfree(info);
|
||||
return err;
|
||||
@ -1036,7 +1047,10 @@ static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,
|
||||
if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
|
||||
return 0;
|
||||
down_read(&mixer->card->controls_rwsem);
|
||||
if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
|
||||
kctl = NULL;
|
||||
if (!ptr->index)
|
||||
kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
|
||||
if (kctl) {
|
||||
struct snd_ctl_elem_info *uinfo;
|
||||
|
||||
uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);
|
||||
@ -1343,9 +1357,10 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
|
||||
if (mixer == NULL)
|
||||
return -ENOMEM;
|
||||
mutex_init(&mixer->reg_mutex);
|
||||
if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
|
||||
card, 0,
|
||||
&snd_mixer_oss_f_ops, card)) < 0) {
|
||||
err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
|
||||
card, 0,
|
||||
&snd_mixer_oss_f_ops, card);
|
||||
if (err < 0) {
|
||||
dev_err(card->dev,
|
||||
"unable to register OSS mixer device %i:%i\n",
|
||||
card->number, 0);
|
||||
|
@ -955,9 +955,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
|
||||
if (!direct) {
|
||||
/* add necessary plugins */
|
||||
snd_pcm_oss_plugin_clear(substream);
|
||||
if ((err = snd_pcm_plug_format_plugins(substream,
|
||||
params,
|
||||
sparams)) < 0) {
|
||||
err = snd_pcm_plug_format_plugins(substream, params, sparams);
|
||||
if (err < 0) {
|
||||
pcm_dbg(substream->pcm,
|
||||
"snd_pcm_plug_format_plugins failed: %i\n", err);
|
||||
snd_pcm_oss_plugin_clear(substream);
|
||||
@ -965,7 +964,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
|
||||
}
|
||||
if (runtime->oss.plugin_first) {
|
||||
struct snd_pcm_plugin *plugin;
|
||||
if ((err = snd_pcm_plugin_build_io(substream, sparams, &plugin)) < 0) {
|
||||
err = snd_pcm_plugin_build_io(substream, sparams, &plugin);
|
||||
if (err < 0) {
|
||||
pcm_dbg(substream->pcm,
|
||||
"snd_pcm_plugin_build_io failed: %i\n", err);
|
||||
snd_pcm_oss_plugin_clear(substream);
|
||||
@ -1011,7 +1011,8 @@ static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
|
||||
sw_params->silence_size = frames;
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
|
||||
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params);
|
||||
if (err < 0) {
|
||||
pcm_dbg(substream->pcm, "SW_PARAMS failed: %i\n", err);
|
||||
goto failure;
|
||||
}
|
||||
@ -1573,7 +1574,8 @@ static int snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
|
||||
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
if (substream != NULL) {
|
||||
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
|
||||
err = snd_pcm_oss_make_ready(substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
|
||||
}
|
||||
@ -1645,7 +1647,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
runtime = substream->runtime;
|
||||
if (atomic_read(&substream->mmap_count))
|
||||
goto __direct;
|
||||
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
|
||||
err = snd_pcm_oss_make_ready(substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
atomic_inc(&runtime->oss.rw_ref);
|
||||
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
|
||||
@ -1711,7 +1714,8 @@ unlock:
|
||||
|
||||
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
|
||||
if (substream != NULL) {
|
||||
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
|
||||
err = snd_pcm_oss_make_ready(substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
runtime = substream->runtime;
|
||||
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
|
||||
@ -1758,7 +1762,8 @@ static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
struct snd_pcm_substream *substream;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
|
||||
err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return substream->runtime->oss.rate;
|
||||
}
|
||||
@ -1795,7 +1800,8 @@ static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
struct snd_pcm_substream *substream;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
|
||||
err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return substream->runtime->oss.channels;
|
||||
}
|
||||
@ -1805,7 +1811,8 @@ static int snd_pcm_oss_get_block_size(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
struct snd_pcm_substream *substream;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
|
||||
err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return substream->runtime->oss.period_bytes;
|
||||
}
|
||||
@ -1820,7 +1827,8 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
const struct snd_mask *format_mask;
|
||||
int fmt;
|
||||
|
||||
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
|
||||
err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (atomic_read(&substream->mmap_count))
|
||||
direct = 1;
|
||||
@ -1890,7 +1898,8 @@ static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
struct snd_pcm_substream *substream;
|
||||
int err;
|
||||
|
||||
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
|
||||
err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return substream->runtime->oss.format;
|
||||
}
|
||||
@ -2050,11 +2059,13 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
|
||||
csubstream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
|
||||
|
||||
if (psubstream) {
|
||||
if ((err = snd_pcm_oss_make_ready(psubstream)) < 0)
|
||||
err = snd_pcm_oss_make_ready(psubstream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (csubstream) {
|
||||
if ((err = snd_pcm_oss_make_ready(csubstream)) < 0)
|
||||
err = snd_pcm_oss_make_ready(csubstream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (psubstream) {
|
||||
@ -2141,7 +2152,8 @@ static int snd_pcm_oss_get_odelay(struct snd_pcm_oss_file *pcm_oss_file)
|
||||
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
|
||||
if (substream == NULL)
|
||||
return -EINVAL;
|
||||
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
|
||||
err = snd_pcm_oss_make_ready(substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
runtime = substream->runtime;
|
||||
if (runtime->oss.params || runtime->oss.prepare)
|
||||
@ -2168,7 +2180,8 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
|
||||
substream = pcm_oss_file->streams[stream];
|
||||
if (substream == NULL)
|
||||
return -EINVAL;
|
||||
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
|
||||
err = snd_pcm_oss_make_ready(substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
runtime = substream->runtime;
|
||||
if (runtime->oss.params || runtime->oss.prepare) {
|
||||
@ -2239,9 +2252,11 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
|
||||
return -EINVAL;
|
||||
runtime = substream->runtime;
|
||||
|
||||
if (runtime->oss.params &&
|
||||
(err = snd_pcm_oss_change_params(substream, false)) < 0)
|
||||
return err;
|
||||
if (runtime->oss.params) {
|
||||
err = snd_pcm_oss_change_params(substream, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
info.fragsize = runtime->oss.period_bytes;
|
||||
info.fragstotal = runtime->periods;
|
||||
@ -2601,7 +2616,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
|
||||
case SNDCTL_DSP_SPEED:
|
||||
if (get_user(res, p))
|
||||
return -EFAULT;
|
||||
if ((res = snd_pcm_oss_set_rate(pcm_oss_file, res))<0)
|
||||
res = snd_pcm_oss_set_rate(pcm_oss_file, res);
|
||||
if (res < 0)
|
||||
return res;
|
||||
return put_user(res, p);
|
||||
case SOUND_PCM_READ_RATE:
|
||||
@ -2613,7 +2629,8 @@ static long snd_pcm_oss_ioctl(struct file *file, unsigned int cmd, unsigned long
|
||||
if (get_user(res, p))
|
||||
return -EFAULT;
|
||||
res = res > 0 ? 2 : 1;
|
||||
if ((res = snd_pcm_oss_set_channels(pcm_oss_file, res)) < 0)
|
||||
res = snd_pcm_oss_set_channels(pcm_oss_file, res);
|
||||
if (res < 0)
|
||||
return res;
|
||||
return put_user(--res, p);
|
||||
case SNDCTL_DSP_GETBLKSIZE:
|
||||
@ -2829,7 +2846,8 @@ static __poll_t snd_pcm_oss_poll(struct file *file, poll_table * wait)
|
||||
snd_pcm_state_t ostate;
|
||||
poll_wait(file, &runtime->sleep, wait);
|
||||
snd_pcm_stream_lock_irq(csubstream);
|
||||
if ((ostate = runtime->status->state) != SNDRV_PCM_STATE_RUNNING ||
|
||||
ostate = runtime->status->state;
|
||||
if (ostate != SNDRV_PCM_STATE_RUNNING ||
|
||||
snd_pcm_oss_capture_ready(csubstream))
|
||||
mask |= EPOLLIN | EPOLLRDNORM;
|
||||
snd_pcm_stream_unlock_irq(csubstream);
|
||||
@ -3043,7 +3061,8 @@ static void snd_pcm_oss_proc_init(struct snd_pcm *pcm)
|
||||
struct snd_pcm_str *pstr = &pcm->streams[stream];
|
||||
if (pstr->substream_count == 0)
|
||||
continue;
|
||||
if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) {
|
||||
entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root);
|
||||
if (entry) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->mode = S_IFREG | 0644;
|
||||
entry->c.text.read = snd_pcm_oss_proc_read;
|
||||
@ -3191,7 +3210,8 @@ static int __init alsa_pcm_oss_init(void)
|
||||
adsp_map[i] = 1;
|
||||
}
|
||||
}
|
||||
if ((err = snd_pcm_notify(&snd_pcm_oss_notify, 0)) < 0)
|
||||
err = snd_pcm_notify(&snd_pcm_oss_notify, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
@ -59,7 +59,8 @@ static int snd_pcm_plugin_alloc(struct snd_pcm_plugin *plugin, snd_pcm_uframes_t
|
||||
} else {
|
||||
format = &plugin->dst_format;
|
||||
}
|
||||
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
|
||||
width = snd_pcm_format_physical_width(format->format);
|
||||
if (width < 0)
|
||||
return width;
|
||||
size = frames * format->channels * width;
|
||||
if (snd_BUG_ON(size % 8))
|
||||
@ -572,7 +573,8 @@ snd_pcm_sframes_t snd_pcm_plug_client_channels_buf(struct snd_pcm_substream *plu
|
||||
}
|
||||
v = plugin->buf_channels;
|
||||
*channels = v;
|
||||
if ((width = snd_pcm_format_physical_width(format->format)) < 0)
|
||||
width = snd_pcm_format_physical_width(format->format);
|
||||
if (width < 0)
|
||||
return width;
|
||||
nchannels = format->channels;
|
||||
if (snd_BUG_ON(plugin->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
|
||||
@ -600,16 +602,17 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
|
||||
while (plugin) {
|
||||
if (frames <= 0)
|
||||
return frames;
|
||||
if ((next = plugin->next) != NULL) {
|
||||
next = plugin->next;
|
||||
if (next) {
|
||||
snd_pcm_sframes_t frames1 = frames;
|
||||
if (plugin->dst_frames) {
|
||||
frames1 = plugin->dst_frames(plugin, frames);
|
||||
if (frames1 <= 0)
|
||||
return frames1;
|
||||
}
|
||||
if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
|
||||
err = next->client_channels(next, frames1, &dst_channels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (err != frames1) {
|
||||
frames = err;
|
||||
if (plugin->src_frames) {
|
||||
@ -621,7 +624,8 @@ snd_pcm_sframes_t snd_pcm_plug_write_transfer(struct snd_pcm_substream *plug, st
|
||||
} else
|
||||
dst_channels = NULL;
|
||||
pdprintf("write plugin: %s, %li\n", plugin->name, frames);
|
||||
if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
|
||||
frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
src_channels = dst_channels;
|
||||
plugin = next;
|
||||
@ -643,16 +647,18 @@ snd_pcm_sframes_t snd_pcm_plug_read_transfer(struct snd_pcm_substream *plug, str
|
||||
src_channels = NULL;
|
||||
plugin = snd_pcm_plug_first(plug);
|
||||
while (plugin && frames > 0) {
|
||||
if ((next = plugin->next) != NULL) {
|
||||
if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) {
|
||||
next = plugin->next;
|
||||
if (next) {
|
||||
err = plugin->client_channels(plugin, frames, &dst_channels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
frames = err;
|
||||
} else {
|
||||
dst_channels = dst_channels_final;
|
||||
}
|
||||
pdprintf("read plugin: %s, %li\n", plugin->name, frames);
|
||||
if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0)
|
||||
frames = plugin->transfer(plugin, src_channels, dst_channels, frames);
|
||||
if (frames < 0)
|
||||
return frames;
|
||||
plugin = next;
|
||||
src_channels = dst_channels;
|
||||
|
@ -1004,7 +1004,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
|
||||
substream->pstr->substream_opened--;
|
||||
}
|
||||
|
||||
static ssize_t show_pcm_class(struct device *dev,
|
||||
static ssize_t pcm_class_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
|
||||
@ -1024,7 +1024,7 @@ static ssize_t show_pcm_class(struct device *dev,
|
||||
return sprintf(buf, "%s\n", str);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(pcm_class, 0444, show_pcm_class, NULL);
|
||||
static DEVICE_ATTR_RO(pcm_class);
|
||||
static struct attribute *pcm_dev_attrs[] = {
|
||||
&dev_attr_pcm_class.attr,
|
||||
NULL
|
||||
|
@ -239,7 +239,8 @@ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_runtime *runtime;
|
||||
int err;
|
||||
|
||||
if (! (runtime = substream->runtime))
|
||||
runtime = substream->runtime;
|
||||
if (!runtime)
|
||||
return -ENOTTY;
|
||||
|
||||
data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
@ -343,7 +344,8 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream,
|
||||
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
|
||||
return -EBADFD;
|
||||
|
||||
if ((ch = substream->runtime->channels) > 128)
|
||||
ch = substream->runtime->channels;
|
||||
if (ch > 128)
|
||||
return -EINVAL;
|
||||
if (get_user(buf, &data32->bufs) ||
|
||||
get_user(frames, &data32->frames))
|
||||
|
@ -1778,27 +1778,38 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
|
||||
EXPORT_SYMBOL(snd_pcm_lib_ioctl);
|
||||
|
||||
/**
|
||||
* snd_pcm_period_elapsed - update the pcm status for the next period
|
||||
* @substream: the pcm substream instance
|
||||
* snd_pcm_period_elapsed_under_stream_lock() - update the status of runtime for the next period
|
||||
* under acquired lock of PCM substream.
|
||||
* @substream: the instance of pcm substream.
|
||||
*
|
||||
* This function is called from the interrupt handler when the
|
||||
* PCM has processed the period size. It will update the current
|
||||
* pointer, wake up sleepers, etc.
|
||||
* This function is called when the batch of audio data frames as the same size as the period of
|
||||
* buffer is already processed in audio data transmission.
|
||||
*
|
||||
* Even if more than one periods have elapsed since the last call, you
|
||||
* have to call this only once.
|
||||
* The call of function updates the status of runtime with the latest position of audio data
|
||||
* transmission, checks overrun and underrun over buffer, awaken user processes from waiting for
|
||||
* available audio data frames, sampling audio timestamp, and performs stop or drain the PCM
|
||||
* substream according to configured threshold.
|
||||
*
|
||||
* The function is intended to use for the case that PCM driver operates audio data frames under
|
||||
* acquired lock of PCM substream; e.g. in callback of any operation of &snd_pcm_ops in process
|
||||
* context. In any interrupt context, it's preferrable to use ``snd_pcm_period_elapsed()`` instead
|
||||
* since lock of PCM substream should be acquired in advance.
|
||||
*
|
||||
* Developer should pay enough attention that some callbacks in &snd_pcm_ops are done by the call of
|
||||
* function:
|
||||
*
|
||||
* - .pointer - to retrieve current position of audio data transmission by frame count or XRUN state.
|
||||
* - .trigger - with SNDRV_PCM_TRIGGER_STOP at XRUN or DRAINING state.
|
||||
* - .get_time_info - to retrieve audio time stamp if needed.
|
||||
*
|
||||
* Even if more than one periods have elapsed since the last call, you have to call this only once.
|
||||
*/
|
||||
void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
|
||||
void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime;
|
||||
unsigned long flags;
|
||||
|
||||
if (snd_BUG_ON(!substream))
|
||||
return;
|
||||
|
||||
snd_pcm_stream_lock_irqsave(substream, flags);
|
||||
if (PCM_RUNTIME_CHECK(substream))
|
||||
goto _unlock;
|
||||
return;
|
||||
runtime = substream->runtime;
|
||||
|
||||
if (!snd_pcm_running(substream) ||
|
||||
@ -1811,7 +1822,30 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
|
||||
#endif
|
||||
_end:
|
||||
kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
|
||||
_unlock:
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock);
|
||||
|
||||
/**
|
||||
* snd_pcm_period_elapsed() - update the status of runtime for the next period by acquiring lock of
|
||||
* PCM substream.
|
||||
* @substream: the instance of PCM substream.
|
||||
*
|
||||
* This function is mostly similar to ``snd_pcm_period_elapsed_under_stream_lock()`` except for
|
||||
* acquiring lock of PCM substream voluntarily.
|
||||
*
|
||||
* It's typically called by any type of IRQ handler when hardware IRQ occurs to notify event that
|
||||
* the batch of audio data frames as the same size as the period of buffer is already processed in
|
||||
* audio data transmission.
|
||||
*/
|
||||
void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (snd_BUG_ON(!substream))
|
||||
return;
|
||||
|
||||
snd_pcm_stream_lock_irqsave(substream, flags);
|
||||
snd_pcm_period_elapsed_under_stream_lock(substream);
|
||||
snd_pcm_stream_unlock_irqrestore(substream, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_period_elapsed);
|
||||
|
@ -65,11 +65,6 @@ void __snd_pcm_xrun(struct snd_pcm_substream *substream);
|
||||
void snd_pcm_group_init(struct snd_pcm_group *group);
|
||||
void snd_pcm_sync_stop(struct snd_pcm_substream *substream, bool sync_irq);
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
|
||||
unsigned long offset);
|
||||
#endif
|
||||
|
||||
#define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
|
||||
|
||||
/* loop over all PCM substreams */
|
||||
|
@ -337,27 +337,6 @@ void snd_pcm_set_managed_buffer_all(struct snd_pcm *pcm, int type,
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_set_managed_buffer_all);
|
||||
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
/*
|
||||
* snd_pcm_sgbuf_ops_page - get the page struct at the given offset
|
||||
* @substream: the pcm substream instance
|
||||
* @offset: the buffer offset
|
||||
*
|
||||
* Used as the page callback of PCM ops.
|
||||
*
|
||||
* Return: The page struct at the given buffer offset. %NULL on failure.
|
||||
*/
|
||||
struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigned long offset)
|
||||
{
|
||||
struct snd_sg_buf *sgbuf = snd_pcm_substream_sgbuf(substream);
|
||||
|
||||
unsigned int idx = offset >> PAGE_SHIFT;
|
||||
if (idx >= (unsigned int)sgbuf->pages)
|
||||
return NULL;
|
||||
return sgbuf->page_table[idx];
|
||||
}
|
||||
#endif /* CONFIG_SND_DMA_SGBUF */
|
||||
|
||||
/**
|
||||
* snd_pcm_lib_malloc_pages - allocate the DMA buffer
|
||||
* @substream: the substream to allocate the DMA buffer to
|
||||
|
@ -266,7 +266,8 @@ int snd_pcm_format_signed(snd_pcm_format_t format)
|
||||
int val;
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].signd) < 0)
|
||||
val = pcm_formats[(INT)format].signd;
|
||||
if (val < 0)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
@ -314,7 +315,8 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format)
|
||||
int val;
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].le) < 0)
|
||||
val = pcm_formats[(INT)format].le;
|
||||
if (val < 0)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
@ -350,7 +352,8 @@ int snd_pcm_format_width(snd_pcm_format_t format)
|
||||
int val;
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].width) == 0)
|
||||
val = pcm_formats[(INT)format].width;
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
@ -368,7 +371,8 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format)
|
||||
int val;
|
||||
if (!valid_format(format))
|
||||
return -EINVAL;
|
||||
if ((val = pcm_formats[(INT)format].phys) == 0)
|
||||
val = pcm_formats[(INT)format].phys;
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
return val;
|
||||
}
|
||||
|
@ -768,7 +768,8 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
|
||||
if (cpu_latency_qos_request_active(&substream->latency_pm_qos_req))
|
||||
cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
|
||||
if ((usecs = period_to_usecs(runtime)) >= 0)
|
||||
usecs = period_to_usecs(runtime);
|
||||
if (usecs >= 0)
|
||||
cpu_latency_qos_add_request(&substream->latency_pm_qos_req,
|
||||
usecs);
|
||||
return 0;
|
||||
@ -2658,7 +2659,8 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((err = substream->ops->open(substream)) < 0)
|
||||
err = substream->ops->open(substream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
substream->hw_opened = 1;
|
||||
@ -2799,6 +2801,10 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
|
||||
if (snd_BUG_ON(!substream))
|
||||
return -ENXIO;
|
||||
pcm = substream->pcm;
|
||||
|
||||
/* block until the device gets woken up as it may touch the hardware */
|
||||
snd_power_wait(pcm->card);
|
||||
|
||||
mutex_lock(&pcm->open_mutex);
|
||||
snd_pcm_release_substream(substream);
|
||||
kfree(pcm_file);
|
||||
@ -3193,7 +3199,7 @@ static int snd_pcm_common_ioctl(struct file *file,
|
||||
if (PCM_RUNTIME_CHECK(substream))
|
||||
return -ENXIO;
|
||||
|
||||
res = snd_power_wait(substream->pcm->card, SNDRV_CTL_POWER_D0);
|
||||
res = snd_power_wait(substream->pcm->card);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
@ -3638,24 +3644,6 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file
|
||||
}
|
||||
#endif /* coherent mmap */
|
||||
|
||||
static inline struct page *
|
||||
snd_pcm_default_page_ops(struct snd_pcm_substream *substream, unsigned long ofs)
|
||||
{
|
||||
void *vaddr = substream->runtime->dma_area + ofs;
|
||||
|
||||
switch (substream->dma_buffer.dev.type) {
|
||||
#ifdef CONFIG_SND_DMA_SGBUF
|
||||
case SNDRV_DMA_TYPE_DEV_SG:
|
||||
case SNDRV_DMA_TYPE_DEV_UC_SG:
|
||||
return snd_pcm_sgbuf_ops_page(substream, ofs);
|
||||
#endif /* CONFIG_SND_DMA_SGBUF */
|
||||
case SNDRV_DMA_TYPE_VMALLOC:
|
||||
return vmalloc_to_page(vaddr);
|
||||
default:
|
||||
return virt_to_page(vaddr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fault callback for mmapping a RAM page
|
||||
*/
|
||||
@ -3677,7 +3665,7 @@ static vm_fault_t snd_pcm_mmap_data_fault(struct vm_fault *vmf)
|
||||
if (substream->ops->page)
|
||||
page = substream->ops->page(substream, offset);
|
||||
else
|
||||
page = snd_pcm_default_page_ops(substream, offset);
|
||||
page = snd_sgbuf_get_page(snd_pcm_get_dma_buf(substream), offset);
|
||||
if (!page)
|
||||
return VM_FAULT_SIGBUS;
|
||||
get_page(page);
|
||||
@ -3712,22 +3700,9 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *area)
|
||||
{
|
||||
area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
|
||||
#ifdef CONFIG_GENERIC_ALLOCATOR
|
||||
if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) {
|
||||
area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
|
||||
return remap_pfn_range(area, area->vm_start,
|
||||
substream->dma_buffer.addr >> PAGE_SHIFT,
|
||||
area->vm_end - area->vm_start, area->vm_page_prot);
|
||||
}
|
||||
#endif /* CONFIG_GENERIC_ALLOCATOR */
|
||||
if (IS_ENABLED(CONFIG_HAS_DMA) && !substream->ops->page &&
|
||||
(substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV ||
|
||||
substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_UC))
|
||||
return dma_mmap_coherent(substream->dma_buffer.dev.dev,
|
||||
area,
|
||||
substream->runtime->dma_area,
|
||||
substream->runtime->dma_addr,
|
||||
substream->runtime->dma_bytes);
|
||||
if (!substream->ops->page &&
|
||||
!snd_dma_buffer_mmap(snd_pcm_get_dma_buf(substream), area))
|
||||
return 0;
|
||||
/* mmap with fault handler */
|
||||
area->vm_ops = &snd_pcm_vm_ops_data_fault;
|
||||
return 0;
|
||||
|
@ -680,9 +680,12 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime,
|
||||
bool is_input)
|
||||
{
|
||||
char *newbuf, *oldbuf;
|
||||
unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
|
||||
|
||||
if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L)
|
||||
return -EINVAL;
|
||||
if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP && (params->buffer_size & 0x1f) != 0)
|
||||
return -EINVAL;
|
||||
if (params->avail_min < 1 || params->avail_min > params->buffer_size)
|
||||
return -EINVAL;
|
||||
if (params->buffer_size != runtime->buffer_size) {
|
||||
@ -720,8 +723,24 @@ EXPORT_SYMBOL(snd_rawmidi_output_params);
|
||||
int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
|
||||
struct snd_rawmidi_params *params)
|
||||
{
|
||||
unsigned int framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK;
|
||||
unsigned int clock_type = params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK;
|
||||
int err;
|
||||
|
||||
if (framing == SNDRV_RAWMIDI_MODE_FRAMING_NONE && clock_type != SNDRV_RAWMIDI_MODE_CLOCK_NONE)
|
||||
return -EINVAL;
|
||||
else if (clock_type > SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW)
|
||||
return -EINVAL;
|
||||
if (framing > SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP)
|
||||
return -EINVAL;
|
||||
snd_rawmidi_drain_input(substream);
|
||||
return resize_runtime_buffer(substream->runtime, params, true);
|
||||
err = resize_runtime_buffer(substream->runtime, params, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
substream->framing = framing;
|
||||
substream->clock_type = clock_type;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_rawmidi_input_params);
|
||||
|
||||
@ -963,6 +982,62 @@ static int snd_rawmidi_control_ioctl(struct snd_card *card,
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static int receive_with_tstamp_framing(struct snd_rawmidi_substream *substream,
|
||||
const unsigned char *buffer, int src_count, const struct timespec64 *tstamp)
|
||||
{
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
struct snd_rawmidi_framing_tstamp *dest_ptr;
|
||||
struct snd_rawmidi_framing_tstamp frame = { .tv_sec = tstamp->tv_sec, .tv_nsec = tstamp->tv_nsec };
|
||||
int dest_frames = 0;
|
||||
int orig_count = src_count;
|
||||
int frame_size = sizeof(struct snd_rawmidi_framing_tstamp);
|
||||
|
||||
BUILD_BUG_ON(frame_size != 0x20);
|
||||
if (snd_BUG_ON((runtime->hw_ptr & 0x1f) != 0))
|
||||
return -EINVAL;
|
||||
|
||||
while (src_count > 0) {
|
||||
if ((int)(runtime->buffer_size - runtime->avail) < frame_size) {
|
||||
runtime->xruns += src_count;
|
||||
break;
|
||||
}
|
||||
if (src_count >= SNDRV_RAWMIDI_FRAMING_DATA_LENGTH)
|
||||
frame.length = SNDRV_RAWMIDI_FRAMING_DATA_LENGTH;
|
||||
else {
|
||||
frame.length = src_count;
|
||||
memset(frame.data, 0, SNDRV_RAWMIDI_FRAMING_DATA_LENGTH);
|
||||
}
|
||||
memcpy(frame.data, buffer, frame.length);
|
||||
buffer += frame.length;
|
||||
src_count -= frame.length;
|
||||
dest_ptr = (struct snd_rawmidi_framing_tstamp *) (runtime->buffer + runtime->hw_ptr);
|
||||
*dest_ptr = frame;
|
||||
runtime->avail += frame_size;
|
||||
runtime->hw_ptr += frame_size;
|
||||
runtime->hw_ptr %= runtime->buffer_size;
|
||||
dest_frames++;
|
||||
}
|
||||
return orig_count - src_count;
|
||||
}
|
||||
|
||||
static struct timespec64 get_framing_tstamp(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct timespec64 ts64 = {0, 0};
|
||||
|
||||
switch (substream->clock_type) {
|
||||
case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW:
|
||||
ktime_get_raw_ts64(&ts64);
|
||||
break;
|
||||
case SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC:
|
||||
ktime_get_ts64(&ts64);
|
||||
break;
|
||||
case SNDRV_RAWMIDI_MODE_CLOCK_REALTIME:
|
||||
ktime_get_real_ts64(&ts64);
|
||||
break;
|
||||
}
|
||||
return ts64;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_rawmidi_receive - receive the input data from the device
|
||||
* @substream: the rawmidi substream
|
||||
@ -977,6 +1052,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
|
||||
const unsigned char *buffer, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct timespec64 ts64 = get_framing_tstamp(substream);
|
||||
int result = 0, count1;
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
|
||||
@ -987,8 +1063,11 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream,
|
||||
"snd_rawmidi_receive: input is not active!!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
if (count == 1) { /* special case, faster code */
|
||||
if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
|
||||
result = receive_with_tstamp_framing(substream, buffer, count, &ts64);
|
||||
} else if (count == 1) { /* special case, faster code */
|
||||
substream->bytes++;
|
||||
if (runtime->avail < runtime->buffer_size) {
|
||||
runtime->buffer[runtime->hw_ptr++] = buffer[0];
|
||||
@ -1541,6 +1620,8 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
|
||||
struct snd_rawmidi_substream *substream;
|
||||
struct snd_rawmidi_runtime *runtime;
|
||||
unsigned long buffer_size, avail, xruns;
|
||||
unsigned int clock_type;
|
||||
static const char *clock_names[4] = { "none", "realtime", "monotonic", "monotonic raw" };
|
||||
|
||||
rmidi = entry->private_data;
|
||||
snd_iprintf(buffer, "%s\n\n", rmidi->name);
|
||||
@ -1596,6 +1677,14 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry,
|
||||
" Avail : %lu\n"
|
||||
" Overruns : %lu\n",
|
||||
buffer_size, avail, xruns);
|
||||
if (substream->framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) {
|
||||
clock_type = substream->clock_type >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT;
|
||||
if (!snd_BUG_ON(clock_type >= ARRAY_SIZE(clock_names)))
|
||||
snd_iprintf(buffer,
|
||||
" Framing : tstamp\n"
|
||||
" Clock type : %s\n",
|
||||
clock_names[clock_type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ struct snd_rawmidi_params32 {
|
||||
u32 buffer_size;
|
||||
u32 avail_min;
|
||||
unsigned int no_active_sensing; /* avoid bit-field */
|
||||
unsigned char reserved[16];
|
||||
unsigned int mode;
|
||||
unsigned char reserved[12];
|
||||
} __attribute__((packed));
|
||||
|
||||
static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
|
||||
@ -25,6 +26,7 @@ static int snd_rawmidi_ioctl_params_compat(struct snd_rawmidi_file *rfile,
|
||||
if (get_user(params.stream, &src->stream) ||
|
||||
get_user(params.buffer_size, &src->buffer_size) ||
|
||||
get_user(params.avail_min, &src->avail_min) ||
|
||||
get_user(params.mode, &src->mode) ||
|
||||
get_user(val, &src->no_active_sensing))
|
||||
return -EFAULT;
|
||||
params.no_active_sensing = val;
|
||||
|
@ -67,13 +67,16 @@ static int __init alsa_seq_oss_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = register_device()) < 0)
|
||||
rc = register_device();
|
||||
if (rc < 0)
|
||||
goto error;
|
||||
if ((rc = register_proc()) < 0) {
|
||||
rc = register_proc();
|
||||
if (rc < 0) {
|
||||
unregister_device();
|
||||
goto error;
|
||||
}
|
||||
if ((rc = snd_seq_oss_create_client()) < 0) {
|
||||
rc = snd_seq_oss_create_client();
|
||||
if (rc < 0) {
|
||||
unregister_proc();
|
||||
unregister_device();
|
||||
goto error;
|
||||
@ -133,7 +136,8 @@ odev_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_oss_devinfo *dp;
|
||||
|
||||
if ((dp = file->private_data) == NULL)
|
||||
dp = file->private_data;
|
||||
if (!dp)
|
||||
return 0;
|
||||
|
||||
mutex_lock(®ister_mutex);
|
||||
@ -226,16 +230,18 @@ register_device(void)
|
||||
int rc;
|
||||
|
||||
mutex_lock(®ister_mutex);
|
||||
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
|
||||
NULL, 0,
|
||||
&seq_oss_f_ops, NULL)) < 0) {
|
||||
rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER,
|
||||
NULL, 0,
|
||||
&seq_oss_f_ops, NULL);
|
||||
if (rc < 0) {
|
||||
pr_err("ALSA: seq_oss: can't register device seq\n");
|
||||
mutex_unlock(®ister_mutex);
|
||||
return rc;
|
||||
}
|
||||
if ((rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
|
||||
NULL, 0,
|
||||
&seq_oss_f_ops, NULL)) < 0) {
|
||||
rc = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MUSIC,
|
||||
NULL, 0,
|
||||
&seq_oss_f_ops, NULL);
|
||||
if (rc < 0) {
|
||||
pr_err("ALSA: seq_oss: can't register device music\n");
|
||||
snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_SEQUENCER, NULL, 0);
|
||||
mutex_unlock(®ister_mutex);
|
||||
|
@ -94,10 +94,10 @@ snd_seq_oss_create_client(void)
|
||||
port_callback.event_input = receive_announce;
|
||||
port->kernel = &port_callback;
|
||||
|
||||
call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port);
|
||||
if ((system_port = port->addr.port) >= 0) {
|
||||
if (call_ctl(SNDRV_SEQ_IOCTL_CREATE_PORT, port) >= 0) {
|
||||
struct snd_seq_port_subscribe subs;
|
||||
|
||||
system_port = port->addr.port;
|
||||
memset(&subs, 0, sizeof(subs));
|
||||
subs.sender.client = SNDRV_SEQ_CLIENT_SYSTEM;
|
||||
subs.sender.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE;
|
||||
@ -354,7 +354,8 @@ alloc_seq_queue(struct seq_oss_devinfo *dp)
|
||||
qinfo.owner = system_client;
|
||||
qinfo.locked = 1;
|
||||
strcpy(qinfo.name, "OSS Sequencer Emulation");
|
||||
if ((rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo)) < 0)
|
||||
rc = call_ctl(SNDRV_SEQ_IOCTL_CREATE_QUEUE, &qinfo);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
dp->queue = qinfo.queue;
|
||||
return 0;
|
||||
@ -485,7 +486,8 @@ snd_seq_oss_system_info_read(struct snd_info_buffer *buf)
|
||||
snd_iprintf(buf, "\nNumber of applications: %d\n", num_clients);
|
||||
for (i = 0; i < num_clients; i++) {
|
||||
snd_iprintf(buf, "\nApplication %d: ", i);
|
||||
if ((dp = client_table[i]) == NULL) {
|
||||
dp = client_table[i];
|
||||
if (!dp) {
|
||||
snd_iprintf(buf, "*empty*\n");
|
||||
continue;
|
||||
}
|
||||
|
@ -152,7 +152,8 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo)
|
||||
/*
|
||||
* look for the identical slot
|
||||
*/
|
||||
if ((mdev = find_slot(pinfo->addr.client, pinfo->addr.port)) != NULL) {
|
||||
mdev = find_slot(pinfo->addr.client, pinfo->addr.port);
|
||||
if (mdev) {
|
||||
/* already exists */
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
return 0;
|
||||
@ -218,7 +219,8 @@ snd_seq_oss_midi_check_exit_port(int client, int port)
|
||||
unsigned long flags;
|
||||
int index;
|
||||
|
||||
if ((mdev = find_slot(client, port)) != NULL) {
|
||||
mdev = find_slot(client, port);
|
||||
if (mdev) {
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
midi_devs[mdev->seq_device] = NULL;
|
||||
spin_unlock_irqrestore(®ister_lock, flags);
|
||||
@ -250,7 +252,8 @@ snd_seq_oss_midi_clear_all(void)
|
||||
|
||||
spin_lock_irqsave(®ister_lock, flags);
|
||||
for (i = 0; i < max_midi_devs; i++) {
|
||||
if ((mdev = midi_devs[i]) != NULL) {
|
||||
mdev = midi_devs[i];
|
||||
if (mdev) {
|
||||
snd_midi_event_free(mdev->coder);
|
||||
kfree(mdev);
|
||||
midi_devs[i] = NULL;
|
||||
@ -318,7 +321,8 @@ snd_seq_oss_midi_open(struct seq_oss_devinfo *dp, int dev, int fmode)
|
||||
struct seq_oss_midi *mdev;
|
||||
struct snd_seq_port_subscribe subs;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
mdev = get_mididev(dp, dev);
|
||||
if (!mdev)
|
||||
return -ENODEV;
|
||||
|
||||
/* already used? */
|
||||
@ -384,7 +388,8 @@ snd_seq_oss_midi_close(struct seq_oss_devinfo *dp, int dev)
|
||||
struct seq_oss_midi *mdev;
|
||||
struct snd_seq_port_subscribe subs;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
mdev = get_mididev(dp, dev);
|
||||
if (!mdev)
|
||||
return -ENODEV;
|
||||
if (! mdev->opened || mdev->devinfo != dp) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
@ -421,7 +426,8 @@ snd_seq_oss_midi_filemode(struct seq_oss_devinfo *dp, int dev)
|
||||
struct seq_oss_midi *mdev;
|
||||
int mode;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
mdev = get_mididev(dp, dev);
|
||||
if (!mdev)
|
||||
return 0;
|
||||
|
||||
mode = 0;
|
||||
@ -443,7 +449,8 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
|
||||
{
|
||||
struct seq_oss_midi *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
mdev = get_mididev(dp, dev);
|
||||
if (!mdev)
|
||||
return;
|
||||
if (! mdev->opened) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
@ -491,7 +498,8 @@ snd_seq_oss_midi_get_addr(struct seq_oss_devinfo *dp, int dev, struct snd_seq_ad
|
||||
{
|
||||
struct seq_oss_midi *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
mdev = get_mididev(dp, dev);
|
||||
if (!mdev)
|
||||
return;
|
||||
addr->client = mdev->client;
|
||||
addr->port = mdev->port;
|
||||
@ -511,7 +519,8 @@ snd_seq_oss_midi_input(struct snd_seq_event *ev, int direct, void *private_data)
|
||||
|
||||
if (dp->readq == NULL)
|
||||
return 0;
|
||||
if ((mdev = find_slot(ev->source.client, ev->source.port)) == NULL)
|
||||
mdev = find_slot(ev->source.client, ev->source.port);
|
||||
if (!mdev)
|
||||
return 0;
|
||||
if (! (mdev->opened & PERM_READ)) {
|
||||
snd_use_lock_free(&mdev->use_lock);
|
||||
@ -623,7 +632,8 @@ snd_seq_oss_midi_putc(struct seq_oss_devinfo *dp, int dev, unsigned char c, stru
|
||||
{
|
||||
struct seq_oss_midi *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
mdev = get_mididev(dp, dev);
|
||||
if (!mdev)
|
||||
return -ENODEV;
|
||||
if (snd_midi_event_encode_byte(mdev->coder, c, ev)) {
|
||||
snd_seq_oss_fill_addr(dp, ev, mdev->client, mdev->port);
|
||||
@ -642,7 +652,8 @@ snd_seq_oss_midi_make_info(struct seq_oss_devinfo *dp, int dev, struct midi_info
|
||||
{
|
||||
struct seq_oss_midi *mdev;
|
||||
|
||||
if ((mdev = get_mididev(dp, dev)) == NULL)
|
||||
mdev = get_mididev(dp, dev);
|
||||
if (!mdev)
|
||||
return -ENXIO;
|
||||
inf->device = dev;
|
||||
inf->dev_type = 0; /* FIXME: ?? */
|
||||
|
@ -132,7 +132,8 @@ snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int count,
|
||||
}
|
||||
|
||||
/* insert queue */
|
||||
if ((err = insert_queue(dp, &rec, opt)) < 0)
|
||||
err = insert_queue(dp, &rec, opt);
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
result += ev_size;
|
||||
|
@ -451,7 +451,8 @@ snd_seq_oss_synth_load_patch(struct seq_oss_devinfo *dp, int dev, int fmt,
|
||||
|
||||
if (info->is_midi)
|
||||
return 0;
|
||||
if ((rec = get_synthdev(dp, dev)) == NULL)
|
||||
rec = get_synthdev(dp, dev);
|
||||
if (!rec)
|
||||
return -ENXIO;
|
||||
|
||||
if (rec->oper.load_patch == NULL)
|
||||
@ -569,7 +570,8 @@ snd_seq_oss_synth_ioctl(struct seq_oss_devinfo *dp, int dev, unsigned int cmd, u
|
||||
info = get_synthinfo_nospec(dp, dev);
|
||||
if (!info || info->is_midi)
|
||||
return -ENXIO;
|
||||
if ((rec = get_synthdev(dp, dev)) == NULL)
|
||||
rec = get_synthdev(dp, dev);
|
||||
if (!rec)
|
||||
return -ENXIO;
|
||||
if (rec->oper.ioctl == NULL)
|
||||
rc = -ENXIO;
|
||||
@ -619,7 +621,8 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in
|
||||
inf->device = dev;
|
||||
strscpy(inf->name, minf.name, sizeof(inf->name));
|
||||
} else {
|
||||
if ((rec = get_synthdev(dp, dev)) == NULL)
|
||||
rec = get_synthdev(dp, dev);
|
||||
if (!rec)
|
||||
return -ENXIO;
|
||||
inf->synth_type = rec->synth_type;
|
||||
inf->synth_subtype = rec->synth_subtype;
|
||||
|
@ -27,7 +27,8 @@ snd_seq_oss_writeq_new(struct seq_oss_devinfo *dp, int maxlen)
|
||||
struct seq_oss_writeq *q;
|
||||
struct snd_seq_client_pool pool;
|
||||
|
||||
if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL)
|
||||
q = kzalloc(sizeof(*q), GFP_KERNEL);
|
||||
if (!q)
|
||||
return NULL;
|
||||
q->dp = dp;
|
||||
q->maxlen = maxlen;
|
||||
|
@ -416,7 +416,10 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
|
||||
if (snd_BUG_ON(!client))
|
||||
return -ENXIO;
|
||||
|
||||
if (!client->accept_input || (fifo = client->data.user.fifo) == NULL)
|
||||
if (!client->accept_input)
|
||||
return -ENXIO;
|
||||
fifo = client->data.user.fifo;
|
||||
if (!fifo)
|
||||
return -ENXIO;
|
||||
|
||||
if (atomic_read(&fifo->overflow) > 0) {
|
||||
@ -435,9 +438,9 @@ static ssize_t snd_seq_read(struct file *file, char __user *buf, size_t count,
|
||||
int nonblock;
|
||||
|
||||
nonblock = (file->f_flags & O_NONBLOCK) || result > 0;
|
||||
if ((err = snd_seq_fifo_cell_out(fifo, &cell, nonblock)) < 0) {
|
||||
err = snd_seq_fifo_cell_out(fifo, &cell, nonblock);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
if (snd_seq_ev_is_variable(&cell->event)) {
|
||||
struct snd_seq_event tmpev;
|
||||
tmpev = cell->event;
|
||||
@ -970,7 +973,8 @@ static int snd_seq_client_enqueue_event(struct snd_seq_client *client,
|
||||
return err;
|
||||
|
||||
/* we got a cell. enqueue it. */
|
||||
if ((err = snd_seq_enqueue_event(cell, atomic, hop)) < 0) {
|
||||
err = snd_seq_enqueue_event(cell, atomic, hop);
|
||||
if (err < 0) {
|
||||
snd_seq_cell_free(cell);
|
||||
return err;
|
||||
}
|
||||
@ -1312,7 +1316,8 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
|
||||
return -EINVAL;
|
||||
}
|
||||
if (client->type == KERNEL_CLIENT) {
|
||||
if ((callback = info->kernel) != NULL) {
|
||||
callback = info->kernel;
|
||||
if (callback) {
|
||||
if (callback->owner)
|
||||
port->owner = callback->owner;
|
||||
port->private_data = callback->private_data;
|
||||
@ -1466,13 +1471,17 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
|
||||
struct snd_seq_client *receiver = NULL, *sender = NULL;
|
||||
struct snd_seq_client_port *sport = NULL, *dport = NULL;
|
||||
|
||||
if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL)
|
||||
receiver = snd_seq_client_use_ptr(subs->dest.client);
|
||||
if (!receiver)
|
||||
goto __end;
|
||||
if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
|
||||
sender = snd_seq_client_use_ptr(subs->sender.client);
|
||||
if (!sender)
|
||||
goto __end;
|
||||
if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
|
||||
sport = snd_seq_port_use_ptr(sender, subs->sender.port);
|
||||
if (!sport)
|
||||
goto __end;
|
||||
if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL)
|
||||
dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
|
||||
if (!dport)
|
||||
goto __end;
|
||||
|
||||
result = check_subscription_permission(client, sport, dport, subs);
|
||||
@ -1508,13 +1517,17 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
|
||||
struct snd_seq_client *receiver = NULL, *sender = NULL;
|
||||
struct snd_seq_client_port *sport = NULL, *dport = NULL;
|
||||
|
||||
if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL)
|
||||
receiver = snd_seq_client_use_ptr(subs->dest.client);
|
||||
if (!receiver)
|
||||
goto __end;
|
||||
if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
|
||||
sender = snd_seq_client_use_ptr(subs->sender.client);
|
||||
if (!sender)
|
||||
goto __end;
|
||||
if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
|
||||
sport = snd_seq_port_use_ptr(sender, subs->sender.port);
|
||||
if (!sport)
|
||||
goto __end;
|
||||
if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL)
|
||||
dport = snd_seq_port_use_ptr(receiver, subs->dest.port);
|
||||
if (!dport)
|
||||
goto __end;
|
||||
|
||||
result = check_subscription_permission(client, sport, dport, subs);
|
||||
@ -1926,9 +1939,11 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
|
||||
struct snd_seq_client_port *sport = NULL;
|
||||
|
||||
result = -EINVAL;
|
||||
if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
|
||||
sender = snd_seq_client_use_ptr(subs->sender.client);
|
||||
if (!sender)
|
||||
goto __end;
|
||||
if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
|
||||
sport = snd_seq_port_use_ptr(sender, subs->sender.port);
|
||||
if (!sport)
|
||||
goto __end;
|
||||
result = snd_seq_port_get_subscription(&sport->c_src, &subs->dest,
|
||||
subs);
|
||||
@ -1955,9 +1970,11 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
|
||||
struct list_head *p;
|
||||
int i;
|
||||
|
||||
if ((cptr = snd_seq_client_use_ptr(subs->root.client)) == NULL)
|
||||
cptr = snd_seq_client_use_ptr(subs->root.client);
|
||||
if (!cptr)
|
||||
goto __end;
|
||||
if ((port = snd_seq_port_use_ptr(cptr, subs->root.port)) == NULL)
|
||||
port = snd_seq_port_use_ptr(cptr, subs->root.port);
|
||||
if (!port)
|
||||
goto __end;
|
||||
|
||||
switch (subs->type) {
|
||||
|
@ -109,7 +109,8 @@ create_port(int idx, int type)
|
||||
struct snd_seq_port_callback pcb;
|
||||
struct snd_seq_dummy_port *rec;
|
||||
|
||||
if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
|
||||
rec = kzalloc(sizeof(*rec), GFP_KERNEL);
|
||||
if (!rec)
|
||||
return NULL;
|
||||
|
||||
rec->client = my_client;
|
||||
|
@ -143,7 +143,8 @@ static struct snd_seq_event_cell *fifo_cell_out(struct snd_seq_fifo *f)
|
||||
{
|
||||
struct snd_seq_event_cell *cell;
|
||||
|
||||
if ((cell = f->head) != NULL) {
|
||||
cell = f->head;
|
||||
if (cell) {
|
||||
f->head = cell->next;
|
||||
|
||||
/* reset tail if this was the last element */
|
||||
|
@ -69,7 +69,8 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event,
|
||||
int len, err;
|
||||
struct snd_seq_event_cell *cell;
|
||||
|
||||
if ((len = get_var_len(event)) <= 0)
|
||||
len = get_var_len(event);
|
||||
if (len <= 0)
|
||||
return len;
|
||||
|
||||
if (event->data.ext.len & SNDRV_SEQ_EXT_USRPTR) {
|
||||
@ -133,7 +134,8 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char
|
||||
int len, newlen;
|
||||
int err;
|
||||
|
||||
if ((len = get_var_len(event)) < 0)
|
||||
len = get_var_len(event);
|
||||
if (len < 0)
|
||||
return len;
|
||||
newlen = len;
|
||||
if (size_aligned > 0)
|
||||
|
@ -101,7 +101,8 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
|
||||
if (snd_BUG_ON(!substream || !buf))
|
||||
return -EINVAL;
|
||||
runtime = substream->runtime;
|
||||
if ((tmp = runtime->avail) < count) {
|
||||
tmp = runtime->avail;
|
||||
if (tmp < count) {
|
||||
if (printk_ratelimit())
|
||||
pr_err("ALSA: seq_midi: MIDI output buffer overrun\n");
|
||||
return -ENOMEM;
|
||||
@ -167,10 +168,11 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe
|
||||
struct snd_rawmidi_params params;
|
||||
|
||||
/* open midi port */
|
||||
if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
|
||||
msynth->subdevice,
|
||||
SNDRV_RAWMIDI_LFLG_INPUT,
|
||||
&msynth->input_rfile)) < 0) {
|
||||
err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
|
||||
msynth->subdevice,
|
||||
SNDRV_RAWMIDI_LFLG_INPUT,
|
||||
&msynth->input_rfile);
|
||||
if (err < 0) {
|
||||
pr_debug("ALSA: seq_midi: midi input open failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
@ -178,7 +180,8 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.avail_min = 1;
|
||||
params.buffer_size = input_buffer_size;
|
||||
if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms)) < 0) {
|
||||
err = snd_rawmidi_input_params(msynth->input_rfile.input, ¶ms);
|
||||
if (err < 0) {
|
||||
snd_rawmidi_kernel_release(&msynth->input_rfile);
|
||||
return err;
|
||||
}
|
||||
@ -209,10 +212,11 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
|
||||
struct snd_rawmidi_params params;
|
||||
|
||||
/* open midi port */
|
||||
if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
|
||||
msynth->subdevice,
|
||||
SNDRV_RAWMIDI_LFLG_OUTPUT,
|
||||
&msynth->output_rfile)) < 0) {
|
||||
err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
|
||||
msynth->subdevice,
|
||||
SNDRV_RAWMIDI_LFLG_OUTPUT,
|
||||
&msynth->output_rfile);
|
||||
if (err < 0) {
|
||||
pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
|
||||
return err;
|
||||
}
|
||||
@ -220,7 +224,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
|
||||
params.avail_min = 1;
|
||||
params.buffer_size = output_buffer_size;
|
||||
params.no_active_sensing = 1;
|
||||
if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms)) < 0) {
|
||||
err = snd_rawmidi_output_params(msynth->output_rfile.output, ¶ms);
|
||||
if (err < 0) {
|
||||
snd_rawmidi_kernel_release(&msynth->output_rfile);
|
||||
return err;
|
||||
}
|
||||
|
@ -222,7 +222,8 @@ struct snd_seq_queue *snd_seq_queue_find_name(char *name)
|
||||
struct snd_seq_queue *q;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) != NULL) {
|
||||
q = queueptr(i);
|
||||
if (q) {
|
||||
if (strncmp(q->name, name, sizeof(q->name)) == 0)
|
||||
return q;
|
||||
queuefree(q);
|
||||
@ -432,7 +433,8 @@ int snd_seq_queue_timer_open(int queueid)
|
||||
if (queue == NULL)
|
||||
return -EINVAL;
|
||||
tmr = queue->timer;
|
||||
if ((result = snd_seq_timer_open(queue)) < 0) {
|
||||
result = snd_seq_timer_open(queue);
|
||||
if (result < 0) {
|
||||
snd_seq_timer_defaults(tmr);
|
||||
result = snd_seq_timer_open(queue);
|
||||
}
|
||||
@ -548,7 +550,8 @@ void snd_seq_queue_client_leave(int client)
|
||||
|
||||
/* delete own queues from queue list */
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queue_list_remove(i, client)) != NULL)
|
||||
q = queue_list_remove(i, client);
|
||||
if (q)
|
||||
queue_delete(q);
|
||||
}
|
||||
|
||||
@ -556,7 +559,8 @@ void snd_seq_queue_client_leave(int client)
|
||||
* they are not owned by this client
|
||||
*/
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
q = queueptr(i);
|
||||
if (!q)
|
||||
continue;
|
||||
if (test_bit(client, q->clients_bitmap)) {
|
||||
snd_seq_prioq_leave(q->tickq, client, 0);
|
||||
@ -578,7 +582,8 @@ void snd_seq_queue_client_leave_cells(int client)
|
||||
struct snd_seq_queue *q;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
q = queueptr(i);
|
||||
if (!q)
|
||||
continue;
|
||||
snd_seq_prioq_leave(q->tickq, client, 0);
|
||||
snd_seq_prioq_leave(q->timeq, client, 0);
|
||||
@ -593,7 +598,8 @@ void snd_seq_queue_remove_cells(int client, struct snd_seq_remove_events *info)
|
||||
struct snd_seq_queue *q;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
q = queueptr(i);
|
||||
if (!q)
|
||||
continue;
|
||||
if (test_bit(client, q->clients_bitmap) &&
|
||||
(! (info->remove_mode & SNDRV_SEQ_REMOVE_DEST) ||
|
||||
@ -724,7 +730,8 @@ void snd_seq_info_queues_read(struct snd_info_entry *entry,
|
||||
int owner;
|
||||
|
||||
for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) {
|
||||
if ((q = queueptr(i)) == NULL)
|
||||
q = queueptr(i);
|
||||
if (!q)
|
||||
continue;
|
||||
|
||||
tmr = q->timer;
|
||||
|
@ -482,10 +482,11 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi
|
||||
int err;
|
||||
|
||||
*rrmidi = NULL;
|
||||
if ((err = snd_rawmidi_new(card, "VirMidi", device,
|
||||
16, /* may be configurable */
|
||||
16, /* may be configurable */
|
||||
&rmidi)) < 0)
|
||||
err = snd_rawmidi_new(card, "VirMidi", device,
|
||||
16, /* may be configurable */
|
||||
16, /* may be configurable */
|
||||
&rmidi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
strcpy(rmidi->name, rmidi->id);
|
||||
rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
|
||||
|
@ -10,20 +10,34 @@
|
||||
#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)
|
||||
|
||||
int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
|
||||
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 -EINVAL;
|
||||
if (!sgbuf)
|
||||
return;
|
||||
|
||||
vunmap(dmab->area);
|
||||
dmab->area = NULL;
|
||||
@ -45,15 +59,11 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab)
|
||||
kfree(sgbuf->page_table);
|
||||
kfree(sgbuf);
|
||||
dmab->private_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_ALLOC_PAGES 32
|
||||
|
||||
void *snd_malloc_sgbuf_pages(struct device *device,
|
||||
size_t size, struct snd_dma_buffer *dmab,
|
||||
size_t *res_size)
|
||||
static int snd_dma_sg_alloc(struct snd_dma_buffer *dmab, size_t size)
|
||||
{
|
||||
struct snd_sg_buf *sgbuf;
|
||||
unsigned int i, pages, chunk, maxpages;
|
||||
@ -63,18 +73,16 @@ void *snd_malloc_sgbuf_pages(struct device *device,
|
||||
int type = SNDRV_DMA_TYPE_DEV;
|
||||
pgprot_t prot = PAGE_KERNEL;
|
||||
|
||||
dmab->area = NULL;
|
||||
dmab->addr = 0;
|
||||
dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL);
|
||||
if (! sgbuf)
|
||||
return NULL;
|
||||
if (!sgbuf)
|
||||
return -ENOMEM;
|
||||
if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC_SG) {
|
||||
type = SNDRV_DMA_TYPE_DEV_UC;
|
||||
#ifdef pgprot_noncached
|
||||
prot = pgprot_noncached(PAGE_KERNEL);
|
||||
#endif
|
||||
}
|
||||
sgbuf->dev = device;
|
||||
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);
|
||||
@ -94,12 +102,10 @@ void *snd_malloc_sgbuf_pages(struct device *device,
|
||||
if (chunk > maxpages)
|
||||
chunk = maxpages;
|
||||
chunk <<= PAGE_SHIFT;
|
||||
if (snd_dma_alloc_pages_fallback(type, device,
|
||||
if (snd_dma_alloc_pages_fallback(type, dmab->dev.dev,
|
||||
chunk, &tmpb) < 0) {
|
||||
if (!sgbuf->pages)
|
||||
goto _failed;
|
||||
if (!res_size)
|
||||
goto _failed;
|
||||
size = sgbuf->pages * PAGE_SIZE;
|
||||
break;
|
||||
}
|
||||
@ -124,27 +130,42 @@ void *snd_malloc_sgbuf_pages(struct device *device,
|
||||
dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot);
|
||||
if (! dmab->area)
|
||||
goto _failed;
|
||||
if (res_size)
|
||||
*res_size = sgbuf->size;
|
||||
return dmab->area;
|
||||
return 0;
|
||||
|
||||
_failed:
|
||||
snd_free_sgbuf_pages(dmab); /* free the table */
|
||||
return NULL;
|
||||
snd_dma_sg_free(dmab); /* free the table */
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* compute the max chunk size with continuous pages on sg-buffer
|
||||
*/
|
||||
unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
|
||||
unsigned int ofs, unsigned int size)
|
||||
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;
|
||||
|
||||
if (!sg)
|
||||
return size;
|
||||
|
||||
start = ofs >> PAGE_SHIFT;
|
||||
end = (ofs + size - 1) >> PAGE_SHIFT;
|
||||
/* check page continuity */
|
||||
@ -160,4 +181,11 @@ unsigned int snd_sgbuf_get_chunk_size(struct snd_dma_buffer *dmab,
|
||||
/* ok, all on continuous pages */
|
||||
return size;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_sgbuf_get_chunk_size);
|
||||
|
||||
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,
|
||||
};
|
||||
|
@ -357,7 +357,8 @@ static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_bu
|
||||
|
||||
mutex_lock(&sound_mutex);
|
||||
for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) {
|
||||
if (!(mptr = snd_minors[minor]))
|
||||
mptr = snd_minors[minor];
|
||||
if (!mptr)
|
||||
continue;
|
||||
if (mptr->card >= 0) {
|
||||
if (mptr->device >= 0)
|
||||
|
@ -217,7 +217,8 @@ static void snd_minor_info_oss_read(struct snd_info_entry *entry,
|
||||
|
||||
mutex_lock(&sound_oss_mutex);
|
||||
for (minor = 0; minor < SNDRV_OSS_MINORS; ++minor) {
|
||||
if (!(mptr = snd_oss_minors[minor]))
|
||||
mptr = snd_oss_minors[minor];
|
||||
if (!mptr)
|
||||
continue;
|
||||
if (mptr->card >= 0)
|
||||
snd_iprintf(buffer, "%3i: [%i-%2i]: %s\n", minor,
|
||||
|
@ -104,7 +104,8 @@ static int snd_mpu401_probe(struct platform_device *devptr)
|
||||
err = snd_mpu401_create(&devptr->dev, dev, &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if ((err = snd_card_register(card)) < 0) {
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
@ -182,7 +183,8 @@ static int snd_mpu401_pnp_probe(struct pnp_dev *pnp_dev,
|
||||
err = snd_mpu401_create(&pnp_dev->dev, dev, &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if ((err = snd_card_register(card)) < 0) {
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
@ -227,7 +229,8 @@ static int __init alsa_card_mpu401_init(void)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
if ((err = platform_driver_register(&snd_mpu401_driver)) < 0)
|
||||
err = platform_driver_register(&snd_mpu401_driver);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < SNDRV_CARDS; i++) {
|
||||
|
@ -271,8 +271,11 @@ static int snd_mpu401_uart_input_open(struct snd_rawmidi_substream *substream)
|
||||
int err;
|
||||
|
||||
mpu = substream->rmidi->private_data;
|
||||
if (mpu->open_input && (err = mpu->open_input(mpu)) < 0)
|
||||
return err;
|
||||
if (mpu->open_input) {
|
||||
err = mpu->open_input(mpu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (! test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode)) {
|
||||
if (snd_mpu401_do_reset(mpu) < 0)
|
||||
goto error_out;
|
||||
@ -293,8 +296,11 @@ static int snd_mpu401_uart_output_open(struct snd_rawmidi_substream *substream)
|
||||
int err;
|
||||
|
||||
mpu = substream->rmidi->private_data;
|
||||
if (mpu->open_output && (err = mpu->open_output(mpu)) < 0)
|
||||
return err;
|
||||
if (mpu->open_output) {
|
||||
err = mpu->open_output(mpu);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (! test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) {
|
||||
if (snd_mpu401_do_reset(mpu) < 0)
|
||||
goto error_out;
|
||||
@ -524,8 +530,9 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
|
||||
info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT;
|
||||
in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0;
|
||||
out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0;
|
||||
if ((err = snd_rawmidi_new(card, "MPU-401U", device,
|
||||
out_enable, in_enable, &rmidi)) < 0)
|
||||
err = snd_rawmidi_new(card, "MPU-401U", device,
|
||||
out_enable, in_enable, &rmidi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mpu = kzalloc(sizeof(*mpu), GFP_KERNEL);
|
||||
if (!mpu) {
|
||||
|
@ -566,7 +566,8 @@ static irqreturn_t snd_mtpav_irqh(int irq, void *dev_id)
|
||||
*/
|
||||
static int snd_mtpav_get_ISA(struct mtpav *mcard)
|
||||
{
|
||||
if ((mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI")) == NULL) {
|
||||
mcard->res_port = request_region(port, 3, "MotuMTPAV MIDI");
|
||||
if (!mcard->res_port) {
|
||||
snd_printk(KERN_ERR "MTVAP port 0x%lx is busy\n", port);
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -628,10 +629,11 @@ static int snd_mtpav_get_RAWMIDI(struct mtpav *mcard)
|
||||
hwports = 8;
|
||||
mcard->num_ports = hwports;
|
||||
|
||||
if ((rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
|
||||
mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
|
||||
mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
|
||||
&mcard->rmidi)) < 0)
|
||||
rval = snd_rawmidi_new(mcard->card, "MotuMIDI", 0,
|
||||
mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
|
||||
mcard->num_ports * 2 + MTPAV_PIDX_BROADCAST + 1,
|
||||
&mcard->rmidi);
|
||||
if (rval < 0)
|
||||
return rval;
|
||||
rawmidi = mcard->rmidi;
|
||||
rawmidi->private_data = mcard;
|
||||
@ -744,7 +746,8 @@ static int __init alsa_card_mtpav_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = platform_driver_register(&snd_mtpav_driver)) < 0)
|
||||
err = platform_driver_register(&snd_mtpav_driver);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0);
|
||||
|
@ -950,7 +950,8 @@ static int snd_mts64_probe(struct platform_device *pdev)
|
||||
goto free_pardev;
|
||||
}
|
||||
|
||||
if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
|
||||
err = snd_mts64_create(card, pardev, &mts);
|
||||
if (err < 0) {
|
||||
snd_printd("Cannot create main component\n");
|
||||
goto release_pardev;
|
||||
}
|
||||
@ -963,19 +964,22 @@ static int snd_mts64_probe(struct platform_device *pdev)
|
||||
goto __err;
|
||||
}
|
||||
|
||||
if ((err = snd_mts64_rawmidi_create(card)) < 0) {
|
||||
err = snd_mts64_rawmidi_create(card);
|
||||
if (err < 0) {
|
||||
snd_printd("Creating Rawmidi component failed\n");
|
||||
goto __err;
|
||||
}
|
||||
|
||||
/* init device */
|
||||
if ((err = mts64_device_init(p)) < 0)
|
||||
err = mts64_device_init(p);
|
||||
if (err < 0)
|
||||
goto __err;
|
||||
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
/* At this point card will be usable */
|
||||
if ((err = snd_card_register(card)) < 0) {
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_printd("Cannot register card\n");
|
||||
goto __err;
|
||||
}
|
||||
@ -1031,7 +1035,8 @@ static int __init snd_mts64_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = platform_driver_register(&snd_mts64_driver)) < 0)
|
||||
err = platform_driver_register(&snd_mts64_driver);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (parport_register_driver(&mts64_parport_driver) != 0) {
|
||||
|
@ -243,7 +243,8 @@ static int snd_opl3_timer1_init(struct snd_opl3 * opl3, int timer_no)
|
||||
tid.card = opl3->card->number;
|
||||
tid.device = timer_no;
|
||||
tid.subdevice = 0;
|
||||
if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) {
|
||||
err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer);
|
||||
if (err >= 0) {
|
||||
strcpy(timer->name, "AdLib timer #1");
|
||||
timer->private_data = opl3;
|
||||
timer->hw = snd_opl3_timer1;
|
||||
@ -263,7 +264,8 @@ static int snd_opl3_timer2_init(struct snd_opl3 * opl3, int timer_no)
|
||||
tid.card = opl3->card->number;
|
||||
tid.device = timer_no;
|
||||
tid.subdevice = 0;
|
||||
if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) {
|
||||
err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer);
|
||||
if (err >= 0) {
|
||||
strcpy(timer->name, "AdLib timer #2");
|
||||
timer->private_data = opl3;
|
||||
timer->hw = snd_opl3_timer2;
|
||||
@ -348,7 +350,8 @@ int snd_opl3_new(struct snd_card *card,
|
||||
spin_lock_init(&opl3->reg_lock);
|
||||
spin_lock_init(&opl3->timer_lock);
|
||||
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {
|
||||
err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops);
|
||||
if (err < 0) {
|
||||
snd_opl3_free(opl3);
|
||||
return err;
|
||||
}
|
||||
@ -396,19 +399,23 @@ int snd_opl3_create(struct snd_card *card,
|
||||
int err;
|
||||
|
||||
*ropl3 = NULL;
|
||||
if ((err = snd_opl3_new(card, hardware, &opl3)) < 0)
|
||||
err = snd_opl3_new(card, hardware, &opl3);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (! integrated) {
|
||||
if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) {
|
||||
opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)");
|
||||
if (!opl3->res_l_port) {
|
||||
snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port);
|
||||
snd_device_free(card, opl3);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (r_port != 0 &&
|
||||
(opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) {
|
||||
snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port);
|
||||
snd_device_free(card, opl3);
|
||||
return -EBUSY;
|
||||
if (r_port != 0) {
|
||||
opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)");
|
||||
if (!opl3->res_r_port) {
|
||||
snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port);
|
||||
snd_device_free(card, opl3);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
}
|
||||
opl3->l_port = l_port;
|
||||
@ -423,7 +430,8 @@ int snd_opl3_create(struct snd_card *card,
|
||||
break;
|
||||
default:
|
||||
opl3->command = &snd_opl2_command;
|
||||
if ((err = snd_opl3_detect(opl3)) < 0) {
|
||||
err = snd_opl3_detect(opl3);
|
||||
if (err < 0) {
|
||||
snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n",
|
||||
opl3->l_port, opl3->r_port);
|
||||
snd_device_free(card, opl3);
|
||||
@ -449,11 +457,14 @@ int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (timer1_dev >= 0)
|
||||
if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0)
|
||||
if (timer1_dev >= 0) {
|
||||
err = snd_opl3_timer1_init(opl3, timer1_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (timer2_dev >= 0) {
|
||||
if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) {
|
||||
err = snd_opl3_timer2_init(opl3, timer2_dev);
|
||||
if (err < 0) {
|
||||
snd_device_free(opl3->card, opl3->timer1);
|
||||
opl3->timer1 = NULL;
|
||||
return err;
|
||||
@ -477,7 +488,8 @@ int snd_opl3_hwdep_new(struct snd_opl3 * opl3,
|
||||
|
||||
/* create hardware dependent device (direct FM) */
|
||||
|
||||
if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) {
|
||||
err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw);
|
||||
if (err < 0) {
|
||||
snd_device_free(card, opl3);
|
||||
return err;
|
||||
}
|
||||
|
@ -180,8 +180,7 @@ static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op,
|
||||
if (vp2->state == SNDRV_OPL3_ST_ON_2OP) {
|
||||
/* kill two voices, EXPENSIVE */
|
||||
bp++;
|
||||
voice_time = (voice_time > vp->time) ?
|
||||
voice_time : vp->time;
|
||||
voice_time = max(voice_time, vp2->time);
|
||||
}
|
||||
} else {
|
||||
/* allocate 2op voice */
|
||||
|
@ -136,7 +136,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
|
||||
if (snd_BUG_ON(!arg))
|
||||
return -ENXIO;
|
||||
|
||||
if ((err = snd_opl3_synth_setup(opl3)) < 0)
|
||||
err = snd_opl3_synth_setup(opl3);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* fill the argument data */
|
||||
@ -144,7 +145,8 @@ static int snd_opl3_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure)
|
||||
arg->addr.client = opl3->oss_chset->client;
|
||||
arg->addr.port = opl3->oss_chset->port;
|
||||
|
||||
if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
|
||||
err = snd_opl3_synth_use_inc(opl3);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
opl3->synth_mode = SNDRV_OPL3_MODE_SYNTH;
|
||||
|
@ -92,7 +92,8 @@ static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe
|
||||
struct snd_opl3 *opl3 = private_data;
|
||||
int err;
|
||||
|
||||
if ((err = snd_opl3_synth_setup(opl3)) < 0)
|
||||
err = snd_opl3_synth_setup(opl3);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (use_internal_drums) {
|
||||
@ -107,7 +108,8 @@ static int snd_opl3_synth_use(void *private_data, struct snd_seq_port_subscribe
|
||||
}
|
||||
|
||||
if (info->sender.client != SNDRV_SEQ_CLIENT_SYSTEM) {
|
||||
if ((err = snd_opl3_synth_use_inc(opl3)) < 0)
|
||||
err = snd_opl3_synth_use_inc(opl3);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
opl3->synth_mode = SNDRV_OPL3_MODE_SEQ;
|
||||
@ -227,7 +229,8 @@ static int snd_opl3_seq_probe(struct device *_dev)
|
||||
if (client < 0)
|
||||
return client;
|
||||
|
||||
if ((err = snd_opl3_synth_create_port(opl3)) < 0) {
|
||||
err = snd_opl3_synth_create_port(opl3);
|
||||
if (err < 0) {
|
||||
snd_seq_delete_kernel_client(client);
|
||||
opl3->seq_client = -1;
|
||||
return err;
|
||||
|
@ -749,7 +749,8 @@ static int snd_portman_probe(struct platform_device *pdev)
|
||||
goto free_pardev;
|
||||
}
|
||||
|
||||
if ((err = portman_create(card, pardev, &pm)) < 0) {
|
||||
err = portman_create(card, pardev, &pm);
|
||||
if (err < 0) {
|
||||
snd_printd("Cannot create main component\n");
|
||||
goto release_pardev;
|
||||
}
|
||||
@ -762,19 +763,22 @@ static int snd_portman_probe(struct platform_device *pdev)
|
||||
goto __err;
|
||||
}
|
||||
|
||||
if ((err = snd_portman_rawmidi_create(card)) < 0) {
|
||||
err = snd_portman_rawmidi_create(card);
|
||||
if (err < 0) {
|
||||
snd_printd("Creating Rawmidi component failed\n");
|
||||
goto __err;
|
||||
}
|
||||
|
||||
/* init device */
|
||||
if ((err = portman_device_init(pm)) < 0)
|
||||
err = portman_device_init(pm);
|
||||
if (err < 0)
|
||||
goto __err;
|
||||
|
||||
platform_set_drvdata(pdev, card);
|
||||
|
||||
/* At this point card will be usable */
|
||||
if ((err = snd_card_register(card)) < 0) {
|
||||
err = snd_card_register(card);
|
||||
if (err < 0) {
|
||||
snd_printd("Cannot register card\n");
|
||||
goto __err;
|
||||
}
|
||||
@ -831,7 +835,8 @@ static int __init snd_portman_module_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = platform_driver_register(&snd_portman_driver)) < 0)
|
||||
err = platform_driver_register(&snd_portman_driver);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (parport_register_driver(&portman_parport_driver) != 0) {
|
||||
|
@ -783,7 +783,8 @@ static int snd_uart16550_create(struct snd_card *card,
|
||||
int err;
|
||||
|
||||
|
||||
if ((uart = kzalloc(sizeof(*uart), GFP_KERNEL)) == NULL)
|
||||
uart = kzalloc(sizeof(*uart), GFP_KERNEL);
|
||||
if (!uart)
|
||||
return -ENOMEM;
|
||||
uart->adaptor = adaptor;
|
||||
uart->card = card;
|
||||
@ -792,7 +793,8 @@ static int snd_uart16550_create(struct snd_card *card,
|
||||
uart->base = iobase;
|
||||
uart->drop_on_full = droponfull;
|
||||
|
||||
if ((err = snd_uart16550_detect(uart)) <= 0) {
|
||||
err = snd_uart16550_detect(uart);
|
||||
if (err <= 0) {
|
||||
printk(KERN_ERR "no UART detected at 0x%lx\n", iobase);
|
||||
snd_uart16550_free(uart);
|
||||
return -ENODEV;
|
||||
@ -818,7 +820,8 @@ static int snd_uart16550_create(struct snd_card *card,
|
||||
uart->timer_running = 0;
|
||||
|
||||
/* Register device */
|
||||
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops)) < 0) {
|
||||
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, uart, &ops);
|
||||
if (err < 0) {
|
||||
snd_uart16550_free(uart);
|
||||
return err;
|
||||
}
|
||||
@ -932,14 +935,10 @@ static int snd_serial_probe(struct platform_device *devptr)
|
||||
strcpy(card->driver, "Serial");
|
||||
strcpy(card->shortname, "Serial MIDI (UART16550A)");
|
||||
|
||||
if ((err = snd_uart16550_create(card,
|
||||
port[dev],
|
||||
irq[dev],
|
||||
speed[dev],
|
||||
base[dev],
|
||||
adaptor[dev],
|
||||
droponfull[dev],
|
||||
&uart)) < 0)
|
||||
err = snd_uart16550_create(card, port[dev], irq[dev], speed[dev],
|
||||
base[dev], adaptor[dev], droponfull[dev],
|
||||
&uart);
|
||||
if (err < 0)
|
||||
goto _err;
|
||||
|
||||
err = snd_uart16550_rmidi(uart, 0, outs[dev], ins[dev], &uart->rmidi);
|
||||
@ -952,7 +951,8 @@ static int snd_serial_probe(struct platform_device *devptr)
|
||||
uart->base,
|
||||
uart->irq);
|
||||
|
||||
if ((err = snd_card_register(card)) < 0)
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto _err;
|
||||
|
||||
platform_set_drvdata(devptr, card);
|
||||
@ -992,7 +992,8 @@ static int __init alsa_card_serial_init(void)
|
||||
{
|
||||
int i, cards, err;
|
||||
|
||||
if ((err = platform_driver_register(&snd_serial_driver)) < 0)
|
||||
err = platform_driver_register(&snd_serial_driver);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
cards = 0;
|
||||
|
@ -110,20 +110,25 @@ static int vx_transfer_end(struct vx_core *chip, int cmd)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((err = vx_reset_chk(chip)) < 0)
|
||||
err = vx_reset_chk(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* irq MESS_READ/WRITE_END */
|
||||
if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
|
||||
err = vx_send_irq_dsp(chip, cmd);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Wait CHK = 1 */
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
|
||||
err = vx_wait_isr_bit(chip, ISR_CHK);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* If error, Read RX */
|
||||
if ((err = vx_inb(chip, ISR)) & ISR_ERR) {
|
||||
if ((err = vx_wait_for_rx_full(chip)) < 0) {
|
||||
err = vx_inb(chip, ISR);
|
||||
if (err & ISR_ERR) {
|
||||
err = vx_wait_for_rx_full(chip);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "transfer_end: error in rx_full\n");
|
||||
return err;
|
||||
}
|
||||
@ -232,7 +237,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
|
||||
if (chip->chip_status & VX_STAT_IS_STALE)
|
||||
return -EBUSY;
|
||||
|
||||
if ((err = vx_reset_chk(chip)) < 0) {
|
||||
err = vx_reset_chk(chip);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "vx_send_msg: vx_reset_chk error\n");
|
||||
return err;
|
||||
}
|
||||
@ -254,7 +260,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
|
||||
rmh->Cmd[0] &= MASK_1_WORD_COMMAND;
|
||||
|
||||
/* Wait for TX empty */
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
|
||||
err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "vx_send_msg: wait tx empty error\n");
|
||||
return err;
|
||||
}
|
||||
@ -265,18 +272,21 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
|
||||
vx_outb(chip, TXL, rmh->Cmd[0] & 0xff);
|
||||
|
||||
/* Trigger irq MESSAGE */
|
||||
if ((err = vx_send_irq_dsp(chip, IRQ_MESSAGE)) < 0) {
|
||||
err = vx_send_irq_dsp(chip, IRQ_MESSAGE);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "vx_send_msg: send IRQ_MESSAGE error\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Wait for CHK = 1 */
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
|
||||
err = vx_wait_isr_bit(chip, ISR_CHK);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* If error, get error value from RX */
|
||||
if (vx_inb(chip, ISR) & ISR_ERR) {
|
||||
if ((err = vx_wait_for_rx_full(chip)) < 0) {
|
||||
err = vx_wait_for_rx_full(chip);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "vx_send_msg: rx_full read error\n");
|
||||
return err;
|
||||
}
|
||||
@ -292,7 +302,8 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
|
||||
if (rmh->LgCmd > 1) {
|
||||
for (i = 1; i < rmh->LgCmd; i++) {
|
||||
/* Wait for TX ready */
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
|
||||
err = vx_wait_isr_bit(chip, ISR_TX_READY);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "vx_send_msg: tx_ready error\n");
|
||||
return err;
|
||||
}
|
||||
@ -303,13 +314,15 @@ int vx_send_msg_nolock(struct vx_core *chip, struct vx_rmh *rmh)
|
||||
vx_outb(chip, TXL, rmh->Cmd[i] & 0xff);
|
||||
|
||||
/* Trigger irq MESS_READ_NEXT */
|
||||
if ((err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT)) < 0) {
|
||||
err = vx_send_irq_dsp(chip, IRQ_MESS_READ_NEXT);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "vx_send_msg: IRQ_READ_NEXT error\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
/* Wait for TX empty */
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_TX_READY)) < 0) {
|
||||
err = vx_wait_isr_bit(chip, ISR_TX_READY);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_DEBUG "vx_send_msg: TX_READY error\n");
|
||||
return err;
|
||||
}
|
||||
@ -362,17 +375,21 @@ int vx_send_rih_nolock(struct vx_core *chip, int cmd)
|
||||
#if 0
|
||||
printk(KERN_DEBUG "send_rih: cmd = 0x%x\n", cmd);
|
||||
#endif
|
||||
if ((err = vx_reset_chk(chip)) < 0)
|
||||
err = vx_reset_chk(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* send the IRQ */
|
||||
if ((err = vx_send_irq_dsp(chip, cmd)) < 0)
|
||||
err = vx_send_irq_dsp(chip, cmd);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* Wait CHK = 1 */
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
|
||||
err = vx_wait_isr_bit(chip, ISR_CHK);
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* If error, read RX */
|
||||
if (vx_inb(chip, ISR) & ISR_ERR) {
|
||||
if ((err = vx_wait_for_rx_full(chip)) < 0)
|
||||
err = vx_wait_for_rx_full(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = vx_inb(chip, RXH) << 16;
|
||||
err |= vx_inb(chip, RXM) << 8;
|
||||
@ -648,7 +665,8 @@ int snd_vx_dsp_boot(struct vx_core *chip, const struct firmware *boot)
|
||||
vx_reset_board(chip, cold_reset);
|
||||
vx_validate_irq(chip, 0);
|
||||
|
||||
if ((err = snd_vx_load_boot_image(chip, boot)) < 0)
|
||||
err = snd_vx_load_boot_image(chip, boot);
|
||||
if (err < 0)
|
||||
return err;
|
||||
msleep(10);
|
||||
|
||||
@ -678,7 +696,8 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
|
||||
for (i = 0; i < dsp->size; i += 3) {
|
||||
image = dsp->data + i;
|
||||
/* Wait DSP ready for a new read */
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_TX_EMPTY)) < 0) {
|
||||
err = vx_wait_isr_bit(chip, ISR_TX_EMPTY);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR
|
||||
"dsp loading error at position %d\n", i);
|
||||
return err;
|
||||
@ -698,7 +717,8 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
|
||||
|
||||
msleep(200);
|
||||
|
||||
if ((err = vx_wait_isr_bit(chip, ISR_CHK)) < 0)
|
||||
err = vx_wait_isr_bit(chip, ISR_CHK);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
vx_toggle_dac_mute(chip, 0);
|
||||
|
@ -78,15 +78,19 @@ int snd_vx_setup_firmware(struct vx_core *chip)
|
||||
|
||||
/* ok, we reached to the last one */
|
||||
/* create the devices if not built yet */
|
||||
if ((err = snd_vx_pcm_new(chip)) < 0)
|
||||
err = snd_vx_pcm_new(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if ((err = snd_vx_mixer_new(chip)) < 0)
|
||||
err = snd_vx_mixer_new(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (chip->ops->add_controls)
|
||||
if ((err = chip->ops->add_controls(chip)) < 0)
|
||||
if (chip->ops->add_controls) {
|
||||
err = chip->ops->add_controls(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->chip_status |= VX_STAT_DEVICE_INIT;
|
||||
chip->chip_status |= VX_STAT_CHIP_INIT;
|
||||
|
@ -910,7 +910,8 @@ int snd_vx_mixer_new(struct vx_core *chip)
|
||||
temp = vx_control_output_level;
|
||||
temp.index = i;
|
||||
temp.tlv.p = chip->hw->output_level_db_scale;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -921,22 +922,26 @@ int snd_vx_mixer_new(struct vx_core *chip)
|
||||
temp.index = i;
|
||||
temp.name = "PCM Playback Volume";
|
||||
temp.private_value = val;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
temp = vx_control_output_switch;
|
||||
temp.index = i;
|
||||
temp.private_value = val;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
temp = vx_control_monitor_gain;
|
||||
temp.index = i;
|
||||
temp.private_value = val;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
temp = vx_control_monitor_switch;
|
||||
temp.index = i;
|
||||
temp.private_value = val;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
for (i = 0; i < chip->hw->num_outs; i++) {
|
||||
@ -944,20 +949,25 @@ int snd_vx_mixer_new(struct vx_core *chip)
|
||||
temp.index = i;
|
||||
temp.name = "PCM Capture Volume";
|
||||
temp.private_value = (i * 2) | (1 << 8);
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Audio source */
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&vx_control_audio_src, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* clock mode */
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&vx_control_clock_mode, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* IEC958 controls */
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958_mask, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
/* VU, peak, saturation meters */
|
||||
for (c = 0; c < 2; c++) {
|
||||
@ -968,7 +978,8 @@ int snd_vx_mixer_new(struct vx_core *chip)
|
||||
temp = vx_control_saturation;
|
||||
temp.index = i;
|
||||
temp.private_value = val;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
sprintf(name, "%s VU Meter", dir[c]);
|
||||
@ -976,14 +987,16 @@ int snd_vx_mixer_new(struct vx_core *chip)
|
||||
temp.index = i;
|
||||
temp.name = name;
|
||||
temp.private_value = val;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
sprintf(name, "%s Peak Meter", dir[c]);
|
||||
temp = vx_control_peak_meter;
|
||||
temp.index = i;
|
||||
temp.name = name;
|
||||
temp.private_value = val;
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
|
||||
err = snd_ctl_add(card, snd_ctl_new1(&temp, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
@ -341,10 +341,12 @@ static int vx_toggle_pipe(struct vx_core *chip, struct vx_pipe *pipe, int state)
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = vx_conf_pipe(chip, pipe)) < 0)
|
||||
err = vx_conf_pipe(chip, pipe);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if ((err = vx_send_irqa(chip)) < 0)
|
||||
err = vx_send_irqa(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* If it completes successfully, wait for the pipes
|
||||
@ -680,8 +682,9 @@ static void vx_pcm_playback_transfer(struct vx_core *chip,
|
||||
if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE))
|
||||
return;
|
||||
for (i = 0; i < nchunks; i++) {
|
||||
if ((err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe,
|
||||
chip->ibl.size)) < 0)
|
||||
err = vx_pcm_playback_transfer_chunk(chip, runtime, pipe,
|
||||
chip->ibl.size);
|
||||
if (err < 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -698,7 +701,8 @@ static void vx_pcm_playback_update(struct vx_core *chip,
|
||||
struct snd_pcm_runtime *runtime = subs->runtime;
|
||||
|
||||
if (pipe->running && ! (chip->chip_status & VX_STAT_IS_STALE)) {
|
||||
if ((err = vx_update_pipe_position(chip, runtime, pipe)) < 0)
|
||||
err = vx_update_pipe_position(chip, runtime, pipe);
|
||||
if (err < 0)
|
||||
return;
|
||||
if (pipe->transferred >= (int)runtime->period_size) {
|
||||
pipe->transferred %= runtime->period_size;
|
||||
@ -747,11 +751,13 @@ static int vx_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
|
||||
pipe->running = 0;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if ((err = vx_toggle_pipe(chip, pipe, 0)) < 0)
|
||||
err = vx_toggle_pipe(chip, pipe, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if ((err = vx_toggle_pipe(chip, pipe, 1)) < 0)
|
||||
err = vx_toggle_pipe(chip, pipe, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
@ -792,13 +798,15 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
|
||||
snd_printdd(KERN_DEBUG "reopen the pipe with data_mode = %d\n", data_mode);
|
||||
vx_init_rmh(&rmh, CMD_FREE_PIPE);
|
||||
vx_set_pipe_cmd_params(&rmh, 0, pipe->number, 0);
|
||||
if ((err = vx_send_msg(chip, &rmh)) < 0)
|
||||
err = vx_send_msg(chip, &rmh);
|
||||
if (err < 0)
|
||||
return err;
|
||||
vx_init_rmh(&rmh, CMD_RES_PIPE);
|
||||
vx_set_pipe_cmd_params(&rmh, 0, pipe->number, pipe->channels);
|
||||
if (data_mode)
|
||||
rmh.Cmd[0] |= BIT_DATA_MODE;
|
||||
if ((err = vx_send_msg(chip, &rmh)) < 0)
|
||||
err = vx_send_msg(chip, &rmh);
|
||||
if (err < 0)
|
||||
return err;
|
||||
pipe->data_mode = data_mode;
|
||||
}
|
||||
@ -810,7 +818,8 @@ static int vx_pcm_prepare(struct snd_pcm_substream *subs)
|
||||
}
|
||||
vx_set_clock(chip, runtime->rate);
|
||||
|
||||
if ((err = vx_set_format(chip, pipe, runtime)) < 0)
|
||||
err = vx_set_format(chip, pipe, runtime);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (vx_is_pcmcia(chip)) {
|
||||
@ -1187,7 +1196,8 @@ int snd_vx_pcm_new(struct vx_core *chip)
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
if ((err = vx_init_audio_io(chip)) < 0)
|
||||
err = vx_init_audio_io(chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < chip->hw->num_codecs; i++) {
|
||||
|
@ -18,8 +18,25 @@ config SND_DICE
|
||||
select SND_HWDEP
|
||||
select SND_FIREWIRE_LIB
|
||||
help
|
||||
Say Y here to include support for many DACs based on the DICE
|
||||
chip family (DICE-II/Jr/Mini) which TC Applied Technologies produces.
|
||||
Say Y here to include support for devices based on the DICE chip family
|
||||
(DICE-II/TCD2210(Mini)/TCD2220(Jr.)) which TC Applied Technologies (TCAT) produced.
|
||||
* Allen and Heath Zed R16
|
||||
* Alesis iO 14/26 FireWire, MasterControl, MultiMix 8/12/16 FireWire
|
||||
* Avid Mbox 3 Pro
|
||||
* FlexRadio Systems FLEX-3000, FLEX-5000
|
||||
* Focusrite Liquid Saffire 56
|
||||
* Focusrite Saffire Pro 14, Pro 24, Pro 24 DSP, Pro 26, Pro 40(TCD2220)
|
||||
* Harman Music Group Lexicon I-ONIX FW810S
|
||||
* Loud Technologies Mackie Onyx Blackbird, Onyx 820i/1220i/1620i/1640i (latter models)
|
||||
* M-Audio ProFire 610/2626
|
||||
* Mytek Stereo192-DSD DAC
|
||||
* Midas Klark Teknik VeniceF series
|
||||
* PreSonus FireStudio, FireStudio Mobile, FireStudio Project, FireStudio Tube
|
||||
* PreSonus StudioLive 16.4.2, 16.0.2, 24.4.2, 32.4.2
|
||||
* Solid State Logic Duende Classic, Duende Mini
|
||||
* TC Electronic Studio Konnekt 48, Konnekt 24D, Konnekt Live, Impact Twin
|
||||
* TC Electronic Digital Konnekt x32, Desktop Konnekt 6
|
||||
* Weiss Engineering ADC2, Vesta, Minerva, AFI1, DAC1, INT202, DAC202
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-dice.
|
||||
@ -38,7 +55,7 @@ config SND_OXFW
|
||||
* Mackie(Loud) Onyx 1640i (former model)
|
||||
* Mackie(Loud) Onyx Satellite
|
||||
* Mackie(Loud) Tapco Link.Firewire
|
||||
* Mackie(Loud) d.4 pro
|
||||
* Mackie(Loud) d.2 pro/d.4 pro (built-in FireWire card with OXFW971 ASIC)
|
||||
* Mackie(Loud) U.420/U.420d
|
||||
* TASCAM FireOne
|
||||
* Stanton Controllers & Systems 1 Deck/Mixer
|
||||
@ -84,7 +101,7 @@ config SND_BEBOB
|
||||
* PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
|
||||
* BridgeCo RDAudio1/Audio5
|
||||
* Mackie Onyx 1220/1620/1640 (FireWire I/O Card)
|
||||
* Mackie d.2 (FireWire Option) and d.2 Pro
|
||||
* Mackie d.2 (optional FireWire card with DM1000 ASIC)
|
||||
* Stanton FinalScratch 2 (ScratchAmp)
|
||||
* Tascam IF-FW/DM
|
||||
* Behringer XENIX UFX 1204/1604
|
||||
@ -110,6 +127,7 @@ config SND_BEBOB
|
||||
* M-Audio Ozonic/NRV10/ProfireLightBridge
|
||||
* M-Audio FireWire 1814/ProjectMix IO
|
||||
* Digidesign Mbox 2 Pro
|
||||
* ToneWeal FW66
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-bebob.
|
||||
@ -148,12 +166,16 @@ config SND_FIREWIRE_MOTU
|
||||
select SND_HWDEP
|
||||
help
|
||||
Say Y here to enable support for FireWire devices which MOTU produced:
|
||||
* 828
|
||||
* 896
|
||||
* 828mk2
|
||||
* Traveler
|
||||
* Ultralite
|
||||
* 8pre
|
||||
* 828mk3 (FireWire only)
|
||||
* 828mk3 (Hybrid)
|
||||
* Ultralite mk3 (FireWire only)
|
||||
* Ultralite mk3 (Hybrid)
|
||||
* Audio Express
|
||||
* 4pre
|
||||
|
||||
|
@ -410,10 +410,10 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
* @s: the AMDTP stream to initialize
|
||||
* @unit: the target of the stream
|
||||
* @dir: the direction of stream
|
||||
* @flags: the packet transmission method to use
|
||||
* @flags: the details of the streaming protocol consist of cip_flags enumeration-constants.
|
||||
*/
|
||||
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags)
|
||||
enum amdtp_stream_direction dir, unsigned int flags)
|
||||
{
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
|
||||
|
@ -45,5 +45,5 @@ void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
|
||||
struct snd_rawmidi_substream *midi);
|
||||
|
||||
int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags);
|
||||
enum amdtp_stream_direction dir, unsigned int flags);
|
||||
#endif
|
||||
|
@ -49,7 +49,7 @@ TRACE_EVENT(amdtp_packet,
|
||||
__entry->data_blocks = data_blocks;
|
||||
__entry->data_block_counter = data_block_counter,
|
||||
__entry->packet_index = packet_index;
|
||||
__entry->irq = !!in_interrupt();
|
||||
__entry->irq = !!in_softirq();
|
||||
__entry->index = index;
|
||||
),
|
||||
TP_printk(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,8 @@
|
||||
* @CIP_NO_HEADERS: a lack of headers in packets
|
||||
* @CIP_UNALIGHED_DBC: Only for in-stream. The value of dbc is not alighed to
|
||||
* the value of current SYT_INTERVAL; e.g. initial value is not zero.
|
||||
* @CIP_UNAWARE_SYT: For outgoing packet, the value in SYT field of CIP is 0xffff.
|
||||
* For incoming packet, the value in SYT field of CIP is not handled.
|
||||
*/
|
||||
enum cip_flags {
|
||||
CIP_NONBLOCKING = 0x00,
|
||||
@ -48,6 +50,7 @@ enum cip_flags {
|
||||
CIP_HEADER_WITHOUT_EOH = 0x80,
|
||||
CIP_NO_HEADER = 0x100,
|
||||
CIP_UNALIGHED_DBC = 0x200,
|
||||
CIP_UNAWARE_SYT = 0x400,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -112,7 +115,8 @@ typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
|
||||
struct amdtp_domain;
|
||||
struct amdtp_stream {
|
||||
struct fw_unit *unit;
|
||||
enum cip_flags flags;
|
||||
// The combination of cip_flags enumeration-constants.
|
||||
unsigned int flags;
|
||||
enum amdtp_stream_direction direction;
|
||||
struct mutex mutex;
|
||||
|
||||
@ -134,19 +138,37 @@ struct amdtp_stream {
|
||||
// Fixed interval of dbc between previos/current
|
||||
// packets.
|
||||
unsigned int dbc_interval;
|
||||
|
||||
// The device starts multiplexing events to the packet.
|
||||
bool event_starts;
|
||||
|
||||
struct {
|
||||
struct seq_desc *descs;
|
||||
unsigned int size;
|
||||
unsigned int tail;
|
||||
} cache;
|
||||
} tx;
|
||||
struct {
|
||||
// To calculate CIP data blocks and tstamp.
|
||||
unsigned int transfer_delay;
|
||||
unsigned int seq_index;
|
||||
|
||||
// To generate CIP header.
|
||||
unsigned int fdf;
|
||||
int syt_override;
|
||||
|
||||
// To generate constant hardware IRQ.
|
||||
unsigned int event_count;
|
||||
unsigned int events_per_period;
|
||||
|
||||
// To calculate CIP data blocks and tstamp.
|
||||
struct {
|
||||
struct seq_desc *descs;
|
||||
unsigned int size;
|
||||
unsigned int tail;
|
||||
unsigned int head;
|
||||
} seq;
|
||||
|
||||
unsigned int data_block_state;
|
||||
unsigned int syt_offset_state;
|
||||
unsigned int last_syt_offset;
|
||||
|
||||
struct amdtp_stream *replay_target;
|
||||
unsigned int cache_head;
|
||||
} rx;
|
||||
} ctx_data;
|
||||
|
||||
@ -157,20 +179,21 @@ struct amdtp_stream {
|
||||
unsigned int sph;
|
||||
unsigned int fmt;
|
||||
|
||||
/* Internal flags. */
|
||||
// Internal flags.
|
||||
unsigned int transfer_delay;
|
||||
enum cip_sfc sfc;
|
||||
unsigned int syt_interval;
|
||||
|
||||
/* For a PCM substream processing. */
|
||||
struct snd_pcm_substream *pcm;
|
||||
struct work_struct period_work;
|
||||
snd_pcm_uframes_t pcm_buffer_pointer;
|
||||
unsigned int pcm_period_pointer;
|
||||
|
||||
/* To wait for first packet. */
|
||||
bool callbacked;
|
||||
wait_queue_head_t callback_wait;
|
||||
u32 start_cycle;
|
||||
// To start processing content of packets at the same cycle in several contexts for
|
||||
// each direction.
|
||||
bool ready_processing;
|
||||
wait_queue_head_t ready_wait;
|
||||
unsigned int next_cycle;
|
||||
|
||||
/* For backends to process data blocks. */
|
||||
void *protocol;
|
||||
@ -184,7 +207,7 @@ struct amdtp_stream {
|
||||
};
|
||||
|
||||
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir, enum cip_flags flags,
|
||||
enum amdtp_stream_direction dir, unsigned int flags,
|
||||
unsigned int fmt,
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads,
|
||||
unsigned int protocol_size);
|
||||
@ -259,21 +282,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
|
||||
return sfc & 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdtp_stream_wait_callback - sleep till callbacked or timeout
|
||||
* @s: the AMDTP stream
|
||||
* @timeout: msec till timeout
|
||||
*
|
||||
* If this function return false, the AMDTP stream should be stopped.
|
||||
*/
|
||||
static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
|
||||
unsigned int timeout)
|
||||
{
|
||||
return wait_event_timeout(s->callback_wait,
|
||||
s->callbacked,
|
||||
msecs_to_jiffies(timeout)) > 0;
|
||||
}
|
||||
|
||||
struct seq_desc {
|
||||
unsigned int syt_offset;
|
||||
unsigned int data_blocks;
|
||||
@ -287,13 +295,16 @@ struct amdtp_domain {
|
||||
|
||||
struct amdtp_stream *irq_target;
|
||||
|
||||
struct seq_desc *seq_descs;
|
||||
unsigned int seq_size;
|
||||
unsigned int seq_tail;
|
||||
struct {
|
||||
unsigned int tx_init_skip;
|
||||
unsigned int tx_start;
|
||||
unsigned int rx_start;
|
||||
} processing_cycle;
|
||||
|
||||
unsigned int data_block_state;
|
||||
unsigned int syt_offset_state;
|
||||
unsigned int last_syt_offset;
|
||||
struct {
|
||||
bool enable:1;
|
||||
bool on_the_fly:1;
|
||||
} replay;
|
||||
};
|
||||
|
||||
int amdtp_domain_init(struct amdtp_domain *d);
|
||||
@ -302,7 +313,8 @@ void amdtp_domain_destroy(struct amdtp_domain *d);
|
||||
int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s,
|
||||
int channel, int speed);
|
||||
|
||||
int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle);
|
||||
int amdtp_domain_start(struct amdtp_domain *d, unsigned int tx_init_skip_cycles, bool replay_seq,
|
||||
bool replay_on_the_fly);
|
||||
void amdtp_domain_stop(struct amdtp_domain *d);
|
||||
|
||||
static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d,
|
||||
@ -319,4 +331,25 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d,
|
||||
struct amdtp_stream *s);
|
||||
int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s);
|
||||
|
||||
/**
|
||||
* amdtp_domain_wait_ready - sleep till being ready to process packets or timeout
|
||||
* @d: the AMDTP domain
|
||||
* @timeout_ms: msec till timeout
|
||||
*
|
||||
* If this function return false, the AMDTP domain should be stopped.
|
||||
*/
|
||||
static inline bool amdtp_domain_wait_ready(struct amdtp_domain *d, unsigned int timeout_ms)
|
||||
{
|
||||
struct amdtp_stream *s;
|
||||
|
||||
list_for_each_entry(s, &d->streams, list) {
|
||||
unsigned int j = msecs_to_jiffies(timeout_ms);
|
||||
|
||||
if (wait_event_interruptible_timeout(s->ready_wait, s->ready_processing, j) <= 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -40,14 +40,12 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
|
||||
#define VEN_EDIROL 0x000040ab
|
||||
#define VEN_PRESONUS 0x00000a92
|
||||
#define VEN_BRIDGECO 0x000007f5
|
||||
#define VEN_MACKIE1 0x0000000f
|
||||
#define VEN_MACKIE2 0x00000ff2
|
||||
#define VEN_MACKIE 0x00000ff2
|
||||
#define VEN_STANTON 0x00001260
|
||||
#define VEN_TASCAM 0x0000022e
|
||||
#define VEN_BEHRINGER 0x00001564
|
||||
#define VEN_APOGEE 0x000003db
|
||||
#define VEN_ESI 0x00000f1b
|
||||
#define VEN_ACOUSTIC 0x00000002
|
||||
#define VEN_CME 0x0000000a
|
||||
#define VEN_PHONIC 0x00001496
|
||||
#define VEN_LYNX 0x000019e5
|
||||
@ -56,14 +54,15 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
|
||||
#define VEN_TERRATEC 0x00000aac
|
||||
#define VEN_YAMAHA 0x0000a0de
|
||||
#define VEN_FOCUSRITE 0x0000130e
|
||||
#define VEN_MAUDIO1 0x00000d6c
|
||||
#define VEN_MAUDIO2 0x000007f5
|
||||
#define VEN_MAUDIO 0x00000d6c
|
||||
#define VEN_DIGIDESIGN 0x00a07e
|
||||
#define OUI_SHOUYO 0x002327
|
||||
|
||||
#define MODEL_FOCUSRITE_SAFFIRE_BOTH 0x00000000
|
||||
#define MODEL_MAUDIO_AUDIOPHILE_BOTH 0x00010060
|
||||
#define MODEL_MAUDIO_FW1814 0x00010071
|
||||
#define MODEL_MAUDIO_PROJECTMIX 0x00010091
|
||||
#define MODEL_MAUDIO_PROFIRELIGHTBRIDGE 0x000100a1
|
||||
|
||||
static int
|
||||
name_device(struct snd_bebob *bebob)
|
||||
@ -74,7 +73,6 @@ name_device(struct snd_bebob *bebob)
|
||||
u32 hw_id;
|
||||
u32 data[2] = {0};
|
||||
u32 revision;
|
||||
u32 version;
|
||||
int err;
|
||||
|
||||
/* get vendor name from root directory */
|
||||
@ -107,12 +105,6 @@ name_device(struct snd_bebob *bebob)
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_BEBOB_VERSION,
|
||||
&version);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
bebob->version = version;
|
||||
|
||||
strcpy(bebob->card->driver, "BeBoB");
|
||||
strcpy(bebob->card->shortname, model);
|
||||
strcpy(bebob->card->mixername, model);
|
||||
@ -135,6 +127,9 @@ bebob_card_free(struct snd_card *card)
|
||||
mutex_unlock(&devices_mutex);
|
||||
|
||||
snd_bebob_stream_destroy_duplex(bebob);
|
||||
|
||||
mutex_destroy(&bebob->mutex);
|
||||
fw_unit_put(bebob->unit);
|
||||
}
|
||||
|
||||
static const struct snd_bebob_spec *
|
||||
@ -162,16 +157,55 @@ check_audiophile_booted(struct fw_unit *unit)
|
||||
return strncmp(name, "FW Audiophile Bootloader", 24) != 0;
|
||||
}
|
||||
|
||||
static void
|
||||
do_registration(struct work_struct *work)
|
||||
static int detect_quirks(struct snd_bebob *bebob, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
if (entry->vendor_id == VEN_MAUDIO) {
|
||||
switch (entry->model_id) {
|
||||
case MODEL_MAUDIO_PROFIRELIGHTBRIDGE:
|
||||
// M-Audio ProFire Lightbridge has a quirk to transfer packets with
|
||||
// discontinuous cycle or data block counter in early stage of packet
|
||||
// streaming. The cycle span from the first packet with event is variable.
|
||||
bebob->quirks |= SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC;
|
||||
break;
|
||||
case MODEL_MAUDIO_FW1814:
|
||||
case MODEL_MAUDIO_PROJECTMIX:
|
||||
// At high sampling rate, M-Audio special firmware transmits empty packet
|
||||
// with the value of dbc incremented by 8.
|
||||
bebob->quirks |= SND_BEBOB_QUIRK_WRONG_DBC;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_bebob *bebob =
|
||||
container_of(work, struct snd_bebob, dwork.work);
|
||||
unsigned int card_index;
|
||||
struct snd_card *card;
|
||||
struct snd_bebob *bebob;
|
||||
const struct snd_bebob_spec *spec;
|
||||
int err;
|
||||
|
||||
if (bebob->registered)
|
||||
return;
|
||||
if (entry->vendor_id == VEN_FOCUSRITE &&
|
||||
entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
|
||||
spec = get_saffire_spec(unit);
|
||||
else if (entry->vendor_id == VEN_MAUDIO &&
|
||||
entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
|
||||
!check_audiophile_booted(unit))
|
||||
spec = NULL;
|
||||
else
|
||||
spec = (const struct snd_bebob_spec *)entry->driver_data;
|
||||
|
||||
if (spec == NULL) {
|
||||
// To boot up M-Audio models.
|
||||
if (entry->vendor_id == VEN_MAUDIO || entry->vendor_id == VEN_BRIDGECO)
|
||||
return snd_bebob_maudio_load_firmware(unit);
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mutex_lock(&devices_mutex);
|
||||
for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
|
||||
@ -180,27 +214,40 @@ do_registration(struct work_struct *work)
|
||||
}
|
||||
if (card_index >= SNDRV_CARDS) {
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
err = snd_card_new(&bebob->unit->device, index[card_index],
|
||||
id[card_index], THIS_MODULE, 0, &bebob->card);
|
||||
err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
|
||||
sizeof(*bebob), &card);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
return err;
|
||||
}
|
||||
card->private_free = bebob_card_free;
|
||||
set_bit(card_index, devices_used);
|
||||
mutex_unlock(&devices_mutex);
|
||||
|
||||
bebob->card->private_free = bebob_card_free;
|
||||
bebob->card->private_data = bebob;
|
||||
bebob = card->private_data;
|
||||
bebob->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, bebob);
|
||||
bebob->card = card;
|
||||
bebob->card_index = card_index;
|
||||
|
||||
bebob->spec = spec;
|
||||
mutex_init(&bebob->mutex);
|
||||
spin_lock_init(&bebob->lock);
|
||||
init_waitqueue_head(&bebob->hwdep_wait);
|
||||
|
||||
err = name_device(bebob);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = detect_quirks(bebob, entry);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (bebob->spec == &maudio_special_spec) {
|
||||
if (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
|
||||
if (entry->model_id == MODEL_MAUDIO_FW1814)
|
||||
err = snd_bebob_maudio_special_discover(bebob, true);
|
||||
else
|
||||
err = snd_bebob_maudio_special_discover(bebob, false);
|
||||
@ -230,80 +277,26 @@ do_registration(struct work_struct *work)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(bebob->card);
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
bebob->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_card_free(bebob->card);
|
||||
dev_info(&bebob->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int
|
||||
bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_bebob *bebob;
|
||||
const struct snd_bebob_spec *spec;
|
||||
|
||||
if (entry->vendor_id == VEN_FOCUSRITE &&
|
||||
entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
|
||||
spec = get_saffire_spec(unit);
|
||||
else if (entry->vendor_id == VEN_MAUDIO1 &&
|
||||
entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
|
||||
!check_audiophile_booted(unit))
|
||||
spec = NULL;
|
||||
else
|
||||
spec = (const struct snd_bebob_spec *)entry->driver_data;
|
||||
|
||||
if (spec == NULL) {
|
||||
if (entry->vendor_id == VEN_MAUDIO1 ||
|
||||
entry->vendor_id == VEN_MAUDIO2)
|
||||
return snd_bebob_maudio_load_firmware(unit);
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
bebob = devm_kzalloc(&unit->device, sizeof(struct snd_bebob),
|
||||
GFP_KERNEL);
|
||||
if (!bebob)
|
||||
return -ENOMEM;
|
||||
bebob->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, bebob);
|
||||
|
||||
bebob->entry = entry;
|
||||
bebob->spec = spec;
|
||||
mutex_init(&bebob->mutex);
|
||||
spin_lock_init(&bebob->lock);
|
||||
init_waitqueue_head(&bebob->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration);
|
||||
|
||||
if (entry->vendor_id != VEN_MAUDIO1 ||
|
||||
(entry->model_id != MODEL_MAUDIO_FW1814 &&
|
||||
entry->model_id != MODEL_MAUDIO_PROJECTMIX)) {
|
||||
snd_fw_schedule_registration(unit, &bebob->dwork);
|
||||
} else {
|
||||
/*
|
||||
* This is a workaround. This bus reset seems to have an effect
|
||||
* to make devices correctly handling transactions. Without
|
||||
* this, the devices have gap_count mismatch. This causes much
|
||||
* failure of transaction.
|
||||
*
|
||||
* Just after registration, user-land application receive
|
||||
* signals from dbus and starts I/Os. To avoid I/Os till the
|
||||
* future bus reset, registration is done in next update().
|
||||
*/
|
||||
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
|
||||
false, true);
|
||||
if (entry->vendor_id == VEN_MAUDIO &&
|
||||
(entry->model_id == MODEL_MAUDIO_FW1814 || entry->model_id == MODEL_MAUDIO_PROJECTMIX)) {
|
||||
// This is a workaround. This bus reset seems to have an effect to make devices
|
||||
// correctly handling transactions. Without this, the devices have gap_count
|
||||
// mismatch. This causes much failure of transaction.
|
||||
//
|
||||
// Just after registration, user-land application receive signals from dbus and
|
||||
// starts I/Os. To avoid I/Os till the future bus reset, registration is done in
|
||||
// next update().
|
||||
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card, false, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -330,11 +323,7 @@ bebob_update(struct fw_unit *unit)
|
||||
if (bebob == NULL)
|
||||
return;
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!bebob->registered)
|
||||
snd_fw_schedule_registration(unit, &bebob->dwork);
|
||||
else
|
||||
fcp_bus_reset(bebob->unit);
|
||||
fcp_bus_reset(bebob->unit);
|
||||
}
|
||||
|
||||
static void bebob_remove(struct fw_unit *unit)
|
||||
@ -344,20 +333,8 @@ static void bebob_remove(struct fw_unit *unit)
|
||||
if (bebob == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&bebob->dwork);
|
||||
|
||||
if (bebob->registered) {
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(bebob->card);
|
||||
}
|
||||
|
||||
mutex_destroy(&bebob->mutex);
|
||||
fw_unit_put(bebob->unit);
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(bebob->card);
|
||||
}
|
||||
|
||||
static const struct snd_bebob_rate_spec normal_rate_spec = {
|
||||
@ -370,6 +347,22 @@ static const struct snd_bebob_spec spec_normal = {
|
||||
.meter = NULL
|
||||
};
|
||||
|
||||
#define SPECIFIER_1394TA 0x00a02d
|
||||
|
||||
// The immediate entry for version in unit directory differs depending on models:
|
||||
// * 0x010001
|
||||
// * 0x014001
|
||||
#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
IEEE1394_MATCH_MODEL_ID | \
|
||||
IEEE1394_MATCH_SPECIFIER_ID, \
|
||||
.vendor_id = vendor, \
|
||||
.model_id = model, \
|
||||
.specifier_id = SPECIFIER_1394TA, \
|
||||
.driver_data = (kernel_ulong_t)data \
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id bebob_id_table[] = {
|
||||
/* Edirol, FA-66 */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal),
|
||||
@ -386,9 +379,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
|
||||
/* BridgeCo, Audio5 */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
|
||||
/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal),
|
||||
// Mackie, d.2 (Firewire option card) and d.2 Pro (the card is built-in).
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
|
||||
// Mackie, d.2 (optional Firewire card with DM1000).
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
|
||||
/* Stanton, ScratchAmp */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
|
||||
/* Tascam, IF-FW DM */
|
||||
@ -410,17 +403,20 @@ static const struct ieee1394_device_id bebob_id_table[] = {
|
||||
SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x01eeee, &spec_normal),
|
||||
/* ESI, Quatafire610 */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
|
||||
/* AcousticReality, eARMasterOne */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002, &spec_normal),
|
||||
// AcousticReality, eARMasterOne. Terratec OEM.
|
||||
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
|
||||
/* CME, MatrixKFW */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
|
||||
/* Phonic, Helix Board 12 MkII */
|
||||
// Phonic Helix Board 12 FireWire MkII.
|
||||
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
|
||||
/* Phonic, Helix Board 18 MkII */
|
||||
// Phonic Helix Board 18 FireWire MkII.
|
||||
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
|
||||
/* Phonic, Helix Board 24 MkII */
|
||||
// Phonic Helix Board 24 FireWire MkII.
|
||||
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
|
||||
/* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */
|
||||
// Phonic FireFly 808 FireWire.
|
||||
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00080000, &spec_normal),
|
||||
// Phonic FireFly 202, 302, 808 Universal.
|
||||
// Phinic Helix Board 12/18/24 FireWire, 12/18/24 Universal
|
||||
SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal),
|
||||
/* Lynx, Aurora 8/16 (LT-FW) */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal),
|
||||
@ -447,45 +443,35 @@ static const struct ieee1394_device_id bebob_id_table[] = {
|
||||
/* Focusrite, SaffirePro 26 I/O */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
|
||||
/* Focusrite, SaffirePro 10 I/O */
|
||||
{
|
||||
// The combination of vendor_id and model_id is the same as the
|
||||
// same as the one of Liquid Saffire 56.
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID |
|
||||
IEEE1394_MATCH_VERSION,
|
||||
.vendor_id = VEN_FOCUSRITE,
|
||||
.model_id = 0x000006,
|
||||
.specifier_id = 0x00a02d,
|
||||
.version = 0x010001,
|
||||
.driver_data = (kernel_ulong_t)&saffirepro_10_spec,
|
||||
},
|
||||
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x000006, &saffirepro_10_spec),
|
||||
/* Focusrite, Saffire(no label and LE) */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
|
||||
&saffire_spec),
|
||||
/* M-Audio, Firewire 410 */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010058, NULL), /* bootloader */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010046, &maudio_fw410_spec),
|
||||
// M-Audio, Firewire 410. The vendor field is left as BridgeCo. AG.
|
||||
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010058, NULL),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010046, &maudio_fw410_spec),
|
||||
/* M-Audio, Firewire Audiophile */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_AUDIOPHILE_BOTH,
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_AUDIOPHILE_BOTH,
|
||||
&maudio_audiophile_spec),
|
||||
/* M-Audio, Firewire Solo */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010062, &maudio_solo_spec),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010062, &maudio_solo_spec),
|
||||
/* M-Audio, Ozonic */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x0000000a, &maudio_ozonic_spec),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x0000000a, &maudio_ozonic_spec),
|
||||
/* M-Audio NRV10 */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010081, &maudio_nrv10_spec),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010081, &maudio_nrv10_spec),
|
||||
/* M-Audio, ProFireLightbridge */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x000100a1, &spec_normal),
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_PROFIRELIGHTBRIDGE, &spec_normal),
|
||||
/* Firewire 1814 */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010070, NULL), /* bootloader */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_FW1814,
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, 0x00010070, NULL), /* bootloader */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_FW1814,
|
||||
&maudio_special_spec),
|
||||
/* M-Audio ProjectMix */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
|
||||
SND_BEBOB_DEV_ENTRY(VEN_MAUDIO, MODEL_MAUDIO_PROJECTMIX,
|
||||
&maudio_special_spec),
|
||||
/* Digidesign Mbox 2 Pro */
|
||||
SND_BEBOB_DEV_ENTRY(VEN_DIGIDESIGN, 0x0000a9, &spec_normal),
|
||||
// Toneweal FW66.
|
||||
SND_BEBOB_DEV_ENTRY(OUI_SHOUYO, 0x020002, &spec_normal),
|
||||
/* IDs are unknown but able to be supported */
|
||||
/* Apogee, Mini-ME Firewire */
|
||||
/* Apogee, Mini-DAC Firewire */
|
||||
@ -496,11 +482,6 @@ static const struct ieee1394_device_id bebob_id_table[] = {
|
||||
/* Infrasonic, Windy6 */
|
||||
/* Mackie, Digital X Bus x.200 */
|
||||
/* Mackie, Digital X Bus x.400 */
|
||||
/* Phonic, HB 12 */
|
||||
/* Phonic, HB 24 */
|
||||
/* Phonic, HB 18 */
|
||||
/* Phonic, FireFly 202 */
|
||||
/* Phonic, FireFly 302 */
|
||||
/* Rolf Spuler, Firewire Guitar */
|
||||
{}
|
||||
};
|
||||
|
@ -75,6 +75,11 @@ struct snd_bebob_spec {
|
||||
const struct snd_bebob_meter_spec *meter;
|
||||
};
|
||||
|
||||
enum snd_bebob_quirk {
|
||||
SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC = (1 << 0),
|
||||
SND_BEBOB_QUIRK_WRONG_DBC = (1 << 1),
|
||||
};
|
||||
|
||||
struct snd_bebob {
|
||||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
@ -83,11 +88,8 @@ struct snd_bebob {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
const struct ieee1394_device_id *entry;
|
||||
const struct snd_bebob_spec *spec;
|
||||
unsigned int quirks; // Combination of snd_bebob_quirk enumerations.
|
||||
|
||||
unsigned int midi_input_ports;
|
||||
unsigned int midi_output_ports;
|
||||
@ -113,9 +115,6 @@ struct snd_bebob {
|
||||
/* for M-Audio special devices */
|
||||
void *maudio_special_quirk;
|
||||
|
||||
/* For BeBoB version quirk. */
|
||||
unsigned int version;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
};
|
||||
|
||||
@ -254,13 +253,4 @@ extern const struct snd_bebob_spec maudio_special_spec;
|
||||
int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
|
||||
int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
|
||||
|
||||
#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
IEEE1394_MATCH_MODEL_ID, \
|
||||
.vendor_id = vendor, \
|
||||
.model_id = model, \
|
||||
.driver_data = (kernel_ulong_t)data \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -7,8 +7,7 @@
|
||||
|
||||
#include "./bebob.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT 2500
|
||||
#define FW_ISO_RESOURCE_DELAY 1000
|
||||
#define READY_TIMEOUT_MS 4000
|
||||
|
||||
/*
|
||||
* NOTE;
|
||||
@ -402,12 +401,6 @@ static void break_both_connections(struct snd_bebob *bebob)
|
||||
{
|
||||
cmp_connection_break(&bebob->in_conn);
|
||||
cmp_connection_break(&bebob->out_conn);
|
||||
|
||||
// These models seem to be in transition state for a longer time. When
|
||||
// accessing in the state, any transactions is corrupted. In the worst
|
||||
// case, the device is going to reboot.
|
||||
if (bebob->version < 2)
|
||||
msleep(600);
|
||||
}
|
||||
|
||||
static int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
@ -437,6 +430,7 @@ static int start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
|
||||
static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
{
|
||||
unsigned int flags = CIP_BLOCKING;
|
||||
enum amdtp_stream_direction dir_stream;
|
||||
struct cmp_connection *conn;
|
||||
enum cmp_direction dir_conn;
|
||||
@ -452,32 +446,21 @@ static int init_stream(struct snd_bebob *bebob, struct amdtp_stream *stream)
|
||||
dir_conn = CMP_INPUT;
|
||||
}
|
||||
|
||||
if (stream == &bebob->tx_stream) {
|
||||
if (bebob->quirks & SND_BEBOB_QUIRK_WRONG_DBC)
|
||||
flags |= CIP_EMPTY_HAS_WRONG_DBC;
|
||||
}
|
||||
|
||||
err = cmp_connection_init(conn, bebob->unit, dir_conn, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(stream, bebob->unit, dir_stream, CIP_BLOCKING);
|
||||
err = amdtp_am824_init(stream, bebob->unit, dir_stream, flags);
|
||||
if (err < 0) {
|
||||
cmp_connection_destroy(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (stream == &bebob->tx_stream) {
|
||||
// BeBoB v3 transfers packets with these qurks:
|
||||
// - In the beginning of streaming, the value of dbc is
|
||||
// incremented even if no data blocks are transferred.
|
||||
// - The value of dbc is reset suddenly.
|
||||
if (bebob->version > 2)
|
||||
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC |
|
||||
CIP_SKIP_DBC_ZERO_CHECK;
|
||||
|
||||
// At high sampling rate, M-Audio special firmware transmits
|
||||
// empty packet with the value of dbc incremented by 8 but the
|
||||
// others are valid to IEC 61883-1.
|
||||
if (bebob->maudio_special_quirk)
|
||||
bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -624,9 +607,8 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
|
||||
if (!amdtp_stream_running(&bebob->rx_stream)) {
|
||||
enum snd_bebob_clock_type src;
|
||||
struct amdtp_stream *master, *slave;
|
||||
unsigned int curr_rate;
|
||||
unsigned int ir_delay_cycle;
|
||||
unsigned int tx_init_skip_cycles;
|
||||
|
||||
if (bebob->maudio_special_quirk) {
|
||||
err = bebob->spec->rate->get(bebob, &curr_rate);
|
||||
@ -638,36 +620,28 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (src != SND_BEBOB_CLOCK_TYPE_SYT) {
|
||||
master = &bebob->tx_stream;
|
||||
slave = &bebob->rx_stream;
|
||||
} else {
|
||||
master = &bebob->rx_stream;
|
||||
slave = &bebob->tx_stream;
|
||||
}
|
||||
|
||||
err = start_stream(bebob, master);
|
||||
err = start_stream(bebob, &bebob->rx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = start_stream(bebob, slave);
|
||||
err = start_stream(bebob, &bebob->tx_stream);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// The device postpones start of transmission mostly for 1 sec
|
||||
// after receives packets firstly. For safe, IR context starts
|
||||
// 0.4 sec (=3200 cycles) later to version 1 or 2 firmware,
|
||||
// 2.0 sec (=16000 cycles) for version 3 firmware. This is
|
||||
// within 2.5 sec (=CALLBACK_TIMEOUT).
|
||||
// Furthermore, some devices transfer isoc packets with
|
||||
// discontinuous counter in the beginning of packet streaming.
|
||||
// The delay has an effect to avoid detection of this
|
||||
// discontinuity.
|
||||
if (bebob->version < 2)
|
||||
ir_delay_cycle = 3200;
|
||||
if (!(bebob->quirks & SND_BEBOB_QUIRK_INITIAL_DISCONTINUOUS_DBC))
|
||||
tx_init_skip_cycles = 0;
|
||||
else
|
||||
ir_delay_cycle = 16000;
|
||||
err = amdtp_domain_start(&bebob->domain, ir_delay_cycle);
|
||||
tx_init_skip_cycles = 16000;
|
||||
|
||||
// MEMO: Some devices start packet transmission long enough after establishment of
|
||||
// CMP connection. In the early stage of packet streaming, any device transfers
|
||||
// NODATA packets. After several hundred cycles, it begins to multiplex event into
|
||||
// the packet with adequate value of syt field in CIP header. Some devices are
|
||||
// strictly to generate any discontinuity in the sequence of tx packet when they
|
||||
// receives inadequate sequence of value in syt field of CIP header. In the case,
|
||||
// the request to break CMP connection is often corrupted, then any transaction
|
||||
// results in unrecoverable error, sometimes generate bus-reset.
|
||||
err = amdtp_domain_start(&bebob->domain, tx_init_skip_cycles, true, false);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
@ -684,10 +658,9 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob)
|
||||
}
|
||||
}
|
||||
|
||||
if (!amdtp_stream_wait_callback(&bebob->rx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&bebob->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
// Some devices postpone start of transmission mostly for 1 sec after receives
|
||||
// packets firstly.
|
||||
if (!amdtp_domain_wait_ready(&bebob->domain, READY_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
@ -883,6 +856,11 @@ static int detect_midi_ports(struct snd_bebob *bebob,
|
||||
err = avc_bridgeco_get_plug_ch_count(bebob->unit, addr, &ch_count);
|
||||
if (err < 0)
|
||||
break;
|
||||
// Yamaha GO44, GO46, Terratec Phase 24, Phase x24 reports 0 for the number of
|
||||
// channels in external output plug 3 (MIDI type) even if it has a pair of physical
|
||||
// MIDI jacks. As a workaround, assume it as one.
|
||||
if (ch_count == 0)
|
||||
ch_count = 1;
|
||||
*midi_ports += ch_count;
|
||||
}
|
||||
|
||||
@ -961,12 +939,12 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob)
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = detect_midi_ports(bebob, bebob->rx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_IN,
|
||||
err = detect_midi_ports(bebob, bebob->tx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_IN,
|
||||
plugs[2], &bebob->midi_input_ports);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
||||
err = detect_midi_ports(bebob, bebob->tx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_OUT,
|
||||
err = detect_midi_ports(bebob, bebob->rx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_OUT,
|
||||
plugs[3], &bebob->midi_output_ports);
|
||||
if (err < 0)
|
||||
goto end;
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
#include "dice.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT 200
|
||||
#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
|
||||
#define READY_TIMEOUT_MS 200
|
||||
#define NOTIFICATION_TIMEOUT_MS 100
|
||||
|
||||
struct reg_params {
|
||||
unsigned int count;
|
||||
@ -57,13 +57,9 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
|
||||
* to GLOBAL_STATUS. Especially, just after powering on, these are different.
|
||||
*/
|
||||
static int ensure_phase_lock(struct snd_dice *dice, unsigned int rate)
|
||||
static int select_clock(struct snd_dice *dice, unsigned int rate)
|
||||
{
|
||||
__be32 reg, nominal;
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int i;
|
||||
int err;
|
||||
@ -94,19 +90,8 @@ static int ensure_phase_lock(struct snd_dice *dice, unsigned int rate)
|
||||
return err;
|
||||
|
||||
if (wait_for_completion_timeout(&dice->clock_accepted,
|
||||
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
|
||||
/*
|
||||
* Old versions of Dice firmware transfer no notification when
|
||||
* the same clock status as current one is set. In this case,
|
||||
* just check current clock status.
|
||||
*/
|
||||
err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
|
||||
&nominal, sizeof(nominal));
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -304,7 +289,7 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate,
|
||||
// Just after owning the unit (GLOBAL_OWNER), the unit can
|
||||
// return invalid stream formats. Selecting clock parameters
|
||||
// have an effect for the unit to refine it.
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
err = select_clock(dice, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -459,20 +444,17 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = amdtp_domain_start(&dice->domain, 0);
|
||||
// MEMO: The device immediately starts packet transmission when enabled. Some
|
||||
// devices are strictly to generate any discontinuity in the sequence of tx packet
|
||||
// when they receives invalid sequence of presentation time in CIP header. The
|
||||
// sequence replay for media clock recovery can suppress the behaviour.
|
||||
err = amdtp_domain_start(&dice->domain, 0, true, false);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < MAX_STREAMS; i++) {
|
||||
if ((i < tx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->tx_stream[i],
|
||||
CALLBACK_TIMEOUT)) ||
|
||||
(i < rx_params.count &&
|
||||
!amdtp_stream_wait_callback(&dice->rx_stream[i],
|
||||
CALLBACK_TIMEOUT))) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
if (!amdtp_domain_wait_ready(&dice->domain, READY_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,7 +635,7 @@ int snd_dice_stream_detect_current_formats(struct snd_dice *dice)
|
||||
* invalid stream formats. Selecting clock parameters have an effect
|
||||
* for the unit to refine it.
|
||||
*/
|
||||
err = ensure_phase_lock(dice, rate);
|
||||
err = select_clock(dice, rate);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -155,7 +155,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
|
||||
|
||||
fw_send_response(card, request, RCODE_COMPLETE);
|
||||
|
||||
if (bits & NOTIFY_LOCK_CHG)
|
||||
if (bits & NOTIFY_CLOCK_ACCEPTED)
|
||||
complete(&dice->clock_accepted);
|
||||
wake_up(&dice->hwdep_wait);
|
||||
}
|
||||
|
@ -135,22 +135,51 @@ static void dice_card_free(struct snd_card *card)
|
||||
|
||||
snd_dice_stream_destroy_duplex(dice);
|
||||
snd_dice_transaction_destroy(dice);
|
||||
|
||||
mutex_destroy(&dice->mutex);
|
||||
fw_unit_put(dice->unit);
|
||||
}
|
||||
|
||||
static void do_registration(struct work_struct *work)
|
||||
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_dice *dice = container_of(work, struct snd_dice, dwork.work);
|
||||
struct snd_card *card;
|
||||
struct snd_dice *dice;
|
||||
snd_dice_detect_formats_t detect_formats;
|
||||
int err;
|
||||
|
||||
if (dice->registered)
|
||||
return;
|
||||
if (!entry->driver_data && entry->vendor_id != OUI_SSL) {
|
||||
err = check_dice_category(unit);
|
||||
if (err < 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = snd_card_new(&dice->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&dice->card);
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dice), &card);
|
||||
if (err < 0)
|
||||
return;
|
||||
dice->card->private_free = dice_card_free;
|
||||
dice->card->private_data = dice;
|
||||
return err;
|
||||
card->private_free = dice_card_free;
|
||||
|
||||
dice = card->private_data;
|
||||
dice->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, dice);
|
||||
dice->card = card;
|
||||
|
||||
if (!entry->driver_data)
|
||||
detect_formats = snd_dice_stream_detect_current_formats;
|
||||
else
|
||||
detect_formats = (snd_dice_detect_formats_t)entry->driver_data;
|
||||
|
||||
// Below models are compliant to IEC 61883-1/6 and have no quirk at high sampling transfer
|
||||
// frequency.
|
||||
// * Avid M-Box 3 Pro
|
||||
// * M-Audio Profire 610
|
||||
// * M-Audio Profire 2626
|
||||
if (entry->vendor_id == OUI_MAUDIO || entry->vendor_id == OUI_AVID)
|
||||
dice->disable_double_pcm_frames = true;
|
||||
|
||||
spin_lock_init(&dice->lock);
|
||||
mutex_init(&dice->mutex);
|
||||
init_completion(&dice->clock_accepted);
|
||||
init_waitqueue_head(&dice->hwdep_wait);
|
||||
|
||||
err = snd_dice_transaction_init(dice);
|
||||
if (err < 0)
|
||||
@ -162,7 +191,7 @@ static void do_registration(struct work_struct *work)
|
||||
|
||||
dice_card_strings(dice);
|
||||
|
||||
err = dice->detect_formats(dice);
|
||||
err = detect_formats(dice);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
@ -184,105 +213,34 @@ static void do_registration(struct work_struct *work)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(dice->card);
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
dice->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_card_free(dice->card);
|
||||
dev_info(&dice->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int dice_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_dice *dice;
|
||||
int err;
|
||||
|
||||
if (!entry->driver_data && entry->vendor_id != OUI_SSL) {
|
||||
err = check_dice_category(unit);
|
||||
if (err < 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
dice = devm_kzalloc(&unit->device, sizeof(struct snd_dice), GFP_KERNEL);
|
||||
if (!dice)
|
||||
return -ENOMEM;
|
||||
dice->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, dice);
|
||||
|
||||
if (!entry->driver_data) {
|
||||
dice->detect_formats = snd_dice_stream_detect_current_formats;
|
||||
} else {
|
||||
dice->detect_formats =
|
||||
(snd_dice_detect_formats_t)entry->driver_data;
|
||||
}
|
||||
|
||||
// Below models are compliant to IEC 61883-1/6 and have no quirk at high sampling transfer
|
||||
// frequency.
|
||||
// * Avid M-Box 3 Pro
|
||||
// * M-Audio Profire 610
|
||||
// * M-Audio Profire 2626
|
||||
if (entry->vendor_id == OUI_MAUDIO || entry->vendor_id == OUI_AVID)
|
||||
dice->disable_double_pcm_frames = true;
|
||||
|
||||
spin_lock_init(&dice->lock);
|
||||
mutex_init(&dice->mutex);
|
||||
init_completion(&dice->clock_accepted);
|
||||
init_waitqueue_head(&dice->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &dice->dwork);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void dice_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dice *dice = dev_get_drvdata(&unit->device);
|
||||
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&dice->dwork);
|
||||
|
||||
if (dice->registered) {
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(dice->card);
|
||||
}
|
||||
|
||||
mutex_destroy(&dice->mutex);
|
||||
fw_unit_put(dice->unit);
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(dice->card);
|
||||
}
|
||||
|
||||
static void dice_bus_reset(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dice *dice = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!dice->registered)
|
||||
snd_fw_schedule_registration(unit, &dice->dwork);
|
||||
|
||||
/* The handler address register becomes initialized. */
|
||||
snd_dice_transaction_reinit(dice);
|
||||
|
||||
/*
|
||||
* After registration, userspace can start packet streaming, then this
|
||||
* code block works fine.
|
||||
*/
|
||||
if (dice->registered) {
|
||||
mutex_lock(&dice->mutex);
|
||||
snd_dice_stream_update_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
mutex_lock(&dice->mutex);
|
||||
snd_dice_stream_update_duplex(dice);
|
||||
mutex_unlock(&dice->mutex);
|
||||
}
|
||||
|
||||
#define DICE_INTERFACE 0x000001
|
||||
|
@ -78,9 +78,6 @@ struct snd_dice {
|
||||
spinlock_t lock;
|
||||
struct mutex mutex;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
/* Offsets for sub-addresses */
|
||||
unsigned int global_offset;
|
||||
unsigned int rx_offset;
|
||||
@ -93,7 +90,6 @@ struct snd_dice {
|
||||
unsigned int rx_pcm_chs[MAX_STREAMS][SND_DICE_RATE_MODE_COUNT];
|
||||
unsigned int tx_midi_ports[MAX_STREAMS];
|
||||
unsigned int rx_midi_ports[MAX_STREAMS];
|
||||
snd_dice_detect_formats_t detect_formats;
|
||||
|
||||
struct fw_address_handler notification_handler;
|
||||
int owner_generation;
|
||||
|
@ -396,16 +396,13 @@ int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir)
|
||||
{
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
enum cip_flags flags;
|
||||
unsigned int flags = CIP_NONBLOCKING | CIP_UNAWARE_SYT;
|
||||
|
||||
// Use different mode between incoming/outgoing.
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
flags = CIP_NONBLOCKING;
|
||||
if (dir == AMDTP_IN_STREAM)
|
||||
process_ctx_payloads = process_ir_ctx_payloads;
|
||||
} else {
|
||||
flags = CIP_BLOCKING;
|
||||
else
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
}
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
|
||||
process_ctx_payloads, sizeof(struct amdtp_dot));
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "digi00x.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT 500
|
||||
#define READY_TIMEOUT_MS 200
|
||||
|
||||
const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
|
||||
[SND_DG00X_RATE_44100] = 44100,
|
||||
@ -375,14 +375,15 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&dg00x->domain, 0);
|
||||
// NOTE: The device doesn't start packet transmission till receiving any packet.
|
||||
// It ignores presentation time expressed by the value of syt field of CIP header
|
||||
// in received packets. The sequence of the number of data blocks per packet is
|
||||
// important for media clock recovery.
|
||||
err = amdtp_domain_start(&dg00x->domain, 0, true, true);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&dg00x->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
if (!amdtp_domain_wait_ready(&dg00x->domain, READY_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
@ -47,23 +47,32 @@ static void dg00x_card_free(struct snd_card *card)
|
||||
|
||||
snd_dg00x_stream_destroy_duplex(dg00x);
|
||||
snd_dg00x_transaction_unregister(dg00x);
|
||||
|
||||
mutex_destroy(&dg00x->mutex);
|
||||
fw_unit_put(dg00x->unit);
|
||||
}
|
||||
|
||||
static void do_registration(struct work_struct *work)
|
||||
static int snd_dg00x_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_dg00x *dg00x =
|
||||
container_of(work, struct snd_dg00x, dwork.work);
|
||||
struct snd_card *card;
|
||||
struct snd_dg00x *dg00x;
|
||||
int err;
|
||||
|
||||
if (dg00x->registered)
|
||||
return;
|
||||
|
||||
err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&dg00x->card);
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*dg00x), &card);
|
||||
if (err < 0)
|
||||
return;
|
||||
dg00x->card->private_free = dg00x_card_free;
|
||||
dg00x->card->private_data = dg00x;
|
||||
return err;
|
||||
card->private_free = dg00x_card_free;
|
||||
|
||||
dg00x = card->private_data;
|
||||
dg00x->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, dg00x);
|
||||
dg00x->card = card;
|
||||
|
||||
mutex_init(&dg00x->mutex);
|
||||
spin_lock_init(&dg00x->lock);
|
||||
init_waitqueue_head(&dg00x->hwdep_wait);
|
||||
|
||||
dg00x->is_console = entry->model_id == MODEL_CONSOLE;
|
||||
|
||||
err = name_card(dg00x);
|
||||
if (err < 0)
|
||||
@ -91,85 +100,33 @@ static void do_registration(struct work_struct *work)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(dg00x->card);
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
dg00x->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_card_free(dg00x->card);
|
||||
dev_info(&dg00x->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int snd_dg00x_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_dg00x *dg00x;
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
dg00x = devm_kzalloc(&unit->device, sizeof(struct snd_dg00x),
|
||||
GFP_KERNEL);
|
||||
if (!dg00x)
|
||||
return -ENOMEM;
|
||||
|
||||
dg00x->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, dg00x);
|
||||
|
||||
mutex_init(&dg00x->mutex);
|
||||
spin_lock_init(&dg00x->lock);
|
||||
init_waitqueue_head(&dg00x->hwdep_wait);
|
||||
|
||||
dg00x->is_console = entry->model_id == MODEL_CONSOLE;
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &dg00x->dwork);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_dg00x_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!dg00x->registered)
|
||||
snd_fw_schedule_registration(unit, &dg00x->dwork);
|
||||
|
||||
snd_dg00x_transaction_reregister(dg00x);
|
||||
|
||||
/*
|
||||
* After registration, userspace can start packet streaming, then this
|
||||
* code block works fine.
|
||||
*/
|
||||
if (dg00x->registered) {
|
||||
mutex_lock(&dg00x->mutex);
|
||||
snd_dg00x_stream_update_duplex(dg00x);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
mutex_lock(&dg00x->mutex);
|
||||
snd_dg00x_stream_update_duplex(dg00x);
|
||||
mutex_unlock(&dg00x->mutex);
|
||||
}
|
||||
|
||||
static void snd_dg00x_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
|
||||
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&dg00x->dwork);
|
||||
|
||||
if (dg00x->registered) {
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(dg00x->card);
|
||||
}
|
||||
|
||||
mutex_destroy(&dg00x->mutex);
|
||||
fw_unit_put(dg00x->unit);
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(dg00x->card);
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id snd_dg00x_id_table[] = {
|
||||
|
@ -37,9 +37,6 @@ struct snd_dg00x {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
struct amdtp_stream tx_stream;
|
||||
struct fw_iso_resources tx_resources;
|
||||
|
||||
|
@ -168,6 +168,6 @@ int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
else
|
||||
process_ctx_payloads = process_it_ctx_payloads;
|
||||
|
||||
return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
|
||||
return amdtp_stream_init(s, unit, dir, CIP_BLOCKING | CIP_UNAWARE_SYT | CIP_NO_HEADER, 0,
|
||||
process_ctx_payloads, sizeof(struct amdtp_ff));
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT_MS 200
|
||||
#define READY_TIMEOUT_MS 200
|
||||
|
||||
int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc,
|
||||
enum snd_ff_stream_mode *mode)
|
||||
@ -199,14 +199,15 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&ff->domain, 0);
|
||||
// NOTE: The device doesn't transfer packets unless receiving any packet. The
|
||||
// sequence of tx packets includes cycle skip corresponding to empty packet or
|
||||
// NODATA packet in IEC 61883-1/6. The sequence of the number of data blocks per
|
||||
// packet is important for media clock recovery.
|
||||
err = amdtp_domain_start(&ff->domain, 0, true, true);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&ff->rx_stream,
|
||||
CALLBACK_TIMEOUT_MS) ||
|
||||
!amdtp_stream_wait_callback(&ff->tx_stream,
|
||||
CALLBACK_TIMEOUT_MS)) {
|
||||
if (!amdtp_domain_wait_ready(&ff->domain, READY_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
@ -42,22 +42,33 @@ static void ff_card_free(struct snd_card *card)
|
||||
|
||||
snd_ff_stream_destroy_duplex(ff);
|
||||
snd_ff_transaction_unregister(ff);
|
||||
|
||||
mutex_destroy(&ff->mutex);
|
||||
fw_unit_put(ff->unit);
|
||||
}
|
||||
|
||||
static void do_registration(struct work_struct *work)
|
||||
static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_ff *ff = container_of(work, struct snd_ff, dwork.work);
|
||||
struct snd_card *card;
|
||||
struct snd_ff *ff;
|
||||
int err;
|
||||
|
||||
if (ff->registered)
|
||||
return;
|
||||
|
||||
err = snd_card_new(&ff->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&ff->card);
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*ff), &card);
|
||||
if (err < 0)
|
||||
return;
|
||||
ff->card->private_free = ff_card_free;
|
||||
ff->card->private_data = ff;
|
||||
return err;
|
||||
card->private_free = ff_card_free;
|
||||
|
||||
ff = card->private_data;
|
||||
ff->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, ff);
|
||||
ff->card = card;
|
||||
|
||||
mutex_init(&ff->mutex);
|
||||
spin_lock_init(&ff->lock);
|
||||
init_waitqueue_head(&ff->hwdep_wait);
|
||||
|
||||
ff->unit_version = entry->version;
|
||||
ff->spec = (const struct snd_ff_spec *)entry->driver_data;
|
||||
|
||||
err = snd_ff_transaction_register(ff);
|
||||
if (err < 0)
|
||||
@ -83,76 +94,31 @@ static void do_registration(struct work_struct *work)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(ff->card);
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
ff->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_card_free(ff->card);
|
||||
dev_info(&ff->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int snd_ff_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_ff *ff;
|
||||
|
||||
ff = devm_kzalloc(&unit->device, sizeof(struct snd_ff), GFP_KERNEL);
|
||||
if (!ff)
|
||||
return -ENOMEM;
|
||||
ff->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, ff);
|
||||
|
||||
mutex_init(&ff->mutex);
|
||||
spin_lock_init(&ff->lock);
|
||||
init_waitqueue_head(&ff->hwdep_wait);
|
||||
|
||||
ff->unit_version = entry->version;
|
||||
ff->spec = (const struct snd_ff_spec *)entry->driver_data;
|
||||
|
||||
/* Register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&ff->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &ff->dwork);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_ff_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_ff *ff = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!ff->registered)
|
||||
snd_fw_schedule_registration(unit, &ff->dwork);
|
||||
|
||||
snd_ff_transaction_reregister(ff);
|
||||
|
||||
if (ff->registered)
|
||||
snd_ff_stream_update_duplex(ff);
|
||||
snd_ff_stream_update_duplex(ff);
|
||||
}
|
||||
|
||||
static void snd_ff_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_ff *ff = dev_get_drvdata(&unit->device);
|
||||
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_work_sync(&ff->dwork.work);
|
||||
|
||||
if (ff->registered) {
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(ff->card);
|
||||
}
|
||||
|
||||
mutex_destroy(&ff->mutex);
|
||||
fw_unit_put(ff->unit);
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(ff->card);
|
||||
}
|
||||
|
||||
static const struct snd_ff_spec spec_ff800 = {
|
||||
|
@ -69,9 +69,6 @@ struct snd_ff {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
enum snd_ff_unit_version unit_version;
|
||||
const struct snd_ff_spec *spec;
|
||||
|
||||
|
@ -194,19 +194,19 @@ efw_card_free(struct snd_card *card)
|
||||
|
||||
snd_efw_stream_destroy_duplex(efw);
|
||||
snd_efw_transaction_remove_instance(efw);
|
||||
|
||||
mutex_destroy(&efw->mutex);
|
||||
fw_unit_put(efw->unit);
|
||||
}
|
||||
|
||||
static void
|
||||
do_registration(struct work_struct *work)
|
||||
static int efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
|
||||
unsigned int card_index;
|
||||
struct snd_card *card;
|
||||
struct snd_efw *efw;
|
||||
int err;
|
||||
|
||||
if (efw->registered)
|
||||
return;
|
||||
|
||||
/* check registered cards */
|
||||
// check registered cards.
|
||||
mutex_lock(&devices_mutex);
|
||||
for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) {
|
||||
if (!test_bit(card_index, devices_used) && enable[card_index])
|
||||
@ -214,26 +214,32 @@ do_registration(struct work_struct *work)
|
||||
}
|
||||
if (card_index >= SNDRV_CARDS) {
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
err = snd_card_new(&efw->unit->device, index[card_index],
|
||||
id[card_index], THIS_MODULE, 0, &efw->card);
|
||||
err = snd_card_new(&unit->device, index[card_index], id[card_index], THIS_MODULE,
|
||||
sizeof(*efw), &card);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&devices_mutex);
|
||||
return;
|
||||
return err;
|
||||
}
|
||||
card->private_free = efw_card_free;
|
||||
set_bit(card_index, devices_used);
|
||||
mutex_unlock(&devices_mutex);
|
||||
|
||||
efw->card->private_free = efw_card_free;
|
||||
efw->card->private_data = efw;
|
||||
efw = card->private_data;
|
||||
efw->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, efw);
|
||||
efw->card = card;
|
||||
efw->card_index = card_index;
|
||||
|
||||
/* prepare response buffer */
|
||||
snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
|
||||
SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
|
||||
efw->resp_buf = devm_kzalloc(&efw->card->card_dev,
|
||||
snd_efw_resp_buf_size, GFP_KERNEL);
|
||||
mutex_init(&efw->mutex);
|
||||
spin_lock_init(&efw->lock);
|
||||
init_waitqueue_head(&efw->hwdep_wait);
|
||||
|
||||
// prepare response buffer.
|
||||
snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size, SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
|
||||
efw->resp_buf = devm_kzalloc(&card->card_dev, snd_efw_resp_buf_size, GFP_KERNEL);
|
||||
if (!efw->resp_buf) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
@ -265,80 +271,48 @@ do_registration(struct work_struct *work)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(efw->card);
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
efw->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_card_free(efw->card);
|
||||
dev_info(&efw->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int
|
||||
efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_efw *efw;
|
||||
|
||||
efw = devm_kzalloc(&unit->device, sizeof(struct snd_efw), GFP_KERNEL);
|
||||
if (efw == NULL)
|
||||
return -ENOMEM;
|
||||
efw->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, efw);
|
||||
|
||||
mutex_init(&efw->mutex);
|
||||
spin_lock_init(&efw->lock);
|
||||
init_waitqueue_head(&efw->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&efw->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &efw->dwork);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void efw_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_efw *efw = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!efw->registered)
|
||||
snd_fw_schedule_registration(unit, &efw->dwork);
|
||||
|
||||
snd_efw_transaction_bus_reset(efw->unit);
|
||||
|
||||
/*
|
||||
* After registration, userspace can start packet streaming, then this
|
||||
* code block works fine.
|
||||
*/
|
||||
if (efw->registered) {
|
||||
mutex_lock(&efw->mutex);
|
||||
snd_efw_stream_update_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
mutex_lock(&efw->mutex);
|
||||
snd_efw_stream_update_duplex(efw);
|
||||
mutex_unlock(&efw->mutex);
|
||||
}
|
||||
|
||||
static void efw_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_efw *efw = dev_get_drvdata(&unit->device);
|
||||
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&efw->dwork);
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(efw->card);
|
||||
}
|
||||
|
||||
if (efw->registered) {
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(efw->card);
|
||||
}
|
||||
#define SPECIFIER_1394TA 0x00a02d
|
||||
#define VERSION_EFW 0x010000
|
||||
|
||||
mutex_destroy(&efw->mutex);
|
||||
fw_unit_put(efw->unit);
|
||||
#define SND_EFW_DEV_ENTRY(vendor, model) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
IEEE1394_MATCH_MODEL_ID | \
|
||||
IEEE1394_MATCH_SPECIFIER_ID | \
|
||||
IEEE1394_MATCH_VERSION, \
|
||||
.vendor_id = vendor,\
|
||||
.model_id = model, \
|
||||
.specifier_id = SPECIFIER_1394TA, \
|
||||
.version = VERSION_EFW, \
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id efw_id_table[] = {
|
||||
|
@ -65,9 +65,6 @@ struct snd_efw {
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
/* for transaction */
|
||||
u32 seqnum;
|
||||
bool resp_addr_changable;
|
||||
@ -181,7 +178,7 @@ struct snd_efw_phys_meters {
|
||||
} __packed;
|
||||
enum snd_efw_clock_source {
|
||||
SND_EFW_CLOCK_SOURCE_INTERNAL = 0,
|
||||
SND_EFW_CLOCK_SOURCE_SYTMATCH = 1,
|
||||
// Unused.
|
||||
SND_EFW_CLOCK_SOURCE_WORDCLOCK = 2,
|
||||
SND_EFW_CLOCK_SOURCE_SPDIF = 3,
|
||||
SND_EFW_CLOCK_SOURCE_ADAT_1 = 4,
|
||||
@ -227,12 +224,4 @@ int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
|
||||
|
||||
int snd_efw_create_hwdep_device(struct snd_efw *efw);
|
||||
|
||||
#define SND_EFW_DEV_ENTRY(vendor, model) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
IEEE1394_MATCH_MODEL_ID, \
|
||||
.vendor_id = vendor,\
|
||||
.model_id = model \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -6,7 +6,7 @@
|
||||
*/
|
||||
#include "./fireworks.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT 100
|
||||
#define READY_TIMEOUT_MS 1000
|
||||
|
||||
static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
{
|
||||
@ -29,7 +29,7 @@ static int init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
|
||||
err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING | CIP_UNAWARE_SYT);
|
||||
if (err < 0) {
|
||||
amdtp_stream_destroy(stream);
|
||||
cmp_connection_destroy(conn);
|
||||
@ -264,6 +264,15 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
|
||||
return err;
|
||||
|
||||
if (!amdtp_stream_running(&efw->rx_stream)) {
|
||||
unsigned int tx_init_skip_cycles;
|
||||
|
||||
// Audiofire 2/4 skip an isochronous cycle several thousands after starting
|
||||
// packet transmission.
|
||||
if (efw->is_fireworks3 && !efw->is_af9)
|
||||
tx_init_skip_cycles = 6000;
|
||||
else
|
||||
tx_init_skip_cycles = 0;
|
||||
|
||||
err = start_stream(efw, &efw->rx_stream, rate);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
@ -272,15 +281,14 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = amdtp_domain_start(&efw->domain, 0);
|
||||
// NOTE: The device ignores presentation time expressed by the value of syt field
|
||||
// of CIP header in received packets. The sequence of the number of data blocks per
|
||||
// packet is important for media clock recovery.
|
||||
err = amdtp_domain_start(&efw->domain, tx_init_skip_cycles, true, false);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// Wait first callback.
|
||||
if (!amdtp_stream_wait_callback(&efw->rx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&efw->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
if (!amdtp_domain_wait_ready(&efw->domain, READY_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
@ -67,38 +67,6 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
|
||||
}
|
||||
EXPORT_SYMBOL(snd_fw_transaction);
|
||||
|
||||
#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
|
||||
|
||||
/**
|
||||
* snd_fw_schedule_registration - schedule work for sound card registration
|
||||
* @unit: an instance for unit on IEEE 1394 bus
|
||||
* @dwork: delayed work with callback function
|
||||
*
|
||||
* This function is not designed for general purposes. When new unit is
|
||||
* connected to IEEE 1394 bus, the bus is under bus-reset state because of
|
||||
* topological change. In this state, units tend to fail both of asynchronous
|
||||
* and isochronous communication. To avoid this problem, this function is used
|
||||
* to postpone sound card registration after the state. The callers must
|
||||
* set up instance of delayed work in advance.
|
||||
*/
|
||||
void snd_fw_schedule_registration(struct fw_unit *unit,
|
||||
struct delayed_work *dwork)
|
||||
{
|
||||
u64 now, delay;
|
||||
|
||||
now = get_jiffies_64();
|
||||
delay = fw_parent_device(unit)->card->reset_jiffies
|
||||
+ msecs_to_jiffies(PROBE_DELAY_MS);
|
||||
|
||||
if (time_after64(delay, now))
|
||||
delay -= now;
|
||||
else
|
||||
delay = 0;
|
||||
|
||||
mod_delayed_work(system_wq, dwork, delay);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_fw_schedule_registration);
|
||||
|
||||
MODULE_DESCRIPTION("FireWire audio helper functions");
|
||||
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -23,7 +23,4 @@ static inline bool rcode_is_permanent_error(int rcode)
|
||||
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
|
||||
}
|
||||
|
||||
void snd_fw_schedule_registration(struct fw_unit *unit,
|
||||
struct delayed_work *dwork);
|
||||
|
||||
#endif
|
||||
|
@ -3,5 +3,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-v2.o motu-protocol-v3.o \
|
||||
motu-protocol-v1.o
|
||||
obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
|
||||
|
@ -16,6 +16,14 @@
|
||||
#define CIP_FMT_MOTU_TX_V3 0x22
|
||||
#define MOTU_FDF_AM824 0x22
|
||||
|
||||
#define TICKS_PER_CYCLE 3072
|
||||
#define CYCLES_PER_SECOND 8000
|
||||
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
|
||||
|
||||
#define CIP_SPH_CYCLE_SHIFT 12
|
||||
#define CIP_SPH_CYCLE_MASK 0x01fff000
|
||||
#define CIP_SPH_OFFSET_MASK 0x00000fff
|
||||
|
||||
/*
|
||||
* Nominally 3125 bytes/second, but the MIDI port's clock might be
|
||||
* 1% too slow, and the bus clock 100 ppm too fast.
|
||||
@ -23,14 +31,6 @@
|
||||
#define MIDI_BYTES_PER_SECOND 3093
|
||||
|
||||
struct amdtp_motu {
|
||||
/* For timestamp processing. */
|
||||
unsigned int quotient_ticks_per_event;
|
||||
unsigned int remainder_ticks_per_event;
|
||||
unsigned int next_ticks;
|
||||
unsigned int next_accumulated;
|
||||
unsigned int next_cycles;
|
||||
unsigned int next_seconds;
|
||||
|
||||
unsigned int pcm_chunks;
|
||||
unsigned int pcm_byte_offset;
|
||||
|
||||
@ -41,26 +41,16 @@ struct amdtp_motu {
|
||||
|
||||
int midi_db_count;
|
||||
unsigned int midi_db_interval;
|
||||
|
||||
struct amdtp_motu_cache *cache;
|
||||
};
|
||||
|
||||
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int midi_ports,
|
||||
struct snd_motu_packet_format *formats)
|
||||
{
|
||||
static const struct {
|
||||
unsigned int quotient_ticks_per_event;
|
||||
unsigned int remainder_ticks_per_event;
|
||||
} params[] = {
|
||||
[CIP_SFC_44100] = { 557, 123 },
|
||||
[CIP_SFC_48000] = { 512, 0 },
|
||||
[CIP_SFC_88200] = { 278, 282 },
|
||||
[CIP_SFC_96000] = { 256, 0 },
|
||||
[CIP_SFC_176400] = { 139, 141 },
|
||||
[CIP_SFC_192000] = { 128, 0 },
|
||||
};
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int pcm_chunks, data_chunks, data_block_quadlets;
|
||||
unsigned int delay;
|
||||
unsigned int mode;
|
||||
int i, err;
|
||||
|
||||
@ -97,19 +87,6 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
p->midi_db_count = 0;
|
||||
p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
|
||||
|
||||
/* IEEE 1394 bus requires. */
|
||||
delay = 0x2e00;
|
||||
|
||||
/* For no-data or empty packets to adjust PCM sampling frequency. */
|
||||
delay += 8000 * 3072 * s->syt_interval / rate;
|
||||
|
||||
p->next_seconds = 0;
|
||||
p->next_cycles = delay / 3072;
|
||||
p->quotient_ticks_per_event = params[s->sfc].quotient_ticks_per_event;
|
||||
p->remainder_ticks_per_event = params[s->sfc].remainder_ticks_per_event;
|
||||
p->next_ticks = delay % 3072;
|
||||
p->next_accumulated = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -322,6 +299,34 @@ static void probe_tracepoints_events(struct amdtp_stream *s,
|
||||
}
|
||||
}
|
||||
|
||||
static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *buf,
|
||||
unsigned int data_blocks, unsigned int data_block_quadlets)
|
||||
{
|
||||
unsigned int *event_offsets = cache->event_offsets;
|
||||
const unsigned int cache_size = cache->size;
|
||||
unsigned int cache_tail = cache->tail;
|
||||
unsigned int base_tick = cache->tx_cycle_count * TICKS_PER_CYCLE;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data_blocks; ++i) {
|
||||
u32 sph = be32_to_cpu(*buf);
|
||||
unsigned int tick;
|
||||
|
||||
tick = ((sph & CIP_SPH_CYCLE_MASK) >> CIP_SPH_CYCLE_SHIFT) * TICKS_PER_CYCLE +
|
||||
(sph & CIP_SPH_OFFSET_MASK);
|
||||
|
||||
if (tick < base_tick)
|
||||
tick += TICKS_PER_SECOND;
|
||||
event_offsets[cache_tail] = tick - base_tick;
|
||||
|
||||
cache_tail = (cache_tail + 1) % cache_size;
|
||||
buf += data_block_quadlets;
|
||||
}
|
||||
|
||||
cache->tail = cache_tail;
|
||||
cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND;
|
||||
}
|
||||
|
||||
static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
const struct pkt_desc *descs,
|
||||
unsigned int packets,
|
||||
@ -331,12 +336,17 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (p->cache->tx_cycle_count == UINT_MAX)
|
||||
p->cache->tx_cycle_count = (s->domain->processing_cycle.tx_start % CYCLES_PER_SECOND);
|
||||
|
||||
// For data block processing.
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
__be32 *buf = desc->ctx_payload;
|
||||
unsigned int data_blocks = desc->data_blocks;
|
||||
|
||||
cache_event_offsets(p->cache, buf, data_blocks, s->data_block_quadlets);
|
||||
|
||||
if (pcm) {
|
||||
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
|
||||
pcm_frames += data_blocks;
|
||||
@ -354,46 +364,26 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
|
||||
return pcm_frames;
|
||||
}
|
||||
|
||||
static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
|
||||
static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks,
|
||||
unsigned int data_block_quadlets)
|
||||
{
|
||||
p->next_accumulated += p->remainder_ticks_per_event;
|
||||
if (p->next_accumulated >= 441) {
|
||||
p->next_accumulated -= 441;
|
||||
p->next_ticks++;
|
||||
}
|
||||
|
||||
p->next_ticks += p->quotient_ticks_per_event;
|
||||
if (p->next_ticks >= 3072) {
|
||||
p->next_ticks -= 3072;
|
||||
p->next_cycles++;
|
||||
}
|
||||
|
||||
if (p->next_cycles >= 8000) {
|
||||
p->next_cycles -= 8000;
|
||||
p->next_seconds++;
|
||||
}
|
||||
|
||||
if (p->next_seconds >= 128)
|
||||
p->next_seconds -= 128;
|
||||
}
|
||||
|
||||
static void write_sph(struct amdtp_stream *s, __be32 *buffer,
|
||||
unsigned int data_blocks)
|
||||
{
|
||||
struct amdtp_motu *p = s->protocol;
|
||||
unsigned int next_cycles;
|
||||
unsigned int i;
|
||||
u32 sph;
|
||||
unsigned int *event_offsets = cache->event_offsets;
|
||||
const unsigned int cache_size = cache->size;
|
||||
unsigned int cache_head = cache->head;
|
||||
unsigned int base_tick = cache->rx_cycle_count * TICKS_PER_CYCLE;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < data_blocks; i++) {
|
||||
next_cycles = (s->start_cycle + p->next_cycles) % 8000;
|
||||
sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff;
|
||||
unsigned int tick = (base_tick + event_offsets[cache_head]) % TICKS_PER_SECOND;
|
||||
u32 sph = ((tick / TICKS_PER_CYCLE) << CIP_SPH_CYCLE_SHIFT) | (tick % TICKS_PER_CYCLE);
|
||||
*buffer = cpu_to_be32(sph);
|
||||
|
||||
compute_next_elapse_from_start(p);
|
||||
|
||||
buffer += s->data_block_quadlets;
|
||||
cache_head = (cache_head + 1) % cache_size;
|
||||
buffer += data_block_quadlets;
|
||||
}
|
||||
|
||||
cache->head = cache_head;
|
||||
cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND;
|
||||
}
|
||||
|
||||
static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
@ -405,6 +395,9 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
unsigned int pcm_frames = 0;
|
||||
int i;
|
||||
|
||||
if (p->cache->rx_cycle_count == UINT_MAX)
|
||||
p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND);
|
||||
|
||||
// For data block processing.
|
||||
for (i = 0; i < packets; ++i) {
|
||||
const struct pkt_desc *desc = descs + i;
|
||||
@ -423,7 +416,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
|
||||
// TODO: how to interact control messages between userspace?
|
||||
|
||||
write_sph(s, buf, data_blocks);
|
||||
write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
|
||||
}
|
||||
|
||||
// For tracepoints.
|
||||
@ -436,11 +429,12 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
|
||||
|
||||
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
const struct snd_motu_spec *spec)
|
||||
const struct snd_motu_spec *spec, struct amdtp_motu_cache *cache)
|
||||
{
|
||||
amdtp_stream_process_ctx_payloads_t process_ctx_payloads;
|
||||
int fmt = CIP_FMT_MOTU;
|
||||
int flags = CIP_BLOCKING;
|
||||
unsigned int flags = CIP_BLOCKING | CIP_UNAWARE_SYT;
|
||||
struct amdtp_motu *p;
|
||||
int err;
|
||||
|
||||
if (dir == AMDTP_IN_STREAM) {
|
||||
@ -478,9 +472,10 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
if (dir == AMDTP_OUT_STREAM) {
|
||||
// Use fixed value for FDF field.
|
||||
s->ctx_data.rx.fdf = MOTU_FDF_AM824;
|
||||
// Not used.
|
||||
s->ctx_data.rx.syt_override = 0xffff;
|
||||
}
|
||||
|
||||
p = s->protocol;
|
||||
p->cache = cache;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
470
sound/firewire/motu/motu-protocol-v1.c
Normal file
470
sound/firewire/motu/motu-protocol-v1.c
Normal file
@ -0,0 +1,470 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
// motu-protocol-v1.c - a part of driver for MOTU FireWire series
|
||||
//
|
||||
// Copyright (c) 2021 Takashi Sakamoto <o-takashi@sakamocchi.jp>
|
||||
//
|
||||
// Licensed under the terms of the GNU General Public License, version 2.
|
||||
|
||||
#include "motu.h"
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
||||
// Status register for MOTU 828 (0x'ffff'f000'0b00).
|
||||
//
|
||||
// 0xffff0000: ISOC_COMM_CONTROL_MASK in motu-stream.c.
|
||||
// 0x00008000: mode of optical input interface.
|
||||
// 0x00008000: for S/PDIF signal.
|
||||
// 0x00000000: disabled or for ADAT signal.
|
||||
// 0x00004000: mode of optical output interface.
|
||||
// 0x00004000: for S/PDIF signal.
|
||||
// 0x00000000: disabled or for ADAT signal.
|
||||
// 0x00003f00: monitor input mode.
|
||||
// 0x00000800: analog-1/2
|
||||
// 0x00001a00: analog-3/4
|
||||
// 0x00002c00: analog-5/6
|
||||
// 0x00003e00: analog-7/8
|
||||
// 0x00000000: analog-1
|
||||
// 0x00000900: analog-2
|
||||
// 0x00001200: analog-3
|
||||
// 0x00001b00: analog-4
|
||||
// 0x00002400: analog-5
|
||||
// 0x00002d00: analog-6
|
||||
// 0x00003600: analog-7
|
||||
// 0x00003f00: analog-8
|
||||
// 0x00000080: enable stream input.
|
||||
// 0x00000040: disable monitor input.
|
||||
// 0x00000008: enable main out.
|
||||
// 0x00000004: rate of sampling clock.
|
||||
// 0x00000004: 48.0 kHz
|
||||
// 0x00000000: 44.1 kHz
|
||||
// 0x00000023: source of sampling clock.
|
||||
// 0x00000003: source packet header (SPH)
|
||||
// 0x00000002: S/PDIF on optical/coaxial interface.
|
||||
// 0x00000021: ADAT on optical interface
|
||||
// 0x00000001: ADAT on Dsub 9pin
|
||||
// 0x00000000: internal
|
||||
|
||||
#define CLK_828_STATUS_OFFSET 0x0b00
|
||||
#define CLK_828_STATUS_MASK 0x0000ffff
|
||||
#define CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF 0x00008000
|
||||
#define CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF 0x00004000
|
||||
#define CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES 0x00000080
|
||||
#define CLK_828_STATUS_FLAG_ENABLE_OUTPUT 0x00000008
|
||||
#define CLK_828_STATUS_FLAG_RATE_48000 0x00000004
|
||||
#define CLK_828_STATUS_MASK_SRC 0x00000023
|
||||
#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000021
|
||||
#define CLK_828_STATUS_FLAG_SRC_SPH 0x00000003
|
||||
#define CLK_828_STATUS_FLAG_SRC_SPDIF 0x00000002
|
||||
#define CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000001
|
||||
#define CLK_828_STATUS_FLAG_SRC_INTERNAL 0x00000000
|
||||
|
||||
// Status register for MOTU 896 (0x'ffff'f000'0b14).
|
||||
//
|
||||
// 0xf0000000: enable physical and stream input to DAC.
|
||||
// 0x80000000: disable
|
||||
// 0x40000000: disable
|
||||
// 0x20000000: enable (prior to the other bits)
|
||||
// 0x10000000: disable
|
||||
// 0x00000000: disable
|
||||
// 0x08000000: speed of word clock signal output on BNC interface.
|
||||
// 0x00000000: force to low rate (44.1/48.0 kHz).
|
||||
// 0x08000000: follow to system clock.
|
||||
// 0x04000000: something relevant to clock.
|
||||
// 0x03000000: enable output.
|
||||
// 0x02000000: enabled irreversibly once standing unless the device voluntarily disables it.
|
||||
// 0x01000000: enabled irreversibly once standing unless the device voluntarily disables it.
|
||||
// 0x00ffff00: monitor input mode.
|
||||
// 0x00000000: disabled
|
||||
// 0x00004800: analog-1/2
|
||||
// 0x00005a00: analog-3/4
|
||||
// 0x00006c00: analog-5/6
|
||||
// 0x00007e00: analog-7/8
|
||||
// 0x00104800: AES/EBU-1/2
|
||||
// 0x00004000: analog-1
|
||||
// 0x00004900: analog-2
|
||||
// 0x00005200: analog-3
|
||||
// 0x00005b00: analog-4
|
||||
// 0x00006400: analog-5
|
||||
// 0x00006d00: analog-6
|
||||
// 0x00007600: analog-7
|
||||
// 0x00007f00: analog-8
|
||||
// 0x00104000: AES/EBU-1
|
||||
// 0x00104900: AES/EBU-2
|
||||
// 0x00000060: sample rate conversion for AES/EBU input/output.
|
||||
// 0x00000000: None
|
||||
// 0x00000020: input signal is converted to system rate
|
||||
// 0x00000040: output is slave to input, ignoring system rate
|
||||
// 0x00000060: output is double rate than system rate
|
||||
// 0x00000018: nominal rate of sampling clock.
|
||||
// 0x00000000: 44.1 kHz
|
||||
// 0x00000008: 48.0 kHz
|
||||
// 0x00000010: 88.2 kHz
|
||||
// 0x00000018: 96.0 kHz
|
||||
// 0x00000007: source of sampling clock.
|
||||
// 0x00000000: internal
|
||||
// 0x00000001: ADAT on optical interface
|
||||
// 0x00000002: AES/EBU on XLR
|
||||
// 0x00000003: source packet header (SPH)
|
||||
// 0x00000004: word clock on BNC
|
||||
// 0x00000005: ADAT on Dsub 9pin
|
||||
|
||||
#define CLK_896_STATUS_OFFSET 0x0b14
|
||||
#define CLK_896_STATUS_FLAG_FETCH_ENABLE 0x20000000
|
||||
#define CLK_896_STATUS_FLAG_OUTPUT_ON 0x03000000
|
||||
#define CLK_896_STATUS_MASK_SRC 0x00000007
|
||||
#define CLK_896_STATUS_FLAG_SRC_INTERNAL 0x00000000
|
||||
#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT 0x00000001
|
||||
#define CLK_896_STATUS_FLAG_SRC_AESEBU 0x00000002
|
||||
#define CLK_896_STATUS_FLAG_SRC_SPH 0x00000003
|
||||
#define CLK_896_STATUS_FLAG_SRC_WORD 0x00000004
|
||||
#define CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB 0x00000005
|
||||
#define CLK_896_STATUS_MASK_RATE 0x00000018
|
||||
#define CLK_896_STATUS_FLAG_RATE_44100 0x00000000
|
||||
#define CLK_896_STATUS_FLAG_RATE_48000 0x00000008
|
||||
#define CLK_896_STATUS_FLAG_RATE_88200 0x00000010
|
||||
#define CLK_896_STATUS_FLAG_RATE_96000 0x00000018
|
||||
|
||||
static void parse_clock_rate_828(u32 data, unsigned int *rate)
|
||||
{
|
||||
if (data & CLK_828_STATUS_FLAG_RATE_48000)
|
||||
*rate = 48000;
|
||||
else
|
||||
*rate = 44100;
|
||||
}
|
||||
|
||||
static int get_clock_rate_828(struct snd_motu *motu, unsigned int *rate)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
parse_clock_rate_828(be32_to_cpu(reg), rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_clock_rate_896(u32 data, unsigned int *rate)
|
||||
{
|
||||
switch (data & CLK_896_STATUS_MASK_RATE) {
|
||||
case CLK_896_STATUS_FLAG_RATE_44100:
|
||||
*rate = 44100;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_RATE_48000:
|
||||
*rate = 48000;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_RATE_88200:
|
||||
*rate = 88200;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_RATE_96000:
|
||||
*rate = 96000;
|
||||
break;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_clock_rate_896(struct snd_motu *motu, unsigned int *rate)
|
||||
{
|
||||
__be32 reg;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
return parse_clock_rate_896(be32_to_cpu(reg), rate);
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
|
||||
{
|
||||
if (motu->spec == &snd_motu_spec_828)
|
||||
return get_clock_rate_828(motu, rate);
|
||||
else if (motu->spec == &snd_motu_spec_896)
|
||||
return get_clock_rate_896(motu, rate);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int set_clock_rate_828(struct snd_motu *motu, unsigned int rate)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
|
||||
|
||||
data &= ~CLK_828_STATUS_FLAG_RATE_48000;
|
||||
if (rate == 48000)
|
||||
data |= CLK_828_STATUS_FLAG_RATE_48000;
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg));
|
||||
}
|
||||
|
||||
static int set_clock_rate_896(struct snd_motu *motu, unsigned int rate)
|
||||
{
|
||||
unsigned int flag;
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
switch (rate) {
|
||||
case 44100:
|
||||
flag = CLK_896_STATUS_FLAG_RATE_44100;
|
||||
break;
|
||||
case 48000:
|
||||
flag = CLK_896_STATUS_FLAG_RATE_48000;
|
||||
break;
|
||||
case 88200:
|
||||
flag = CLK_896_STATUS_FLAG_RATE_88200;
|
||||
break;
|
||||
case 96000:
|
||||
flag = CLK_896_STATUS_FLAG_RATE_96000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data &= ~CLK_896_STATUS_MASK_RATE;
|
||||
data |= flag;
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg));
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu, unsigned int rate)
|
||||
{
|
||||
if (motu->spec == &snd_motu_spec_828)
|
||||
return set_clock_rate_828(motu, rate);
|
||||
else if (motu->spec == &snd_motu_spec_896)
|
||||
return set_clock_rate_896(motu, rate);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int get_clock_source_828(struct snd_motu *motu, enum snd_motu_clock_source *src)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
|
||||
|
||||
switch (data & CLK_828_STATUS_MASK_SRC) {
|
||||
case CLK_828_STATUS_FLAG_SRC_ADAT_ON_OPT:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
|
||||
break;
|
||||
case CLK_828_STATUS_FLAG_SRC_SPH:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case CLK_828_STATUS_FLAG_SRC_SPDIF:
|
||||
{
|
||||
if (data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
|
||||
break;
|
||||
}
|
||||
case CLK_828_STATUS_FLAG_SRC_ADAT_ON_DSUB:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
|
||||
break;
|
||||
case CLK_828_STATUS_FLAG_SRC_INTERNAL:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_clock_source_896(struct snd_motu *motu, enum snd_motu_clock_source *src)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
switch (data & CLK_896_STATUS_MASK_SRC) {
|
||||
case CLK_896_STATUS_FLAG_SRC_INTERNAL:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_SRC_ADAT_ON_OPT:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_SRC_AESEBU:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_SRC_SPH:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_SRC_WORD:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case CLK_896_STATUS_FLAG_SRC_ADAT_ON_DSUB:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
|
||||
break;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu, enum snd_motu_clock_source *src)
|
||||
{
|
||||
if (motu->spec == &snd_motu_spec_828)
|
||||
return get_clock_source_828(motu, src);
|
||||
else if (motu->spec == &snd_motu_spec_896)
|
||||
return get_clock_source_896(motu, src);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int switch_fetching_mode_828(struct snd_motu *motu, bool enable)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
|
||||
|
||||
data &= ~(CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT);
|
||||
if (enable) {
|
||||
// This transaction should be initiated after the device receives batch of packets
|
||||
// since the device voluntarily mutes outputs. As a workaround, yield processor over
|
||||
// 100 msec.
|
||||
msleep(100);
|
||||
data |= CLK_828_STATUS_FLAG_FETCH_PCM_FRAMES | CLK_828_STATUS_FLAG_ENABLE_OUTPUT;
|
||||
}
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
return snd_motu_transaction_write(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg));
|
||||
}
|
||||
|
||||
static int switch_fetching_mode_896(struct snd_motu *motu, bool enable)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg);
|
||||
|
||||
data &= ~CLK_896_STATUS_FLAG_FETCH_ENABLE;
|
||||
if (enable)
|
||||
data |= CLK_896_STATUS_FLAG_FETCH_ENABLE | CLK_896_STATUS_FLAG_OUTPUT_ON;
|
||||
|
||||
reg = cpu_to_be32(data);
|
||||
return snd_motu_transaction_write(motu, CLK_896_STATUS_OFFSET, ®, sizeof(reg));
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu, bool enable)
|
||||
{
|
||||
if (motu->spec == &snd_motu_spec_828)
|
||||
return switch_fetching_mode_828(motu, enable);
|
||||
else if (motu->spec == &snd_motu_spec_896)
|
||||
return switch_fetching_mode_896(motu, enable);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static int detect_packet_formats_828(struct snd_motu *motu)
|
||||
{
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
motu->tx_packet_formats.pcm_byte_offset = 4;
|
||||
motu->tx_packet_formats.msg_chunks = 2;
|
||||
|
||||
motu->rx_packet_formats.pcm_byte_offset = 4;
|
||||
motu->rx_packet_formats.msg_chunks = 0;
|
||||
|
||||
err = snd_motu_transaction_read(motu, CLK_828_STATUS_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
data = be32_to_cpu(reg) & CLK_828_STATUS_MASK;
|
||||
|
||||
// The number of chunks is just reduced when SPDIF is activated.
|
||||
if (!(data & CLK_828_STATUS_FLAG_OPT_IN_IFACE_IS_SPDIF))
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
|
||||
if (!(data & CLK_828_STATUS_FLAG_OPT_OUT_IFACE_IS_SPDIF))
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int detect_packet_formats_896(struct snd_motu *motu)
|
||||
{
|
||||
// 24bit PCM frames follow to source packet header without message chunk.
|
||||
motu->tx_packet_formats.pcm_byte_offset = 4;
|
||||
motu->rx_packet_formats.pcm_byte_offset = 4;
|
||||
|
||||
// No message chunk in data block.
|
||||
motu->tx_packet_formats.msg_chunks = 0;
|
||||
motu->rx_packet_formats.msg_chunks = 0;
|
||||
|
||||
// Always enable optical interface for ADAT signal since the device have no registers
|
||||
// to refer to current configuration.
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 8;
|
||||
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 8;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
memcpy(motu->tx_packet_formats.pcm_chunks, motu->spec->tx_fixed_pcm_chunks,
|
||||
sizeof(motu->tx_packet_formats.pcm_chunks));
|
||||
memcpy(motu->rx_packet_formats.pcm_chunks, motu->spec->rx_fixed_pcm_chunks,
|
||||
sizeof(motu->rx_packet_formats.pcm_chunks));
|
||||
|
||||
if (motu->spec == &snd_motu_spec_828)
|
||||
return detect_packet_formats_828(motu);
|
||||
else if (motu->spec == &snd_motu_spec_896)
|
||||
return detect_packet_formats_896(motu);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_828 = {
|
||||
.name = "828",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V1,
|
||||
.tx_fixed_pcm_chunks = {10, 0, 0},
|
||||
.rx_fixed_pcm_chunks = {10, 0, 0},
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_896 = {
|
||||
.name = "896",
|
||||
.tx_fixed_pcm_chunks = {10, 10, 0},
|
||||
.rx_fixed_pcm_chunks = {10, 10, 0},
|
||||
};
|
@ -12,6 +12,13 @@
|
||||
#define V2_CLOCK_RATE_SHIFT 3
|
||||
#define V2_CLOCK_SRC_MASK 0x00000007
|
||||
#define V2_CLOCK_SRC_SHIFT 0
|
||||
#define V2_CLOCK_SRC_AESEBU_ON_XLR 0x07
|
||||
#define V2_CLOCK_SRC_ADAT_ON_DSUB 0x05
|
||||
#define V2_CLOCK_SRC_WORD_ON_BNC 0x04
|
||||
#define V2_CLOCK_SRC_SPH 0x03
|
||||
#define V2_CLOCK_SRC_SPDIF 0x02 // on either coaxial or optical
|
||||
#define V2_CLOCK_SRC_ADAT_ON_OPT 0x01
|
||||
#define V2_CLOCK_SRC_INTERNAL 0x00
|
||||
#define V2_CLOCK_FETCH_ENABLE 0x02000000
|
||||
#define V2_CLOCK_MODEL_SPECIFIC 0x04000000
|
||||
|
||||
@ -78,82 +85,58 @@ int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
|
||||
sizeof(reg));
|
||||
}
|
||||
|
||||
static int detect_clock_source_optical_model(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
switch (data) {
|
||||
case 0:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
__be32 reg;
|
||||
|
||||
// To check the configuration of optical interface.
|
||||
int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET,
|
||||
®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (be32_to_cpu(reg) & 0x00000200)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
case 3:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case 4:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case 5:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
|
||||
break;
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v2_detect_clock_source(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
switch (data) {
|
||||
case 0:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case 2:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
case 3:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case 4:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_clock_source(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
data &= V2_CLOCK_SRC_MASK;
|
||||
if (motu->spec == &snd_motu_spec_828mk2 ||
|
||||
motu->spec == &snd_motu_spec_traveler)
|
||||
return detect_clock_source_optical_model(motu, data, src);
|
||||
else
|
||||
return v2_detect_clock_source(motu, data, src);
|
||||
switch (data & V2_CLOCK_SRC_MASK) {
|
||||
case V2_CLOCK_SRC_INTERNAL:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case V2_CLOCK_SRC_ADAT_ON_OPT:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
|
||||
break;
|
||||
case V2_CLOCK_SRC_SPDIF:
|
||||
{
|
||||
bool support_iec60958_on_opt = (motu->spec == &snd_motu_spec_828mk2 ||
|
||||
motu->spec == &snd_motu_spec_traveler);
|
||||
|
||||
if (!support_iec60958_on_opt) {
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
} else {
|
||||
__be32 reg;
|
||||
|
||||
// To check the configuration of optical interface.
|
||||
int err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®,
|
||||
sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_SPDIF)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case V2_CLOCK_SRC_SPH:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case V2_CLOCK_SRC_WORD_ON_BNC:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case V2_CLOCK_SRC_ADAT_ON_DSUB:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
|
||||
break;
|
||||
case V2_CLOCK_SRC_AESEBU_ON_XLR:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR;
|
||||
break;
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu,
|
||||
@ -235,59 +218,9 @@ int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu,
|
||||
}
|
||||
}
|
||||
|
||||
static int detect_packet_formats_828mk2(struct snd_motu *motu, u32 data)
|
||||
{
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int detect_packet_formats_traveler(struct snd_motu *motu, u32 data)
|
||||
{
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int detect_packet_formats_8pre(struct snd_motu *motu, u32 data)
|
||||
{
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 8;
|
||||
}
|
||||
|
||||
if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) ==
|
||||
V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
|
||||
{
|
||||
bool has_two_opt_ifaces = (motu->spec == &snd_motu_spec_8pre);
|
||||
__be32 reg;
|
||||
u32 data;
|
||||
int err;
|
||||
@ -311,14 +244,25 @@ int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu)
|
||||
motu->spec->rx_fixed_pcm_chunks,
|
||||
sizeof(motu->rx_packet_formats.pcm_chunks));
|
||||
|
||||
if (motu->spec == &snd_motu_spec_828mk2)
|
||||
return detect_packet_formats_828mk2(motu, data);
|
||||
else if (motu->spec == &snd_motu_spec_traveler)
|
||||
return detect_packet_formats_traveler(motu, data);
|
||||
else if (motu->spec == &snd_motu_spec_8pre)
|
||||
return detect_packet_formats_8pre(motu, data);
|
||||
else
|
||||
return 0;
|
||||
if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->tx_packet_formats.pcm_chunks[0] += 8;
|
||||
|
||||
if (!has_two_opt_ifaces)
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 4;
|
||||
else
|
||||
motu->tx_packet_formats.pcm_chunks[1] += 8;
|
||||
}
|
||||
|
||||
if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == V2_OPT_IFACE_MODE_ADAT) {
|
||||
motu->rx_packet_formats.pcm_chunks[0] += 8;
|
||||
|
||||
if (!has_two_opt_ifaces)
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 4;
|
||||
else
|
||||
motu->rx_packet_formats.pcm_chunks[1] += 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_828mk2 = {
|
||||
@ -353,6 +297,7 @@ const struct snd_motu_spec snd_motu_spec_8pre = {
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V2,
|
||||
.flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q |
|
||||
SND_MOTU_SPEC_TX_MIDI_2ND_Q,
|
||||
.tx_fixed_pcm_chunks = {10, 6, 0},
|
||||
.rx_fixed_pcm_chunks = {10, 6, 0},
|
||||
// Two dummy chunks always in the end of data block.
|
||||
.tx_fixed_pcm_chunks = {10, 10, 0},
|
||||
.rx_fixed_pcm_chunks = {6, 6, 0},
|
||||
};
|
||||
|
@ -13,6 +13,12 @@
|
||||
#define V3_CLOCK_RATE_MASK 0x0000ff00
|
||||
#define V3_CLOCK_RATE_SHIFT 8
|
||||
#define V3_CLOCK_SOURCE_MASK 0x000000ff
|
||||
#define V3_CLOCK_SRC_INTERNAL 0x00
|
||||
#define V3_CLOCK_SRC_WORD_ON_BNC 0x01
|
||||
#define V3_CLOCK_SRC_SPH 0x02
|
||||
#define V3_CLOCK_SRC_SPDIF_ON_COAX 0x10
|
||||
#define V3_CLOCK_SRC_OPT_IFACE_A 0x18
|
||||
#define V3_CLOCK_SRC_OPT_IFACE_B 0x19
|
||||
|
||||
#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
|
||||
#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
|
||||
@ -97,81 +103,6 @@ int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
switch (data) {
|
||||
case 0x00:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case 0x01:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case 0x02:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case 0x10:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
{
|
||||
__be32 reg;
|
||||
u32 options;
|
||||
int err;
|
||||
|
||||
err = snd_motu_transaction_read(motu,
|
||||
V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
options = be32_to_cpu(reg);
|
||||
|
||||
if (data == 0x18) {
|
||||
if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
|
||||
} else {
|
||||
if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int v3_detect_clock_source(struct snd_motu *motu, u32 data,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
switch (data) {
|
||||
case 0x00:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case 0x01:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case 0x02:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case 0x10:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src)
|
||||
{
|
||||
@ -185,10 +116,50 @@ int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu,
|
||||
return err;
|
||||
data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK;
|
||||
|
||||
if (motu->spec == &snd_motu_spec_828mk3)
|
||||
return detect_clock_source_828mk3(motu, data, src);
|
||||
else
|
||||
return v3_detect_clock_source(motu, data, src);
|
||||
switch (data) {
|
||||
case V3_CLOCK_SRC_INTERNAL:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
|
||||
break;
|
||||
case V3_CLOCK_SRC_WORD_ON_BNC:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
|
||||
break;
|
||||
case V3_CLOCK_SRC_SPH:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPH;
|
||||
break;
|
||||
case V3_CLOCK_SRC_SPDIF_ON_COAX:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
|
||||
break;
|
||||
case V3_CLOCK_SRC_OPT_IFACE_A:
|
||||
case V3_CLOCK_SRC_OPT_IFACE_B:
|
||||
{
|
||||
__be32 reg;
|
||||
u32 options;
|
||||
|
||||
err = snd_motu_transaction_read(motu,
|
||||
V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg));
|
||||
if (err < 0)
|
||||
return err;
|
||||
options = be32_to_cpu(reg);
|
||||
|
||||
if (data == V3_CLOCK_SRC_OPT_IFACE_A) {
|
||||
if (options & V3_NO_ADAT_OPT_IN_IFACE_A)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
|
||||
} else {
|
||||
if (options & V3_NO_ADAT_OPT_IN_IFACE_B)
|
||||
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
|
||||
else
|
||||
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu,
|
||||
@ -284,14 +255,14 @@ int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu)
|
||||
motu->spec->rx_fixed_pcm_chunks,
|
||||
sizeof(motu->rx_packet_formats.pcm_chunks));
|
||||
|
||||
if (motu->spec == &snd_motu_spec_828mk3)
|
||||
if (motu->spec == &snd_motu_spec_828mk3_fw || motu->spec == &snd_motu_spec_828mk3_hybrid)
|
||||
return detect_packet_formats_828mk3(motu, data);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_828mk3 = {
|
||||
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 |
|
||||
@ -300,6 +271,15 @@ const struct snd_motu_spec snd_motu_spec_828mk3 = {
|
||||
.rx_fixed_pcm_chunks = {14, 14, 10},
|
||||
};
|
||||
|
||||
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,
|
||||
.tx_fixed_pcm_chunks = {18, 18, 14},
|
||||
.rx_fixed_pcm_chunks = {14, 14, 14}, // Additional 4 dummy chunks at higher rate.
|
||||
};
|
||||
|
||||
const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = {
|
||||
.name = "UltraLiteMk3",
|
||||
.protocol_version = SND_MOTU_PROTOCOL_V3,
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "motu.h"
|
||||
|
||||
#define CALLBACK_TIMEOUT 200
|
||||
#define READY_TIMEOUT_MS 200
|
||||
|
||||
#define ISOC_COMM_CONTROL_OFFSET 0x0b00
|
||||
#define ISOC_COMM_CONTROL_MASK 0xffff0000
|
||||
@ -153,6 +153,9 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
|
||||
kfree(motu->cache.event_offsets);
|
||||
motu->cache.event_offsets = NULL;
|
||||
|
||||
err = snd_motu_protocol_set_clock_rate(motu, rate);
|
||||
if (err < 0) {
|
||||
dev_err(&motu->unit->device,
|
||||
@ -181,6 +184,15 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate,
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
return err;
|
||||
}
|
||||
|
||||
motu->cache.size = motu->tx_stream.syt_interval * frames_per_buffer;
|
||||
motu->cache.event_offsets = kcalloc(motu->cache.size, sizeof(*motu->cache.event_offsets),
|
||||
GFP_KERNEL);
|
||||
if (!motu->cache.event_offsets) {
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -260,14 +272,19 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu)
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
err = amdtp_domain_start(&motu->domain, 0);
|
||||
motu->cache.tail = 0;
|
||||
motu->cache.tx_cycle_count = UINT_MAX;
|
||||
motu->cache.head = 0;
|
||||
motu->cache.rx_cycle_count = UINT_MAX;
|
||||
|
||||
// NOTE: The device requires both of replay; the sequence of the number of data
|
||||
// blocks per packet, and the sequence of source packet header per data block as
|
||||
// presentation time.
|
||||
err = amdtp_domain_start(&motu->domain, 0, true, false);
|
||||
if (err < 0)
|
||||
goto stop_streams;
|
||||
|
||||
if (!amdtp_stream_wait_callback(&motu->tx_stream,
|
||||
CALLBACK_TIMEOUT) ||
|
||||
!amdtp_stream_wait_callback(&motu->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
if (!amdtp_domain_wait_ready(&motu->domain, READY_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto stop_streams;
|
||||
}
|
||||
@ -296,6 +313,9 @@ void snd_motu_stream_stop_duplex(struct snd_motu *motu)
|
||||
|
||||
fw_iso_resources_free(&motu->tx_resources);
|
||||
fw_iso_resources_free(&motu->rx_resources);
|
||||
|
||||
kfree(motu->cache.event_offsets);
|
||||
motu->cache.event_offsets = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,7 +337,7 @@ static int init_stream(struct snd_motu *motu, struct amdtp_stream *s)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_motu_init(s, motu->unit, dir, motu->spec);
|
||||
err = amdtp_motu_init(s, motu->unit, dir, motu->spec, &motu->cache);
|
||||
if (err < 0)
|
||||
fw_iso_resources_destroy(resources);
|
||||
|
||||
|
@ -57,22 +57,31 @@ static void motu_card_free(struct snd_card *card)
|
||||
|
||||
snd_motu_transaction_unregister(motu);
|
||||
snd_motu_stream_destroy_duplex(motu);
|
||||
|
||||
mutex_destroy(&motu->mutex);
|
||||
fw_unit_put(motu->unit);
|
||||
}
|
||||
|
||||
static void do_registration(struct work_struct *work)
|
||||
static int motu_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_motu *motu = container_of(work, struct snd_motu, dwork.work);
|
||||
struct snd_card *card;
|
||||
struct snd_motu *motu;
|
||||
int err;
|
||||
|
||||
if (motu->registered)
|
||||
return;
|
||||
|
||||
err = snd_card_new(&motu->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&motu->card);
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*motu), &card);
|
||||
if (err < 0)
|
||||
return;
|
||||
motu->card->private_free = motu_card_free;
|
||||
motu->card->private_data = motu;
|
||||
return err;
|
||||
card->private_free = motu_card_free;
|
||||
|
||||
motu = card->private_data;
|
||||
motu->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, motu);
|
||||
motu->card = card;
|
||||
|
||||
motu->spec = (const struct snd_motu_spec *)entry->driver_data;
|
||||
mutex_init(&motu->mutex);
|
||||
spin_lock_init(&motu->lock);
|
||||
init_waitqueue_head(&motu->hwdep_wait);
|
||||
|
||||
name_card(motu);
|
||||
|
||||
@ -103,71 +112,28 @@ static void do_registration(struct work_struct *work)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = snd_card_register(motu->card);
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
motu->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_card_free(motu->card);
|
||||
dev_info(&motu->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int motu_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_motu *motu;
|
||||
|
||||
/* Allocate this independently of sound card instance. */
|
||||
motu = devm_kzalloc(&unit->device, sizeof(struct snd_motu), GFP_KERNEL);
|
||||
if (!motu)
|
||||
return -ENOMEM;
|
||||
motu->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, motu);
|
||||
|
||||
motu->spec = (const struct snd_motu_spec *)entry->driver_data;
|
||||
mutex_init(&motu->mutex);
|
||||
spin_lock_init(&motu->lock);
|
||||
init_waitqueue_head(&motu->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &motu->dwork);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void motu_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_motu *motu = dev_get_drvdata(&unit->device);
|
||||
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&motu->dwork);
|
||||
|
||||
if (motu->registered) {
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(motu->card);
|
||||
}
|
||||
|
||||
mutex_destroy(&motu->mutex);
|
||||
fw_unit_put(motu->unit);
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(motu->card);
|
||||
}
|
||||
|
||||
static void motu_bus_update(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_motu *motu = dev_get_drvdata(&unit->device);
|
||||
|
||||
/* Postpone a workqueue for deferred registration. */
|
||||
if (!motu->registered)
|
||||
snd_fw_schedule_registration(unit, &motu->dwork);
|
||||
|
||||
/* The handler address register becomes initialized. */
|
||||
snd_motu_transaction_reregister(motu);
|
||||
}
|
||||
@ -184,13 +150,16 @@ static void motu_bus_update(struct fw_unit *unit)
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id motu_id_table[] = {
|
||||
SND_MOTU_DEV_ENTRY(0x000001, &snd_motu_spec_828),
|
||||
SND_MOTU_DEV_ENTRY(0x000002, &snd_motu_spec_896),
|
||||
SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2),
|
||||
SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler),
|
||||
SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite),
|
||||
SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre),
|
||||
SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3), // FireWire only.
|
||||
SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3_fw), // FireWire only.
|
||||
SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only.
|
||||
SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3), // Hybrid.
|
||||
SND_MOTU_DEV_ENTRY(0x000030, &snd_motu_spec_ultralite_mk3), // Hybrid.
|
||||
SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3_hybrid), // Hybrid.
|
||||
SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express),
|
||||
SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre),
|
||||
{ }
|
||||
|
@ -39,15 +39,21 @@ struct snd_motu_packet_format {
|
||||
unsigned char pcm_chunks[3];
|
||||
};
|
||||
|
||||
struct amdtp_motu_cache {
|
||||
unsigned int *event_offsets;
|
||||
unsigned int size;
|
||||
unsigned int tail;
|
||||
unsigned int tx_cycle_count;
|
||||
unsigned int head;
|
||||
unsigned int rx_cycle_count;
|
||||
};
|
||||
|
||||
struct snd_motu {
|
||||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
bool registered;
|
||||
struct delayed_work dwork;
|
||||
|
||||
/* Model dependent information. */
|
||||
const struct snd_motu_spec *spec;
|
||||
|
||||
@ -70,6 +76,8 @@ struct snd_motu {
|
||||
wait_queue_head_t hwdep_wait;
|
||||
|
||||
struct amdtp_domain domain;
|
||||
|
||||
struct amdtp_motu_cache cache;
|
||||
};
|
||||
|
||||
enum snd_motu_spec_flags {
|
||||
@ -99,6 +107,7 @@ enum snd_motu_clock_source {
|
||||
};
|
||||
|
||||
enum snd_motu_protocol_version {
|
||||
SND_MOTU_PROTOCOL_V1,
|
||||
SND_MOTU_PROTOCOL_V2,
|
||||
SND_MOTU_PROTOCOL_V3,
|
||||
};
|
||||
@ -106,25 +115,31 @@ enum snd_motu_protocol_version {
|
||||
struct snd_motu_spec {
|
||||
const char *const name;
|
||||
enum snd_motu_protocol_version protocol_version;
|
||||
enum snd_motu_spec_flags flags;
|
||||
// The combination of snd_motu_spec_flags enumeration-constants.
|
||||
unsigned int flags;
|
||||
|
||||
unsigned char tx_fixed_pcm_chunks[3];
|
||||
unsigned char rx_fixed_pcm_chunks[3];
|
||||
};
|
||||
|
||||
extern const struct snd_motu_spec snd_motu_spec_828;
|
||||
extern const struct snd_motu_spec snd_motu_spec_896;
|
||||
|
||||
extern const struct snd_motu_spec snd_motu_spec_828mk2;
|
||||
extern const struct snd_motu_spec snd_motu_spec_traveler;
|
||||
extern const struct snd_motu_spec snd_motu_spec_ultralite;
|
||||
extern const struct snd_motu_spec snd_motu_spec_8pre;
|
||||
|
||||
extern const struct snd_motu_spec snd_motu_spec_828mk3;
|
||||
extern const struct snd_motu_spec snd_motu_spec_828mk3_fw;
|
||||
extern const struct snd_motu_spec snd_motu_spec_828mk3_hybrid;
|
||||
extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3;
|
||||
extern const struct snd_motu_spec snd_motu_spec_audio_express;
|
||||
extern const struct snd_motu_spec snd_motu_spec_4pre;
|
||||
|
||||
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
|
||||
enum amdtp_stream_direction dir,
|
||||
const struct snd_motu_spec *spec);
|
||||
const struct snd_motu_spec *spec,
|
||||
struct amdtp_motu_cache *cache);
|
||||
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
|
||||
unsigned int midi_ports,
|
||||
struct snd_motu_packet_format *formats);
|
||||
@ -160,6 +175,16 @@ int snd_motu_create_midi_devices(struct snd_motu *motu);
|
||||
|
||||
int snd_motu_create_hwdep_device(struct snd_motu *motu);
|
||||
|
||||
int snd_motu_protocol_v1_get_clock_rate(struct snd_motu *motu,
|
||||
unsigned int *rate);
|
||||
int snd_motu_protocol_v1_set_clock_rate(struct snd_motu *motu,
|
||||
unsigned int rate);
|
||||
int snd_motu_protocol_v1_get_clock_source(struct snd_motu *motu,
|
||||
enum snd_motu_clock_source *src);
|
||||
int snd_motu_protocol_v1_switch_fetching_mode(struct snd_motu *motu,
|
||||
bool enable);
|
||||
int snd_motu_protocol_v1_cache_packet_formats(struct snd_motu *motu);
|
||||
|
||||
int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu,
|
||||
unsigned int *rate);
|
||||
int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu,
|
||||
@ -187,6 +212,8 @@ static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu,
|
||||
return snd_motu_protocol_v2_get_clock_rate(motu, rate);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_get_clock_rate(motu, rate);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
|
||||
return snd_motu_protocol_v1_get_clock_rate(motu, rate);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
@ -198,6 +225,8 @@ static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu,
|
||||
return snd_motu_protocol_v2_set_clock_rate(motu, rate);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_set_clock_rate(motu, rate);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
|
||||
return snd_motu_protocol_v1_set_clock_rate(motu, rate);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
@ -209,6 +238,8 @@ static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu,
|
||||
return snd_motu_protocol_v2_get_clock_source(motu, source);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_get_clock_source(motu, source);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
|
||||
return snd_motu_protocol_v1_get_clock_source(motu, source);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
@ -220,6 +251,8 @@ static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu,
|
||||
return snd_motu_protocol_v2_switch_fetching_mode(motu, enable);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_switch_fetching_mode(motu, enable);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
|
||||
return snd_motu_protocol_v1_switch_fetching_mode(motu, enable);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
@ -230,6 +263,8 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
|
||||
return snd_motu_protocol_v2_cache_packet_formats(motu);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3)
|
||||
return snd_motu_protocol_v3_cache_packet_formats(motu);
|
||||
else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V1)
|
||||
return snd_motu_protocol_v1_cache_packet_formats(motu);
|
||||
else
|
||||
return -ENXIO;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define AVC_GENERIC_FRAME_MAXIMUM_BYTES 512
|
||||
#define CALLBACK_TIMEOUT 200
|
||||
#define READY_TIMEOUT_MS 200
|
||||
|
||||
/*
|
||||
* According to datasheet of Oxford Semiconductor:
|
||||
@ -153,12 +153,23 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
struct cmp_connection *conn;
|
||||
enum cmp_direction c_dir;
|
||||
enum amdtp_stream_direction s_dir;
|
||||
unsigned int flags = CIP_UNAWARE_SYT;
|
||||
int err;
|
||||
|
||||
if (!(oxfw->quirks & SND_OXFW_QUIRK_BLOCKING_TRANSMISSION))
|
||||
flags |= CIP_NONBLOCKING;
|
||||
else
|
||||
flags |= CIP_BLOCKING;
|
||||
|
||||
if (stream == &oxfw->tx_stream) {
|
||||
conn = &oxfw->out_conn;
|
||||
c_dir = CMP_OUTPUT;
|
||||
s_dir = AMDTP_IN_STREAM;
|
||||
|
||||
if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD)
|
||||
flags |= CIP_JUMBO_PAYLOAD;
|
||||
if (oxfw->quirks & SND_OXFW_QUIRK_WRONG_DBS)
|
||||
flags |= CIP_WRONG_DBS;
|
||||
} else {
|
||||
conn = &oxfw->in_conn;
|
||||
c_dir = CMP_INPUT;
|
||||
@ -169,24 +180,12 @@ static int init_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
|
||||
err = amdtp_am824_init(stream, oxfw->unit, s_dir, flags);
|
||||
if (err < 0) {
|
||||
cmp_connection_destroy(conn);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* OXFW starts to transmit packets with non-zero dbc.
|
||||
* OXFW postpone transferring packets till handling any asynchronous
|
||||
* packets. As a result, next isochronous packet includes more data
|
||||
* blocks than IEC 61883-6 defines.
|
||||
*/
|
||||
if (stream == &oxfw->tx_stream) {
|
||||
oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
|
||||
if (oxfw->wrong_dbs)
|
||||
oxfw->tx_stream.flags |= CIP_WRONG_DBS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -338,6 +337,9 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
|
||||
}
|
||||
|
||||
if (!amdtp_stream_running(&oxfw->rx_stream)) {
|
||||
unsigned int tx_init_skip_cycles = 0;
|
||||
bool replay_seq = false;
|
||||
|
||||
err = start_stream(oxfw, &oxfw->rx_stream);
|
||||
if (err < 0) {
|
||||
dev_err(&oxfw->unit->device,
|
||||
@ -353,26 +355,27 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw)
|
||||
"fail to prepare tx stream: %d\n", err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (oxfw->quirks & SND_OXFW_QUIRK_JUMBO_PAYLOAD) {
|
||||
// Just after changing sampling transfer frequency, many cycles are
|
||||
// skipped for packet transmission.
|
||||
tx_init_skip_cycles = 400;
|
||||
} else {
|
||||
replay_seq = true;
|
||||
}
|
||||
}
|
||||
|
||||
err = amdtp_domain_start(&oxfw->domain, 0);
|
||||
// NOTE: The device ignores presentation time expressed by the value of syt field
|
||||
// of CIP header in received packets. The sequence of the number of data blocks per
|
||||
// packet is important for media clock recovery.
|
||||
err = amdtp_domain_start(&oxfw->domain, tx_init_skip_cycles, replay_seq, false);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
// Wait first packet.
|
||||
if (!amdtp_stream_wait_callback(&oxfw->rx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
if (!amdtp_domain_wait_ready(&oxfw->domain, READY_TIMEOUT_MS)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (oxfw->has_output) {
|
||||
if (!amdtp_stream_wait_callback(&oxfw->tx_stream,
|
||||
CALLBACK_TIMEOUT)) {
|
||||
err = -ETIMEDOUT;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -23,6 +23,8 @@
|
||||
#define OUI_APOGEE 0x0003db
|
||||
|
||||
#define MODEL_SATELLITE 0x00200f
|
||||
#define MODEL_SCS1M 0x001000
|
||||
#define MODEL_DUET_FW 0x01dddd
|
||||
|
||||
#define SPECIFIER_1394TA 0x00a02d
|
||||
#define VERSION_AVC 0x010001
|
||||
@ -46,8 +48,6 @@ static bool detect_loud_models(struct fw_unit *unit)
|
||||
"Onyx-i",
|
||||
"Onyx 1640i",
|
||||
"d.Pro",
|
||||
"Mackie Onyx Satellite",
|
||||
"Tapco LINK.firewire 4x6",
|
||||
"U.420"};
|
||||
char model[32];
|
||||
int err;
|
||||
@ -60,7 +60,7 @@ static bool detect_loud_models(struct fw_unit *unit)
|
||||
return match_string(models, ARRAY_SIZE(models), model) >= 0;
|
||||
}
|
||||
|
||||
static int name_card(struct snd_oxfw *oxfw)
|
||||
static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
|
||||
const struct compat_info *info;
|
||||
@ -88,10 +88,12 @@ static int name_card(struct snd_oxfw *oxfw)
|
||||
goto end;
|
||||
be32_to_cpus(&firmware);
|
||||
|
||||
if (firmware >> 20 == 0x970)
|
||||
oxfw->quirks |= SND_OXFW_QUIRK_JUMBO_PAYLOAD;
|
||||
|
||||
/* to apply card definitions */
|
||||
if (oxfw->entry->vendor_id == VENDOR_GRIFFIN ||
|
||||
oxfw->entry->vendor_id == VENDOR_LACIE) {
|
||||
info = (const struct compat_info *)oxfw->entry->driver_data;
|
||||
if (entry->vendor_id == VENDOR_GRIFFIN || entry->vendor_id == VENDOR_LACIE) {
|
||||
info = (const struct compat_info *)entry->driver_data;
|
||||
d = info->driver_name;
|
||||
v = info->vendor_name;
|
||||
m = info->model_name;
|
||||
@ -120,9 +122,12 @@ static void oxfw_card_free(struct snd_card *card)
|
||||
|
||||
if (oxfw->has_output || oxfw->has_input)
|
||||
snd_oxfw_stream_destroy_duplex(oxfw);
|
||||
|
||||
mutex_destroy(&oxfw->mutex);
|
||||
fw_unit_put(oxfw->unit);
|
||||
}
|
||||
|
||||
static int detect_quirks(struct snd_oxfw *oxfw)
|
||||
static int detect_quirks(struct snd_oxfw *oxfw, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
|
||||
struct fw_csr_iterator it;
|
||||
@ -133,28 +138,35 @@ static int detect_quirks(struct snd_oxfw *oxfw)
|
||||
* Add ALSA control elements for two models to keep compatibility to
|
||||
* old firewire-speaker module.
|
||||
*/
|
||||
if (oxfw->entry->vendor_id == VENDOR_GRIFFIN)
|
||||
if (entry->vendor_id == VENDOR_GRIFFIN)
|
||||
return snd_oxfw_add_spkr(oxfw, false);
|
||||
if (oxfw->entry->vendor_id == VENDOR_LACIE)
|
||||
if (entry->vendor_id == VENDOR_LACIE)
|
||||
return snd_oxfw_add_spkr(oxfw, true);
|
||||
|
||||
/*
|
||||
* Stanton models supports asynchronous transactions for unique MIDI
|
||||
* messages.
|
||||
*/
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON) {
|
||||
/* No physical MIDI ports. */
|
||||
if (entry->vendor_id == OUI_STANTON) {
|
||||
oxfw->quirks |= SND_OXFW_QUIRK_SCS_TRANSACTION;
|
||||
if (entry->model_id == MODEL_SCS1M)
|
||||
oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
|
||||
|
||||
// No physical MIDI ports.
|
||||
oxfw->midi_input_ports = 0;
|
||||
oxfw->midi_output_ports = 0;
|
||||
|
||||
return snd_oxfw_scs1x_add(oxfw);
|
||||
}
|
||||
|
||||
if (entry->vendor_id == OUI_APOGEE && entry->model_id == MODEL_DUET_FW)
|
||||
oxfw->quirks |= SND_OXFW_QUIRK_BLOCKING_TRANSMISSION;
|
||||
|
||||
/*
|
||||
* TASCAM FireOne has physical control and requires a pair of additional
|
||||
* MIDI ports.
|
||||
*/
|
||||
if (oxfw->entry->vendor_id == VENDOR_TASCAM) {
|
||||
if (entry->vendor_id == VENDOR_TASCAM) {
|
||||
oxfw->midi_input_ports++;
|
||||
oxfw->midi_output_ports++;
|
||||
return 0;
|
||||
@ -175,27 +187,35 @@ static int detect_quirks(struct snd_oxfw *oxfw)
|
||||
* value in 'dbs' field of CIP header against its format information.
|
||||
*/
|
||||
if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
|
||||
oxfw->wrong_dbs = true;
|
||||
oxfw->quirks |= SND_OXFW_QUIRK_WRONG_DBS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_registration(struct work_struct *work)
|
||||
static int oxfw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
|
||||
struct snd_card *card;
|
||||
struct snd_oxfw *oxfw;
|
||||
int err;
|
||||
|
||||
if (oxfw->registered)
|
||||
return;
|
||||
if (entry->vendor_id == VENDOR_LOUD && entry->model_id == 0 && !detect_loud_models(unit))
|
||||
return -ENODEV;
|
||||
|
||||
err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
|
||||
&oxfw->card);
|
||||
err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE, sizeof(*oxfw), &card);
|
||||
if (err < 0)
|
||||
return;
|
||||
oxfw->card->private_free = oxfw_card_free;
|
||||
oxfw->card->private_data = oxfw;
|
||||
return err;
|
||||
card->private_free = oxfw_card_free;
|
||||
|
||||
err = name_card(oxfw);
|
||||
oxfw = card->private_data;
|
||||
oxfw->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, oxfw);
|
||||
oxfw->card = card;
|
||||
|
||||
mutex_init(&oxfw->mutex);
|
||||
spin_lock_init(&oxfw->lock);
|
||||
init_waitqueue_head(&oxfw->hwdep_wait);
|
||||
|
||||
err = name_card(oxfw, entry);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
@ -203,7 +223,7 @@ static void do_registration(struct work_struct *work)
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = detect_quirks(oxfw);
|
||||
err = detect_quirks(oxfw, entry);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
@ -227,85 +247,38 @@ static void do_registration(struct work_struct *work)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = snd_card_register(oxfw->card);
|
||||
err = snd_card_register(card);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
oxfw->registered = true;
|
||||
|
||||
return;
|
||||
error:
|
||||
snd_card_free(oxfw->card);
|
||||
dev_info(&oxfw->unit->device,
|
||||
"Sound card registration failed: %d\n", err);
|
||||
}
|
||||
|
||||
static int oxfw_probe(struct fw_unit *unit,
|
||||
const struct ieee1394_device_id *entry)
|
||||
{
|
||||
struct snd_oxfw *oxfw;
|
||||
|
||||
if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
|
||||
return -ENODEV;
|
||||
|
||||
/* Allocate this independent of sound card instance. */
|
||||
oxfw = devm_kzalloc(&unit->device, sizeof(struct snd_oxfw), GFP_KERNEL);
|
||||
if (!oxfw)
|
||||
return -ENOMEM;
|
||||
oxfw->unit = fw_unit_get(unit);
|
||||
dev_set_drvdata(&unit->device, oxfw);
|
||||
|
||||
oxfw->entry = entry;
|
||||
mutex_init(&oxfw->mutex);
|
||||
spin_lock_init(&oxfw->lock);
|
||||
init_waitqueue_head(&oxfw->hwdep_wait);
|
||||
|
||||
/* Allocate and register this sound card later. */
|
||||
INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
|
||||
snd_fw_schedule_registration(unit, &oxfw->dwork);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void oxfw_bus_reset(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
|
||||
|
||||
if (!oxfw->registered)
|
||||
snd_fw_schedule_registration(unit, &oxfw->dwork);
|
||||
|
||||
fcp_bus_reset(oxfw->unit);
|
||||
|
||||
if (oxfw->registered) {
|
||||
if (oxfw->has_output || oxfw->has_input) {
|
||||
mutex_lock(&oxfw->mutex);
|
||||
snd_oxfw_stream_update_duplex(oxfw);
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
}
|
||||
|
||||
if (oxfw->entry->vendor_id == OUI_STANTON)
|
||||
snd_oxfw_scs1x_update(oxfw);
|
||||
if (oxfw->has_output || oxfw->has_input) {
|
||||
mutex_lock(&oxfw->mutex);
|
||||
snd_oxfw_stream_update_duplex(oxfw);
|
||||
mutex_unlock(&oxfw->mutex);
|
||||
}
|
||||
|
||||
if (oxfw->quirks & SND_OXFW_QUIRK_SCS_TRANSACTION)
|
||||
snd_oxfw_scs1x_update(oxfw);
|
||||
}
|
||||
|
||||
static void oxfw_remove(struct fw_unit *unit)
|
||||
{
|
||||
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
|
||||
|
||||
/*
|
||||
* Confirm to stop the work for registration before the sound card is
|
||||
* going to be released. The work is not scheduled again because bus
|
||||
* reset handler is not called anymore.
|
||||
*/
|
||||
cancel_delayed_work_sync(&oxfw->dwork);
|
||||
|
||||
if (oxfw->registered) {
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(oxfw->card);
|
||||
}
|
||||
|
||||
mutex_destroy(&oxfw->mutex);
|
||||
fw_unit_put(oxfw->unit);
|
||||
// Block till all of ALSA character devices are released.
|
||||
snd_card_free(oxfw->card);
|
||||
}
|
||||
|
||||
static const struct compat_info griffin_firewave = {
|
||||
@ -320,81 +293,67 @@ static const struct compat_info lacie_speakers = {
|
||||
.model_name = "FireWire Speakers",
|
||||
};
|
||||
|
||||
#define OXFW_DEV_ENTRY(vendor, model, data) \
|
||||
{ \
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
|
||||
IEEE1394_MATCH_MODEL_ID | \
|
||||
IEEE1394_MATCH_SPECIFIER_ID | \
|
||||
IEEE1394_MATCH_VERSION, \
|
||||
.vendor_id = vendor, \
|
||||
.model_id = model, \
|
||||
.specifier_id = SPECIFIER_1394TA, \
|
||||
.version = VERSION_AVC, \
|
||||
.driver_data = (kernel_ulong_t)data, \
|
||||
}
|
||||
|
||||
static const struct ieee1394_device_id oxfw_id_table[] = {
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID |
|
||||
IEEE1394_MATCH_VERSION,
|
||||
.vendor_id = VENDOR_GRIFFIN,
|
||||
.model_id = 0x00f970,
|
||||
.specifier_id = SPECIFIER_1394TA,
|
||||
.version = VERSION_AVC,
|
||||
.driver_data = (kernel_ulong_t)&griffin_firewave,
|
||||
},
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID |
|
||||
IEEE1394_MATCH_VERSION,
|
||||
.vendor_id = VENDOR_LACIE,
|
||||
.model_id = 0x00f970,
|
||||
.specifier_id = SPECIFIER_1394TA,
|
||||
.version = VERSION_AVC,
|
||||
.driver_data = (kernel_ulong_t)&lacie_speakers,
|
||||
},
|
||||
/* Behringer,F-Control Audio 202 */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = VENDOR_BEHRINGER,
|
||||
.model_id = 0x00fc22,
|
||||
},
|
||||
/*
|
||||
* Any Mackie(Loud) models (name string/model id):
|
||||
* Onyx-i series (former models): 0x081216
|
||||
* Mackie Onyx Satellite: 0x00200f
|
||||
* Tapco LINK.firewire 4x6: 0x000460
|
||||
* d.4 pro: Unknown
|
||||
* U.420: Unknown
|
||||
* U.420d: Unknown
|
||||
*/
|
||||
//
|
||||
// OXFW970 devices:
|
||||
// Initial firmware has a quirk to postpone isoc packet transmission during finishing async
|
||||
// transaction. As a result, several isochronous cycles are skipped to transfer the packets
|
||||
// and the audio data frames which should have been transferred during the cycles are put
|
||||
// into packet at the first isoc cycle after the postpone. Furthermore, the value of SYT
|
||||
// field in CIP header is not reliable as synchronization timing,
|
||||
//
|
||||
OXFW_DEV_ENTRY(VENDOR_GRIFFIN, 0x00f970, &griffin_firewave),
|
||||
OXFW_DEV_ENTRY(VENDOR_LACIE, 0x00f970, &lacie_speakers),
|
||||
// Behringer,F-Control Audio 202. The value of SYT field is not reliable at all.
|
||||
OXFW_DEV_ENTRY(VENDOR_BEHRINGER, 0x00fc22, NULL),
|
||||
// Loud Technologies, Tapco Link.FireWire 4x6. The value of SYT field is always 0xffff.
|
||||
OXFW_DEV_ENTRY(VENDOR_LOUD, 0x000460, NULL),
|
||||
// Loud Technologies, Mackie Onyx Satellite. Although revised version of firmware is
|
||||
// installed to avoid the postpone, the value of SYT field is always 0xffff.
|
||||
OXFW_DEV_ENTRY(VENDOR_LOUD, MODEL_SATELLITE, NULL),
|
||||
// Miglia HarmonyAudio. Not yet identified.
|
||||
|
||||
//
|
||||
// OXFW971 devices:
|
||||
// The value of SYT field in CIP header is enough reliable. Both of blocking and non-blocking
|
||||
// transmission methods are available.
|
||||
//
|
||||
// Any Mackie(Loud) models (name string/model id):
|
||||
// Onyx-i series (former models): 0x081216
|
||||
// Onyx 1640i: 0x001640
|
||||
// d.2 pro/d.4 pro (built-in card): Unknown
|
||||
// U.420: Unknown
|
||||
// U.420d: Unknown
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_SPECIFIER_ID |
|
||||
IEEE1394_MATCH_VERSION,
|
||||
.vendor_id = VENDOR_LOUD,
|
||||
.model_id = 0,
|
||||
.specifier_id = SPECIFIER_1394TA,
|
||||
.version = VERSION_AVC,
|
||||
},
|
||||
/* TASCAM, FireOne */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = VENDOR_TASCAM,
|
||||
.model_id = 0x800007,
|
||||
},
|
||||
/* Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m) */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_STANTON,
|
||||
.model_id = 0x001000,
|
||||
},
|
||||
/* Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d) */
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_STANTON,
|
||||
.model_id = 0x002000,
|
||||
},
|
||||
// APOGEE, duet FireWire
|
||||
{
|
||||
.match_flags = IEEE1394_MATCH_VENDOR_ID |
|
||||
IEEE1394_MATCH_MODEL_ID,
|
||||
.vendor_id = OUI_APOGEE,
|
||||
.model_id = 0x01dddd,
|
||||
},
|
||||
// TASCAM, FireOne.
|
||||
OXFW_DEV_ENTRY(VENDOR_TASCAM, 0x800007, NULL),
|
||||
// Stanton, Stanton Controllers & Systems 1 Mixer (SCS.1m).
|
||||
OXFW_DEV_ENTRY(OUI_STANTON, MODEL_SCS1M, NULL),
|
||||
// Stanton, Stanton Controllers & Systems 1 Deck (SCS.1d).
|
||||
OXFW_DEV_ENTRY(OUI_STANTON, 0x002000, NULL),
|
||||
// APOGEE, duet FireWire.
|
||||
OXFW_DEV_ENTRY(OUI_APOGEE, MODEL_DUET_FW, NULL),
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user