Merge branch 'for-next' into for-linus

For 4.12 merge.
This commit is contained in:
Takashi Iwai 2017-05-02 08:24:42 +02:00
commit d7dc450d5a
104 changed files with 6482 additions and 744 deletions

View File

@ -494,6 +494,8 @@ add_hp_mic (bool)
hp_mic_detect (bool)
enable/disable the hp/mic shared input for a single built-in mic
case; default true
vmaster (bool)
enable/disable the virtual Master control; default true
mixer_nid (int)
specifies the widget NID of the analog-loopback mixer

View File

@ -106,8 +106,26 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define AZX_REG_HSW_EM4 0x100c
#define AZX_REG_HSW_EM5 0x1010
/* Skylake/Broxton display HD-A controller Extended Mode registers */
#define AZX_REG_SKL_EM4L 0x1040
/* Skylake/Broxton vendor-specific registers */
#define AZX_REG_VS_EM1 0x1000
#define AZX_REG_VS_INRC 0x1004
#define AZX_REG_VS_OUTRC 0x1008
#define AZX_REG_VS_FIFOTRK 0x100C
#define AZX_REG_VS_FIFOTRK2 0x1010
#define AZX_REG_VS_EM2 0x1030
#define AZX_REG_VS_EM3L 0x1038
#define AZX_REG_VS_EM3U 0x103C
#define AZX_REG_VS_EM4L 0x1040
#define AZX_REG_VS_EM4U 0x1044
#define AZX_REG_VS_LTRC 0x1048
#define AZX_REG_VS_D0I3C 0x104A
#define AZX_REG_VS_PCE 0x104B
#define AZX_REG_VS_L2MAGC 0x1050
#define AZX_REG_VS_L2LAHPT 0x1054
#define AZX_REG_VS_SDXDPIB_XBASE 0x1084
#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
/* PCI space */
#define AZX_PCIREG_TCSEL 0x44
@ -243,9 +261,11 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define AZX_REG_ML_LOUTPAY 0x20
#define AZX_REG_ML_LINPAY 0x30
#define AZX_MLCTL_SPA (1<<16)
#define AZX_MLCTL_CPA 23
#define ML_LCTL_SCF_MASK 0xF
#define AZX_MLCTL_SPA (0x1 << 16)
#define AZX_MLCTL_CPA (0x1 << 23)
#define AZX_MLCTL_SPA_SHIFT 16
#define AZX_MLCTL_CPA_SHIFT 23
/* registers for DMA Resume Capability Structure */
#define AZX_DRSM_CAP_ID 0x5

View File

@ -368,24 +368,32 @@ void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus);
/*
* macros for easy use
*/
#define _snd_hdac_chip_write(type, chip, reg, value) \
((chip)->io_ops->reg_write ## type(value, (chip)->remap_addr + (reg)))
#define _snd_hdac_chip_read(type, chip, reg) \
((chip)->io_ops->reg_read ## type((chip)->remap_addr + (reg)))
#define _snd_hdac_chip_writeb(chip, reg, value) \
((chip)->io_ops->reg_writeb(value, (chip)->remap_addr + (reg)))
#define _snd_hdac_chip_readb(chip, reg) \
((chip)->io_ops->reg_readb((chip)->remap_addr + (reg)))
#define _snd_hdac_chip_writew(chip, reg, value) \
((chip)->io_ops->reg_writew(value, (chip)->remap_addr + (reg)))
#define _snd_hdac_chip_readw(chip, reg) \
((chip)->io_ops->reg_readw((chip)->remap_addr + (reg)))
#define _snd_hdac_chip_writel(chip, reg, value) \
((chip)->io_ops->reg_writel(value, (chip)->remap_addr + (reg)))
#define _snd_hdac_chip_readl(chip, reg) \
((chip)->io_ops->reg_readl((chip)->remap_addr + (reg)))
/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_writel(chip, reg, value) \
_snd_hdac_chip_write(l, chip, AZX_REG_ ## reg, value)
_snd_hdac_chip_writel(chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_writew(chip, reg, value) \
_snd_hdac_chip_write(w, chip, AZX_REG_ ## reg, value)
_snd_hdac_chip_writew(chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_writeb(chip, reg, value) \
_snd_hdac_chip_write(b, chip, AZX_REG_ ## reg, value)
_snd_hdac_chip_writeb(chip, AZX_REG_ ## reg, value)
#define snd_hdac_chip_readl(chip, reg) \
_snd_hdac_chip_read(l, chip, AZX_REG_ ## reg)
_snd_hdac_chip_readl(chip, AZX_REG_ ## reg)
#define snd_hdac_chip_readw(chip, reg) \
_snd_hdac_chip_read(w, chip, AZX_REG_ ## reg)
_snd_hdac_chip_readw(chip, AZX_REG_ ## reg)
#define snd_hdac_chip_readb(chip, reg) \
_snd_hdac_chip_read(b, chip, AZX_REG_ ## reg)
_snd_hdac_chip_readb(chip, AZX_REG_ ## reg)
/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_updatel(chip, reg, mask, val) \

View File

@ -107,9 +107,11 @@ enum {
SNDRV_HWDEP_IFACE_FW_DIGI00X, /* Digidesign Digi 002/003 family */
SNDRV_HWDEP_IFACE_FW_TASCAM, /* TASCAM FireWire series */
SNDRV_HWDEP_IFACE_LINE6, /* Line6 USB processors */
SNDRV_HWDEP_IFACE_FW_MOTU, /* MOTU FireWire series */
SNDRV_HWDEP_IFACE_FW_FIREFACE, /* RME Fireface series */
/* Don't forget to change the following: */
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_LINE6
SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREFACE
};
struct snd_hwdep_info {

View File

@ -10,6 +10,7 @@
#define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE 0x4e617475
#define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE 0x746e736c
#define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION 0x64776479
struct snd_firewire_event_common {
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@ -46,12 +47,18 @@ struct snd_firewire_event_digi00x_message {
__u32 message; /* Digi00x-specific message */
};
struct snd_firewire_event_motu_notification {
unsigned int type;
__u32 message; /* MOTU-specific bits. */
};
union snd_firewire_event {
struct snd_firewire_event_common common;
struct snd_firewire_event_lock_status lock_status;
struct snd_firewire_event_dice_notification dice_notification;
struct snd_firewire_event_efw_response efw_response;
struct snd_firewire_event_digi00x_message digi00x_message;
struct snd_firewire_event_motu_notification motu_notification;
};
@ -65,7 +72,8 @@ union snd_firewire_event {
#define SNDRV_FIREWIRE_TYPE_OXFW 4
#define SNDRV_FIREWIRE_TYPE_DIGI00X 5
#define SNDRV_FIREWIRE_TYPE_TASCAM 6
/* RME, MOTU, ... */
#define SNDRV_FIREWIRE_TYPE_MOTU 7
#define SNDRV_FIREWIRE_TYPE_FIREFACE 8
struct snd_firewire_get_info {
unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */

View File

@ -1277,6 +1277,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
struct timespec tstamp;
int prev, append = 0;
memset(&r1, 0, sizeof(r1));
memset(&tstamp, 0, sizeof(tstamp));
spin_lock(&tu->qlock);
if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
@ -1292,7 +1293,6 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
}
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) {
memset(&r1, 0, sizeof(r1));
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
r1.tstamp = tstamp;
r1.val = resolution;
@ -1430,18 +1430,13 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
if (id.card < 0) {
id.card = 0;
} else {
if (id.card < 0) {
id.card = 0;
if (id.device < 0) {
id.device = 0;
} else {
if (id.device < 0) {
id.device = 0;
} else {
if (id.subdevice < 0) {
id.subdevice = 0;
} else {
id.subdevice++;
}
}
if (id.subdevice < 0)
id.subdevice = 0;
else
id.subdevice++;
}
}
list_for_each(p, &snd_timer_list) {

View File

@ -795,10 +795,8 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
return NULL;
chip = kzalloc(sizeof(*chip) + extra_size, GFP_KERNEL);
if (! chip) {
snd_printk(KERN_ERR "vx_core: no memory\n");
if (! chip)
return NULL;
}
mutex_init(&chip->lock);
chip->irq = -1;
chip->hw = hw;

View File

@ -140,4 +140,24 @@ config SND_FIREWIRE_TASCAM
To compile this driver as a module, choose M here: the module
will be called snd-firewire-tascam.
config SND_FIREWIRE_MOTU
tristate "Mark of the unicorn FireWire series support"
select SND_FIREWIRE_LIB
select SND_HWDEP
help
Say Y here to enable support for FireWire devices which MOTU produced:
* 828mk2
* 828mk3
To compile this driver as a module, choose M here: the module
will be called snd-firewire-motu.
config SND_FIREFACE
tristate "RME Fireface series support"
select SND_FIREWIRE_LIB
select SND_HWDEP
help
Say Y here to include support for RME fireface series.
* Fireface 400
endif # SND_FIREWIRE

View File

@ -13,3 +13,5 @@ obj-$(CONFIG_SND_FIREWORKS) += fireworks/
obj-$(CONFIG_SND_BEBOB) += bebob/
obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
obj-$(CONFIG_SND_FIREWIRE_MOTU) += motu/
obj-$(CONFIG_SND_FIREFACE) += fireface/

View File

@ -14,8 +14,8 @@
#include <linux/tracepoint.h>
TRACE_EVENT(in_packet,
TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_quadlets, index),
TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_length, unsigned int index),
TP_ARGS(s, cycles, cip_header, payload_length, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
@ -37,7 +37,7 @@ TRACE_EVENT(in_packet,
__entry->dest = fw_parent_device(s->unit)->card->node_id;
__entry->cip_header0 = cip_header[0];
__entry->cip_header1 = cip_header[1];
__entry->payload_quadlets = payload_quadlets;
__entry->payload_quadlets = payload_length / 4;
__entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index;
@ -101,6 +101,94 @@ TRACE_EVENT(out_packet,
__entry->index)
);
TRACE_EVENT(in_packet_without_header,
TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_quadlets, unsigned int data_blocks, unsigned int index),
TP_ARGS(s, cycles, payload_quadlets, data_blocks, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
__field(int, channel)
__field(int, src)
__field(int, dest)
__field(unsigned int, payload_quadlets)
__field(unsigned int, data_blocks)
__field(unsigned int, data_block_counter)
__field(unsigned int, packet_index)
__field(unsigned int, irq)
__field(unsigned int, index)
),
TP_fast_assign(
__entry->second = cycles / CYCLES_PER_SECOND;
__entry->cycle = cycles % CYCLES_PER_SECOND;
__entry->channel = s->context->channel;
__entry->src = fw_parent_device(s->unit)->node_id;
__entry->dest = fw_parent_device(s->unit)->card->node_id;
__entry->payload_quadlets = payload_quadlets;
__entry->data_blocks = data_blocks,
__entry->data_block_counter = s->data_block_counter,
__entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index;
),
TP_printk(
"%02u %04u %04x %04x %02d %03u %3u %3u %02u %01u %02u",
__entry->second,
__entry->cycle,
__entry->src,
__entry->dest,
__entry->channel,
__entry->payload_quadlets,
__entry->data_blocks,
__entry->data_block_counter,
__entry->packet_index,
__entry->irq,
__entry->index)
);
TRACE_EVENT(out_packet_without_header,
TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
TP_ARGS(s, cycles, payload_length, data_blocks, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
__field(int, channel)
__field(int, src)
__field(int, dest)
__field(unsigned int, payload_quadlets)
__field(unsigned int, data_blocks)
__field(unsigned int, data_block_counter)
__field(unsigned int, packet_index)
__field(unsigned int, irq)
__field(unsigned int, index)
),
TP_fast_assign(
__entry->second = cycles / CYCLES_PER_SECOND;
__entry->cycle = cycles % CYCLES_PER_SECOND;
__entry->channel = s->context->channel;
__entry->src = fw_parent_device(s->unit)->card->node_id;
__entry->dest = fw_parent_device(s->unit)->node_id;
__entry->payload_quadlets = payload_length / 4;
__entry->data_blocks = data_blocks,
__entry->data_blocks = s->data_block_counter,
__entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index;
),
TP_printk(
"%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u",
__entry->second,
__entry->cycle,
__entry->src,
__entry->dest,
__entry->channel,
__entry->payload_quadlets,
__entry->data_blocks,
__entry->data_block_counter,
__entry->packet_index,
__entry->irq,
__entry->index)
);
#endif
#undef TRACE_INCLUDE_PATH

View File

@ -27,6 +27,7 @@
/* isochronous header parameters */
#define ISO_DATA_LENGTH_SHIFT 16
#define TAG_NO_CIP_HEADER 0
#define TAG_CIP 1
/* common isochronous packet header parameters */
@ -37,6 +38,8 @@
#define CIP_SID_MASK 0x3f000000
#define CIP_DBS_MASK 0x00ff0000
#define CIP_DBS_SHIFT 16
#define CIP_SPH_MASK 0x00000400
#define CIP_SPH_SHIFT 10
#define CIP_DBC_MASK 0x000000ff
#define CIP_FMT_SHIFT 24
#define CIP_FMT_MASK 0x3f000000
@ -232,11 +235,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
unsigned int multiplier = 1;
unsigned int header_size = 0;
if (s->flags & CIP_JUMBO_PAYLOAD)
multiplier = 5;
if (!(s->flags & CIP_NO_HEADER))
header_size = 8;
return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
return header_size +
s->syt_interval * s->data_block_quadlets * 4 * multiplier;
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
@ -378,7 +385,7 @@ static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
goto end;
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
p.tag = TAG_CIP;
p.tag = s->tag;
p.header_length = header_length;
if (payload_length > 0)
p.payload_length = payload_length;
@ -405,17 +412,16 @@ static inline int queue_out_packet(struct amdtp_stream *s,
static inline int queue_in_packet(struct amdtp_stream *s)
{
return queue_packet(s, IN_PACKET_HEADER_SIZE,
amdtp_stream_get_max_payload(s));
return queue_packet(s, IN_PACKET_HEADER_SIZE, s->max_payload_length);
}
static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
static int handle_out_packet(struct amdtp_stream *s,
unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
unsigned int syt;
unsigned int data_blocks;
unsigned int payload_length;
unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
@ -424,15 +430,22 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter =
(s->data_block_counter + data_blocks) & 0xff;
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
s->data_block_counter);
buffer[1] = cpu_to_be32(CIP_EOH |
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
(syt & CIP_SYT_MASK));
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
if (!(s->flags & CIP_DBC_IS_END_EVENT))
s->data_block_counter =
(s->data_block_counter + data_blocks) & 0xff;
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
trace_out_packet(s, cycle, buffer, payload_length, index);
@ -448,13 +461,45 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
return 0;
}
static int handle_out_packet_without_header(struct amdtp_stream *s,
unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
unsigned int syt;
unsigned int data_blocks;
unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
buffer = s->buffer.packets[s->packet_index].buffer;
syt = calculate_syt(s, cycle);
data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer, data_blocks, &syt);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
payload_length = data_blocks * 4 * s->data_block_quadlets;
trace_out_packet_without_header(s, cycle, payload_length, data_blocks,
index);
if (queue_out_packet(s, payload_length) < 0)
return -EIO;
pcm = ACCESS_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
/* No need to return the number of handled data blocks. */
return 0;
}
static int handle_in_packet(struct amdtp_stream *s,
unsigned int payload_quadlets, unsigned int cycle,
unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
u32 cip_header[2];
unsigned int fmt, fdf, syt;
unsigned int sph, fmt, fdf, syt;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
unsigned int data_blocks;
struct snd_pcm_substream *pcm;
@ -465,14 +510,15 @@ static int handle_in_packet(struct amdtp_stream *s,
cip_header[0] = be32_to_cpu(buffer[0]);
cip_header[1] = be32_to_cpu(buffer[1]);
trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
trace_in_packet(s, cycle, cip_header, payload_length, index);
/*
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not.
*/
if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
(!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
@ -482,8 +528,9 @@ static int handle_in_packet(struct amdtp_stream *s,
}
/* Check valid protocol or not. */
sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT;
fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
if (fmt != s->fmt) {
if (sph != s->sph || fmt != s->fmt) {
dev_info_ratelimited(&s->unit->device,
"Detect unexpected protocol: %08x %08x\n",
cip_header[0], cip_header[1]);
@ -494,7 +541,7 @@ static int handle_in_packet(struct amdtp_stream *s,
/* Calculate data blocks */
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
if (payload_quadlets < 3 ||
if (payload_length < 12 ||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
data_blocks = 0;
} else {
@ -510,7 +557,8 @@ static int handle_in_packet(struct amdtp_stream *s,
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
data_blocks = (payload_quadlets - 2) / data_block_quadlets;
data_blocks = (payload_length / 4 - 2) /
data_block_quadlets;
}
/* Check data block counter continuity */
@ -561,6 +609,34 @@ end:
return 0;
}
static int handle_in_packet_without_header(struct amdtp_stream *s,
unsigned int payload_quadlets, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
unsigned int data_blocks;
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
buffer = s->buffer.packets[s->packet_index].buffer;
data_blocks = payload_quadlets / s->data_block_quadlets;
trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks,
index);
pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
if (queue_in_packet(s) < 0)
return -EIO;
pcm = ACCESS_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
return 0;
}
/*
* In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
* the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
@ -604,7 +680,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
for (i = 0; i < packets; ++i) {
cycle = increment_cycle_count(cycle, 1);
if (handle_out_packet(s, cycle, i) < 0) {
if (s->handle_packet(s, 0, cycle, i) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
@ -620,7 +696,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
{
struct amdtp_stream *s = private_data;
unsigned int i, packets;
unsigned int payload_quadlets, max_payload_quadlets;
unsigned int payload_length, max_payload_length;
__be32 *headers = header;
u32 cycle;
@ -636,22 +712,22 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
cycle = decrement_cycle_count(cycle, packets);
/* For buffer-over-run prevention. */
max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
max_payload_length = s->max_payload_length;
for (i = 0; i < packets; i++) {
cycle = increment_cycle_count(cycle, 1);
/* The number of quadlets in this packet */
payload_quadlets =
(be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
if (payload_quadlets > max_payload_quadlets) {
/* The number of bytes in this packet */
payload_length =
(be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT);
if (payload_length > max_payload_length) {
dev_err(&s->unit->device,
"Detect jumbo payload: %02x %02x\n",
payload_quadlets, max_payload_quadlets);
"Detect jumbo payload: %04x %04x\n",
payload_length, max_payload_length);
break;
}
if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
if (s->handle_packet(s, payload_length, cycle, i) < 0)
break;
}
@ -671,6 +747,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
void *header, void *private_data)
{
struct amdtp_stream *s = private_data;
u32 cycle;
unsigned int packets;
s->max_payload_length = amdtp_stream_get_max_payload(s);
/*
* For in-stream, first packet has come.
@ -679,10 +759,27 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
s->callbacked = true;
wake_up(&s->callback_wait);
if (s->direction == AMDTP_IN_STREAM)
cycle = compute_cycle_count(tstamp);
if (s->direction == AMDTP_IN_STREAM) {
packets = header_length / IN_PACKET_HEADER_SIZE;
cycle = decrement_cycle_count(cycle, packets);
context->callback.sc = in_stream_callback;
else
if (s->flags & CIP_NO_HEADER)
s->handle_packet = handle_in_packet_without_header;
else
s->handle_packet = handle_in_packet;
} else {
packets = header_length / 4;
cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
context->callback.sc = out_stream_callback;
if (s->flags & CIP_NO_HEADER)
s->handle_packet = handle_out_packet_without_header;
else
s->handle_packet = handle_out_packet;
}
s->start_cycle = cycle;
context->callback.sc(context, tstamp, header_length, header, s);
}
@ -759,6 +856,11 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
amdtp_stream_update(s);
if (s->flags & CIP_NO_HEADER)
s->tag = TAG_NO_CIP_HEADER;
else
s->tag = TAG_CIP;
s->packet_index = 0;
do {
if (s->direction == AMDTP_IN_STREAM)
@ -771,7 +873,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
/* NOTE: TAG1 matches CIP. This just affects in stream. */
tag = FW_ISO_CONTEXT_MATCH_TAG1;
if (s->flags & CIP_EMPTY_WITH_TAG0)
if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
tag |= FW_ISO_CONTEXT_MATCH_TAG0;
s->callbacked = false;

View File

@ -18,8 +18,8 @@
* SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right.
* @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
* @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
* corresponds to the end of event in the packet. Out of IEC 61883.
* @CIP_DBC_IS_END_EVENT: The value of dbc in an packet corresponds to the end
* of event in the packet. Out of IEC 61883.
* @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
* The value of data_block_quadlets is used instead of reported value.
* @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
@ -29,6 +29,9 @@
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
* packet is larger than IEC 61883-6 defines. Current implementation
* allows 5 times as large as IEC 61883-6 defines.
* @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
* valid EOH.
* @CIP_NO_HEADERS: a lack of headers in packets
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@ -39,6 +42,8 @@ enum cip_flags {
CIP_SKIP_DBC_ZERO_CHECK = 0x10,
CIP_EMPTY_HAS_WRONG_DBC = 0x20,
CIP_JUMBO_PAYLOAD = 0x40,
CIP_HEADER_WITHOUT_EOH = 0x80,
CIP_NO_HEADER = 0x100,
};
/**
@ -101,11 +106,17 @@ struct amdtp_stream {
struct fw_iso_context *context;
struct iso_packets_buffer buffer;
int packet_index;
int tag;
int (*handle_packet)(struct amdtp_stream *s,
unsigned int payload_quadlets, unsigned int cycle,
unsigned int index);
unsigned int max_payload_length;
/* For CIP headers. */
unsigned int source_node_id_field;
unsigned int data_block_quadlets;
unsigned int data_block_counter;
unsigned int sph;
unsigned int fmt;
unsigned int fdf;
/* quirk: fixed interval of dbc between previos/current packets. */
@ -130,6 +141,7 @@ struct amdtp_stream {
/* To wait for first packet. */
bool callbacked;
wait_queue_head_t callback_wait;
u32 start_cycle;
/* For backends to process data blocks. */
void *protocol;

View File

@ -31,13 +31,15 @@ int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(8));
if (err > 0 && err < 9)
if (err < 0)
;
else if (err < 9)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (err > 0)
else
err = 0;
kfree(buf);
@ -67,7 +69,9 @@ int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(8));
if (err > 0 && err < 9)
if (err < 0)
;
else if (err < 9)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -120,7 +124,9 @@ int avc_bridgeco_get_plug_type(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(9));
if ((err >= 0) && (err < 8))
if (err < 0)
;
else if (err < 11)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -150,7 +156,9 @@ int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, 256,
BIT(1) | BIT(2) | BIT(3) | BIT(4) |
BIT(5) | BIT(6) | BIT(7) | BIT(9));
if ((err >= 0) && (err < 8))
if (err < 0)
;
else if (err < 11)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -187,7 +195,9 @@ int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(9) | BIT(10));
if ((err >= 0) && (err < 8))
if (err < 0)
;
else if (err < 12)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -221,7 +231,9 @@ int avc_bridgeco_get_plug_input(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 16, buf, 16,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7));
if ((err >= 0) && (err < 8))
if (err < 0)
;
else if (err < 16)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -260,7 +272,9 @@ int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, *len,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(10));
if ((err >= 0) && (err < 12))
if (err < 0)
;
else if (err < 12)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;

View File

@ -28,6 +28,9 @@
*/
#define MAX_MIDI_RX_BLOCKS 8
/* 3 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) + 1. */
#define MAX_MIDI_PORTS 3
/*
* The double-oh-three algorithm was discovered by Robin Gareus and Damien
* Zammit in 2012, with reverse-engineering for Digi 003 Rack.
@ -42,10 +45,8 @@ struct amdtp_dot {
unsigned int pcm_channels;
struct dot_state state;
unsigned int midi_ports;
/* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
struct snd_rawmidi_substream *midi[2];
int midi_fifo_used[2];
struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS];
int midi_fifo_used[MAX_MIDI_PORTS];
int midi_fifo_limit;
void (*transfer_samples)(struct amdtp_stream *s,
@ -124,8 +125,8 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
return -EBUSY;
/*
* A first data channel is for MIDI conformant data channel, the rest is
* Multi Bit Linear Audio data channel.
* A first data channel is for MIDI messages, the rest is Multi Bit
* Linear Audio data channel.
*/
err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
if (err < 0)
@ -135,11 +136,6 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->pcm_channels = pcm_channels;
if (s->direction == AMDTP_IN_STREAM)
p->midi_ports = DOT_MIDI_IN_PORTS;
else
p->midi_ports = DOT_MIDI_OUT_PORTS;
/*
* We do not know the actual MIDI FIFO size of most devices. Just
* assume two bytes, i.e., one byte can be received over the bus while
@ -281,13 +277,25 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
b = (u8 *)&buffer[0];
len = 0;
if (port < p->midi_ports &&
if (port < MAX_MIDI_PORTS &&
midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL)
len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
if (len > 0) {
b[3] = (0x10 << port) | len;
/*
* Upper 4 bits of LSB represent port number.
* - 0000b: physical MIDI port 1.
* - 0010b: physical MIDI port 2.
* - 1110b: console MIDI port.
*/
if (port == 2)
b[3] = 0xe0;
else if (port == 1)
b[3] = 0x20;
else
b[3] = 0x00;
b[3] |= len;
midi_use_bytes(s, port, len);
} else {
b[1] = 0;
@ -309,11 +317,22 @@ static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
for (f = 0; f < data_blocks; f++) {
b = (u8 *)&buffer[0];
port = b[3] >> 4;
len = b[3] & 0x0f;
if (port < p->midi_ports && p->midi[port] && len > 0)
snd_rawmidi_receive(p->midi[port], b + 1, len);
len = b[3] & 0x0f;
if (len > 0) {
/*
* Upper 4 bits of LSB represent port number.
* - 0000b: physical MIDI port 1. Use port 0.
* - 1110b: console MIDI port. Use port 2.
*/
if (b[3] >> 4 > 0)
port = 2;
else
port = 0;
if (port < MAX_MIDI_PORTS && p->midi[port])
snd_rawmidi_receive(p->midi[port], b + 1, len);
}
buffer += s->data_block_quadlets;
}
@ -364,7 +383,7 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
{
struct amdtp_dot *p = s->protocol;
if (port < p->midi_ports)
if (port < MAX_MIDI_PORTS)
ACCESS_ONCE(p->midi[port]) = midi;
}

View File

@ -8,7 +8,7 @@
#include "digi00x.h"
static int midi_phys_open(struct snd_rawmidi_substream *substream)
static int midi_open(struct snd_rawmidi_substream *substream)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
int err;
@ -27,7 +27,7 @@ static int midi_phys_open(struct snd_rawmidi_substream *substream)
return err;
}
static int midi_phys_close(struct snd_rawmidi_substream *substream)
static int midi_close(struct snd_rawmidi_substream *substream)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
@ -40,180 +40,130 @@ static int midi_phys_close(struct snd_rawmidi_substream *substream)
return 0;
}
static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream,
int up)
static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
unsigned int port;
unsigned long flags;
spin_lock_irqsave(&dg00x->lock, flags);
if (up)
amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
substream);
if (substream->rmidi->device == 0)
port = substream->number;
else
amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
NULL);
spin_unlock_irqrestore(&dg00x->lock, flags);
}
static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
unsigned long flags;
port = 2;
spin_lock_irqsave(&dg00x->lock, flags);
if (up)
amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
substream);
amdtp_dot_midi_trigger(&dg00x->tx_stream, port, substream);
else
amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
NULL);
amdtp_dot_midi_trigger(&dg00x->tx_stream, port, NULL);
spin_unlock_irqrestore(&dg00x->lock, flags);
}
static int midi_ctl_open(struct snd_rawmidi_substream *substream)
{
/* Do nothing. */
return 0;
}
static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
{
/* Do nothing. */
return 0;
}
static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
snd_fw_async_midi_port_finish(&dg00x->out_control);
return 0;
}
static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
int up)
static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
unsigned int port;
unsigned long flags;
spin_lock_irqsave(&dg00x->lock, flags);
if (up)
dg00x->in_control = substream;
if (substream->rmidi->device == 0)
port = substream->number;
else
dg00x->in_control = NULL;
spin_unlock_irqrestore(&dg00x->lock, flags);
}
static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
unsigned long flags;
port = 2;
spin_lock_irqsave(&dg00x->lock, flags);
if (up)
snd_fw_async_midi_port_run(&dg00x->out_control, substream);
amdtp_dot_midi_trigger(&dg00x->rx_stream, port, substream);
else
amdtp_dot_midi_trigger(&dg00x->rx_stream, port, NULL);
spin_unlock_irqrestore(&dg00x->lock, flags);
}
static void set_midi_substream_names(struct snd_dg00x *dg00x,
struct snd_rawmidi_str *str,
bool is_ctl)
static void set_substream_names(struct snd_dg00x *dg00x,
struct snd_rawmidi *rmidi, bool is_console)
{
struct snd_rawmidi_substream *subs;
struct snd_rawmidi_str *str;
int i;
list_for_each_entry(subs, &str->substreams, list) {
if (!is_ctl)
snprintf(subs->name, sizeof(subs->name),
"%s MIDI %d",
dg00x->card->shortname, subs->number + 1);
else
/* This port is for asynchronous transaction. */
snprintf(subs->name, sizeof(subs->name),
"%s control",
dg00x->card->shortname);
for (i = 0; i < 2; ++i) {
str = &rmidi->streams[i];
list_for_each_entry(subs, &str->substreams, list) {
if (!is_console) {
snprintf(subs->name, sizeof(subs->name),
"%s MIDI %d",
dg00x->card->shortname,
subs->number + 1);
} else {
snprintf(subs->name, sizeof(subs->name),
"%s control",
dg00x->card->shortname);
}
}
}
}
static int add_substream_pair(struct snd_dg00x *dg00x, unsigned int out_ports,
unsigned int in_ports, bool is_console)
{
static const struct snd_rawmidi_ops capture_ops = {
.open = midi_open,
.close = midi_close,
.trigger = midi_capture_trigger,
};
static const struct snd_rawmidi_ops playback_ops = {
.open = midi_open,
.close = midi_close,
.trigger = midi_playback_trigger,
};
const char *label;
struct snd_rawmidi *rmidi;
int err;
/* Add physical midi ports. */
err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, is_console,
out_ports, in_ports, &rmidi);
if (err < 0)
return err;
rmidi->private_data = dg00x;
if (!is_console)
label = "%s control";
else
label = "%s MIDI";
snprintf(rmidi->name, sizeof(rmidi->name), label,
dg00x->card->shortname);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &playback_ops);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &capture_ops);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
set_substream_names(dg00x, rmidi, is_console);
return 0;
}
int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
{
static const struct snd_rawmidi_ops phys_capture_ops = {
.open = midi_phys_open,
.close = midi_phys_close,
.trigger = midi_phys_capture_trigger,
};
static const struct snd_rawmidi_ops phys_playback_ops = {
.open = midi_phys_open,
.close = midi_phys_close,
.trigger = midi_phys_playback_trigger,
};
static const struct snd_rawmidi_ops ctl_capture_ops = {
.open = midi_ctl_open,
.close = midi_ctl_capture_close,
.trigger = midi_ctl_capture_trigger,
};
static const struct snd_rawmidi_ops ctl_playback_ops = {
.open = midi_ctl_open,
.close = midi_ctl_playback_close,
.trigger = midi_ctl_playback_trigger,
};
struct snd_rawmidi *rmidi[2];
struct snd_rawmidi_str *str;
unsigned int i;
int err;
/* Add physical midi ports. */
err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
err = add_substream_pair(dg00x, DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS,
false);
if (err < 0)
return err;
snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
"%s MIDI", dg00x->card->shortname);
if (dg00x->is_console)
err = add_substream_pair(dg00x, 1, 1, true);
snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
&phys_capture_ops);
snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
&phys_playback_ops);
/* Add a pair of control midi ports. */
err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
1, 1, &rmidi[1]);
if (err < 0)
return err;
snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
"%s control", dg00x->card->shortname);
snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
&ctl_capture_ops);
snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
&ctl_playback_ops);
for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
rmidi[i]->private_data = dg00x;
rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
set_midi_substream_names(dg00x, str, i);
rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
set_midi_substream_names(dg00x, str, i);
rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
}
return 0;
return err;
}

View File

@ -9,40 +9,6 @@
#include <sound/asound.h>
#include "digi00x.h"
static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
{
int bytes;
buf[0] = 0x80;
bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
if (bytes >= 0)
buf[3] = 0xc0 | bytes;
return bytes;
}
static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
unsigned int length)
{
struct snd_rawmidi_substream *substream;
unsigned int i;
unsigned int len;
u8 *b;
substream = ACCESS_ONCE(dg00x->in_control);
if (substream == NULL)
return;
length /= 4;
for (i = 0; i < length; i++) {
b = (u8 *)&buf[i];
len = b[3] & 0xf;
if (len > 0)
snd_rawmidi_receive(dg00x->in_control, b + 1, len);
}
}
static void handle_unknown_message(struct snd_dg00x *dg00x,
unsigned long long offset, __be32 *buf)
{
@ -63,39 +29,36 @@ static void handle_message(struct fw_card *card, struct fw_request *request,
struct snd_dg00x *dg00x = callback_data;
__be32 *buf = (__be32 *)data;
fw_send_response(card, request, RCODE_COMPLETE);
if (offset == dg00x->async_handler.offset)
handle_unknown_message(dg00x, offset, buf);
else if (offset == dg00x->async_handler.offset + 4)
handle_midi_control(dg00x, buf, length);
fw_send_response(card, request, RCODE_COMPLETE);
}
int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
{
struct fw_device *device = fw_parent_device(dg00x->unit);
__be32 data[2];
int err;
/* Unknown. 4bytes. */
data[0] = cpu_to_be32((device->card->node_id << 16) |
(dg00x->async_handler.offset >> 32));
data[1] = cpu_to_be32(dg00x->async_handler.offset);
err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
&data, sizeof(data), 0);
if (err < 0)
return err;
/* Asynchronous transactions for MIDI control message. */
data[0] = cpu_to_be32((device->card->node_id << 16) |
(dg00x->async_handler.offset >> 32));
data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
&data, sizeof(data), 0);
}
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
{
if (dg00x->async_handler.callback_data == NULL)
return;
fw_core_remove_address_handler(&dg00x->async_handler);
dg00x->async_handler.callback_data = NULL;
}
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
{
static const struct fw_address_region resp_register_region = {
@ -104,7 +67,7 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
};
int err;
dg00x->async_handler.length = 12;
dg00x->async_handler.length = 4;
dg00x->async_handler.address_callback = handle_message;
dg00x->async_handler.callback_data = dg00x;
@ -115,28 +78,7 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
err = snd_dg00x_transaction_reregister(dg00x);
if (err < 0)
goto error;
err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
4, fill_midi_message);
if (err < 0)
goto error;
snd_dg00x_transaction_unregister(dg00x);
return err;
error:
fw_core_remove_address_handler(&dg00x->async_handler);
dg00x->async_handler.callback_data = NULL;
return err;
}
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
{
if (dg00x->async_handler.callback_data == NULL)
return;
snd_fw_async_midi_port_destroy(&dg00x->out_control);
fw_core_remove_address_handler(&dg00x->async_handler);
dg00x->async_handler.callback_data = NULL;
}

View File

@ -13,7 +13,8 @@ MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
MODULE_LICENSE("GPL v2");
#define VENDOR_DIGIDESIGN 0x00a07e
#define MODEL_DIGI00X 0x000002
#define MODEL_CONSOLE 0x000001
#define MODEL_RACK 0x000002
static int name_card(struct snd_dg00x *dg00x)
{
@ -129,6 +130,8 @@ static int snd_dg00x_probe(struct fw_unit *unit,
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);
@ -183,7 +186,13 @@ static const struct ieee1394_device_id snd_dg00x_id_table[] = {
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = VENDOR_DIGIDESIGN,
.model_id = MODEL_DIGI00X,
.model_id = MODEL_CONSOLE,
},
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = VENDOR_DIGIDESIGN,
.model_id = MODEL_RACK,
},
{}
};

View File

@ -58,16 +58,15 @@ struct snd_dg00x {
struct fw_address_handler async_handler;
u32 msg;
/* For asynchronous MIDI controls. */
struct snd_rawmidi_substream *in_control;
struct snd_fw_async_midi_port out_control;
/* Console models have additional MIDI ports for control surface. */
bool is_console;
};
#define DG00X_ADDR_BASE 0xffffe0000000ull
#define DG00X_OFFSET_STREAMING_STATE 0x0000
#define DG00X_OFFSET_STREAMING_SET 0x0004
#define DG00X_OFFSET_MIDI_CTL_ADDR 0x0008
/* unknown but address in host space 0x0008 */
/* For LSB of the address 0x000c */
/* unknown 0x0010 */
#define DG00X_OFFSET_MESSAGE_ADDR 0x0014

View File

@ -63,7 +63,9 @@ int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
/* do transaction and check buf[1-5] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
if (err >= 0 && err < 8)
if (err < 0)
;
else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -106,7 +108,9 @@ int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
/* do transaction and check buf[1-4] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4));
if (err >= 0 && err < 8)
if (err < 0)
;
else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -154,7 +158,9 @@ int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
buf[3] = 0xff & subfunction;
err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
if (err >= 0 && err < 8)
if (err < 0)
;
else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;

View File

@ -0,0 +1,3 @@
snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o
obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o

View File

@ -0,0 +1,155 @@
/*
* amdtp-ff.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/pcm.h>
#include "ff.h"
struct amdtp_ff {
unsigned int pcm_channels;
};
int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int pcm_channels)
{
struct amdtp_ff *p = s->protocol;
unsigned int data_channels;
if (amdtp_stream_running(s))
return -EBUSY;
p->pcm_channels = pcm_channels;
data_channels = pcm_channels;
return amdtp_stream_set_parameters(s, rate, data_channels);
}
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames)
{
struct amdtp_ff *p = s->protocol;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
const u32 *src;
channels = p->pcm_channels;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
buffer[c] = cpu_to_le32(*src);
src++;
}
buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
}
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_substream *pcm,
__le32 *buffer, unsigned int frames)
{
struct amdtp_ff *p = s->protocol;
struct snd_pcm_runtime *runtime = pcm->runtime;
unsigned int channels, remaining_frames, i, c;
u32 *dst;
channels = p->pcm_channels;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c) {
*dst = le32_to_cpu(buffer[c]) & 0xffffff00;
dst++;
}
buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
dst = (void *)runtime->dma_area;
}
}
static void write_pcm_silence(struct amdtp_stream *s,
__le32 *buffer, unsigned int frames)
{
struct amdtp_ff *p = s->protocol;
unsigned int i, c, channels = p->pcm_channels;
for (i = 0; i < frames; ++i) {
for (c = 0; c < channels; ++c)
buffer[c] = cpu_to_le32(0x00000000);
buffer += s->data_block_quadlets;
}
}
int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime)
{
int err;
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if (err < 0)
return err;
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
{
struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
unsigned int pcm_frames;
if (pcm) {
write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
pcm_frames = data_blocks;
} else {
write_pcm_silence(s, (__le32 *)buffer, data_blocks);
pcm_frames = 0;
}
return pcm_frames;
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer,
unsigned int data_blocks,
unsigned int *syt)
{
struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
unsigned int pcm_frames;
if (pcm) {
read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
pcm_frames = data_blocks;
} else {
pcm_frames = 0;
}
return pcm_frames;
}
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
if (dir == AMDTP_IN_STREAM)
process_data_blocks = process_tx_data_blocks;
else
process_data_blocks = process_rx_data_blocks;
return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
process_data_blocks, sizeof(struct amdtp_ff));
}

View File

@ -0,0 +1,191 @@
/*
* ff-hwdep.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
/*
* This codes give three functionality.
*
* 1.get firewire node information
* 2.get notification about starting/stopping stream
* 3.lock/unlock stream
*/
#include "ff.h"
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
struct snd_ff *ff = hwdep->private_data;
DEFINE_WAIT(wait);
union snd_firewire_event event;
spin_lock_irq(&ff->lock);
while (!ff->dev_lock_changed) {
prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&ff->lock);
schedule();
finish_wait(&ff->hwdep_wait, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&ff->lock);
}
memset(&event, 0, sizeof(event));
if (ff->dev_lock_changed) {
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (ff->dev_lock_count > 0);
ff->dev_lock_changed = false;
count = min_t(long, count, sizeof(event.lock_status));
}
spin_unlock_irq(&ff->lock);
if (copy_to_user(buf, &event, count))
return -EFAULT;
return count;
}
static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_table *wait)
{
struct snd_ff *ff = hwdep->private_data;
unsigned int events;
poll_wait(file, &ff->hwdep_wait, wait);
spin_lock_irq(&ff->lock);
if (ff->dev_lock_changed)
events = POLLIN | POLLRDNORM;
else
events = 0;
spin_unlock_irq(&ff->lock);
return events;
}
static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
{
struct fw_device *dev = fw_parent_device(ff->unit);
struct snd_firewire_get_info info;
memset(&info, 0, sizeof(info));
info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
info.card = dev->card->index;
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
strlcpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int hwdep_lock(struct snd_ff *ff)
{
int err;
spin_lock_irq(&ff->lock);
if (ff->dev_lock_count == 0) {
ff->dev_lock_count = -1;
err = 0;
} else {
err = -EBUSY;
}
spin_unlock_irq(&ff->lock);
return err;
}
static int hwdep_unlock(struct snd_ff *ff)
{
int err;
spin_lock_irq(&ff->lock);
if (ff->dev_lock_count == -1) {
ff->dev_lock_count = 0;
err = 0;
} else {
err = -EBADFD;
}
spin_unlock_irq(&ff->lock);
return err;
}
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
{
struct snd_ff *ff = hwdep->private_data;
spin_lock_irq(&ff->lock);
if (ff->dev_lock_count == -1)
ff->dev_lock_count = 0;
spin_unlock_irq(&ff->lock);
return 0;
}
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct snd_ff *ff = hwdep->private_data;
switch (cmd) {
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
return hwdep_get_info(ff, (void __user *)arg);
case SNDRV_FIREWIRE_IOCTL_LOCK:
return hwdep_lock(ff);
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
return hwdep_unlock(ff);
default:
return -ENOIOCTLCMD;
}
}
#ifdef CONFIG_COMPAT
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
return hwdep_ioctl(hwdep, file, cmd,
(unsigned long)compat_ptr(arg));
}
#else
#define hwdep_compat_ioctl NULL
#endif
int snd_ff_create_hwdep_devices(struct snd_ff *ff)
{
static const struct snd_hwdep_ops hwdep_ops = {
.read = hwdep_read,
.release = hwdep_release,
.poll = hwdep_poll,
.ioctl = hwdep_ioctl,
.ioctl_compat = hwdep_compat_ioctl,
};
struct snd_hwdep *hwdep;
int err;
err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
if (err < 0)
return err;
strcpy(hwdep->name, ff->card->driver);
hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
hwdep->ops = hwdep_ops;
hwdep->private_data = ff;
hwdep->exclusive = true;
return 0;
}

View File

@ -0,0 +1,131 @@
/*
* ff-midi.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
static int midi_capture_open(struct snd_rawmidi_substream *substream)
{
/* Do nothing. */
return 0;
}
static int midi_playback_open(struct snd_rawmidi_substream *substream)
{
struct snd_ff *ff = substream->rmidi->private_data;
/* Initialize internal status. */
ff->running_status[substream->number] = 0;
ff->rx_midi_error[substream->number] = false;
ACCESS_ONCE(ff->rx_midi_substreams[substream->number]) = substream;
return 0;
}
static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
/* Do nothing. */
return 0;
}
static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_ff *ff = substream->rmidi->private_data;
cancel_work_sync(&ff->rx_midi_work[substream->number]);
ACCESS_ONCE(ff->rx_midi_substreams[substream->number]) = NULL;
return 0;
}
static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct snd_ff *ff = substream->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&ff->lock, flags);
if (up)
ACCESS_ONCE(ff->tx_midi_substreams[substream->number]) =
substream;
else
ACCESS_ONCE(ff->tx_midi_substreams[substream->number]) = NULL;
spin_unlock_irqrestore(&ff->lock, flags);
}
static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
int up)
{
struct snd_ff *ff = substream->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&ff->lock, flags);
if (up || !ff->rx_midi_error[substream->number])
schedule_work(&ff->rx_midi_work[substream->number]);
spin_unlock_irqrestore(&ff->lock, flags);
}
static struct snd_rawmidi_ops midi_capture_ops = {
.open = midi_capture_open,
.close = midi_capture_close,
.trigger = midi_capture_trigger,
};
static struct snd_rawmidi_ops midi_playback_ops = {
.open = midi_playback_open,
.close = midi_playback_close,
.trigger = midi_playback_trigger,
};
static void set_midi_substream_names(struct snd_rawmidi_str *stream,
const char *const name)
{
struct snd_rawmidi_substream *substream;
list_for_each_entry(substream, &stream->substreams, list) {
snprintf(substream->name, sizeof(substream->name),
"%s MIDI %d", name, substream->number + 1);
}
}
int snd_ff_create_midi_devices(struct snd_ff *ff)
{
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *stream;
int err;
err = snd_rawmidi_new(ff->card, ff->card->driver, 0,
ff->spec->midi_out_ports, ff->spec->midi_in_ports,
&rmidi);
if (err < 0)
return err;
snprintf(rmidi->name, sizeof(rmidi->name),
"%s MIDI", ff->card->shortname);
rmidi->private_data = ff;
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
&midi_capture_ops);
stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
set_midi_substream_names(stream, ff->card->shortname);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&midi_playback_ops);
stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
set_midi_substream_names(stream, ff->card->shortname);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
return 0;
}

View File

@ -0,0 +1,409 @@
/*
* ff-pcm.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
static inline unsigned int get_multiplier_mode_with_index(unsigned int index)
{
return ((int)index - 1) / 2;
}
static int hw_rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
const unsigned int *pcm_channels = rule->private;
struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
const struct snd_interval *c =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, mode;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
mode = get_multiplier_mode_with_index(i);
if (!snd_interval_test(c, pcm_channels[mode]))
continue;
t.min = min(t.min, amdtp_rate_table[i]);
t.max = max(t.max, amdtp_rate_table[i]);
}
return snd_interval_refine(r, &t);
}
static int hw_rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
const unsigned int *pcm_channels = rule->private;
struct snd_interval *c =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
const struct snd_interval *r =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval t = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, mode;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
mode = get_multiplier_mode_with_index(i);
if (!snd_interval_test(r, amdtp_rate_table[i]))
continue;
t.min = min(t.min, pcm_channels[mode]);
t.max = max(t.max, pcm_channels[mode]);
}
return snd_interval_refine(c, &t);
}
static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
const unsigned int *pcm_channels)
{
unsigned int mode;
unsigned int rate, channels;
int i;
hw->channels_min = UINT_MAX;
hw->channels_max = 0;
hw->rate_min = UINT_MAX;
hw->rate_max = 0;
for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
mode = get_multiplier_mode_with_index(i);
channels = pcm_channels[mode];
if (pcm_channels[mode] == 0)
continue;
hw->channels_min = min(hw->channels_min, channels);
hw->channels_max = max(hw->channels_max, channels);
rate = amdtp_rate_table[i];
hw->rates |= snd_pcm_rate_to_rate_bit(rate);
hw->rate_min = min(hw->rate_min, rate);
hw->rate_max = max(hw->rate_max, rate);
}
}
static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
{
hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
hw->periods_max = UINT_MAX;
hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */
/* Just to prevent from allocating much pages. */
hw->period_bytes_max = hw->period_bytes_min * 2048;
hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
}
static int pcm_init_hw_params(struct snd_ff *ff,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct amdtp_stream *s;
const unsigned int *pcm_channels;
int err;
runtime->hw.info = SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
s = &ff->tx_stream;
pcm_channels = ff->spec->pcm_capture_channels;
} else {
runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
s = &ff->rx_stream;
pcm_channels = ff->spec->pcm_playback_channels;
}
/* limit rates */
limit_channels_and_rates(&runtime->hw, pcm_channels);
limit_period_and_buffer(&runtime->hw);
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
hw_rule_channels, (void *)pcm_channels,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
return err;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
hw_rule_rate, (void *)pcm_channels,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
return err;
return amdtp_ff_add_pcm_hw_constraints(s, runtime);
}
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
unsigned int rate;
enum snd_ff_clock_src src;
int i, err;
err = snd_ff_stream_lock_try(ff);
if (err < 0)
return err;
err = pcm_init_hw_params(ff, substream);
if (err < 0) {
snd_ff_stream_lock_release(ff);
return err;
}
err = ff->spec->protocol->get_clock(ff, &rate, &src);
if (err < 0) {
snd_ff_stream_lock_release(ff);
return err;
}
if (src != SND_FF_CLOCK_SRC_INTERNAL) {
for (i = 0; i < CIP_SFC_COUNT; ++i) {
if (amdtp_rate_table[i] == rate)
break;
}
/*
* The unit is configured at sampling frequency which packet
* streaming engine can't support.
*/
if (i >= CIP_SFC_COUNT) {
snd_ff_stream_lock_release(ff);
return -EIO;
}
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
} else {
if (amdtp_stream_pcm_running(&ff->rx_stream) ||
amdtp_stream_pcm_running(&ff->tx_stream)) {
rate = amdtp_rate_table[ff->rx_stream.sfc];
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
}
}
snd_pcm_set_sync(substream);
return 0;
}
static int pcm_close(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
snd_ff_stream_lock_release(ff);
return 0;
}
static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_ff *ff = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&ff->mutex);
ff->substreams_counter++;
mutex_unlock(&ff->mutex);
}
return 0;
}
static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_ff *ff = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&ff->mutex);
ff->substreams_counter++;
mutex_unlock(&ff->mutex);
}
return 0;
}
static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
mutex_lock(&ff->mutex);
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
ff->substreams_counter--;
snd_ff_stream_stop_duplex(ff);
mutex_unlock(&ff->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
mutex_lock(&ff->mutex);
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
ff->substreams_counter--;
snd_ff_stream_stop_duplex(ff);
mutex_unlock(&ff->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
mutex_lock(&ff->mutex);
err = snd_ff_stream_start_duplex(ff, runtime->rate);
if (err >= 0)
amdtp_stream_pcm_prepare(&ff->tx_stream);
mutex_unlock(&ff->mutex);
return err;
}
static int pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_ff *ff = substream->private_data;
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
mutex_lock(&ff->mutex);
err = snd_ff_stream_start_duplex(ff, runtime->rate);
if (err >= 0)
amdtp_stream_pcm_prepare(&ff->rx_stream);
mutex_unlock(&ff->mutex);
return err;
}
static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_ff *ff = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&ff->tx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_ff *ff = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&ff->rx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_ff *ff = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&ff->tx_stream);
}
static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
{
struct snd_ff *ff = sbstrm->private_data;
return amdtp_stream_pcm_pointer(&ff->rx_stream);
}
static struct snd_pcm_ops pcm_capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_capture_hw_params,
.hw_free = pcm_capture_hw_free,
.prepare = pcm_capture_prepare,
.trigger = pcm_capture_trigger,
.pointer = pcm_capture_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
};
static struct snd_pcm_ops pcm_playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = pcm_playback_hw_params,
.hw_free = pcm_playback_hw_free,
.prepare = pcm_playback_prepare,
.trigger = pcm_playback_trigger,
.pointer = pcm_playback_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
int snd_ff_create_pcm_devices(struct snd_ff *ff)
{
struct snd_pcm *pcm;
int err;
err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = ff;
snprintf(pcm->name, sizeof(pcm->name),
"%s PCM", ff->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
return 0;
}

View File

@ -0,0 +1,63 @@
/*
* ff-proc.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./ff.h"
static void proc_dump_clock_config(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ff *ff = entry->private_data;
ff->spec->protocol->dump_clock_config(ff, buffer);
}
static void proc_dump_sync_status(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_ff *ff = entry->private_data;
ff->spec->protocol->dump_sync_status(ff, buffer);
}
static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
const char *name,
void (*op)(struct snd_info_entry *e,
struct snd_info_buffer *b))
{
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(ff->card, name, root);
if (entry == NULL)
return;
snd_info_set_text_ops(entry, ff, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
}
void snd_ff_proc_init(struct snd_ff *ff)
{
struct snd_info_entry *root;
/*
* All nodes are automatically removed at snd_card_disconnect(),
* by following to link list.
*/
root = snd_info_create_card_entry(ff->card, "firewire",
ff->card->proc_root);
if (root == NULL)
return;
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(ff, root, "clock-config", proc_dump_clock_config);
add_node(ff, root, "sync-status", proc_dump_sync_status);
}

View File

@ -0,0 +1,371 @@
/*
* ff-protocol-ff400.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/delay.h>
#include "ff.h"
#define FF400_STF 0x000080100500ull
#define FF400_RX_PACKET_FORMAT 0x000080100504ull
#define FF400_ISOC_COMM_START 0x000080100508ull
#define FF400_TX_PACKET_FORMAT 0x00008010050cull
#define FF400_ISOC_COMM_STOP 0x000080100510ull
#define FF400_SYNC_STATUS 0x0000801c0000ull
#define FF400_FETCH_PCM_FRAMES 0x0000801c0000ull /* For block request. */
#define FF400_CLOCK_CONFIG 0x0000801c0004ull
#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull
#define FF400_MIDI_RX_PORT_0 0x000080180000ull
#define FF400_MIDI_RX_PORT_1 0x000080190000ull
static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate,
enum snd_ff_clock_src *src)
{
__le32 reg;
u32 data;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
if (err < 0)
return err;
data = le32_to_cpu(reg);
/* Calculate sampling rate. */
switch ((data >> 1) & 0x03) {
case 0x01:
*rate = 32000;
break;
case 0x00:
*rate = 44100;
break;
case 0x03:
*rate = 48000;
break;
case 0x02:
default:
return -EIO;
}
if (data & 0x08)
*rate *= 2;
else if (data & 0x10)
*rate *= 4;
/* Calculate source of clock. */
if (data & 0x01) {
*src = SND_FF_CLOCK_SRC_INTERNAL;
} else {
/* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */
switch ((data >> 10) & 0x07) {
case 0x03:
*src = SND_FF_CLOCK_SRC_SPDIF;
break;
case 0x04:
*src = SND_FF_CLOCK_SRC_WORD;
break;
case 0x05:
*src = SND_FF_CLOCK_SRC_LTC;
break;
case 0x00:
default:
*src = SND_FF_CLOCK_SRC_ADAT;
break;
}
}
return 0;
}
static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
{
__le32 reg;
int i, err;
/* Check whether the given value is supported or not. */
for (i = 0; i < CIP_SFC_COUNT; i++) {
if (amdtp_rate_table[i] == rate)
break;
}
if (i == CIP_SFC_COUNT)
return -EINVAL;
/* Set the number of data blocks transferred in a second. */
reg = cpu_to_le32(rate);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_STF, &reg, sizeof(reg), 0);
if (err < 0)
return err;
msleep(100);
/*
* Set isochronous channel and the number of quadlets of received
* packets.
*/
reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
ff->rx_resources.channel);
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
if (err < 0)
return err;
/*
* Set isochronous channel and the number of quadlets of transmitted
* packet.
*/
/* TODO: investigate the purpose of this 0x80. */
reg = cpu_to_le32((0x80 << 24) |
(ff->tx_resources.channel << 5) |
(ff->tx_stream.data_block_quadlets));
err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_TX_PACKET_FORMAT, &reg, sizeof(reg), 0);
if (err < 0)
return err;
/* Allow to transmit packets. */
reg = cpu_to_le32(0x00000001);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
}
static void ff400_finish_session(struct snd_ff *ff)
{
__le32 reg;
reg = cpu_to_le32(0x80000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
}
static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable)
{
__le32 *reg;
int i;
reg = kzalloc(sizeof(__le32) * 18, GFP_KERNEL);
if (reg == NULL)
return -ENOMEM;
if (enable) {
/*
* Each quadlet is corresponding to data channels in a data
* blocks in reverse order. Precisely, quadlets for available
* data channels should be enabled. Here, I take second best
* to fetch PCM frames from all of data channels regardless of
* stf.
*/
for (i = 0; i < 18; ++i)
reg[i] = cpu_to_le32(0x00000001);
}
return snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
FF400_FETCH_PCM_FRAMES, reg,
sizeof(__le32) * 18, 0);
}
static void ff400_dump_sync_status(struct snd_ff *ff,
struct snd_info_buffer *buffer)
{
__le32 reg;
u32 data;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
if (err < 0)
return;
data = le32_to_cpu(reg);
snd_iprintf(buffer, "External source detection:\n");
snd_iprintf(buffer, "Word Clock:");
if ((data >> 24) & 0x20) {
if ((data >> 24) & 0x40)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
snd_iprintf(buffer, "S/PDIF:");
if ((data >> 16) & 0x10) {
if ((data >> 16) & 0x04)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
snd_iprintf(buffer, "ADAT:");
if ((data >> 8) & 0x04) {
if ((data >> 8) & 0x10)
snd_iprintf(buffer, "sync\n");
else
snd_iprintf(buffer, "lock\n");
} else {
snd_iprintf(buffer, "none\n");
}
snd_iprintf(buffer, "\nUsed external source:\n");
if (((data >> 22) & 0x07) == 0x07) {
snd_iprintf(buffer, "None\n");
} else {
switch ((data >> 22) & 0x07) {
case 0x00:
snd_iprintf(buffer, "ADAT:");
break;
case 0x03:
snd_iprintf(buffer, "S/PDIF:");
break;
case 0x04:
snd_iprintf(buffer, "Word:");
break;
case 0x07:
snd_iprintf(buffer, "Nothing:");
break;
case 0x01:
case 0x02:
case 0x05:
case 0x06:
default:
snd_iprintf(buffer, "unknown:");
break;
}
if ((data >> 25) & 0x07) {
switch ((data >> 25) & 0x07) {
case 0x01:
snd_iprintf(buffer, "32000\n");
break;
case 0x02:
snd_iprintf(buffer, "44100\n");
break;
case 0x03:
snd_iprintf(buffer, "48000\n");
break;
case 0x04:
snd_iprintf(buffer, "64000\n");
break;
case 0x05:
snd_iprintf(buffer, "88200\n");
break;
case 0x06:
snd_iprintf(buffer, "96000\n");
break;
case 0x07:
snd_iprintf(buffer, "128000\n");
break;
case 0x08:
snd_iprintf(buffer, "176400\n");
break;
case 0x09:
snd_iprintf(buffer, "192000\n");
break;
case 0x00:
snd_iprintf(buffer, "unknown\n");
break;
}
}
}
snd_iprintf(buffer, "Multiplied:");
snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250);
}
static void ff400_dump_clock_config(struct snd_ff *ff,
struct snd_info_buffer *buffer)
{
__le32 reg;
u32 data;
unsigned int rate;
const char *src;
int err;
err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
FF400_CLOCK_CONFIG, &reg, sizeof(reg), 0);
if (err < 0)
return;
data = le32_to_cpu(reg);
snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
(data & 0x20) ? "Professional" : "Consumer",
(data & 0x40) ? "on" : "off");
snd_iprintf(buffer, "Optical output interface format: %s\n",
((data >> 8) & 0x01) ? "S/PDIF" : "ADAT");
snd_iprintf(buffer, "Word output single speed: %s\n",
((data >> 8) & 0x20) ? "on" : "off");
snd_iprintf(buffer, "S/PDIF input interface: %s\n",
((data >> 8) & 0x02) ? "Optical" : "Coaxial");
switch ((data >> 1) & 0x03) {
case 0x01:
rate = 32000;
break;
case 0x00:
rate = 44100;
break;
case 0x03:
rate = 48000;
break;
case 0x02:
default:
return;
}
if (data & 0x08)
rate *= 2;
else if (data & 0x10)
rate *= 4;
snd_iprintf(buffer, "Sampling rate: %d\n", rate);
if (data & 0x01) {
src = "Internal";
} else {
switch ((data >> 10) & 0x07) {
case 0x00:
src = "ADAT";
break;
case 0x03:
src = "S/PDIF";
break;
case 0x04:
src = "Word";
break;
case 0x05:
src = "LTC";
break;
default:
return;
}
}
snd_iprintf(buffer, "Sync to clock source: %s\n", src);
}
struct snd_ff_protocol snd_ff_protocol_ff400 = {
.get_clock = ff400_get_clock,
.begin_session = ff400_begin_session,
.finish_session = ff400_finish_session,
.switch_fetching_mode = ff400_switch_fetching_mode,
.dump_sync_status = ff400_dump_sync_status,
.dump_clock_config = ff400_dump_clock_config,
.midi_high_addr_reg = FF400_MIDI_HIGH_ADDR,
.midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0,
.midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1,
};

View File

@ -0,0 +1,282 @@
/*
* ff-stream.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
#define CALLBACK_TIMEOUT_MS 200
static int get_rate_mode(unsigned int rate, unsigned int *mode)
{
int i;
for (i = 0; i < CIP_SFC_COUNT; i++) {
if (amdtp_rate_table[i] == rate)
break;
}
if (i == CIP_SFC_COUNT)
return -EINVAL;
*mode = ((int)i - 1) / 2;
return 0;
}
/*
* Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
* we can allocate between 0 and 7 channel.
*/
static int keep_resources(struct snd_ff *ff, unsigned int rate)
{
int mode;
int err;
err = get_rate_mode(rate, &mode);
if (err < 0)
return err;
/* Keep resources for in-stream. */
err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
ff->spec->pcm_capture_channels[mode]);
if (err < 0)
return err;
ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->tx_resources,
amdtp_stream_get_max_payload(&ff->tx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
return err;
/* Keep resources for out-stream. */
err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
ff->spec->pcm_playback_channels[mode]);
if (err < 0)
return err;
ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
err = fw_iso_resources_allocate(&ff->rx_resources,
amdtp_stream_get_max_payload(&ff->rx_stream),
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
fw_iso_resources_free(&ff->tx_resources);
return err;
}
static void release_resources(struct snd_ff *ff)
{
fw_iso_resources_free(&ff->tx_resources);
fw_iso_resources_free(&ff->rx_resources);
}
static inline void finish_session(struct snd_ff *ff)
{
ff->spec->protocol->finish_session(ff);
ff->spec->protocol->switch_fetching_mode(ff, false);
}
static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
{
int err;
struct fw_iso_resources *resources;
struct amdtp_stream *stream;
if (dir == AMDTP_IN_STREAM) {
resources = &ff->tx_resources;
stream = &ff->tx_stream;
} else {
resources = &ff->rx_resources;
stream = &ff->rx_stream;
}
err = fw_iso_resources_init(resources, ff->unit);
if (err < 0)
return err;
err = amdtp_ff_init(stream, ff->unit, dir);
if (err < 0)
fw_iso_resources_destroy(resources);
return err;
}
static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
{
if (dir == AMDTP_IN_STREAM) {
amdtp_stream_destroy(&ff->tx_stream);
fw_iso_resources_destroy(&ff->tx_resources);
} else {
amdtp_stream_destroy(&ff->rx_stream);
fw_iso_resources_destroy(&ff->rx_resources);
}
}
int snd_ff_stream_init_duplex(struct snd_ff *ff)
{
int err;
err = init_stream(ff, AMDTP_OUT_STREAM);
if (err < 0)
goto end;
err = init_stream(ff, AMDTP_IN_STREAM);
if (err < 0)
destroy_stream(ff, AMDTP_OUT_STREAM);
end:
return err;
}
/*
* This function should be called before starting streams or after stopping
* streams.
*/
void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
{
destroy_stream(ff, AMDTP_IN_STREAM);
destroy_stream(ff, AMDTP_OUT_STREAM);
}
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
{
unsigned int curr_rate;
enum snd_ff_clock_src src;
int err;
if (ff->substreams_counter == 0)
return 0;
err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
if (err < 0)
return err;
if (curr_rate != rate ||
amdtp_streaming_error(&ff->tx_stream) ||
amdtp_streaming_error(&ff->rx_stream)) {
finish_session(ff);
amdtp_stream_stop(&ff->tx_stream);
amdtp_stream_stop(&ff->rx_stream);
release_resources(ff);
}
/*
* Regardless of current source of clock signal, drivers transfer some
* packets. Then, the device transfers packets.
*/
if (!amdtp_stream_running(&ff->rx_stream)) {
err = keep_resources(ff, rate);
if (err < 0)
goto error;
err = ff->spec->protocol->begin_session(ff, rate);
if (err < 0)
goto error;
err = amdtp_stream_start(&ff->rx_stream,
ff->rx_resources.channel,
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&ff->rx_stream,
CALLBACK_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
}
err = ff->spec->protocol->switch_fetching_mode(ff, true);
if (err < 0)
goto error;
}
if (!amdtp_stream_running(&ff->tx_stream)) {
err = amdtp_stream_start(&ff->tx_stream,
ff->tx_resources.channel,
fw_parent_device(ff->unit)->max_speed);
if (err < 0)
goto error;
if (!amdtp_stream_wait_callback(&ff->tx_stream,
CALLBACK_TIMEOUT_MS)) {
err = -ETIMEDOUT;
goto error;
}
}
return 0;
error:
amdtp_stream_stop(&ff->tx_stream);
amdtp_stream_stop(&ff->rx_stream);
finish_session(ff);
release_resources(ff);
return err;
}
void snd_ff_stream_stop_duplex(struct snd_ff *ff)
{
if (ff->substreams_counter > 0)
return;
amdtp_stream_stop(&ff->tx_stream);
amdtp_stream_stop(&ff->rx_stream);
finish_session(ff);
release_resources(ff);
}
void snd_ff_stream_update_duplex(struct snd_ff *ff)
{
/* The device discontinue to transfer packets. */
amdtp_stream_pcm_abort(&ff->tx_stream);
amdtp_stream_stop(&ff->tx_stream);
amdtp_stream_pcm_abort(&ff->rx_stream);
amdtp_stream_stop(&ff->rx_stream);
fw_iso_resources_update(&ff->tx_resources);
fw_iso_resources_update(&ff->rx_resources);
}
void snd_ff_stream_lock_changed(struct snd_ff *ff)
{
ff->dev_lock_changed = true;
wake_up(&ff->hwdep_wait);
}
int snd_ff_stream_lock_try(struct snd_ff *ff)
{
int err;
spin_lock_irq(&ff->lock);
/* user land lock this */
if (ff->dev_lock_count < 0) {
err = -EBUSY;
goto end;
}
/* this is the first time */
if (ff->dev_lock_count++ == 0)
snd_ff_stream_lock_changed(ff);
err = 0;
end:
spin_unlock_irq(&ff->lock);
return err;
}
void snd_ff_stream_lock_release(struct snd_ff *ff)
{
spin_lock_irq(&ff->lock);
if (WARN_ON(ff->dev_lock_count <= 0))
goto end;
if (--ff->dev_lock_count == 0)
snd_ff_stream_lock_changed(ff);
end:
spin_unlock_irq(&ff->lock);
}

View File

@ -0,0 +1,295 @@
/*
* ff-transaction.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port,
int rcode)
{
struct snd_rawmidi_substream *substream =
ACCESS_ONCE(ff->rx_midi_substreams[port]);
if (rcode_is_permanent_error(rcode)) {
ff->rx_midi_error[port] = true;
return;
}
if (rcode != RCODE_COMPLETE) {
/* Transfer the message again, immediately. */
ff->next_ktime[port] = 0;
schedule_work(&ff->rx_midi_work[port]);
return;
}
snd_rawmidi_transmit_ack(substream, ff->rx_bytes[port]);
ff->rx_bytes[port] = 0;
if (!snd_rawmidi_transmit_empty(substream))
schedule_work(&ff->rx_midi_work[port]);
}
static void finish_transmit_midi0_msg(struct fw_card *card, int rcode,
void *data, size_t length,
void *callback_data)
{
struct snd_ff *ff =
container_of(callback_data, struct snd_ff, transactions[0]);
finish_transmit_midi_msg(ff, 0, rcode);
}
static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,
void *data, size_t length,
void *callback_data)
{
struct snd_ff *ff =
container_of(callback_data, struct snd_ff, transactions[1]);
finish_transmit_midi_msg(ff, 1, rcode);
}
static inline void fill_midi_buf(struct snd_ff *ff, unsigned int port,
unsigned int index, u8 byte)
{
ff->msg_buf[port][index] = cpu_to_le32(byte);
}
static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
{
struct snd_rawmidi_substream *substream =
ACCESS_ONCE(ff->rx_midi_substreams[port]);
u8 *buf = (u8 *)ff->msg_buf[port];
int i, len;
struct fw_device *fw_dev = fw_parent_device(ff->unit);
unsigned long long addr;
int generation;
fw_transaction_callback_t callback;
if (substream == NULL || snd_rawmidi_transmit_empty(substream))
return;
if (ff->rx_bytes[port] > 0 || ff->rx_midi_error[port])
return;
/* Do it in next chance. */
if (ktime_after(ff->next_ktime[port], ktime_get())) {
schedule_work(&ff->rx_midi_work[port]);
return;
}
len = snd_rawmidi_transmit_peek(substream, buf,
SND_FF_MAXIMIM_MIDI_QUADS);
if (len <= 0)
return;
for (i = len - 1; i >= 0; i--)
fill_midi_buf(ff, port, i, buf[i]);
if (port == 0) {
addr = ff->spec->protocol->midi_rx_port_0_reg;
callback = finish_transmit_midi0_msg;
} else {
addr = ff->spec->protocol->midi_rx_port_1_reg;
callback = finish_transmit_midi1_msg;
}
/* Set interval to next transaction. */
ff->next_ktime[port] = ktime_add_ns(ktime_get(),
len * 8 * NSEC_PER_SEC / 31250);
ff->rx_bytes[port] = len;
/*
* In Linux FireWire core, when generation is updated with memory
* barrier, node id has already been updated. In this module, After
* this smp_rmb(), load/store instructions to memory are completed.
* Thus, both of generation and node id are available with recent
* values. This is a light-serialization solution to handle bus reset
* events on IEEE 1394 bus.
*/
generation = fw_dev->generation;
smp_rmb();
fw_send_request(fw_dev->card, &ff->transactions[port],
TCODE_WRITE_BLOCK_REQUEST,
fw_dev->node_id, generation, fw_dev->max_speed,
addr, &ff->msg_buf[port], len * 4,
callback, &ff->transactions[port]);
}
static void transmit_midi0_msg(struct work_struct *work)
{
struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[0]);
transmit_midi_msg(ff, 0);
}
static void transmit_midi1_msg(struct work_struct *work)
{
struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[1]);
transmit_midi_msg(ff, 1);
}
static void handle_midi_msg(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source,
int generation, unsigned long long offset,
void *data, size_t length, void *callback_data)
{
struct snd_ff *ff = callback_data;
__le32 *buf = data;
u32 quad;
u8 byte;
unsigned int index;
struct snd_rawmidi_substream *substream;
int i;
fw_send_response(card, request, RCODE_COMPLETE);
for (i = 0; i < length / 4; i++) {
quad = le32_to_cpu(buf[i]);
/* Message in first port. */
/*
* This value may represent the index of this unit when the same
* units are on the same IEEE 1394 bus. This driver doesn't use
* it.
*/
index = (quad >> 8) & 0xff;
if (index > 0) {
substream = ACCESS_ONCE(ff->tx_midi_substreams[0]);
if (substream != NULL) {
byte = quad & 0xff;
snd_rawmidi_receive(substream, &byte, 1);
}
}
/* Message in second port. */
index = (quad >> 24) & 0xff;
if (index > 0) {
substream = ACCESS_ONCE(ff->tx_midi_substreams[1]);
if (substream != NULL) {
byte = (quad >> 16) & 0xff;
snd_rawmidi_receive(substream, &byte, 1);
}
}
}
}
static int allocate_own_address(struct snd_ff *ff, int i)
{
struct fw_address_region midi_msg_region;
int err;
ff->async_handler.length = SND_FF_MAXIMIM_MIDI_QUADS * 4;
ff->async_handler.address_callback = handle_midi_msg;
ff->async_handler.callback_data = ff;
midi_msg_region.start = 0x000100000000ull * i;
midi_msg_region.end = midi_msg_region.start + ff->async_handler.length;
err = fw_core_add_address_handler(&ff->async_handler, &midi_msg_region);
if (err >= 0) {
/* Controllers are allowed to register this region. */
if (ff->async_handler.offset & 0x0000ffffffff) {
fw_core_remove_address_handler(&ff->async_handler);
err = -EAGAIN;
}
}
return err;
}
/*
* The configuration to start asynchronous transactions for MIDI messages is in
* 0x'0000'8010'051c. This register includes the other options, thus this driver
* doesn't touch it and leaves the decision to userspace. The userspace MUST add
* 0x04000000 to write transactions to the register to receive any MIDI
* messages.
*
* Here, I just describe MIDI-related offsets of the register, in little-endian
* order.
*
* Controllers are allowed to register higher 4 bytes of address to receive
* the transactions. The register is 0x'0000'8010'03f4. On the other hand, the
* controllers are not allowed to register lower 4 bytes of the address. They
* are forced to select from 4 options by writing corresponding bits to
* 0x'0000'8010'051c.
*
* The 3rd-6th bits in MSB of this register are used to indicate lower 4 bytes
* of address to which the device transferrs the transactions.
* - 6th: 0x'....'....'0000'0180
* - 5th: 0x'....'....'0000'0100
* - 4th: 0x'....'....'0000'0080
* - 3rd: 0x'....'....'0000'0000
*
* This driver configure 0x'....'....'0000'0000 for units to receive MIDI
* messages. 3rd bit of the register should be configured, however this driver
* deligates this task to user space applications due to a restriction that
* this register is write-only and the other bits have own effects.
*
* The 1st and 2nd bits in LSB of this register are used to cancel transferring
* asynchronous transactions. These two bits have the same effect.
* - 1st/2nd: cancel transferring
*/
int snd_ff_transaction_reregister(struct snd_ff *ff)
{
struct fw_card *fw_card = fw_parent_device(ff->unit)->card;
u32 addr;
__le32 reg;
/*
* Controllers are allowed to register its node ID and upper 2 byte of
* local address to listen asynchronous transactions.
*/
addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32);
reg = cpu_to_le32(addr);
return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
ff->spec->protocol->midi_high_addr_reg,
&reg, sizeof(reg), 0);
}
int snd_ff_transaction_register(struct snd_ff *ff)
{
int i, err;
/*
* Allocate in Memory Space of IEC 13213, but lower 4 byte in LSB should
* be zero due to device specification.
*/
for (i = 0; i < 0xffff; i++) {
err = allocate_own_address(ff, i);
if (err != -EBUSY && err != -EAGAIN)
break;
}
if (err < 0)
return err;
err = snd_ff_transaction_reregister(ff);
if (err < 0)
return err;
INIT_WORK(&ff->rx_midi_work[0], transmit_midi0_msg);
INIT_WORK(&ff->rx_midi_work[1], transmit_midi1_msg);
return 0;
}
void snd_ff_transaction_unregister(struct snd_ff *ff)
{
__le32 reg;
if (ff->async_handler.callback_data == NULL)
return;
ff->async_handler.callback_data = NULL;
/* Release higher 4 bytes of address. */
reg = cpu_to_le32(0x00000000);
snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
ff->spec->protocol->midi_high_addr_reg,
&reg, sizeof(reg), 0);
fw_core_remove_address_handler(&ff->async_handler);
}

View File

@ -0,0 +1,209 @@
/*
* ff.c - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "ff.h"
#define OUI_RME 0x000a35
MODULE_DESCRIPTION("RME Fireface series Driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
MODULE_LICENSE("GPL v2");
static void name_card(struct snd_ff *ff)
{
struct fw_device *fw_dev = fw_parent_device(ff->unit);
strcpy(ff->card->driver, "Fireface");
strcpy(ff->card->shortname, ff->spec->name);
strcpy(ff->card->mixername, ff->spec->name);
snprintf(ff->card->longname, sizeof(ff->card->longname),
"RME %s, GUID %08x%08x at %s, S%d", ff->spec->name,
fw_dev->config_rom[3], fw_dev->config_rom[4],
dev_name(&ff->unit->device), 100 << fw_dev->max_speed);
}
static void ff_free(struct snd_ff *ff)
{
snd_ff_stream_destroy_duplex(ff);
snd_ff_transaction_unregister(ff);
fw_unit_put(ff->unit);
mutex_destroy(&ff->mutex);
kfree(ff);
}
static void ff_card_free(struct snd_card *card)
{
ff_free(card->private_data);
}
static void do_registration(struct work_struct *work)
{
struct snd_ff *ff = container_of(work, struct snd_ff, dwork.work);
int err;
if (ff->registered)
return;
err = snd_card_new(&ff->unit->device, -1, NULL, THIS_MODULE, 0,
&ff->card);
if (err < 0)
return;
err = snd_ff_transaction_register(ff);
if (err < 0)
goto error;
name_card(ff);
err = snd_ff_stream_init_duplex(ff);
if (err < 0)
goto error;
snd_ff_proc_init(ff);
err = snd_ff_create_midi_devices(ff);
if (err < 0)
goto error;
err = snd_ff_create_pcm_devices(ff);
if (err < 0)
goto error;
err = snd_ff_create_hwdep_devices(ff);
if (err < 0)
goto error;
err = snd_card_register(ff->card);
if (err < 0)
goto error;
ff->card->private_free = ff_card_free;
ff->card->private_data = ff;
ff->registered = true;
return;
error:
snd_ff_transaction_unregister(ff);
snd_ff_stream_destroy_duplex(ff);
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 = kzalloc(sizeof(struct snd_ff), GFP_KERNEL);
if (ff == NULL)
return -ENOMEM;
/* initialize myself */
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->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;
}
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);
}
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) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(ff->card);
} else {
/* Don't forget this case. */
ff_free(ff);
}
}
static struct snd_ff_spec spec_ff400 = {
.name = "Fireface400",
.pcm_capture_channels = {18, 14, 10},
.pcm_playback_channels = {18, 14, 10},
.midi_in_ports = 2,
.midi_out_ports = 2,
.protocol = &snd_ff_protocol_ff400,
};
static const struct ieee1394_device_id snd_ff_id_table[] = {
/* Fireface 400 */
{
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_SPECIFIER_ID |
IEEE1394_MATCH_VERSION |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = OUI_RME,
.specifier_id = 0x000a35,
.version = 0x000002,
.model_id = 0x101800,
.driver_data = (kernel_ulong_t)&spec_ff400,
},
{}
};
MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
static struct fw_driver ff_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "snd-fireface",
.bus = &fw_bus_type,
},
.probe = snd_ff_probe,
.update = snd_ff_update,
.remove = snd_ff_remove,
.id_table = snd_ff_id_table,
};
static int __init snd_ff_init(void)
{
return driver_register(&ff_driver.driver);
}
static void __exit snd_ff_exit(void)
{
driver_unregister(&ff_driver.driver);
}
module_init(snd_ff_init);
module_exit(snd_ff_exit);

View File

@ -0,0 +1,146 @@
/*
* ff.h - a part of driver for RME Fireface series
*
* Copyright (c) 2015-2017 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_FIREFACE_H_INCLUDED
#define SOUND_FIREFACE_H_INCLUDED
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/info.h>
#include <sound/rawmidi.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/hwdep.h>
#include <sound/firewire.h>
#include "../lib.h"
#include "../amdtp-stream.h"
#include "../iso-resources.h"
#define SND_FF_STREAM_MODES 3
#define SND_FF_MAXIMIM_MIDI_QUADS 9
#define SND_FF_IN_MIDI_PORTS 2
#define SND_FF_OUT_MIDI_PORTS 2
struct snd_ff_protocol;
struct snd_ff_spec {
const char *const name;
const unsigned int pcm_capture_channels[SND_FF_STREAM_MODES];
const unsigned int pcm_playback_channels[SND_FF_STREAM_MODES];
unsigned int midi_in_ports;
unsigned int midi_out_ports;
struct snd_ff_protocol *protocol;
};
struct snd_ff {
struct snd_card *card;
struct fw_unit *unit;
struct mutex mutex;
spinlock_t lock;
bool registered;
struct delayed_work dwork;
const struct snd_ff_spec *spec;
/* To handle MIDI tx. */
struct snd_rawmidi_substream *tx_midi_substreams[SND_FF_IN_MIDI_PORTS];
struct fw_address_handler async_handler;
/* TO handle MIDI rx. */
struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS];
u8 running_status[SND_FF_OUT_MIDI_PORTS];
__le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS];
struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS];
struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS];
ktime_t next_ktime[SND_FF_OUT_MIDI_PORTS];
bool rx_midi_error[SND_FF_OUT_MIDI_PORTS];
unsigned int rx_bytes[SND_FF_OUT_MIDI_PORTS];
unsigned int substreams_counter;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct fw_iso_resources tx_resources;
struct fw_iso_resources rx_resources;
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
};
enum snd_ff_clock_src {
SND_FF_CLOCK_SRC_INTERNAL,
SND_FF_CLOCK_SRC_SPDIF,
SND_FF_CLOCK_SRC_ADAT,
SND_FF_CLOCK_SRC_WORD,
SND_FF_CLOCK_SRC_LTC,
/* TODO: perhaps ADAT2 and TCO exists. */
};
struct snd_ff_protocol {
int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
enum snd_ff_clock_src *src);
int (*begin_session)(struct snd_ff *ff, unsigned int rate);
void (*finish_session)(struct snd_ff *ff);
int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
void (*dump_sync_status)(struct snd_ff *ff,
struct snd_info_buffer *buffer);
void (*dump_clock_config)(struct snd_ff *ff,
struct snd_info_buffer *buffer);
u64 midi_high_addr_reg;
u64 midi_rx_port_0_reg;
u64 midi_rx_port_1_reg;
};
extern struct snd_ff_protocol snd_ff_protocol_ff400;
int snd_ff_transaction_register(struct snd_ff *ff);
int snd_ff_transaction_reregister(struct snd_ff *ff);
void snd_ff_transaction_unregister(struct snd_ff *ff);
int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int pcm_channels);
int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir);
int snd_ff_stream_init_duplex(struct snd_ff *ff);
void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
void snd_ff_stream_stop_duplex(struct snd_ff *ff);
void snd_ff_stream_update_duplex(struct snd_ff *ff);
void snd_ff_stream_lock_changed(struct snd_ff *ff);
int snd_ff_stream_lock_try(struct snd_ff *ff);
void snd_ff_stream_lock_release(struct snd_ff *ff);
void snd_ff_proc_init(struct snd_ff *ff);
int snd_ff_create_midi_devices(struct snd_ff *ff);
int snd_ff_create_pcm_devices(struct snd_ff *ff);
int snd_ff_create_hwdep_devices(struct snd_ff *ff);
#endif

View File

@ -99,147 +99,6 @@ void snd_fw_schedule_registration(struct fw_unit *unit,
}
EXPORT_SYMBOL(snd_fw_schedule_registration);
static void async_midi_port_callback(struct fw_card *card, int rcode,
void *data, size_t length,
void *callback_data)
{
struct snd_fw_async_midi_port *port = callback_data;
struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
/* This port is closed. */
if (substream == NULL)
return;
if (rcode == RCODE_COMPLETE)
snd_rawmidi_transmit_ack(substream, port->consume_bytes);
else if (!rcode_is_permanent_error(rcode))
/* To start next transaction immediately for recovery. */
port->next_ktime = 0;
else
/* Don't continue processing. */
port->error = true;
port->idling = true;
if (!snd_rawmidi_transmit_empty(substream))
schedule_work(&port->work);
}
static void midi_port_work(struct work_struct *work)
{
struct snd_fw_async_midi_port *port =
container_of(work, struct snd_fw_async_midi_port, work);
struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
int generation;
int type;
/* Under transacting or error state. */
if (!port->idling || port->error)
return;
/* Nothing to do. */
if (substream == NULL || snd_rawmidi_transmit_empty(substream))
return;
/* Do it in next chance. */
if (ktime_after(port->next_ktime, ktime_get())) {
schedule_work(&port->work);
return;
}
/*
* Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
* Later, snd_rawmidi_transmit_ack() is called.
*/
memset(port->buf, 0, port->len);
port->consume_bytes = port->fill(substream, port->buf);
if (port->consume_bytes <= 0) {
/* Do it in next chance, immediately. */
if (port->consume_bytes == 0) {
port->next_ktime = 0;
schedule_work(&port->work);
} else {
/* Fatal error. */
port->error = true;
}
return;
}
/* Calculate type of transaction. */
if (port->len == 4)
type = TCODE_WRITE_QUADLET_REQUEST;
else
type = TCODE_WRITE_BLOCK_REQUEST;
/* Set interval to next transaction. */
port->next_ktime = ktime_add_ns(ktime_get(),
port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
/* Start this transaction. */
port->idling = false;
/*
* In Linux FireWire core, when generation is updated with memory
* barrier, node id has already been updated. In this module, After
* this smp_rmb(), load/store instructions to memory are completed.
* Thus, both of generation and node id are available with recent
* values. This is a light-serialization solution to handle bus reset
* events on IEEE 1394 bus.
*/
generation = port->parent->generation;
smp_rmb();
fw_send_request(port->parent->card, &port->transaction, type,
port->parent->node_id, generation,
port->parent->max_speed, port->addr,
port->buf, port->len, async_midi_port_callback,
port);
}
/**
* snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
* @port: the asynchronous MIDI port to initialize
* @unit: the target of the asynchronous transaction
* @addr: the address to which transactions are transferred
* @len: the length of transaction
* @fill: the callback function to fill given buffer, and returns the
* number of consumed bytes for MIDI message.
*
*/
int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
struct fw_unit *unit, u64 addr, unsigned int len,
snd_fw_async_midi_port_fill fill)
{
port->len = DIV_ROUND_UP(len, 4) * 4;
port->buf = kzalloc(port->len, GFP_KERNEL);
if (port->buf == NULL)
return -ENOMEM;
port->parent = fw_parent_device(unit);
port->addr = addr;
port->fill = fill;
port->idling = true;
port->next_ktime = 0;
port->error = false;
INIT_WORK(&port->work, midi_port_work);
return 0;
}
EXPORT_SYMBOL(snd_fw_async_midi_port_init);
/**
* snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
* @port: the asynchronous MIDI port structure
*/
void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
{
snd_fw_async_midi_port_finish(port);
cancel_work_sync(&port->work);
kfree(port->buf);
}
EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
MODULE_DESCRIPTION("FireWire audio helper functions");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");

View File

@ -25,58 +25,4 @@ static inline bool rcode_is_permanent_error(int rcode)
void snd_fw_schedule_registration(struct fw_unit *unit,
struct delayed_work *dwork);
struct snd_fw_async_midi_port;
typedef int (*snd_fw_async_midi_port_fill)(
struct snd_rawmidi_substream *substream,
u8 *buf);
struct snd_fw_async_midi_port {
struct fw_device *parent;
struct work_struct work;
bool idling;
ktime_t next_ktime;
bool error;
u64 addr;
struct fw_transaction transaction;
u8 *buf;
unsigned int len;
struct snd_rawmidi_substream *substream;
snd_fw_async_midi_port_fill fill;
int consume_bytes;
};
int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
struct fw_unit *unit, u64 addr, unsigned int len,
snd_fw_async_midi_port_fill fill);
void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
/**
* snd_fw_async_midi_port_run - run transactions for the async MIDI port
* @port: the asynchronous MIDI port
* @substream: the MIDI substream
*/
static inline void
snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
struct snd_rawmidi_substream *substream)
{
if (!port->error) {
port->substream = substream;
schedule_work(&port->work);
}
}
/**
* snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
* @port: the asynchronous MIDI port
*/
static inline void
snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
{
port->substream = NULL;
port->error = false;
}
#endif

View File

@ -0,0 +1,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
obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o

View File

@ -0,0 +1,123 @@
/*
* amdtp-motu-trace.h - tracepoint definitions to dump a part of packet data
*
* Copyright (c) 2017 Takashi Sakamoto
* Licensed under the terms of the GNU General Public License, version 2.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM snd_firewire_motu
#if !defined(_SND_FIREWIRE_MOTU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _SND_FIREWIRE_MOTU_TRACE_H
#include <linux/tracepoint.h>
static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks,
unsigned int data_block_quadlets);
static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks,
unsigned int data_block_quadlets);
TRACE_EVENT(in_data_block_sph,
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
TP_ARGS(s, data_blocks, buffer),
TP_STRUCT__entry(
__field(int, src)
__field(int, dst)
__field(unsigned int, data_blocks)
__dynamic_array(u32, tstamps, data_blocks)
),
TP_fast_assign(
__entry->src = fw_parent_device(s->unit)->node_id;
__entry->dst = fw_parent_device(s->unit)->card->node_id;
__entry->data_blocks = data_blocks;
copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
),
TP_printk(
"%04x %04x %u %s",
__entry->src,
__entry->dst,
__entry->data_blocks,
__print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
)
);
TRACE_EVENT(out_data_block_sph,
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
TP_ARGS(s, data_blocks, buffer),
TP_STRUCT__entry(
__field(int, src)
__field(int, dst)
__field(unsigned int, data_blocks)
__dynamic_array(u32, tstamps, data_blocks)
),
TP_fast_assign(
__entry->src = fw_parent_device(s->unit)->card->node_id;
__entry->dst = fw_parent_device(s->unit)->node_id;
__entry->data_blocks = data_blocks;
copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
),
TP_printk(
"%04x %04x %u %s",
__entry->src,
__entry->dst,
__entry->data_blocks,
__print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
)
);
TRACE_EVENT(in_data_block_message,
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
TP_ARGS(s, data_blocks, buffer),
TP_STRUCT__entry(
__field(int, src)
__field(int, dst)
__field(unsigned int, data_blocks)
__dynamic_array(u64, messages, data_blocks)
),
TP_fast_assign(
__entry->src = fw_parent_device(s->unit)->node_id;
__entry->dst = fw_parent_device(s->unit)->card->node_id;
__entry->data_blocks = data_blocks;
copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
),
TP_printk(
"%04x %04x %u %s",
__entry->src,
__entry->dst,
__entry->data_blocks,
__print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
)
);
TRACE_EVENT(out_data_block_message,
TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
TP_ARGS(s, data_blocks, buffer),
TP_STRUCT__entry(
__field(int, src)
__field(int, dst)
__field(unsigned int, data_blocks)
__dynamic_array(u64, messages, data_blocks)
),
TP_fast_assign(
__entry->src = fw_parent_device(s->unit)->card->node_id;
__entry->dst = fw_parent_device(s->unit)->node_id;
__entry->data_blocks = data_blocks;
copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
),
TP_printk(
"%04x %04x %u %s",
__entry->src,
__entry->dst,
__entry->data_blocks,
__print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
)
);
#endif
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE amdtp-motu-trace
#include <trace/define_trace.h>

View File

@ -0,0 +1,427 @@
/*
* amdtp-motu.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/slab.h>
#include <sound/pcm.h>
#include "motu.h"
#define CREATE_TRACE_POINTS
#include "amdtp-motu-trace.h"
#define CIP_FMT_MOTU 0x02
#define CIP_FMT_MOTU_TX_V3 0x22
#define MOTU_FDF_AM824 0x22
/*
* Nominally 3125 bytes/second, but the MIDI port's clock might be
* 1% too slow, and the bus clock 100 ppm too fast.
*/
#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;
struct snd_rawmidi_substream *midi;
unsigned int midi_ports;
unsigned int midi_flag_offset;
unsigned int midi_byte_offset;
int midi_db_count;
unsigned int midi_db_interval;
};
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;
if (amdtp_stream_running(s))
return -EBUSY;
for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
if (snd_motu_clock_rates[i] == rate) {
mode = i >> 1;
break;
}
}
if (i == ARRAY_SIZE(snd_motu_clock_rates))
return -EINVAL;
pcm_chunks = formats->fixed_part_pcm_chunks[mode] +
formats->differed_part_pcm_chunks[mode];
data_chunks = formats->msg_chunks + pcm_chunks;
/*
* Each data block includes SPH in its head. Data chunks follow with
* 3 byte alignment. Padding follows with zero to conform to quadlet
* alignment.
*/
data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
if (err < 0)
return err;
p->pcm_chunks = pcm_chunks;
p->pcm_byte_offset = formats->pcm_byte_offset;
p->midi_ports = midi_ports;
p->midi_flag_offset = formats->midi_flag_offset;
p->midi_byte_offset = formats->midi_byte_offset;
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;
}
static void read_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime,
__be32 *buffer, unsigned int data_blocks)
{
struct amdtp_motu *p = s->protocol;
unsigned int channels, remaining_frames, i, c;
u8 *byte;
u32 *dst;
channels = p->pcm_chunks;
dst = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset;
for (c = 0; c < channels; ++c) {
*dst = (byte[0] << 24) | (byte[1] << 16) | byte[2];
byte += 3;
dst++;
}
buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
dst = (void *)runtime->dma_area;
}
}
static void write_pcm_s32(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime,
__be32 *buffer, unsigned int data_blocks)
{
struct amdtp_motu *p = s->protocol;
unsigned int channels, remaining_frames, i, c;
u8 *byte;
const u32 *src;
channels = p->pcm_chunks;
src = (void *)runtime->dma_area +
frames_to_bytes(runtime, s->pcm_buffer_pointer);
remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset;
for (c = 0; c < channels; ++c) {
byte[0] = (*src >> 24) & 0xff;
byte[1] = (*src >> 16) & 0xff;
byte[2] = (*src >> 8) & 0xff;
byte += 3;
src++;
}
buffer += s->data_block_quadlets;
if (--remaining_frames == 0)
src = (void *)runtime->dma_area;
}
}
static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks)
{
struct amdtp_motu *p = s->protocol;
unsigned int channels, i, c;
u8 *byte;
channels = p->pcm_chunks;
for (i = 0; i < data_blocks; ++i) {
byte = (u8 *)buffer + p->pcm_byte_offset;
for (c = 0; c < channels; ++c) {
byte[0] = 0;
byte[1] = 0;
byte[2] = 0;
byte += 3;
}
buffer += s->data_block_quadlets;
}
}
int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime)
{
int err;
/* TODO: how to set an constraint for exactly 24bit PCM sample? */
err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if (err < 0)
return err;
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
struct snd_rawmidi_substream *midi)
{
struct amdtp_motu *p = s->protocol;
if (port < p->midi_ports)
WRITE_ONCE(p->midi, midi);
}
static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks)
{
struct amdtp_motu *p = s->protocol;
struct snd_rawmidi_substream *midi = READ_ONCE(p->midi);
u8 *b;
int i;
for (i = 0; i < data_blocks; i++) {
b = (u8 *)buffer;
if (midi && p->midi_db_count == 0 &&
snd_rawmidi_transmit(midi, b + p->midi_byte_offset, 1) == 1) {
b[p->midi_flag_offset] = 0x01;
} else {
b[p->midi_byte_offset] = 0x00;
b[p->midi_flag_offset] = 0x00;
}
buffer += s->data_block_quadlets;
if (--p->midi_db_count < 0)
p->midi_db_count = p->midi_db_interval;
}
}
static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
unsigned int data_blocks)
{
struct amdtp_motu *p = s->protocol;
struct snd_rawmidi_substream *midi;
u8 *b;
int i;
for (i = 0; i < data_blocks; i++) {
b = (u8 *)buffer;
midi = READ_ONCE(p->midi);
if (midi && (b[p->midi_flag_offset] & 0x01))
snd_rawmidi_receive(midi, b + p->midi_byte_offset, 1);
buffer += s->data_block_quadlets;
}
}
/* For tracepoints. */
static void __maybe_unused copy_sph(u32 *frames, __be32 *buffer,
unsigned int data_blocks,
unsigned int data_block_quadlets)
{
unsigned int i;
for (i = 0; i < data_blocks; ++i) {
*frames = be32_to_cpu(*buffer);
buffer += data_block_quadlets;
frames++;
}
}
/* For tracepoints. */
static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
unsigned int data_blocks,
unsigned int data_block_quadlets)
{
unsigned int i;
/* This is just for v2/v3 protocol. */
for (i = 0; i < data_blocks; ++i) {
*frames = (be32_to_cpu(buffer[1]) << 16) |
(be32_to_cpu(buffer[2]) >> 16);
buffer += data_block_quadlets;
frames++;
}
}
static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
{
struct amdtp_motu *p = s->protocol;
struct snd_pcm_substream *pcm;
trace_in_data_block_sph(s, data_blocks, buffer);
trace_in_data_block_message(s, data_blocks, buffer);
if (p->midi_ports)
read_midi_messages(s, buffer, data_blocks);
pcm = ACCESS_ONCE(s->pcm);
if (data_blocks > 0 && pcm)
read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
return data_blocks;
}
static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
{
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;
for (i = 0; i < data_blocks; i++) {
next_cycles = (s->start_cycle + p->next_cycles) % 8000;
sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff;
*buffer = cpu_to_be32(sph);
compute_next_elapse_from_start(p);
buffer += s->data_block_quadlets;
}
}
static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
__be32 *buffer, unsigned int data_blocks,
unsigned int *syt)
{
struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
struct snd_pcm_substream *pcm;
/* Not used. */
*syt = 0xffff;
/* TODO: how to interact control messages between userspace? */
if (p->midi_ports)
write_midi_messages(s, buffer, data_blocks);
pcm = ACCESS_ONCE(s->pcm);
if (pcm)
write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
else
write_pcm_silence(s, buffer, data_blocks);
write_sph(s, buffer, data_blocks);
trace_out_data_block_sph(s, data_blocks, buffer);
trace_out_data_block_message(s, data_blocks, buffer);
return data_blocks;
}
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
const struct snd_motu_protocol *const protocol)
{
amdtp_stream_process_data_blocks_t process_data_blocks;
int fmt = CIP_FMT_MOTU;
int flags = CIP_BLOCKING;
int err;
if (dir == AMDTP_IN_STREAM) {
process_data_blocks = process_tx_data_blocks;
/*
* Units of version 3 transmits packets with invalid CIP header
* against IEC 61883-1.
*/
if (protocol == &snd_motu_protocol_v3) {
flags |= CIP_WRONG_DBS |
CIP_SKIP_DBC_ZERO_CHECK |
CIP_HEADER_WITHOUT_EOH;
fmt = CIP_FMT_MOTU_TX_V3;
}
} else {
process_data_blocks = process_rx_data_blocks;
flags |= CIP_DBC_IS_END_EVENT;
}
err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
sizeof(struct amdtp_motu));
if (err < 0)
return err;
s->sph = 1;
s->fdf = MOTU_FDF_AM824;
return 0;
}

View File

@ -0,0 +1,198 @@
/*
* motu-hwdep.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
/*
* This codes have five functionalities.
*
* 1.get information about firewire node
* 2.get notification about starting/stopping stream
* 3.lock/unlock streaming
*
*/
#include "motu.h"
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
struct snd_motu *motu = hwdep->private_data;
DEFINE_WAIT(wait);
union snd_firewire_event event;
spin_lock_irq(&motu->lock);
while (!motu->dev_lock_changed && motu->msg == 0) {
prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&motu->lock);
schedule();
finish_wait(&motu->hwdep_wait, &wait);
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&motu->lock);
}
memset(&event, 0, sizeof(event));
if (motu->dev_lock_changed) {
event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (motu->dev_lock_count > 0);
motu->dev_lock_changed = false;
count = min_t(long, count, sizeof(event.lock_status));
} else {
event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
event.motu_notification.message = motu->msg;
motu->msg = 0;
count = min_t(long, count, sizeof(event.motu_notification));
}
spin_unlock_irq(&motu->lock);
if (copy_to_user(buf, &event, count))
return -EFAULT;
return count;
}
static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_table *wait)
{
struct snd_motu *motu = hwdep->private_data;
unsigned int events;
poll_wait(file, &motu->hwdep_wait, wait);
spin_lock_irq(&motu->lock);
if (motu->dev_lock_changed || motu->msg)
events = POLLIN | POLLRDNORM;
else
events = 0;
spin_unlock_irq(&motu->lock);
return events | POLLOUT;
}
static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
{
struct fw_device *dev = fw_parent_device(motu->unit);
struct snd_firewire_get_info info;
memset(&info, 0, sizeof(info));
info.type = SNDRV_FIREWIRE_TYPE_MOTU;
info.card = dev->card->index;
*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
strlcpy(info.device_name, dev_name(&dev->device),
sizeof(info.device_name));
if (copy_to_user(arg, &info, sizeof(info)))
return -EFAULT;
return 0;
}
static int hwdep_lock(struct snd_motu *motu)
{
int err;
spin_lock_irq(&motu->lock);
if (motu->dev_lock_count == 0) {
motu->dev_lock_count = -1;
err = 0;
} else {
err = -EBUSY;
}
spin_unlock_irq(&motu->lock);
return err;
}
static int hwdep_unlock(struct snd_motu *motu)
{
int err;
spin_lock_irq(&motu->lock);
if (motu->dev_lock_count == -1) {
motu->dev_lock_count = 0;
err = 0;
} else {
err = -EBADFD;
}
spin_unlock_irq(&motu->lock);
return err;
}
static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
{
struct snd_motu *motu = hwdep->private_data;
spin_lock_irq(&motu->lock);
if (motu->dev_lock_count == -1)
motu->dev_lock_count = 0;
spin_unlock_irq(&motu->lock);
return 0;
}
static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct snd_motu *motu = hwdep->private_data;
switch (cmd) {
case SNDRV_FIREWIRE_IOCTL_GET_INFO:
return hwdep_get_info(motu, (void __user *)arg);
case SNDRV_FIREWIRE_IOCTL_LOCK:
return hwdep_lock(motu);
case SNDRV_FIREWIRE_IOCTL_UNLOCK:
return hwdep_unlock(motu);
default:
return -ENOIOCTLCMD;
}
}
#ifdef CONFIG_COMPAT
static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
unsigned int cmd, unsigned long arg)
{
return hwdep_ioctl(hwdep, file, cmd,
(unsigned long)compat_ptr(arg));
}
#else
#define hwdep_compat_ioctl NULL
#endif
int snd_motu_create_hwdep_device(struct snd_motu *motu)
{
static const struct snd_hwdep_ops ops = {
.read = hwdep_read,
.release = hwdep_release,
.poll = hwdep_poll,
.ioctl = hwdep_ioctl,
.ioctl_compat = hwdep_compat_ioctl,
};
struct snd_hwdep *hwdep;
int err;
err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep);
if (err < 0)
return err;
strcpy(hwdep->name, "MOTU");
hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU;
hwdep->ops = ops;
hwdep->private_data = motu;
hwdep->exclusive = true;
return 0;
}

View File

@ -0,0 +1,169 @@
/*
* motu-midi.h - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
static int midi_capture_open(struct snd_rawmidi_substream *substream)
{
struct snd_motu *motu = substream->rmidi->private_data;
int err;
err = snd_motu_stream_lock_try(motu);
if (err < 0)
return err;
mutex_lock(&motu->mutex);
motu->capture_substreams++;
err = snd_motu_stream_start_duplex(motu, 0);
mutex_unlock(&motu->mutex);
if (err < 0)
snd_motu_stream_lock_release(motu);
return err;
}
static int midi_playback_open(struct snd_rawmidi_substream *substream)
{
struct snd_motu *motu = substream->rmidi->private_data;
int err;
err = snd_motu_stream_lock_try(motu);
if (err < 0)
return err;
mutex_lock(&motu->mutex);
motu->playback_substreams++;
err = snd_motu_stream_start_duplex(motu, 0);
mutex_unlock(&motu->mutex);
if (err < 0)
snd_motu_stream_lock_release(motu);
return err;
}
static int midi_capture_close(struct snd_rawmidi_substream *substream)
{
struct snd_motu *motu = substream->rmidi->private_data;
mutex_lock(&motu->mutex);
motu->capture_substreams--;
snd_motu_stream_stop_duplex(motu);
mutex_unlock(&motu->mutex);
snd_motu_stream_lock_release(motu);
return 0;
}
static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
struct snd_motu *motu = substream->rmidi->private_data;
mutex_lock(&motu->mutex);
motu->playback_substreams--;
snd_motu_stream_stop_duplex(motu);
mutex_unlock(&motu->mutex);
snd_motu_stream_lock_release(motu);
return 0;
}
static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
{
struct snd_motu *motu = substrm->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&motu->lock, flags);
if (up)
amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
substrm);
else
amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
NULL);
spin_unlock_irqrestore(&motu->lock, flags);
}
static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
{
struct snd_motu *motu = substrm->rmidi->private_data;
unsigned long flags;
spin_lock_irqsave(&motu->lock, flags);
if (up)
amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
substrm);
else
amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
NULL);
spin_unlock_irqrestore(&motu->lock, flags);
}
static void set_midi_substream_names(struct snd_motu *motu,
struct snd_rawmidi_str *str)
{
struct snd_rawmidi_substream *subs;
list_for_each_entry(subs, &str->substreams, list) {
snprintf(subs->name, sizeof(subs->name),
"%s MIDI %d", motu->card->shortname, subs->number + 1);
}
}
int snd_motu_create_midi_devices(struct snd_motu *motu)
{
static struct snd_rawmidi_ops capture_ops = {
.open = midi_capture_open,
.close = midi_capture_close,
.trigger = midi_capture_trigger,
};
static struct snd_rawmidi_ops playback_ops = {
.open = midi_playback_open,
.close = midi_playback_close,
.trigger = midi_playback_trigger,
};
struct snd_rawmidi *rmidi;
struct snd_rawmidi_str *str;
int err;
/* create midi ports */
err = snd_rawmidi_new(motu->card, motu->card->driver, 0, 1, 1, &rmidi);
if (err < 0)
return err;
snprintf(rmidi->name, sizeof(rmidi->name),
"%s MIDI", motu->card->shortname);
rmidi->private_data = motu;
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
SNDRV_RAWMIDI_INFO_OUTPUT |
SNDRV_RAWMIDI_INFO_DUPLEX;
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
&capture_ops);
str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
set_midi_substream_names(motu, str);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
&playback_ops);
str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
set_midi_substream_names(motu, str);
return 0;
}

View File

@ -0,0 +1,398 @@
/*
* motu-pcm.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <sound/pcm_params.h>
#include "motu.h"
static int motu_rate_constraint(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_motu_packet_format *formats = rule->private;
const struct snd_interval *c =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_interval *r =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval rates = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, pcm_channels, rate, mode;
for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
rate = snd_motu_clock_rates[i];
mode = i / 2;
pcm_channels = formats->fixed_part_pcm_chunks[mode] +
formats->differed_part_pcm_chunks[mode];
if (!snd_interval_test(c, pcm_channels))
continue;
rates.min = min(rates.min, rate);
rates.max = max(rates.max, rate);
}
return snd_interval_refine(r, &rates);
}
static int motu_channels_constraint(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
struct snd_motu_packet_format *formats = rule->private;
const struct snd_interval *r =
hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *c =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_interval channels = {
.min = UINT_MAX, .max = 0, .integer = 1
};
unsigned int i, pcm_channels, rate, mode;
for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
rate = snd_motu_clock_rates[i];
mode = i / 2;
if (!snd_interval_test(r, rate))
continue;
pcm_channels = formats->fixed_part_pcm_chunks[mode] +
formats->differed_part_pcm_chunks[mode];
channels.min = min(channels.min, pcm_channels);
channels.max = max(channels.max, pcm_channels);
}
return snd_interval_refine(c, &channels);
}
static void limit_channels_and_rates(struct snd_motu *motu,
struct snd_pcm_runtime *runtime,
struct snd_motu_packet_format *formats)
{
struct snd_pcm_hardware *hw = &runtime->hw;
unsigned int i, pcm_channels, rate, mode;
hw->channels_min = UINT_MAX;
hw->channels_max = 0;
for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
rate = snd_motu_clock_rates[i];
mode = i / 2;
pcm_channels = formats->fixed_part_pcm_chunks[mode] +
formats->differed_part_pcm_chunks[mode];
if (pcm_channels == 0)
continue;
hw->rates |= snd_pcm_rate_to_rate_bit(rate);
hw->channels_min = min(hw->channels_min, pcm_channels);
hw->channels_max = max(hw->channels_max, pcm_channels);
}
snd_pcm_limit_hw_rates(runtime);
}
static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
{
hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
hw->periods_max = UINT_MAX;
hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */
/* Just to prevent from allocating much pages. */
hw->period_bytes_max = hw->period_bytes_min * 2048;
hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
}
static int init_hw_info(struct snd_motu *motu,
struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
struct amdtp_stream *stream;
struct snd_motu_packet_format *formats;
int err;
hw->info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_JOINT_DUPLEX |
SNDRV_PCM_INFO_BLOCK_TRANSFER;
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
hw->formats = SNDRV_PCM_FMTBIT_S32;
stream = &motu->tx_stream;
formats = &motu->tx_packet_formats;
} else {
hw->formats = SNDRV_PCM_FMTBIT_S32;
stream = &motu->rx_stream;
formats = &motu->rx_packet_formats;
}
limit_channels_and_rates(motu, runtime, formats);
limit_period_and_buffer(hw);
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
motu_rate_constraint, formats,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
return err;
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
motu_channels_constraint, formats,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
return err;
return amdtp_motu_add_pcm_hw_constraints(stream, runtime);
}
static int pcm_open(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
const struct snd_motu_protocol *const protocol = motu->spec->protocol;
enum snd_motu_clock_source src;
unsigned int rate;
int err;
err = snd_motu_stream_lock_try(motu);
if (err < 0)
return err;
mutex_lock(&motu->mutex);
err = protocol->cache_packet_formats(motu);
if (err < 0)
goto err_locked;
err = init_hw_info(motu, substream);
if (err < 0)
goto err_locked;
/*
* When source of clock is not internal or any PCM streams are running,
* available sampling rate is limited at current sampling rate.
*/
err = protocol->get_clock_source(motu, &src);
if (err < 0)
goto err_locked;
if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL ||
amdtp_stream_pcm_running(&motu->tx_stream) ||
amdtp_stream_pcm_running(&motu->rx_stream)) {
err = protocol->get_clock_rate(motu, &rate);
if (err < 0)
goto err_locked;
substream->runtime->hw.rate_min = rate;
substream->runtime->hw.rate_max = rate;
}
snd_pcm_set_sync(substream);
mutex_unlock(&motu->mutex);
return err;
err_locked:
mutex_unlock(&motu->mutex);
snd_motu_stream_lock_release(motu);
return err;
}
static int pcm_close(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
snd_motu_stream_lock_release(motu);
return 0;
}
static int capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_motu *motu = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&motu->mutex);
motu->capture_substreams++;
mutex_unlock(&motu->mutex);
}
return 0;
}
static int playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_motu *motu = substream->private_data;
int err;
err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (err < 0)
return err;
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
mutex_lock(&motu->mutex);
motu->playback_substreams++;
mutex_unlock(&motu->mutex);
}
return 0;
}
static int capture_hw_free(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
mutex_lock(&motu->mutex);
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
motu->capture_substreams--;
snd_motu_stream_stop_duplex(motu);
mutex_unlock(&motu->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
mutex_lock(&motu->mutex);
if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
motu->playback_substreams--;
snd_motu_stream_stop_duplex(motu);
mutex_unlock(&motu->mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
int err;
mutex_lock(&motu->mutex);
err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
mutex_unlock(&motu->mutex);
if (err >= 0)
amdtp_stream_pcm_prepare(&motu->tx_stream);
return 0;
}
static int playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
int err;
mutex_lock(&motu->mutex);
err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
mutex_unlock(&motu->mutex);
if (err >= 0)
amdtp_stream_pcm_prepare(&motu->rx_stream);
return err;
}
static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_motu *motu = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&motu->tx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_motu *motu = substream->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
amdtp_stream_pcm_trigger(&motu->rx_stream, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);
break;
default:
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
return amdtp_stream_pcm_pointer(&motu->tx_stream);
}
static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_motu *motu = substream->private_data;
return amdtp_stream_pcm_pointer(&motu->rx_stream);
}
int snd_motu_create_pcm_devices(struct snd_motu *motu)
{
static struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = capture_hw_params,
.hw_free = capture_hw_free,
.prepare = capture_prepare,
.trigger = capture_trigger,
.pointer = capture_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
static struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = playback_hw_params,
.hw_free = playback_hw_free,
.prepare = playback_prepare,
.trigger = playback_trigger,
.pointer = playback_pointer,
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
struct snd_pcm *pcm;
int err;
err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = motu;
strcpy(pcm->name, motu->card->shortname);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
return 0;
}

View File

@ -0,0 +1,118 @@
/*
* motu-proc.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "./motu.h"
static const char *const clock_names[] = {
[SND_MOTU_CLOCK_SOURCE_INTERNAL] = "Internal",
[SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB] = "ADAT on Dsub-9pin interface",
[SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT] = "ADAT on optical interface",
[SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A] = "ADAT on optical interface A",
[SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B] = "ADAT on optical interface B",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
[SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PCIF on coaxial interface",
[SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
[SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
};
static void proc_read_clock(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_motu *motu = entry->private_data;
const struct snd_motu_protocol *const protocol = motu->spec->protocol;
unsigned int rate;
enum snd_motu_clock_source source;
if (protocol->get_clock_rate(motu, &rate) < 0)
return;
if (protocol->get_clock_source(motu, &source) < 0)
return;
snd_iprintf(buffer, "Rate:\t%d\n", rate);
snd_iprintf(buffer, "Source:\t%s\n", clock_names[source]);
}
static void proc_read_format(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_motu *motu = entry->private_data;
const struct snd_motu_protocol *const protocol = motu->spec->protocol;
unsigned int mode;
struct snd_motu_packet_format *formats;
int i;
if (protocol->cache_packet_formats(motu) < 0)
return;
snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n");
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
mode = i >> 1;
formats = &motu->tx_packet_formats;
snd_iprintf(buffer,
"%u:\t%u\t%u\t%u\n",
snd_motu_clock_rates[i],
formats->msg_chunks,
formats->fixed_part_pcm_chunks[mode],
formats->differed_part_pcm_chunks[mode]);
}
snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n");
for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
mode = i >> 1;
formats = &motu->rx_packet_formats;
snd_iprintf(buffer,
"%u:\t%u\t%u\t%u\n",
snd_motu_clock_rates[i],
formats->msg_chunks,
formats->fixed_part_pcm_chunks[mode],
formats->differed_part_pcm_chunks[mode]);
}
}
static void add_node(struct snd_motu *motu, struct snd_info_entry *root,
const char *name,
void (*op)(struct snd_info_entry *e,
struct snd_info_buffer *b))
{
struct snd_info_entry *entry;
entry = snd_info_create_card_entry(motu->card, name, root);
if (entry == NULL)
return;
snd_info_set_text_ops(entry, motu, op);
if (snd_info_register(entry) < 0)
snd_info_free_entry(entry);
}
void snd_motu_proc_init(struct snd_motu *motu)
{
struct snd_info_entry *root;
/*
* All nodes are automatically removed at snd_card_disconnect(),
* by following to link list.
*/
root = snd_info_create_card_entry(motu->card, "firewire",
motu->card->proc_root);
if (root == NULL)
return;
root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
if (snd_info_register(root) < 0) {
snd_info_free_entry(root);
return;
}
add_node(motu, root, "clock", proc_read_clock);
add_node(motu, root, "format", proc_read_format);
}

View File

@ -0,0 +1,237 @@
/*
* motu-protocol-v2.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
#define V2_CLOCK_STATUS_OFFSET 0x0b14
#define V2_CLOCK_RATE_MASK 0x00000038
#define V2_CLOCK_RATE_SHIFT 3
#define V2_CLOCK_SRC_MASK 0x00000007
#define V2_CLOCK_SRC_SHIFT 0
#define V2_IN_OUT_CONF_OFFSET 0x0c04
#define V2_OPT_OUT_IFACE_MASK 0x00000c00
#define V2_OPT_OUT_IFACE_SHIFT 10
#define V2_OPT_IN_IFACE_MASK 0x00000300
#define V2_OPT_IN_IFACE_SHIFT 8
#define V2_OPT_IFACE_MODE_NONE 0
#define V2_OPT_IFACE_MODE_ADAT 1
#define V2_OPT_IFACE_MODE_SPDIF 2
static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
{
__be32 reg;
unsigned int index;
int err;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
index = (be32_to_cpu(reg) & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
if (index >= ARRAY_SIZE(snd_motu_clock_rates))
return -EIO;
*rate = snd_motu_clock_rates[index];
return 0;
}
static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
{
__be32 reg;
u32 data;
int i;
int err;
for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
if (snd_motu_clock_rates[i] == rate)
break;
}
if (i == ARRAY_SIZE(snd_motu_clock_rates))
return -EINVAL;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
data &= ~V2_CLOCK_RATE_MASK;
data |= i << V2_CLOCK_RATE_SHIFT;
reg = cpu_to_be32(data);
return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
}
static int v2_get_clock_source(struct snd_motu *motu,
enum snd_motu_clock_source *src)
{
__be32 reg;
unsigned int index;
int err;
err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
index = be32_to_cpu(reg) & V2_CLOCK_SRC_MASK;
if (index > 5)
return -EIO;
/* To check the configuration of optical interface. */
err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
switch (index) {
case 0:
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
break;
case 1:
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 4:
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
break;
case 5:
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
break;
default:
return -EIO;
}
return 0;
}
static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
{
/* V2 protocol doesn't have this feature. */
return 0;
}
static void calculate_fixed_part(struct snd_motu_packet_format *formats,
enum amdtp_stream_direction dir,
enum snd_motu_spec_flags flags,
unsigned char analog_ports)
{
unsigned char pcm_chunks[3] = {0, 0, 0};
formats->msg_chunks = 2;
pcm_chunks[0] = analog_ports;
pcm_chunks[1] = analog_ports;
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
pcm_chunks[2] = analog_ports;
if (dir == AMDTP_IN_STREAM) {
if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
}
if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
}
} else {
/*
* Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
*/
pcm_chunks[0] += 4;
pcm_chunks[1] += 4;
}
/*
* All of v2 models have a pair of coaxial interfaces for digital in/out
* port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
* ports.
*/
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
/* This part should be multiples of 4. */
formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
formats->fixed_part_pcm_chunks[2] =
round_up(2 + pcm_chunks[2], 4) - 2;
}
static void calculate_differed_part(struct snd_motu_packet_format *formats,
enum snd_motu_spec_flags flags,
u32 data, u32 mask, u32 shift)
{
unsigned char pcm_chunks[3] = {0, 0};
/*
* When optical interfaces are configured for S/PDIF (TOSLINK),
* the above PCM frames come from them, instead of coaxial
* interfaces.
*/
data = (data & mask) >> shift;
if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) &&
data == V2_OPT_IFACE_MODE_ADAT) {
pcm_chunks[0] += 8;
pcm_chunks[1] += 4;
}
/* At mode x4, no data chunks are supported in this part. */
formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
}
static int v2_cache_packet_formats(struct snd_motu *motu)
{
__be32 reg;
u32 data;
int err;
err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
motu->spec->flags, motu->spec->analog_in_ports);
calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
motu->spec->flags, motu->spec->analog_out_ports);
calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
motu->tx_packet_formats.midi_flag_offset = 4;
motu->tx_packet_formats.midi_byte_offset = 6;
motu->tx_packet_formats.pcm_byte_offset = 10;
motu->rx_packet_formats.midi_flag_offset = 4;
motu->rx_packet_formats.midi_byte_offset = 6;
motu->rx_packet_formats.pcm_byte_offset = 10;
return 0;
}
const struct snd_motu_protocol snd_motu_protocol_v2 = {
.get_clock_rate = v2_get_clock_rate,
.set_clock_rate = v2_set_clock_rate,
.get_clock_source = v2_get_clock_source,
.switch_fetching_mode = v2_switch_fetching_mode,
.cache_packet_formats = v2_cache_packet_formats,
};

View File

@ -0,0 +1,311 @@
/*
* motu-protocol-v3.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/delay.h>
#include "motu.h"
#define V3_CLOCK_STATUS_OFFSET 0x0b14
#define V3_FETCH_PCM_FRAMES 0x02000000
#define V3_CLOCK_RATE_MASK 0x0000ff00
#define V3_CLOCK_RATE_SHIFT 8
#define V3_CLOCK_SOURCE_MASK 0x000000ff
#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
#define V3_ENABLE_OPT_IN_IFACE_B 0x00000002
#define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100
#define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200
#define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000
#define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000
#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
{
__be32 reg;
u32 data;
int err;
err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
if (data >= ARRAY_SIZE(snd_motu_clock_rates))
return -EIO;
*rate = snd_motu_clock_rates[data];
return 0;
}
static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
{
__be32 reg;
u32 data;
bool need_to_wait;
int i, err;
for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
if (snd_motu_clock_rates[i] == rate)
break;
}
if (i == ARRAY_SIZE(snd_motu_clock_rates))
return -EINVAL;
err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
data |= i << V3_CLOCK_RATE_SHIFT;
need_to_wait = data != be32_to_cpu(reg);
reg = cpu_to_be32(data);
err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
if (need_to_wait) {
/* Cost expensive. */
if (msleep_interruptible(4000) > 0)
return -EINTR;
}
return 0;
}
static int v3_get_clock_source(struct snd_motu *motu,
enum snd_motu_clock_source *src)
{
__be32 reg;
u32 data;
unsigned int val;
int err;
err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
val = data & V3_CLOCK_SOURCE_MASK;
if (val == 0x00) {
*src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
} else if (val == 0x01) {
*src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
} else if (val == 0x10) {
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
} else if (val == 0x18 || val == 0x19) {
err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET,
&reg, sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
if (val == 0x18) {
if (data & V3_NO_ADAT_OPT_IN_IFACE_A)
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
else
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
} else {
if (data & V3_NO_ADAT_OPT_IN_IFACE_B)
*src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
else
*src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
}
} else {
*src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
}
return 0;
}
static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
{
__be32 reg;
u32 data;
int err;
err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return 0;
data = be32_to_cpu(reg);
if (enable)
data |= V3_FETCH_PCM_FRAMES;
else
data &= ~V3_FETCH_PCM_FRAMES;
reg = cpu_to_be32(data);
return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
sizeof(reg));
}
static void calculate_fixed_part(struct snd_motu_packet_format *formats,
enum amdtp_stream_direction dir,
enum snd_motu_spec_flags flags,
unsigned char analog_ports)
{
unsigned char pcm_chunks[3] = {0, 0, 0};
formats->msg_chunks = 2;
pcm_chunks[0] = analog_ports;
pcm_chunks[1] = analog_ports;
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
pcm_chunks[2] = analog_ports;
if (dir == AMDTP_IN_STREAM) {
if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
pcm_chunks[2] += 2;
}
if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
pcm_chunks[2] += 2;
}
if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) {
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
}
} else {
/*
* Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
*/
pcm_chunks[0] += 4;
pcm_chunks[1] += 4;
}
/*
* At least, packets have two data chunks for S/PDIF on coaxial
* interface.
*/
pcm_chunks[0] += 2;
pcm_chunks[1] += 2;
/*
* Fixed part consists of PCM chunks multiple of 4, with msg chunks. As
* a result, this part can includes empty data chunks.
*/
formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
formats->fixed_part_pcm_chunks[2] =
round_up(2 + pcm_chunks[2], 4) - 2;
}
static void calculate_differed_part(struct snd_motu_packet_format *formats,
enum snd_motu_spec_flags flags, u32 data,
u32 a_enable_mask, u32 a_no_adat_mask,
u32 b_enable_mask, u32 b_no_adat_mask)
{
unsigned char pcm_chunks[3] = {0, 0, 0};
int i;
if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) {
if (data & a_no_adat_mask) {
/*
* Additional two data chunks for S/PDIF on optical
* interface A. This includes empty data chunks.
*/
pcm_chunks[0] += 4;
pcm_chunks[1] += 4;
} else {
/*
* Additional data chunks for ADAT on optical interface
* A.
*/
pcm_chunks[0] += 8;
pcm_chunks[1] += 4;
}
}
if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) {
if (data & b_no_adat_mask) {
/*
* Additional two data chunks for S/PDIF on optical
* interface B. This includes empty data chunks.
*/
pcm_chunks[0] += 4;
pcm_chunks[1] += 4;
} else {
/*
* Additional data chunks for ADAT on optical interface
* B.
*/
pcm_chunks[0] += 8;
pcm_chunks[1] += 4;
}
}
for (i = 0; i < 3; ++i) {
if (pcm_chunks[i] > 0)
pcm_chunks[i] = round_up(pcm_chunks[i], 4);
formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
}
}
static int v3_cache_packet_formats(struct snd_motu *motu)
{
__be32 reg;
u32 data;
int err;
err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
motu->spec->flags, motu->spec->analog_in_ports);
calculate_differed_part(&motu->tx_packet_formats,
motu->spec->flags, data,
V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A,
V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B);
calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
motu->spec->flags, motu->spec->analog_out_ports);
calculate_differed_part(&motu->rx_packet_formats,
motu->spec->flags, data,
V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A,
V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B);
motu->tx_packet_formats.midi_flag_offset = 8;
motu->tx_packet_formats.midi_byte_offset = 7;
motu->tx_packet_formats.pcm_byte_offset = 10;
motu->rx_packet_formats.midi_flag_offset = 8;
motu->rx_packet_formats.midi_byte_offset = 7;
motu->rx_packet_formats.pcm_byte_offset = 10;
return 0;
}
const struct snd_motu_protocol snd_motu_protocol_v3 = {
.get_clock_rate = v3_get_clock_rate,
.set_clock_rate = v3_set_clock_rate,
.get_clock_source = v3_get_clock_source,
.switch_fetching_mode = v3_switch_fetching_mode,
.cache_packet_formats = v3_cache_packet_formats,
};

View File

@ -0,0 +1,381 @@
/*
* motu-stream.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
#define CALLBACK_TIMEOUT 200
#define ISOC_COMM_CONTROL_OFFSET 0x0b00
#define ISOC_COMM_CONTROL_MASK 0xffff0000
#define CHANGE_RX_ISOC_COMM_STATE 0x80000000
#define RX_ISOC_COMM_IS_ACTIVATED 0x40000000
#define RX_ISOC_COMM_CHANNEL_MASK 0x3f000000
#define RX_ISOC_COMM_CHANNEL_SHIFT 24
#define CHANGE_TX_ISOC_COMM_STATE 0x00800000
#define TX_ISOC_COMM_IS_ACTIVATED 0x00400000
#define TX_ISOC_COMM_CHANNEL_MASK 0x003f0000
#define TX_ISOC_COMM_CHANNEL_SHIFT 16
#define PACKET_FORMAT_OFFSET 0x0b10
#define TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080
#define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
#define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
static int start_both_streams(struct snd_motu *motu, unsigned int rate)
{
unsigned int midi_ports = 0;
__be32 reg;
u32 data;
int err;
if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI)
midi_ports = 1;
/* Set packet formation to our packet streaming engine. */
err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
&motu->rx_packet_formats);
if (err < 0)
return err;
err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
&motu->tx_packet_formats);
if (err < 0)
return err;
/* Get isochronous resources on the bus. */
err = fw_iso_resources_allocate(&motu->rx_resources,
amdtp_stream_get_max_payload(&motu->rx_stream),
fw_parent_device(motu->unit)->max_speed);
if (err < 0)
return err;
err = fw_iso_resources_allocate(&motu->tx_resources,
amdtp_stream_get_max_payload(&motu->tx_stream),
fw_parent_device(motu->unit)->max_speed);
if (err < 0)
return err;
/* Configure the unit to start isochronous communication. */
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
(motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
(motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
reg = cpu_to_be32(data);
return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg));
}
static void stop_both_streams(struct snd_motu *motu)
{
__be32 reg;
u32 data;
int err;
err = motu->spec->protocol->switch_fetching_mode(motu, false);
if (err < 0)
return;
err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return;
data = be32_to_cpu(reg);
data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
reg = cpu_to_be32(data);
snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
sizeof(reg));
fw_iso_resources_free(&motu->tx_resources);
fw_iso_resources_free(&motu->rx_resources);
}
static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
{
struct fw_iso_resources *resources;
int err;
if (stream == &motu->rx_stream)
resources = &motu->rx_resources;
else
resources = &motu->tx_resources;
err = amdtp_stream_start(stream, resources->channel,
fw_parent_device(motu->unit)->max_speed);
if (err < 0)
return err;
if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
amdtp_stream_stop(stream);
fw_iso_resources_free(resources);
return -ETIMEDOUT;
}
return 0;
}
static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
{
struct fw_iso_resources *resources;
if (stream == &motu->rx_stream)
resources = &motu->rx_resources;
else
resources = &motu->tx_resources;
amdtp_stream_stop(stream);
fw_iso_resources_free(resources);
}
static int ensure_packet_formats(struct snd_motu *motu)
{
__be32 reg;
u32 data;
int err;
err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,
sizeof(reg));
if (err < 0)
return err;
data = be32_to_cpu(reg);
data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
TX_PACKET_TRANSMISSION_SPEED_MASK);
if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
data |= fw_parent_device(motu->unit)->max_speed;
reg = cpu_to_be32(data);
return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,
sizeof(reg));
}
int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
{
const struct snd_motu_protocol *protocol = motu->spec->protocol;
unsigned int curr_rate;
int err = 0;
if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
return 0;
/* Some packet queueing errors. */
if (amdtp_streaming_error(&motu->rx_stream) ||
amdtp_streaming_error(&motu->tx_stream)) {
amdtp_stream_stop(&motu->rx_stream);
amdtp_stream_stop(&motu->tx_stream);
stop_both_streams(motu);
}
err = protocol->cache_packet_formats(motu);
if (err < 0)
return err;
/* Stop stream if rate is different. */
err = protocol->get_clock_rate(motu, &curr_rate);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to get sampling rate: %d\n", err);
return err;
}
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
amdtp_stream_stop(&motu->rx_stream);
amdtp_stream_stop(&motu->tx_stream);
stop_both_streams(motu);
}
if (!amdtp_stream_running(&motu->rx_stream)) {
err = protocol->set_clock_rate(motu, rate);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to set sampling rate: %d\n", err);
return err;
}
err = ensure_packet_formats(motu);
if (err < 0)
return err;
err = start_both_streams(motu, rate);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to start isochronous comm: %d\n", err);
stop_both_streams(motu);
return err;
}
err = start_isoc_ctx(motu, &motu->rx_stream);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to start IT context: %d\n", err);
stop_both_streams(motu);
return err;
}
err = protocol->switch_fetching_mode(motu, true);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to enable frame fetching: %d\n", err);
stop_both_streams(motu);
return err;
}
}
if (!amdtp_stream_running(&motu->tx_stream) &&
motu->capture_substreams > 0) {
err = start_isoc_ctx(motu, &motu->tx_stream);
if (err < 0) {
dev_err(&motu->unit->device,
"fail to start IR context: %d", err);
amdtp_stream_stop(&motu->rx_stream);
stop_both_streams(motu);
return err;
}
}
return 0;
}
void snd_motu_stream_stop_duplex(struct snd_motu *motu)
{
if (motu->capture_substreams == 0) {
if (amdtp_stream_running(&motu->tx_stream))
stop_isoc_ctx(motu, &motu->tx_stream);
if (motu->playback_substreams == 0) {
if (amdtp_stream_running(&motu->rx_stream))
stop_isoc_ctx(motu, &motu->rx_stream);
stop_both_streams(motu);
}
}
}
static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
{
int err;
struct amdtp_stream *stream;
struct fw_iso_resources *resources;
if (dir == AMDTP_IN_STREAM) {
stream = &motu->tx_stream;
resources = &motu->tx_resources;
} else {
stream = &motu->rx_stream;
resources = &motu->rx_resources;
}
err = fw_iso_resources_init(resources, motu->unit);
if (err < 0)
return err;
err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
if (err < 0) {
amdtp_stream_destroy(stream);
fw_iso_resources_destroy(resources);
}
return err;
}
static void destroy_stream(struct snd_motu *motu,
enum amdtp_stream_direction dir)
{
struct amdtp_stream *stream;
struct fw_iso_resources *resources;
if (dir == AMDTP_IN_STREAM) {
stream = &motu->tx_stream;
resources = &motu->tx_resources;
} else {
stream = &motu->rx_stream;
resources = &motu->rx_resources;
}
amdtp_stream_destroy(stream);
fw_iso_resources_free(resources);
}
int snd_motu_stream_init_duplex(struct snd_motu *motu)
{
int err;
err = init_stream(motu, AMDTP_IN_STREAM);
if (err < 0)
return err;
err = init_stream(motu, AMDTP_OUT_STREAM);
if (err < 0)
destroy_stream(motu, AMDTP_IN_STREAM);
return err;
}
/*
* This function should be called before starting streams or after stopping
* streams.
*/
void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
{
destroy_stream(motu, AMDTP_IN_STREAM);
destroy_stream(motu, AMDTP_OUT_STREAM);
motu->playback_substreams = 0;
motu->capture_substreams = 0;
}
static void motu_lock_changed(struct snd_motu *motu)
{
motu->dev_lock_changed = true;
wake_up(&motu->hwdep_wait);
}
int snd_motu_stream_lock_try(struct snd_motu *motu)
{
int err;
spin_lock_irq(&motu->lock);
if (motu->dev_lock_count < 0) {
err = -EBUSY;
goto out;
}
if (motu->dev_lock_count++ == 0)
motu_lock_changed(motu);
err = 0;
out:
spin_unlock_irq(&motu->lock);
return err;
}
void snd_motu_stream_lock_release(struct snd_motu *motu)
{
spin_lock_irq(&motu->lock);
if (WARN_ON(motu->dev_lock_count <= 0))
goto out;
if (--motu->dev_lock_count == 0)
motu_lock_changed(motu);
out:
spin_unlock_irq(&motu->lock);
}

View File

@ -0,0 +1,137 @@
/*
* motu-transaction.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
#define SND_MOTU_ADDR_BASE 0xfffff0000000ULL
#define ASYNC_ADDR_HI 0x0b04
#define ASYNC_ADDR_LO 0x0b08
int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
size_t size)
{
int tcode;
if (size % sizeof(__be32) > 0 || size <= 0)
return -EINVAL;
if (size == sizeof(__be32))
tcode = TCODE_READ_QUADLET_REQUEST;
else
tcode = TCODE_READ_BLOCK_REQUEST;
return snd_fw_transaction(motu->unit, tcode,
SND_MOTU_ADDR_BASE + offset, reg, size, 0);
}
int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
size_t size)
{
int tcode;
if (size % sizeof(__be32) > 0 || size <= 0)
return -EINVAL;
if (size == sizeof(__be32))
tcode = TCODE_WRITE_QUADLET_REQUEST;
else
tcode = TCODE_WRITE_BLOCK_REQUEST;
return snd_fw_transaction(motu->unit, tcode,
SND_MOTU_ADDR_BASE + offset, reg, size, 0);
}
static void handle_message(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source,
int generation, unsigned long long offset,
void *data, size_t length, void *callback_data)
{
struct snd_motu *motu = callback_data;
__be32 *buf = (__be32 *)data;
unsigned long flags;
if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
fw_send_response(card, request, RCODE_COMPLETE);
return;
}
if (offset != motu->async_handler.offset || length != 4) {
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
return;
}
spin_lock_irqsave(&motu->lock, flags);
motu->msg = be32_to_cpu(*buf);
spin_unlock_irqrestore(&motu->lock, flags);
fw_send_response(card, request, RCODE_COMPLETE);
wake_up(&motu->hwdep_wait);
}
int snd_motu_transaction_reregister(struct snd_motu *motu)
{
struct fw_device *device = fw_parent_device(motu->unit);
__be32 data;
int err;
if (motu->async_handler.callback_data == NULL)
return -EINVAL;
/* Register messaging address. Block transaction is not allowed. */
data = cpu_to_be32((device->card->node_id << 16) |
(motu->async_handler.offset >> 32));
err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data,
sizeof(data));
if (err < 0)
return err;
data = cpu_to_be32(motu->async_handler.offset);
return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data,
sizeof(data));
}
int snd_motu_transaction_register(struct snd_motu *motu)
{
static const struct fw_address_region resp_register_region = {
.start = 0xffffe0000000ull,
.end = 0xffffe000ffffull,
};
int err;
/* Perhaps, 4 byte messages are transferred. */
motu->async_handler.length = 4;
motu->async_handler.address_callback = handle_message;
motu->async_handler.callback_data = motu;
err = fw_core_add_address_handler(&motu->async_handler,
&resp_register_region);
if (err < 0)
return err;
err = snd_motu_transaction_reregister(motu);
if (err < 0) {
fw_core_remove_address_handler(&motu->async_handler);
motu->async_handler.address_callback = NULL;
}
return err;
}
void snd_motu_transaction_unregister(struct snd_motu *motu)
{
__be32 data;
if (motu->async_handler.address_callback != NULL)
fw_core_remove_address_handler(&motu->async_handler);
motu->async_handler.address_callback = NULL;
/* Unregister the address. */
data = cpu_to_be32(0x00000000);
snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data));
snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data));
}

264
sound/firewire/motu/motu.c Normal file
View File

@ -0,0 +1,264 @@
/*
* motu.c - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include "motu.h"
#define OUI_MOTU 0x0001f2
MODULE_DESCRIPTION("MOTU FireWire driver");
MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
MODULE_LICENSE("GPL v2");
const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
/* mode 0 */
[0] = 44100,
[1] = 48000,
/* mode 1 */
[2] = 88200,
[3] = 96000,
/* mode 2 */
[4] = 176400,
[5] = 192000,
};
static void name_card(struct snd_motu *motu)
{
struct fw_device *fw_dev = fw_parent_device(motu->unit);
struct fw_csr_iterator it;
int key, val;
u32 version = 0;
fw_csr_iterator_init(&it, motu->unit->directory);
while (fw_csr_iterator_next(&it, &key, &val)) {
switch (key) {
case CSR_VERSION:
version = val;
break;
}
}
strcpy(motu->card->driver, "FW-MOTU");
strcpy(motu->card->shortname, motu->spec->name);
strcpy(motu->card->mixername, motu->spec->name);
snprintf(motu->card->longname, sizeof(motu->card->longname),
"MOTU %s (version:%d), GUID %08x%08x at %s, S%d",
motu->spec->name, version,
fw_dev->config_rom[3], fw_dev->config_rom[4],
dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
}
static void motu_free(struct snd_motu *motu)
{
snd_motu_transaction_unregister(motu);
snd_motu_stream_destroy_duplex(motu);
fw_unit_put(motu->unit);
mutex_destroy(&motu->mutex);
kfree(motu);
}
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
* transactions safely. Thus at returning from .remove(), this module still keep
* references for the unit.
*/
static void motu_card_free(struct snd_card *card)
{
motu_free(card->private_data);
}
static void do_registration(struct work_struct *work)
{
struct snd_motu *motu = container_of(work, struct snd_motu, dwork.work);
int err;
if (motu->registered)
return;
err = snd_card_new(&motu->unit->device, -1, NULL, THIS_MODULE, 0,
&motu->card);
if (err < 0)
return;
name_card(motu);
err = snd_motu_transaction_register(motu);
if (err < 0)
goto error;
err = snd_motu_stream_init_duplex(motu);
if (err < 0)
goto error;
snd_motu_proc_init(motu);
err = snd_motu_create_pcm_devices(motu);
if (err < 0)
goto error;
if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI) {
err = snd_motu_create_midi_devices(motu);
if (err < 0)
goto error;
}
err = snd_motu_create_hwdep_device(motu);
if (err < 0)
goto error;
err = snd_card_register(motu->card);
if (err < 0)
goto error;
/*
* After registered, motu instance can be released corresponding to
* releasing the sound card instance.
*/
motu->card->private_free = motu_card_free;
motu->card->private_data = motu;
motu->registered = true;
return;
error:
snd_motu_transaction_unregister(motu);
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 = kzalloc(sizeof(struct snd_motu), GFP_KERNEL);
if (motu == NULL)
return -ENOMEM;
motu->spec = (const struct snd_motu_spec *)entry->driver_data;
motu->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, motu);
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;
}
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) {
/* No need to wait for releasing card object in this context. */
snd_card_free_when_closed(motu->card);
} else {
/* Don't forget this case. */
motu_free(motu);
}
}
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);
}
static struct snd_motu_spec motu_828mk2 = {
.name = "828mk2",
.protocol = &snd_motu_protocol_v2,
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
SND_MOTU_SPEC_HAS_MIDI,
.analog_in_ports = 8,
.analog_out_ports = 8,
};
static struct snd_motu_spec motu_828mk3 = {
.name = "828mk3",
.protocol = &snd_motu_protocol_v3,
.flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
SND_MOTU_SPEC_TX_MICINST_CHUNK |
SND_MOTU_SPEC_TX_RETURN_CHUNK |
SND_MOTU_SPEC_TX_REVERB_CHUNK |
SND_MOTU_SPEC_HAS_OPT_IFACE_A |
SND_MOTU_SPEC_HAS_OPT_IFACE_B |
SND_MOTU_SPEC_HAS_MIDI,
.analog_in_ports = 8,
.analog_out_ports = 8,
};
#define SND_MOTU_DEV_ENTRY(model, data) \
{ \
.match_flags = IEEE1394_MATCH_VENDOR_ID | \
IEEE1394_MATCH_MODEL_ID | \
IEEE1394_MATCH_SPECIFIER_ID, \
.vendor_id = OUI_MOTU, \
.model_id = model, \
.specifier_id = OUI_MOTU, \
.driver_data = (kernel_ulong_t)data, \
}
static const struct ieee1394_device_id motu_id_table[] = {
SND_MOTU_DEV_ENTRY(0x101800, &motu_828mk2),
SND_MOTU_DEV_ENTRY(0x106800, &motu_828mk3), /* FireWire only. */
SND_MOTU_DEV_ENTRY(0x100800, &motu_828mk3), /* Hybrid. */
{ }
};
MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
static struct fw_driver motu_driver = {
.driver = {
.owner = THIS_MODULE,
.name = KBUILD_MODNAME,
.bus = &fw_bus_type,
},
.probe = motu_probe,
.update = motu_bus_update,
.remove = motu_remove,
.id_table = motu_id_table,
};
static int __init alsa_motu_init(void)
{
return driver_register(&motu_driver.driver);
}
static void __exit alsa_motu_exit(void)
{
driver_unregister(&motu_driver.driver);
}
module_init(alsa_motu_init);
module_exit(alsa_motu_exit);

161
sound/firewire/motu/motu.h Normal file
View File

@ -0,0 +1,161 @@
/*
* motu.h - a part of driver for MOTU FireWire series
*
* Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
*
* Licensed under the terms of the GNU General Public License, version 2.
*/
#ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
#define SOUND_FIREWIRE_MOTU_H_INCLUDED
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/sched/signal.h>
#include <sound/control.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
#include <sound/rawmidi.h>
#include <sound/firewire.h>
#include <sound/hwdep.h>
#include "../lib.h"
#include "../amdtp-stream.h"
#include "../iso-resources.h"
struct snd_motu_packet_format {
unsigned char midi_flag_offset;
unsigned char midi_byte_offset;
unsigned char pcm_byte_offset;
unsigned char msg_chunks;
unsigned char fixed_part_pcm_chunks[3];
unsigned char differed_part_pcm_chunks[3];
};
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;
/* For packet streaming */
struct snd_motu_packet_format tx_packet_formats;
struct snd_motu_packet_format rx_packet_formats;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct fw_iso_resources tx_resources;
struct fw_iso_resources rx_resources;
unsigned int capture_substreams;
unsigned int playback_substreams;
/* For notification. */
struct fw_address_handler async_handler;
u32 msg;
/* For uapi */
int dev_lock_count;
bool dev_lock_changed;
wait_queue_head_t hwdep_wait;
};
enum snd_motu_spec_flags {
SND_MOTU_SPEC_SUPPORT_CLOCK_X2 = 0x0001,
SND_MOTU_SPEC_SUPPORT_CLOCK_X4 = 0x0002,
SND_MOTU_SPEC_TX_MICINST_CHUNK = 0x0004,
SND_MOTU_SPEC_TX_RETURN_CHUNK = 0x0008,
SND_MOTU_SPEC_TX_REVERB_CHUNK = 0x0010,
SND_MOTU_SPEC_TX_AESEBU_CHUNK = 0x0020,
SND_MOTU_SPEC_HAS_OPT_IFACE_A = 0x0040,
SND_MOTU_SPEC_HAS_OPT_IFACE_B = 0x0080,
SND_MOTU_SPEC_HAS_MIDI = 0x0100,
};
#define SND_MOTU_CLOCK_RATE_COUNT 6
extern const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT];
enum snd_motu_clock_source {
SND_MOTU_CLOCK_SOURCE_INTERNAL,
SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB,
SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT,
SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A,
SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B,
SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT,
SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A,
SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B,
SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX,
SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR,
SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC,
SND_MOTU_CLOCK_SOURCE_UNKNOWN,
};
struct snd_motu_protocol {
int (*get_clock_rate)(struct snd_motu *motu, unsigned int *rate);
int (*set_clock_rate)(struct snd_motu *motu, unsigned int rate);
int (*get_clock_source)(struct snd_motu *motu,
enum snd_motu_clock_source *source);
int (*switch_fetching_mode)(struct snd_motu *motu, bool enable);
int (*cache_packet_formats)(struct snd_motu *motu);
};
struct snd_motu_spec {
const char *const name;
enum snd_motu_spec_flags flags;
unsigned char analog_in_ports;
unsigned char analog_out_ports;
const struct snd_motu_protocol *const protocol;
};
extern const struct snd_motu_protocol snd_motu_protocol_v2;
extern const struct snd_motu_protocol snd_motu_protocol_v3;
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
enum amdtp_stream_direction dir,
const struct snd_motu_protocol *const protocol);
int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
unsigned int midi_ports,
struct snd_motu_packet_format *formats);
int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
struct snd_pcm_runtime *runtime);
void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
struct snd_rawmidi_substream *midi);
int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
size_t size);
int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
size_t size);
int snd_motu_transaction_register(struct snd_motu *motu);
int snd_motu_transaction_reregister(struct snd_motu *motu);
void snd_motu_transaction_unregister(struct snd_motu *motu);
int snd_motu_stream_init_duplex(struct snd_motu *motu);
void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
void snd_motu_stream_stop_duplex(struct snd_motu *motu);
int snd_motu_stream_lock_try(struct snd_motu *motu);
void snd_motu_stream_lock_release(struct snd_motu *motu);
void snd_motu_proc_init(struct snd_motu *motu);
int snd_motu_create_pcm_devices(struct snd_motu *motu);
int snd_motu_create_midi_devices(struct snd_motu *motu);
int snd_motu_create_hwdep_device(struct snd_motu *motu);
#endif

View File

@ -34,7 +34,9 @@ int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
err = fcp_avc_transaction(unit, buf, len + 10, buf, len + 10,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(8));
if ((err > 0) && (err < len + 10))
if (err < 0)
;
else if (err < len + 10)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -77,7 +79,9 @@ int avc_stream_get_format(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, *len,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7));
if ((err > 0) && (err < 10))
if (err < 0)
;
else if (err < 12)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@ -139,7 +143,9 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
/* do transaction and check buf[1-5] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
if ((err > 0) && (err < 8))
if (err < 0)
;
else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;

View File

@ -18,9 +18,8 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
{
struct snd_tscm *tscm = substream->rmidi->private_data;
/* Initialize internal status. */
tscm->running_status[substream->number] = 0;
tscm->on_sysex[substream->number] = 0;
snd_fw_async_midi_port_init(&tscm->out_ports[substream->number]);
return 0;
}
@ -31,12 +30,15 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
}
static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
return 0;
}
static void midi_playback_drain(struct snd_rawmidi_substream *substream)
{
struct snd_tscm *tscm = substream->rmidi->private_data;
snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
return 0;
}
static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
@ -78,6 +80,7 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
static const struct snd_rawmidi_ops playback_ops = {
.open = midi_playback_open,
.close = midi_playback_close,
.drain = midi_playback_drain,
.trigger = midi_playback_trigger,
};
struct snd_rawmidi *rmidi;

View File

@ -58,39 +58,38 @@ static inline int calculate_message_bytes(u8 status)
return -EINVAL;
}
static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
static int fill_message(struct snd_fw_async_midi_port *port,
struct snd_rawmidi_substream *substream)
{
struct snd_tscm *tscm = substream->rmidi->private_data;
unsigned int port = substream->number;
int i, len, consume;
u8 *label, *msg;
u8 status;
/* The first byte is used for label, the rest for MIDI bytes. */
label = buf;
msg = buf + 1;
label = port->buf;
msg = port->buf + 1;
consume = snd_rawmidi_transmit_peek(substream, msg, 3);
if (consume == 0)
return 0;
/* On exclusive message. */
if (tscm->on_sysex[port]) {
if (port->on_sysex) {
/* Seek the end of exclusives. */
for (i = 0; i < consume; ++i) {
if (msg[i] == 0xf7) {
tscm->on_sysex[port] = false;
port->on_sysex = false;
break;
}
}
/* At the end of exclusive message, use label 0x07. */
if (!tscm->on_sysex[port]) {
if (!port->on_sysex) {
consume = i + 1;
*label = (port << 4) | 0x07;
*label = (substream->number << 4) | 0x07;
/* During exclusive message, use label 0x04. */
} else if (consume == 3) {
*label = (port << 4) | 0x04;
*label = (substream->number << 4) | 0x04;
/* We need to fill whole 3 bytes. Go to next change. */
} else {
return 0;
@ -101,12 +100,12 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
/* The beginning of exclusives. */
if (msg[0] == 0xf0) {
/* Transfer it in next chance in another condition. */
tscm->on_sysex[port] = true;
port->on_sysex = true;
return 0;
} else {
/* On running-status. */
if ((msg[0] & 0x80) != 0x80)
status = tscm->running_status[port];
status = port->running_status;
else
status = msg[0];
@ -124,18 +123,18 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
msg[2] = msg[1];
msg[1] = msg[0];
msg[0] = tscm->running_status[port];
msg[0] = port->running_status;
} else {
/* Enough MIDI bytes were not retrieved. */
if (consume < len)
return 0;
consume = len;
tscm->running_status[port] = msg[0];
port->running_status = msg[0];
}
}
*label = (port << 4) | (msg[0] >> 4);
*label = (substream->number << 4) | (msg[0] >> 4);
}
if (len > 0 && len < 3)
@ -144,6 +143,106 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
return consume;
}
static void async_midi_port_callback(struct fw_card *card, int rcode,
void *data, size_t length,
void *callback_data)
{
struct snd_fw_async_midi_port *port = callback_data;
struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
/* This port is closed. */
if (substream == NULL)
return;
if (rcode == RCODE_COMPLETE)
snd_rawmidi_transmit_ack(substream, port->consume_bytes);
else if (!rcode_is_permanent_error(rcode))
/* To start next transaction immediately for recovery. */
port->next_ktime = 0;
else
/* Don't continue processing. */
port->error = true;
port->idling = true;
if (!snd_rawmidi_transmit_empty(substream))
schedule_work(&port->work);
}
static void midi_port_work(struct work_struct *work)
{
struct snd_fw_async_midi_port *port =
container_of(work, struct snd_fw_async_midi_port, work);
struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
int generation;
/* Under transacting or error state. */
if (!port->idling || port->error)
return;
/* Nothing to do. */
if (substream == NULL || snd_rawmidi_transmit_empty(substream))
return;
/* Do it in next chance. */
if (ktime_after(port->next_ktime, ktime_get())) {
schedule_work(&port->work);
return;
}
/*
* Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
* Later, snd_rawmidi_transmit_ack() is called.
*/
memset(port->buf, 0, 4);
port->consume_bytes = fill_message(port, substream);
if (port->consume_bytes <= 0) {
/* Do it in next chance, immediately. */
if (port->consume_bytes == 0) {
port->next_ktime = 0;
schedule_work(&port->work);
} else {
/* Fatal error. */
port->error = true;
}
return;
}
/* Set interval to next transaction. */
port->next_ktime = ktime_add_ns(ktime_get(),
port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
/* Start this transaction. */
port->idling = false;
/*
* In Linux FireWire core, when generation is updated with memory
* barrier, node id has already been updated. In this module, After
* this smp_rmb(), load/store instructions to memory are completed.
* Thus, both of generation and node id are available with recent
* values. This is a light-serialization solution to handle bus reset
* events on IEEE 1394 bus.
*/
generation = port->parent->generation;
smp_rmb();
fw_send_request(port->parent->card, &port->transaction,
TCODE_WRITE_QUADLET_REQUEST,
port->parent->node_id, generation,
port->parent->max_speed,
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
port->buf, 4, async_midi_port_callback,
port);
}
void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port)
{
port->idling = true;
port->error = false;
port->running_status = 0;
port->on_sysex = false;
}
static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source,
int generation, unsigned long long offset,
@ -219,12 +318,9 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm)
goto error;
for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
err = snd_fw_async_midi_port_init(
&tscm->out_ports[i], tscm->unit,
TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
4, fill_message);
if (err < 0)
goto error;
tscm->out_ports[i].parent = fw_parent_device(tscm->unit);
tscm->out_ports[i].next_ktime = 0;
INIT_WORK(&tscm->out_ports[i].work, midi_port_work);
}
return err;
@ -275,7 +371,6 @@ int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
{
__be32 reg;
unsigned int i;
if (tscm->async_handler.callback_data == NULL)
return;
@ -302,7 +397,4 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
fw_core_remove_address_handler(&tscm->async_handler);
tscm->async_handler.callback_data = NULL;
for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
}

View File

@ -45,6 +45,23 @@ struct snd_tscm_spec {
#define TSCM_MIDI_IN_PORT_MAX 4
#define TSCM_MIDI_OUT_PORT_MAX 4
struct snd_fw_async_midi_port {
struct fw_device *parent;
struct work_struct work;
bool idling;
ktime_t next_ktime;
bool error;
struct fw_transaction transaction;
u8 buf[4];
u8 running_status;
bool on_sysex;
struct snd_rawmidi_substream *substream;
int consume_bytes;
};
struct snd_tscm {
struct snd_card *card;
struct fw_unit *unit;
@ -72,8 +89,6 @@ struct snd_tscm {
/* For MIDI message outgoing transactions. */
struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
};
#define TSCM_ADDR_BASE 0xffff00000000ull
@ -131,6 +146,26 @@ void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port);
static inline void
snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
struct snd_rawmidi_substream *substream)
{
if (!port->error) {
port->substream = substream;
schedule_work(&port->work);
}
}
static inline void
snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
{
port->substream = NULL;
cancel_work_sync(&port->work);
port->error = false;
}
int snd_tscm_transaction_register(struct snd_tscm *tscm);
int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
void snd_tscm_transaction_unregister(struct snd_tscm *tscm);

View File

@ -171,7 +171,7 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
{
int timeout;
u32 val;
int mask = (1 << AZX_MLCTL_CPA);
int mask = (1 << AZX_MLCTL_CPA_SHIFT);
udelay(3);
timeout = 150;
@ -179,10 +179,10 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
do {
val = readl(link->ml_addr + AZX_REG_ML_LCTL);
if (enable) {
if (((val & mask) >> AZX_MLCTL_CPA))
if (((val & mask) >> AZX_MLCTL_CPA_SHIFT))
return 0;
} else {
if (!((val & mask) >> AZX_MLCTL_CPA))
if (!((val & mask) >> AZX_MLCTL_CPA_SHIFT))
return 0;
}
udelay(3);

View File

@ -272,7 +272,7 @@ int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
/* Lets walk the linked capabilities list */
do {
cur_cap = _snd_hdac_chip_read(l, bus, offset);
cur_cap = _snd_hdac_chip_readl(bus, offset);
dev_dbg(bus->dev, "Capability version: 0x%x\n",
(cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);

View File

@ -555,12 +555,12 @@ void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
if (!reg)
reg = AZX_REG_SSYNC;
val = _snd_hdac_chip_read(l, bus, reg);
val = _snd_hdac_chip_readl(bus, reg);
if (set)
val |= streams;
else
val &= ~streams;
_snd_hdac_chip_write(l, bus, reg, val);
_snd_hdac_chip_writel(bus, reg, val);
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);

View File

@ -742,7 +742,7 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device)
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
sprintf(pcm->name, snd_es1688_chip_id(chip));
strcpy(pcm->name, snd_es1688_chip_id(chip));
chip->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,

View File

@ -1339,7 +1339,7 @@ static int snd_ali_prepare(struct snd_pcm_substream *substream)
rate = snd_ali_get_spdif_in_rate(codec);
if (rate == 0) {
dev_warn(codec->card->dev,
"ali_capture_preapre: spdif rate detect err!\n");
"ali_capture_prepare: spdif rate detect err!\n");
rate = 48000;
}
spin_lock_irq(&codec->reg_lock);

View File

@ -846,7 +846,7 @@ snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new vortex_a3d_kcontrol = {
static const struct snd_kcontrol_new vortex_a3d_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback PCM advanced processing",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,

View File

@ -2279,6 +2279,9 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
} else {
int src[2], mix[2];
if (nr_ch < 1)
return -EINVAL;
/* Get SRC and MIXER hardware resources. */
for (i = 0; i < nr_ch; i++) {
if ((mix[i] =

View File

@ -757,7 +757,7 @@ snd_vortex_eqtoggle_put(struct snd_kcontrol *kcontrol,
return 1; /* Allways changes */
}
static struct snd_kcontrol_new vortex_eqtoggle_kcontrol = {
static const struct snd_kcontrol_new vortex_eqtoggle_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Enable",
.index = 0,
@ -815,7 +815,7 @@ snd_vortex_eq_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucon
return changed;
}
static struct snd_kcontrol_new vortex_eq_kcontrol = {
static const struct snd_kcontrol_new vortex_eq_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = " .",
.index = 0,
@ -855,7 +855,7 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
return 0;
}
static struct snd_kcontrol_new vortex_levels_kcontrol = {
static const struct snd_kcontrol_new vortex_levels_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Peaks",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,

View File

@ -601,7 +601,7 @@ static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
static struct snd_kcontrol_new snd_vortex_pcm_vol = {
static const struct snd_kcontrol_new snd_vortex_pcm_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |

View File

@ -202,7 +202,7 @@ static const struct snd_pcm_ops snd_aw2_capture_ops = {
.pointer = snd_aw2_pcm_pointer_capture,
};
static struct snd_kcontrol_new aw2_control = {
static const struct snd_kcontrol_new aw2_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Route",
.index = 0,

View File

@ -598,7 +598,7 @@ static int snd_bt87x_capture_volume_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_bt87x_capture_volume = {
static const struct snd_kcontrol_new snd_bt87x_capture_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Volume",
.info = snd_bt87x_capture_volume_info,
@ -634,7 +634,7 @@ static int snd_bt87x_capture_boost_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_bt87x_capture_boost = {
static const struct snd_kcontrol_new snd_bt87x_capture_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Boost",
.info = snd_bt87x_capture_boost_info,
@ -676,7 +676,7 @@ static int snd_bt87x_capture_source_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_bt87x_capture_source = {
static const struct snd_kcontrol_new snd_bt87x_capture_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = snd_bt87x_capture_source_info,

View File

@ -301,7 +301,7 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
static const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Mic/Line in Capture Switch",
@ -310,7 +310,7 @@ static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
.put = snd_ca0106_capture_mic_line_in_put
};
static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
static const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Line in/Side out Capture Switch",

View File

@ -1045,7 +1045,7 @@ static int snd_cmipci_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_cmipci_spdif_default =
static const struct snd_kcontrol_new snd_cmipci_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@ -1072,7 +1072,7 @@ static int snd_cmipci_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_cmipci_spdif_mask =
static const struct snd_kcontrol_new snd_cmipci_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1119,7 +1119,7 @@ static int snd_cmipci_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_cmipci_spdif_stream =
static const struct snd_kcontrol_new snd_cmipci_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,

View File

@ -1055,7 +1055,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
static struct snd_kcontrol_new snd_cs4281_fm_vol =
static const struct snd_kcontrol_new snd_cs4281_fm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Playback Volume",
@ -1066,7 +1066,7 @@ static struct snd_kcontrol_new snd_cs4281_fm_vol =
.tlv = { .p = db_scale_dsp },
};
static struct snd_kcontrol_new snd_cs4281_pcm_vol =
static const struct snd_kcontrol_new snd_cs4281_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Stream Playback Volume",

View File

@ -1039,7 +1039,7 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
/* On the Mia this one controls the line-out volume */
static struct snd_kcontrol_new snd_echo_line_output_gain = {
static const struct snd_kcontrol_new snd_echo_line_output_gain = {
.name = "Line Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@ -1050,7 +1050,7 @@ static struct snd_kcontrol_new snd_echo_line_output_gain = {
.tlv = {.p = db_scale_output_gain},
};
#else
static struct snd_kcontrol_new snd_echo_pcm_output_gain = {
static const struct snd_kcontrol_new snd_echo_pcm_output_gain = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@ -1120,7 +1120,7 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
static struct snd_kcontrol_new snd_echo_line_input_gain = {
static const struct snd_kcontrol_new snd_echo_line_input_gain = {
.name = "Line Capture Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@ -1184,7 +1184,7 @@ static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_echo_output_nominal_level = {
static const struct snd_kcontrol_new snd_echo_output_nominal_level = {
.name = "Line Playback Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_output_nominal_info,
@ -1250,7 +1250,7 @@ static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_echo_intput_nominal_level = {
static const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
.name = "Line Capture Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_input_nominal_info,
@ -1477,7 +1477,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_echo_digital_mode_switch = {
static const struct snd_kcontrol_new snd_echo_digital_mode_switch = {
.name = "Digital mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_digital_mode_info,
@ -1527,7 +1527,7 @@ static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
static const struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
.name = "S/PDIF mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_spdif_mode_info,
@ -1600,7 +1600,7 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_echo_clock_source_switch = {
static const struct snd_kcontrol_new snd_echo_clock_source_switch = {
.name = "Sample Clock Source",
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_echo_clock_source_info,
@ -1643,7 +1643,7 @@ static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_echo_phantom_power_switch = {
static const struct snd_kcontrol_new snd_echo_phantom_power_switch = {
.name = "Phantom power Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_phantom_power_info,
@ -1686,7 +1686,7 @@ static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_echo_automute_switch = {
static const struct snd_kcontrol_new snd_echo_automute_switch = {
.name = "Digital Capture Switch (automute)",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_automute_info,
@ -1713,7 +1713,7 @@ static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
return 1;
}
static struct snd_kcontrol_new snd_echo_vumeters_switch = {
static const struct snd_kcontrol_new snd_echo_vumeters_switch = {
.name = "VU-meters Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.access = SNDRV_CTL_ELEM_ACCESS_WRITE,
@ -1751,7 +1751,7 @@ static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_echo_vumeters = {
static const struct snd_kcontrol_new snd_echo_vumeters = {
.name = "VU-meters",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
@ -1804,7 +1804,7 @@ static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_echo_channels_info = {
static const struct snd_kcontrol_new snd_echo_channels_info = {
.name = "Channels info",
.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,

View File

@ -1112,7 +1112,7 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
static const struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog/Digital Output Jack",
@ -1171,7 +1171,7 @@ static int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
static const struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1181,7 +1181,7 @@ static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
.get = snd_emu10k1x_spdif_get_mask
};
static struct snd_kcontrol_new snd_emu10k1x_spdif_control =
static const struct snd_kcontrol_new snd_emu10k1x_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),

View File

@ -795,7 +795,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu1010_internal_clock =
static const struct snd_kcontrol_new snd_emu1010_internal_clock =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@ -847,7 +847,7 @@ static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu1010_optical_out = {
static const struct snd_kcontrol_new snd_emu1010_optical_out = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Optical Output Mode",
@ -898,7 +898,7 @@ static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu1010_optical_in = {
static const struct snd_kcontrol_new snd_emu1010_optical_in = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Optical Input Mode",
@ -978,7 +978,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_audigy_i2c_capture_source =
static const struct snd_kcontrol_new snd_audigy_i2c_capture_source =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
@ -1177,7 +1177,7 @@ static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
static const struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1187,7 +1187,7 @@ static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
.get = snd_emu10k1_spdif_get_mask
};
static struct snd_kcontrol_new snd_emu10k1_spdif_control =
static const struct snd_kcontrol_new snd_emu10k1_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@ -1293,7 +1293,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1_send_routing_control =
static const struct snd_kcontrol_new snd_emu10k1_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1364,7 +1364,7 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1_send_volume_control =
static const struct snd_kcontrol_new snd_emu10k1_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1429,7 +1429,7 @@ static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1_attn_control =
static const struct snd_kcontrol_new snd_emu10k1_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1501,7 +1501,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
static const struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1568,7 +1568,7 @@ static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
}
static struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
static const struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1626,7 +1626,7 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
static const struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1691,7 +1691,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_emu10k1_shared_spdif =
static const struct snd_kcontrol_new snd_emu10k1_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "SB Live Analog/Digital Output Jack",
@ -1700,7 +1700,7 @@ static struct snd_kcontrol_new snd_emu10k1_shared_spdif =
.put = snd_emu10k1_shared_spdif_put
};
static struct snd_kcontrol_new snd_audigy_shared_spdif =
static const struct snd_kcontrol_new snd_audigy_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Audigy Analog/Digital Output Jack",
@ -1738,7 +1738,7 @@ static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
}
static struct snd_kcontrol_new snd_audigy_capture_boost =
static const struct snd_kcontrol_new snd_audigy_capture_boost =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Extra Boost",

View File

@ -1542,7 +1542,7 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
return change;
}
static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
static const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Captured FX8010 Outputs",
.info = snd_emu10k1_pcm_efx_voices_mask_info,

View File

@ -1530,7 +1530,7 @@ static int snd_es1373_rear_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ens1373_rear =
static const struct snd_kcontrol_new snd_ens1373_rear =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 2ch->4ch Copy Switch",
@ -1575,7 +1575,7 @@ static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new snd_ens1373_line =
static const struct snd_kcontrol_new snd_ens1373_line =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line In->Rear Out Switch",

View File

@ -580,6 +580,7 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec,
has_multiple_pins = 1;
if (has_multiple_pins && type == AUTO_PIN_MIC)
has_multiple_pins &= check_mic_location_need(codec, cfg, input);
has_multiple_pins |= codec->force_pin_prefix;
return hda_get_input_pin_label(codec, &cfg->inputs[input],
cfg->inputs[input].pin,
has_multiple_pins);

View File

@ -1965,7 +1965,7 @@ static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
return 1;
}
static struct snd_kcontrol_new vmaster_mute_mode = {
static const struct snd_kcontrol_new vmaster_mute_mode = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mute-LED Mode",
.info = vmaster_mute_mode_info,
@ -2705,7 +2705,7 @@ static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new spdif_share_sw = {
static const struct snd_kcontrol_new spdif_share_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC958 Default PCM Playback Switch",
.info = snd_ctl_boolean_mono_info,

View File

@ -256,6 +256,7 @@ struct hda_codec {
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
unsigned int power_save_node:1; /* advanced PM for each widget */
unsigned int auto_runtime_pm:1; /* enable automatic codec runtime pm */
unsigned int force_pin_prefix:1; /* Add location prefix */
#ifdef CONFIG_PM
unsigned long power_on_acct;
unsigned long power_off_acct;

View File

@ -196,6 +196,9 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
if (val >= 0)
spec->suppress_hp_mic_detect = !val;
val = snd_hda_get_bool_hint(codec, "vmaster");
if (val >= 0)
spec->suppress_vmaster = !val;
if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
spec->mixer_nid = val;
@ -1125,6 +1128,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
*index = 0;
if (cfg->line_outs == 1 && !spec->multi_ios &&
!codec->force_pin_prefix &&
!cfg->hp_outs && !cfg->speaker_outs)
return spec->vmaster_mute.hook ? "PCM" : "Master";
@ -1132,6 +1136,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
* use it master (or "PCM" if a vmaster hook is present)
*/
if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
!codec->force_pin_prefix &&
!spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
return spec->vmaster_mute.hook ? "PCM" : "Master";
@ -5031,7 +5036,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
}
/* if we have no master control, let's create it */
if (!spec->no_analog &&
if (!spec->no_analog && !spec->suppress_vmaster &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
spec->vmaster_tlv, slave_pfxs,
@ -5039,7 +5044,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
if (!spec->no_analog &&
if (!spec->no_analog && !spec->suppress_vmaster &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, slave_pfxs,

View File

@ -229,6 +229,7 @@ struct hda_gen_spec {
unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
unsigned int power_down_unused:1; /* power down unused widgets */
unsigned int dac_min_mute:1; /* minimal = mute for DACs */
unsigned int suppress_vmaster:1; /* don't create vmaster kctls */
/* other internal flags */
unsigned int no_analog:1; /* digital I/O only */

View File

@ -77,6 +77,7 @@ enum {
POS_FIX_POSBUF,
POS_FIX_VIACOMBO,
POS_FIX_COMBO,
POS_FIX_SKL,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
@ -148,7 +149,7 @@ module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "DMA pointer read method."
"(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO).");
"(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
@ -369,8 +370,10 @@ enum {
#define IS_KBL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d71)
#define IS_KBL_H(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa2f0)
#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
#define IS_GLK(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x3198)
#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) || \
IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci)
IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci) || \
IS_GLK(pci)
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
@ -534,9 +537,101 @@ static void bxt_reduce_dma_latency(struct azx *chip)
{
u32 val;
val = azx_readl(chip, SKL_EM4L);
val = azx_readl(chip, VS_EM4L);
val &= (0x3 << 20);
azx_writel(chip, SKL_EM4L, val);
azx_writel(chip, VS_EM4L, val);
}
/*
* ML_LCAP bits:
* bit 0: 6 MHz Supported
* bit 1: 12 MHz Supported
* bit 2: 24 MHz Supported
* bit 3: 48 MHz Supported
* bit 4: 96 MHz Supported
* bit 5: 192 MHz Supported
*/
static int intel_get_lctl_scf(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
static int preferred_bits[] = { 2, 3, 1, 4, 5 };
u32 val, t;
int i;
val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP);
for (i = 0; i < ARRAY_SIZE(preferred_bits); i++) {
t = preferred_bits[i];
if (val & (1 << t))
return t;
}
dev_warn(chip->card->dev, "set audio clock frequency to 6MHz");
return 0;
}
static int intel_ml_lctl_set_power(struct azx *chip, int state)
{
struct hdac_bus *bus = azx_bus(chip);
u32 val;
int timeout;
/*
* the codecs are sharing the first link setting by default
* If other links are enabled for stream, they need similar fix
*/
val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
val &= ~AZX_MLCTL_SPA;
val |= state << AZX_MLCTL_SPA_SHIFT;
writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
/* wait for CPA */
timeout = 50;
while (timeout) {
if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) &
AZX_MLCTL_CPA) == (state << AZX_MLCTL_CPA_SHIFT))
return 0;
timeout--;
udelay(10);
}
return -1;
}
static void intel_init_lctl(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
u32 val;
int ret;
/* 0. check lctl register value is correct or not */
val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
/* if SCF is already set, let's use it */
if ((val & ML_LCTL_SCF_MASK) != 0)
return;
/*
* Before operating on SPA, CPA must match SPA.
* Any deviation may result in undefined behavior.
*/
if (((val & AZX_MLCTL_SPA) >> AZX_MLCTL_SPA_SHIFT) !=
((val & AZX_MLCTL_CPA) >> AZX_MLCTL_CPA_SHIFT))
return;
/* 1. turn link down: set SPA to 0 and wait CPA to 0 */
ret = intel_ml_lctl_set_power(chip, 0);
udelay(100);
if (ret)
goto set_spa;
/* 2. update SCF to select a properly audio clock*/
val &= ~ML_LCTL_SCF_MASK;
val |= intel_get_lctl_scf(chip);
writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
set_spa:
/* 4. turn link up: set SPA to 1 and wait CPA to 1 */
intel_ml_lctl_set_power(chip, 1);
udelay(100);
}
static void hda_intel_init_chip(struct azx *chip, bool full_reset)
@ -564,6 +659,9 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset)
/* reduce dma latency to avoid noise */
if (IS_BXT(pci))
bxt_reduce_dma_latency(chip);
if (bus->mlcap != NULL)
intel_init_lctl(chip);
}
/* calculate runtime delay from LPIB */
@ -815,6 +913,31 @@ static unsigned int azx_via_get_position(struct azx *chip,
return bound_pos + mod_dma_pos;
}
static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
struct azx_dev *azx_dev)
{
return _snd_hdac_chip_readl(azx_bus(chip),
AZX_REG_VS_SDXDPIB_XBASE +
(AZX_REG_VS_SDXDPIB_XINTERVAL *
azx_dev->core.index));
}
/* get the current DMA position with correction on SKL+ chips */
static unsigned int azx_get_pos_skl(struct azx *chip, struct azx_dev *azx_dev)
{
/* DPIB register gives a more accurate position for playback */
if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return azx_skl_get_dpib_pos(chip, azx_dev);
/* For capture, we need to read posbuf, but it requires a delay
* for the possible boundary overlap; the read of DPIB fetches the
* actual posbuf
*/
udelay(20);
azx_skl_get_dpib_pos(chip, azx_dev);
return azx_get_pos_posbuf(chip, azx_dev);
}
#ifdef CONFIG_PM
static DEFINE_MUTEX(card_list_lock);
static LIST_HEAD(card_list);
@ -1351,6 +1474,7 @@ static int check_position_fix(struct azx *chip, int fix)
case POS_FIX_POSBUF:
case POS_FIX_VIACOMBO:
case POS_FIX_COMBO:
case POS_FIX_SKL:
return fix;
}
@ -1371,6 +1495,10 @@ static int check_position_fix(struct azx *chip, int fix)
dev_dbg(chip->card->dev, "Using LPIB position fix\n");
return POS_FIX_LPIB;
}
if (IS_SKL_PLUS(chip->pci)) {
dev_dbg(chip->card->dev, "Using SKL position fix\n");
return POS_FIX_SKL;
}
return POS_FIX_AUTO;
}
@ -1382,6 +1510,7 @@ static void assign_position_fix(struct azx *chip, int fix)
[POS_FIX_POSBUF] = azx_get_pos_posbuf,
[POS_FIX_VIACOMBO] = azx_via_get_position,
[POS_FIX_COMBO] = azx_get_pos_lpib,
[POS_FIX_SKL] = azx_get_pos_skl,
};
chip->get_position[0] = chip->get_position[1] = callbacks[fix];
@ -1390,7 +1519,7 @@ static void assign_position_fix(struct azx *chip, int fix)
if (fix == POS_FIX_COMBO)
chip->get_position[1] = NULL;
if (fix == POS_FIX_POSBUF &&
if ((fix == POS_FIX_POSBUF || fix == POS_FIX_SKL) &&
(chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
chip->get_delay[0] = chip->get_delay[1] =
azx_get_delay_from_lpib;

View File

@ -857,7 +857,7 @@ static int chipio_write_address(struct hda_codec *codec,
chip_addx >> 16);
}
spec->curr_chip_addx = (res < 0) ? ~0UL : chip_addx;
spec->curr_chip_addx = (res < 0) ? ~0U : chip_addx;
return res;
}
@ -882,7 +882,7 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
/*If no error encountered, automatically increment the address
as per chip behaviour*/
spec->curr_chip_addx = (res != -EIO) ?
(spec->curr_chip_addx + 4) : ~0UL;
(spec->curr_chip_addx + 4) : ~0U;
return res;
}
@ -933,7 +933,7 @@ static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
/*If no error encountered, automatically increment the address
as per chip behaviour*/
spec->curr_chip_addx = (res != -EIO) ?
(spec->curr_chip_addx + 4) : ~0UL;
(spec->curr_chip_addx + 4) : ~0U;
return res;
}
@ -1168,7 +1168,7 @@ static int dspio_write_multiple(struct hda_codec *codec,
int status = 0;
unsigned int count;
if ((buffer == NULL))
if (buffer == NULL)
return -EINVAL;
count = 0;
@ -1210,7 +1210,7 @@ static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
unsigned int skip_count;
unsigned int dummy;
if ((buffer == NULL))
if (buffer == NULL)
return -1;
count = 0;

View File

@ -52,6 +52,12 @@ struct conexant_spec {
bool dc_enable;
unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
struct nid_path *dc_mode_path;
int mute_led_polarity;
unsigned int gpio_led;
unsigned int gpio_mute_led_mask;
unsigned int gpio_mic_led_mask;
};
@ -264,6 +270,7 @@ enum {
CXT_FIXUP_HP_DOCK,
CXT_FIXUP_HP_SPECTRE,
CXT_FIXUP_HP_GATE_MIC,
CXT_FIXUP_MUTE_LED_GPIO,
};
/* for hda_fixup_thinkpad_acpi() */
@ -646,6 +653,74 @@ static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
snd_hda_jack_set_gating_jack(codec, 0x19, 0x16);
}
/* update LED status via GPIO */
static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
bool enabled)
{
struct conexant_spec *spec = codec->spec;
unsigned int oldval = spec->gpio_led;
if (spec->mute_led_polarity)
enabled = !enabled;
if (enabled)
spec->gpio_led &= ~mask;
else
spec->gpio_led |= mask;
if (spec->gpio_led != oldval)
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
spec->gpio_led);
}
/* turn on/off mute LED via GPIO per vmaster hook */
static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled)
{
struct hda_codec *codec = private_data;
struct conexant_spec *spec = codec->spec;
cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
}
/* turn on/off mic-mute LED via GPIO per capture hook */
static void cxt_fixup_gpio_mic_mute_hook(struct hda_codec *codec,
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct conexant_spec *spec = codec->spec;
if (ucontrol)
cxt_update_gpio_led(codec, spec->gpio_mic_led_mask,
ucontrol->value.integer.value[0] ||
ucontrol->value.integer.value[1]);
}
static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
static const struct hda_verb gpio_init[] = {
{ 0x01, AC_VERB_SET_GPIO_MASK, 0x03 },
{ 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 },
{}
};
codec_info(codec, "action: %d gpio_led: %d\n", action, spec->gpio_led);
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook;
spec->gen.cap_sync_hook = cxt_fixup_gpio_mic_mute_hook;
spec->gpio_led = 0;
spec->mute_led_polarity = 0;
spec->gpio_mute_led_mask = 0x01;
spec->gpio_mic_led_mask = 0x02;
}
snd_hda_add_verbs(codec, gpio_init);
if (spec->gpio_led)
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
spec->gpio_led);
}
/* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
@ -799,6 +874,10 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_hp_gate_mic_jack,
},
[CXT_FIXUP_MUTE_LED_GPIO] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_mute_led_gpio,
},
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@ -851,6 +930,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
@ -882,6 +962,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
{ .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
{ .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" },
{ .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" },
{}
};

View File

@ -177,6 +177,7 @@ struct hdmi_spec {
bool i915_bound; /* was i915 bound in this driver? */
struct hdac_chmap chmap;
hda_nid_t vendor_nid;
};
#ifdef CONFIG_SND_HDA_I915
@ -383,7 +384,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new eld_bytes_ctl = {
static const struct snd_kcontrol_new eld_bytes_ctl = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
@ -2372,6 +2373,7 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
}
#define INTEL_VENDOR_NID 0x08
#define INTEL_GLK_VENDOR_NID 0x0B
#define INTEL_GET_VENDOR_VERB 0xf81
#define INTEL_SET_VENDOR_VERB 0x781
#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
@ -2381,14 +2383,15 @@ static void intel_haswell_enable_all_pins(struct hda_codec *codec,
bool update_tree)
{
unsigned int vendor_param;
struct hdmi_spec *spec = codec->spec;
vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
INTEL_GET_VENDOR_VERB, 0);
if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
return;
vendor_param |= INTEL_EN_ALL_PIN_CVTS;
vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
if (vendor_param == -1)
return;
@ -2400,8 +2403,9 @@ static void intel_haswell_enable_all_pins(struct hda_codec *codec,
static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
{
unsigned int vendor_param;
struct hdmi_spec *spec = codec->spec;
vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
INTEL_GET_VENDOR_VERB, 0);
if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
return;
@ -2409,7 +2413,7 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
/* enable DP1.2 mode */
vendor_param |= INTEL_EN_DP12;
snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB);
snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
snd_hda_codec_write_cache(codec, spec->vendor_nid, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
}
@ -2503,7 +2507,7 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
}
/* Intel Haswell and onwards; audio component with eld notifier */
static int patch_i915_hsw_hdmi(struct hda_codec *codec)
static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid)
{
struct hdmi_spec *spec;
int err;
@ -2520,6 +2524,7 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec)
spec = codec->spec;
codec->dp_mst = true;
spec->dyn_pcm_assign = true;
spec->vendor_nid = vendor_nid;
intel_haswell_enable_all_pins(codec, true);
intel_haswell_fixup_enable_dp12(codec);
@ -2548,6 +2553,16 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec)
return 0;
}
static int patch_i915_hsw_hdmi(struct hda_codec *codec)
{
return intel_hsw_common_init(codec, INTEL_VENDOR_NID);
}
static int patch_i915_glk_hdmi(struct hda_codec *codec)
{
return intel_hsw_common_init(codec, INTEL_GLK_VENDOR_NID);
}
/* Intel Baytrail and Braswell; with eld notifier */
static int patch_i915_byt_hdmi(struct hda_codec *codec)
{
@ -3800,7 +3815,7 @@ HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),

View File

@ -1800,6 +1800,7 @@ enum {
ALC882_FIXUP_NO_PRIMARY_HP,
ALC887_FIXUP_ASUS_BASS,
ALC887_FIXUP_BASS_CHMAP,
ALC1220_FIXUP_GB_DUAL_CODECS,
};
static void alc889_fixup_coef(struct hda_codec *codec,
@ -1962,6 +1963,61 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
static void alc_fixup_bass_chmap(struct hda_codec *codec,
const struct hda_fixup *fix, int action);
/* For dual-codec configuration, we need to disable some features to avoid
* conflicts of kctls and PCM streams
*/
static void alc_fixup_dual_codecs(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
if (action != HDA_FIXUP_ACT_PRE_PROBE)
return;
/* disable vmaster */
spec->gen.suppress_vmaster = 1;
/* auto-mute and auto-mic switch don't work with multiple codecs */
spec->gen.suppress_auto_mute = 1;
spec->gen.suppress_auto_mic = 1;
/* disable aamix as well */
spec->gen.mixer_nid = 0;
/* add location prefix to avoid conflicts */
codec->force_pin_prefix = 1;
}
static void rename_ctl(struct hda_codec *codec, const char *oldname,
const char *newname)
{
struct snd_kcontrol *kctl;
kctl = snd_hda_find_mixer_ctl(codec, oldname);
if (kctl)
strcpy(kctl->id.name, newname);
}
static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
{
alc_fixup_dual_codecs(codec, fix, action);
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
/* override card longname to provide a unique UCM profile */
strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs");
break;
case HDA_FIXUP_ACT_BUILD:
/* rename Capture controls depending on the codec */
rename_ctl(codec, "Capture Volume",
codec->addr == 0 ?
"Rear-Panel Capture Volume" :
"Front-Panel Capture Volume");
rename_ctl(codec, "Capture Switch",
codec->addr == 0 ?
"Rear-Panel Capture Switch" :
"Front-Panel Capture Switch");
break;
}
}
static const struct hda_fixup alc882_fixups[] = {
[ALC882_FIXUP_ABIT_AW9D_MAX] = {
.type = HDA_FIXUP_PINS,
@ -2198,6 +2254,10 @@ static const struct hda_fixup alc882_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_bass_chmap,
},
[ALC1220_FIXUP_GB_DUAL_CODECS] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc1220_fixup_gb_dual_codecs,
},
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@ -2267,6 +2327,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
@ -4663,7 +4724,6 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec,
{ 0x1b, 0x21114000 }, /* dock speaker pin */
{}
};
struct snd_kcontrol *kctl;
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
@ -4678,12 +4738,10 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec,
/* this is a bit tricky; give more sane names for the main
* (tablet) speaker and the dock speaker, respectively
*/
kctl = snd_hda_find_mixer_ctl(codec, "Speaker Playback Switch");
if (kctl)
strcpy(kctl->id.name, "Dock Speaker Playback Switch");
kctl = snd_hda_find_mixer_ctl(codec, "Bass Speaker Playback Switch");
if (kctl)
strcpy(kctl->id.name, "Speaker Playback Switch");
rename_ctl(codec, "Speaker Playback Switch",
"Dock Speaker Playback Switch");
rename_ctl(codec, "Bass Speaker Playback Switch",
"Speaker Playback Switch");
break;
}
}
@ -4766,6 +4824,30 @@ static void alc280_fixup_hp_9480m(struct hda_codec *codec,
}
}
static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
const struct hda_fixup *fix,
int action)
{
alc_fixup_dual_codecs(codec, fix, action);
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
/* override card longname to provide a unique UCM profile */
strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs");
break;
case HDA_FIXUP_ACT_BUILD:
/* rename Capture controls depending on the codec */
rename_ctl(codec, "Capture Volume",
codec->addr == 0 ?
"Rear-Panel Capture Volume" :
"Front-Panel Capture Volume");
rename_ctl(codec, "Capture Switch",
codec->addr == 0 ?
"Rear-Panel Capture Switch" :
"Front-Panel Capture Switch");
break;
}
}
/* for hda_fixup_thinkpad_acpi() */
#include "thinkpad_helper.c"
@ -4833,6 +4915,8 @@ enum {
ALC290_FIXUP_SUBWOOFER_HSJACK,
ALC269_FIXUP_THINKPAD_ACPI,
ALC269_FIXUP_DMIC_THINKPAD_ACPI,
ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_FIXUP_HEADSET_MODE,
@ -4872,6 +4956,12 @@ enum {
ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER,
ALC269_FIXUP_ATIV_BOOK_8,
ALC221_FIXUP_HP_MIC_NO_PRESENCE,
ALC256_FIXUP_ASUS_HEADSET_MODE,
ALC256_FIXUP_ASUS_MIC,
ALC256_FIXUP_ASUS_AIO_GPIO2,
ALC233_FIXUP_ASUS_MIC_NO_PRESENCE,
ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE,
ALC233_FIXUP_LENOVO_MULTI_CODECS,
};
static const struct hda_fixup alc269_fixups[] = {
@ -5289,6 +5379,24 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_THINKPAD_ACPI,
},
[ALC255_FIXUP_ACER_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
{ }
},
.chained = true,
.chain_id = ALC255_FIXUP_HEADSET_MODE
},
[ALC255_FIXUP_ASUS_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
{ }
},
.chained = true,
.chain_id = ALC255_FIXUP_HEADSET_MODE
},
[ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@ -5579,6 +5687,54 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE
},
[ALC256_FIXUP_ASUS_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode,
},
[ALC256_FIXUP_ASUS_MIC] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x13, 0x90a60160 }, /* use as internal mic */
{ 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
{ }
},
.chained = true,
.chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
},
[ALC256_FIXUP_ASUS_AIO_GPIO2] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* Set up GPIO2 for the speaker amp */
{ 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
{ 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
{ 0x01, AC_VERB_SET_GPIO_DATA, 0x04 },
{}
},
},
[ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
{ }
},
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC
},
[ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
/* Enables internal speaker */
{0x20, AC_VERB_SET_COEF_INDEX, 0x40},
{0x20, AC_VERB_SET_PROC_COEF, 0x8800},
{}
},
.chained = true,
.chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE
},
[ALC233_FIXUP_LENOVO_MULTI_CODECS] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc233_alc662_fixup_lenovo_dual_codecs,
},
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@ -5692,15 +5848,27 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2),
SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
@ -5721,6 +5889,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8),
SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
@ -5875,6 +6044,18 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x21, 0x03211020}
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1025, "Acer", ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
{0x12, 0x90a601c0},
{0x14, 0x90171120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
{0x14, 0x90170110},
{0x1b, 0x90a70130},
{0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
{0x1a, 0x90a70130},
{0x1b, 0x90170110},
{0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
{0x12, 0xb7a60130},
@ -5995,6 +6176,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC256_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
{0x14, 0x90170110},
{0x1b, 0x90a70130},
{0x21, 0x04211020}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
{0x14, 0x90170110},
{0x1b, 0x90a70130},
{0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
{0x14, 0x90170110},
@ -6714,6 +6903,7 @@ enum {
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
ALC662_FIXUP_ASUS_Nx50,
ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
ALC668_FIXUP_ASUS_Nx51,
ALC891_FIXUP_HEADSET_MODE,
ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
@ -6721,6 +6911,7 @@ enum {
ALC892_FIXUP_ASROCK_MOBO,
ALC662_FIXUP_USI_FUNC,
ALC662_FIXUP_USI_HEADSET_MODE,
ALC662_FIXUP_LENOVO_MULTI_CODECS,
};
static const struct hda_fixup alc662_fixups[] = {
@ -6967,14 +7158,21 @@ static const struct hda_fixup alc662_fixups[] = {
.chained = true,
.chain_id = ALC662_FIXUP_BASS_1A
},
[ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc668,
.chain_id = ALC662_FIXUP_BASS_CHMAP
},
[ALC668_FIXUP_ASUS_Nx51] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{0x1a, 0x90170151}, /* bass speaker */
{ 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
{ 0x1a, 0x90170151 }, /* bass speaker */
{ 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
{}
},
.chained = true,
.chain_id = ALC662_FIXUP_BASS_CHMAP,
.chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
},
[ALC891_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
@ -7019,6 +7217,10 @@ static const struct hda_fixup alc662_fixups[] = {
.chained = true,
.chain_id = ALC662_FIXUP_USI_FUNC
},
[ALC662_FIXUP_LENOVO_MULTI_CODECS] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc233_alc662_fixup_lenovo_dual_codecs,
},
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@ -7056,6 +7258,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE),
SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),

View File

@ -432,7 +432,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
return 0;
}
static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status =
static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,

View File

@ -719,7 +719,7 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st
return ndata != data;
}
static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
static const struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@ -728,7 +728,7 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
.count = 8,
};
static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense = {
static const struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Output Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,

View File

@ -279,7 +279,7 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru
return val != nval;
}
static struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 = {
static const struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Mixer To AC97",
.info = snd_ice1712_digmix_route_ac97_info,
@ -1410,7 +1410,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch = {
.private_value = 10,
};
static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = {
static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),
.info = snd_ice1712_pro_mixer_switch_info,
@ -1432,7 +1432,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume = {
.tlv = { .p = db_scale_playback }
};
static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume = {
static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),
.info = snd_ice1712_pro_mixer_volume_info,
@ -1630,7 +1630,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_ice1712_eeprom = {
static const struct snd_kcontrol_new snd_ice1712_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1712 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@ -1666,7 +1666,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_ice1712_spdif_default =
static const struct snd_kcontrol_new snd_ice1712_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@ -1717,7 +1717,7 @@ static int snd_ice1712_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_ice1712_spdif_maskc =
static const struct snd_kcontrol_new snd_ice1712_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1726,7 +1726,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc =
.get = snd_ice1712_spdif_maskc_get,
};
static struct snd_kcontrol_new snd_ice1712_spdif_maskp =
static const struct snd_kcontrol_new snd_ice1712_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1753,7 +1753,7 @@ static int snd_ice1712_spdif_stream_put(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_ice1712_spdif_stream =
static const struct snd_kcontrol_new snd_ice1712_spdif_stream =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE),
@ -1878,7 +1878,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ice1712_pro_internal_clock = {
static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_ice1712_pro_internal_clock_info,
@ -1943,7 +1943,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont
return change;
}
static struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default = {
static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock Default",
.info = snd_ice1712_pro_internal_clock_default_info,
@ -1974,7 +1974,7 @@ static int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ice1712_pro_rate_locking = {
static const struct snd_kcontrol_new snd_ice1712_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_ice1712_pro_rate_locking_info,
@ -2005,7 +2005,7 @@ static int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ice1712_pro_rate_reset = {
static const struct snd_kcontrol_new snd_ice1712_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_ice1712_pro_rate_reset_info,
@ -2173,7 +2173,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route = {
.put = snd_ice1712_pro_route_analog_put,
};
static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route = {
static const struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_ice1712_pro_route_info,
@ -2215,7 +2215,7 @@ static int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate = {
static const struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Volume Rate",
.info = snd_ice1712_pro_volume_rate_info,
@ -2248,7 +2248,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
static const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,

View File

@ -1598,7 +1598,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_vt1724_eeprom = {
static const struct snd_kcontrol_new snd_vt1724_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1724 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@ -1711,7 +1711,7 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
return val != old;
}
static struct snd_kcontrol_new snd_vt1724_spdif_default =
static const struct snd_kcontrol_new snd_vt1724_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@ -1743,7 +1743,7 @@ static int snd_vt1724_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_vt1724_spdif_maskc =
static const struct snd_kcontrol_new snd_vt1724_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1752,7 +1752,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc =
.get = snd_vt1724_spdif_maskc_get,
};
static struct snd_kcontrol_new snd_vt1724_spdif_maskp =
static const struct snd_kcontrol_new snd_vt1724_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1789,7 +1789,7 @@ static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol,
return old != val;
}
static struct snd_kcontrol_new snd_vt1724_spdif_switch =
static const struct snd_kcontrol_new snd_vt1724_spdif_switch =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* FIXME: the following conflict with IEC958 Playback Route */
@ -1964,7 +1964,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return old_rate != new_rate;
}
static struct snd_kcontrol_new snd_vt1724_pro_internal_clock = {
static const struct snd_kcontrol_new snd_vt1724_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_vt1724_pro_internal_clock_info,
@ -1995,7 +1995,7 @@ static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_vt1724_pro_rate_locking = {
static const struct snd_kcontrol_new snd_vt1724_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_vt1724_pro_rate_locking_info,
@ -2026,7 +2026,7 @@ static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_vt1724_pro_rate_reset = {
static const struct snd_kcontrol_new snd_vt1724_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_vt1724_pro_rate_reset_info,
@ -2151,7 +2151,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route =
.put = snd_vt1724_pro_route_analog_put,
};
static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route = {
static const struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_vt1724_pro_route_info,
@ -2187,7 +2187,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak = {
static const struct snd_kcontrol_new snd_vt1724_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,

View File

@ -645,7 +645,7 @@ static int lola_input_src_put(struct snd_kcontrol *kcontrol,
return lola_set_src_config(chip, mask, true);
}
static struct snd_kcontrol_new lola_input_src_mixer = {
static const struct snd_kcontrol_new lola_input_src_mixer = {
.name = "Digital SRC Capture Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = lola_input_src_info,

View File

@ -899,7 +899,7 @@ static int lx_control_playback_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new lx_control_playback_switch = {
static const struct snd_kcontrol_new lx_control_playback_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.index = 0,

View File

@ -448,7 +448,7 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return changed;
}
static struct snd_kcontrol_new mixart_control_output_switch = {
static const struct snd_kcontrol_new mixart_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = mixart_sw_info, /* shared */
@ -1024,7 +1024,7 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
return changed;
}
static struct snd_kcontrol_new mixart_control_monitor_vol = {
static const struct snd_kcontrol_new mixart_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@ -1091,7 +1091,7 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return (changed != 0);
}
static struct snd_kcontrol_new mixart_control_monitor_sw = {
static const struct snd_kcontrol_new mixart_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Switch",
.info = mixart_sw_info, /* shared */

View File

@ -767,6 +767,8 @@ static int get_oxygen_model(struct oxygen *chip,
[MODEL_FANTASIA] = "TempoTec HiFier Fantasia",
[MODEL_SERENADE] = "TempoTec HiFier Serenade",
[MODEL_HG2PCI] = "CMI8787-HG2PCI",
[MODEL_XONAR_DG] = "Xonar DG",
[MODEL_XONAR_DGX] = "Xonar DGX",
};
chip->model = model_generic;
@ -829,12 +831,8 @@ static int get_oxygen_model(struct oxygen *chip,
chip->model.dac_channels_mixer = 2;
break;
case MODEL_XONAR_DG:
chip->model = model_xonar_dg;
chip->model.shortname = "Xonar DG";
break;
case MODEL_XONAR_DGX:
chip->model = model_xonar_dg;
chip->model.shortname = "Xonar DGX";
break;
}
if (id->driver_data == MODEL_MERIDIAN ||

View File

@ -744,7 +744,7 @@ static int hr222_mic_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new hr222_control_mic_level = {
static const struct snd_kcontrol_new hr222_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@ -794,7 +794,7 @@ static int hr222_mic_boost_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new hr222_control_mic_boost = {
static const struct snd_kcontrol_new hr222_control_mic_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@ -836,7 +836,7 @@ static int hr222_phantom_power_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new hr222_phantom_power_switch = {
static const struct snd_kcontrol_new hr222_phantom_power_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Phantom Power Switch",
.info = hr222_phantom_power_info,

View File

@ -235,7 +235,7 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new pcxhr_control_output_switch = {
static const struct snd_kcontrol_new pcxhr_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = pcxhr_sw_info, /* shared */
@ -460,7 +460,7 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new pcxhr_control_pcm_switch = {
static const struct snd_kcontrol_new pcxhr_control_pcm_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.count = PCXHR_PLAYBACK_STREAMS,
@ -509,7 +509,7 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
static const struct snd_kcontrol_new pcxhr_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@ -562,7 +562,7 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
return (changed != 0);
}
static struct snd_kcontrol_new pcxhr_control_monitor_sw = {
static const struct snd_kcontrol_new pcxhr_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Playback Switch",
.info = pcxhr_sw_info, /* shared */
@ -697,7 +697,7 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
return ret;
}
static struct snd_kcontrol_new pcxhr_control_audio_src = {
static const struct snd_kcontrol_new pcxhr_control_audio_src = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = pcxhr_audio_src_info,
@ -798,7 +798,7 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
return ret;
}
static struct snd_kcontrol_new pcxhr_control_clock_type = {
static const struct snd_kcontrol_new pcxhr_control_clock_type = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Clock Mode",
.info = pcxhr_clock_type_info,
@ -842,7 +842,7 @@ static int pcxhr_clock_rate_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new pcxhr_control_clock_rate = {
static const struct snd_kcontrol_new pcxhr_control_clock_rate = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "Clock Rates",
@ -1017,14 +1017,14 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new pcxhr_control_playback_iec958_mask = {
static const struct snd_kcontrol_new pcxhr_control_playback_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
.info = pcxhr_iec958_info,
.get = pcxhr_iec958_mask_get
};
static struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
static const struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.info = pcxhr_iec958_info,
@ -1033,14 +1033,14 @@ static struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
.private_value = 0 /* playback */
};
static struct snd_kcontrol_new pcxhr_control_capture_iec958_mask = {
static const struct snd_kcontrol_new pcxhr_control_capture_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
.info = pcxhr_iec958_info,
.get = pcxhr_iec958_mask_get
};
static struct snd_kcontrol_new pcxhr_control_capture_iec958 = {
static const struct snd_kcontrol_new pcxhr_control_capture_iec958 = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),

View File

@ -2356,7 +2356,7 @@ static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_spdif_control =
static const struct snd_kcontrol_new snd_trident_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
@ -2419,7 +2419,7 @@ static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_spdif_default =
static const struct snd_kcontrol_new snd_trident_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@ -2452,7 +2452,7 @@ static int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_trident_spdif_mask =
static const struct snd_kcontrol_new snd_trident_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -2514,7 +2514,7 @@ static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_spdif_stream =
static const struct snd_kcontrol_new snd_trident_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -2564,7 +2564,7 @@ static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_ac97_rear_control =
static const struct snd_kcontrol_new snd_trident_ac97_rear_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Rear Path",
@ -2622,7 +2622,7 @@ static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_vol_music_control =
static const struct snd_kcontrol_new snd_trident_vol_music_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Music Playback Volume",
@ -2633,7 +2633,7 @@ static struct snd_kcontrol_new snd_trident_vol_music_control =
.tlv = { .p = db_scale_gvol },
};
static struct snd_kcontrol_new snd_trident_vol_wave_control =
static const struct snd_kcontrol_new snd_trident_vol_wave_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Wave Playback Volume",
@ -2700,7 +2700,7 @@ static int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_pcm_vol_control =
static const struct snd_kcontrol_new snd_trident_pcm_vol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Front Playback Volume",
@ -2764,7 +2764,7 @@ static int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_pcm_pan_control =
static const struct snd_kcontrol_new snd_trident_pcm_pan_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Pan Playback Control",
@ -2821,7 +2821,7 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
static struct snd_kcontrol_new snd_trident_pcm_rvol_control =
static const struct snd_kcontrol_new snd_trident_pcm_rvol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Reverb Playback Volume",
@ -2877,7 +2877,7 @@ static int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_trident_pcm_cvol_control =
static const struct snd_kcontrol_new snd_trident_pcm_cvol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Chorus Playback Volume",

View File

@ -1683,7 +1683,7 @@ static int snd_via8233_dxs3_spdif_put(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_via8233_dxs3_spdif_control = {
static const struct snd_kcontrol_new snd_via8233_dxs3_spdif_control = {
.name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_via8233_dxs3_spdif_info,
@ -1772,7 +1772,7 @@ static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_dxs, -4650, 150, 1);
static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control = {
static const struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@ -1783,7 +1783,7 @@ static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control = {
.tlv = { .p = db_scale_dxs }
};
static struct snd_kcontrol_new snd_via8233_dxs_volume_control = {
static const struct snd_kcontrol_new snd_via8233_dxs_volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.device = 0,
/* .subdevice set later */

View File

@ -945,7 +945,7 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
static struct snd_kcontrol_new vx_control_input_level = {
static const struct snd_kcontrol_new vx_control_input_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@ -956,7 +956,7 @@ static struct snd_kcontrol_new vx_control_input_level = {
.tlv = { .p = db_scale_mic },
};
static struct snd_kcontrol_new vx_control_mic_level = {
static const struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),

View File

@ -1316,7 +1316,7 @@ static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ymfpci_spdif_default =
static const struct snd_kcontrol_new snd_ymfpci_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@ -1344,7 +1344,7 @@ static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_ymfpci_spdif_mask =
static const struct snd_kcontrol_new snd_ymfpci_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1391,7 +1391,7 @@ static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
static struct snd_kcontrol_new snd_ymfpci_spdif_stream =
static const struct snd_kcontrol_new snd_ymfpci_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@ -1439,7 +1439,7 @@ static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_
return reg != old_reg;
}
static struct snd_kcontrol_new snd_ymfpci_drec_source = {
static const struct snd_kcontrol_new snd_ymfpci_drec_source = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Direct Recording Source",
@ -1609,7 +1609,7 @@ static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
static struct snd_kcontrol_new snd_ymfpci_dup4ch = {
static const struct snd_kcontrol_new snd_ymfpci_dup4ch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "4ch Duplication",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@ -1712,7 +1712,7 @@ static int snd_ymfpci_gpio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
return 0;
}
static struct snd_kcontrol_new snd_ymfpci_rear_shared = {
static const struct snd_kcontrol_new snd_ymfpci_rear_shared = {
.name = "Shared Rear/Line-In Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_ymfpci_gpio_sw_info,
@ -1776,7 +1776,7 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
return 0;
}
static struct snd_kcontrol_new snd_ymfpci_pcm_volume = {
static const struct snd_kcontrol_new snd_ymfpci_pcm_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |

View File

@ -27,27 +27,6 @@
#define SKL_SUSPEND_DELAY 2000
/* Vendor Specific Registers */
#define AZX_REG_VS_EM1 0x1000
#define AZX_REG_VS_INRC 0x1004
#define AZX_REG_VS_OUTRC 0x1008
#define AZX_REG_VS_FIFOTRK 0x100C
#define AZX_REG_VS_FIFOTRK2 0x1010
#define AZX_REG_VS_EM2 0x1030
#define AZX_REG_VS_EM3L 0x1038
#define AZX_REG_VS_EM3U 0x103C
#define AZX_REG_VS_EM4L 0x1040
#define AZX_REG_VS_EM4U 0x1044
#define AZX_REG_VS_LTRC 0x1048
#define AZX_REG_VS_D0I3C 0x104A
#define AZX_REG_VS_PCE 0x104B
#define AZX_REG_VS_L2MAGC 0x1050
#define AZX_REG_VS_L2LAHPT 0x1054
#define AZX_REG_VS_SDXDPIB_XBASE 0x1084
#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
#define AZX_PCIREG_PGCTL 0x44
#define AZX_PGCTL_LSRMD_MASK (1 << 4)
#define AZX_PCIREG_CGCTL 0x48

View File

@ -225,9 +225,9 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
struct soundfont_patch_info patch;
if (count < (int)sizeof(patch))
rc = -EINVAL;
return -EINVAL;
if (copy_from_user(&patch, buf, sizeof(patch)))
rc = -EFAULT;
return -EFAULT;
if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
patch.type <= SNDRV_SFNT_PROBE_DATA)
rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port));

View File

@ -332,6 +332,7 @@ static int snd_usb_audio_dev_free(struct snd_device *device)
static int snd_usb_audio_create(struct usb_interface *intf,
struct usb_device *dev, int idx,
const struct snd_usb_audio_quirk *quirk,
unsigned int usb_id,
struct snd_usb_audio **rchip)
{
struct snd_card *card;
@ -381,8 +382,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
atomic_set(&chip->usage_count, 0);
atomic_set(&chip->shutdown, 0);
chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
chip->usb_id = usb_id;
INIT_LIST_HEAD(&chip->pcm_list);
INIT_LIST_HEAD(&chip->ep_list);
INIT_LIST_HEAD(&chip->midi_list);
@ -569,7 +569,7 @@ static int usb_audio_probe(struct usb_interface *intf,
(vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
(pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
err = snd_usb_audio_create(intf, dev, i, quirk,
&chip);
id, &chip);
if (err < 0)
goto __error;
chip->pm_intf = intf;

View File

@ -430,7 +430,7 @@ static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
}
/* control definition */
static struct snd_kcontrol_new line6_controls[] = {
static const struct snd_kcontrol_new line6_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",

View File

@ -380,7 +380,7 @@ static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol,
}
/* control definition */
static struct snd_kcontrol_new pod_control_monitor = {
static const struct snd_kcontrol_new pod_control_monitor = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Playback Volume",
.index = 0,

View File

@ -250,7 +250,7 @@ static void toneport_start_pcm(unsigned long arg)
}
/* control definition */
static struct snd_kcontrol_new toneport_control_monitor = {
static const struct snd_kcontrol_new toneport_control_monitor = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Playback Volume",
.index = 0,
@ -261,7 +261,7 @@ static struct snd_kcontrol_new toneport_control_monitor = {
};
/* source selector definition */
static struct snd_kcontrol_new toneport_control_source = {
static const struct snd_kcontrol_new toneport_control_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Source",
.index = 0,

View File

@ -1922,7 +1922,7 @@ static int roland_load_put(struct snd_kcontrol *kcontrol,
return changed;
}
static struct snd_kcontrol_new roland_load_ctl = {
static const struct snd_kcontrol_new roland_load_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIDI Input Mode",
.info = roland_load_info,

View File

@ -1172,7 +1172,7 @@ static struct snd_kcontrol_new usb_feature_unit_ctl = {
};
/* the read-only variant */
static struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
static const struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later manually */
.info = mixer_ctl_feature_info,
@ -1745,7 +1745,7 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
}
/* alsa control interface for processing/extension unit */
static struct snd_kcontrol_new mixer_procunit_ctl = {
static const struct snd_kcontrol_new mixer_procunit_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = mixer_ctl_feature_info,
@ -2033,7 +2033,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
}
/* alsa control interface for selector unit */
static struct snd_kcontrol_new mixer_selectunit_ctl = {
static const struct snd_kcontrol_new mixer_selectunit_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = mixer_ctl_selector_info,

Some files were not shown because too many files have changed in this diff Show More