diff options
56 files changed, 1892 insertions, 848 deletions
diff --git a/include/sound/control.h b/include/sound/control.h index 11feeee31e35..aeaed2a05bae 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -75,7 +75,7 @@ struct snd_kcontrol { unsigned long private_value; void *private_data; void (*private_free)(struct snd_kcontrol *kcontrol); - struct snd_kcontrol_volatile vd[0]; /* volatile data */ + struct snd_kcontrol_volatile vd[]; /* volatile data */ }; #define snd_kcontrol(n) list_entry(n, struct snd_kcontrol, list) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 225154a4f2ed..d16a4229209b 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -288,6 +288,10 @@ struct hda_codec { #define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev) #define hda_codec_dev(_dev) (&(_dev)->core.dev) +#define hdac_to_hda_priv(_hdac) \ + container_of(_hdac, struct hdac_hda_priv, codec.core) +#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core) + #define list_for_each_codec(c, bus) \ list_for_each_entry(c, &(bus)->core.codec_list, core.list) #define list_for_each_codec_safe(c, n, bus) \ @@ -362,13 +366,6 @@ struct hda_verb { void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq); -/* unsolicited event */ -static inline void -snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) -{ - snd_hdac_bus_queue_event(&bus->core, res, res_ex); -} - /* cached write */ static inline int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index affedc2801c4..c1f78d9a6e47 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -207,8 +207,8 @@ static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; static inline int snd_hdac_keep_power_up(struct hdac_device *codec) { return 0; } static inline void snd_hdac_enter_pm(struct hdac_device *codec) {} static inline void snd_hdac_leave_pm(struct hdac_device *codec) {} -static inline bool snd_hdac_is_in_pm(struct hdac_device *codec) { return 0; } -static inline bool snd_hdac_is_power_on(struct hdac_device *codec) { return 1; } +static inline bool snd_hdac_is_in_pm(struct hdac_device *codec) { return false; } +static inline bool snd_hdac_is_power_on(struct hdac_device *codec) { return true; } #endif /* @@ -364,16 +364,16 @@ struct hdac_bus { /* link management */ struct list_head hlink_list; bool cmd_dma_state; + + /* factor used to derive STRIPE control value */ + unsigned int sdo_limit; }; int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, const struct hdac_bus_ops *ops); void snd_hdac_bus_exit(struct hdac_bus *bus); -int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, - unsigned int cmd, unsigned int *res); int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, unsigned int cmd, unsigned int *res); -void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); static inline void snd_hdac_codec_link_up(struct hdac_device *codec) { diff --git a/include/sound/intel-nhlt.h b/include/sound/intel-nhlt.h index f657fd8fc0ad..743c2f442280 100644 --- a/include/sound/intel-nhlt.h +++ b/include/sound/intel-nhlt.h @@ -50,7 +50,7 @@ enum nhlt_device_type { struct nhlt_specific_cfg { u32 size; - u8 caps[0]; + u8 caps[]; } __packed; struct nhlt_fmt_cfg { @@ -60,7 +60,7 @@ struct nhlt_fmt_cfg { struct nhlt_fmt { u8 fmt_count; - struct nhlt_fmt_cfg fmt_config[0]; + struct nhlt_fmt_cfg fmt_config[]; } __packed; struct nhlt_endpoint { @@ -80,7 +80,7 @@ struct nhlt_endpoint { struct nhlt_acpi_table { struct acpi_table_header header; u8 endpoint_count; - struct nhlt_endpoint desc[0]; + struct nhlt_endpoint desc[]; } __packed; struct nhlt_resource_desc { diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index b412d3b3d5ff..21edb8ac95eb 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -216,12 +216,12 @@ static int snd_hwdep_dsp_load(struct snd_hwdep *hw, if (info.index >= 32) return -EINVAL; /* check whether the dsp was already loaded */ - if (hw->dsp_loaded & (1 << info.index)) + if (hw->dsp_loaded & (1u << info.index)) return -EBUSY; err = hw->ops.dsp_load(hw, &info); if (err < 0) return err; - hw->dsp_loaded |= (1 << info.index); + hw->dsp_loaded |= (1u << info.index); return 0; } diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index 8d2f7a4e3ab6..46e273bd4a78 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -64,7 +64,7 @@ struct snd_pcm_plugin { char *buf; snd_pcm_uframes_t buf_frames; struct snd_pcm_plugin_channel *buf_channels; - char extra_data[0]; + char extra_data[]; }; int snd_pcm_plugin_build(struct snd_pcm_substream *handle, diff --git a/sound/core/seq/oss/seq_oss_timer.h b/sound/core/seq/oss/seq_oss_timer.h index 2d86125b5d0f..dee190b4ec6b 100644 --- a/sound/core/seq/oss/seq_oss_timer.h +++ b/sound/core/seq/oss/seq_oss_timer.h @@ -44,14 +44,4 @@ snd_seq_oss_timer_cur_tick(struct seq_oss_timer *timer) return timer->cur_tick; } - -/* - * is realtime event? - */ -static inline int -snd_seq_oss_timer_is_realtime(struct seq_oss_timer *timer) -{ - return timer->realtime; -} - #endif diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index ecefa7c83134..38603cb2bd5b 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -457,7 +457,7 @@ static int portman_probe(struct parport *p) /* Set for RXDATA0 where no damage will be done. */ /* 5 */ - parport_write_control(p, RXDATA0 + STROBE); /* Write Strobe=1 to command reg. */ + parport_write_control(p, RXDATA0 | STROBE); /* Write Strobe=1 to command reg. */ /* 6 */ if ((parport_read_status(p) & ESTB) != ESTB) diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index 995c2cefc222..25778765cbfe 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -150,8 +150,12 @@ config SND_FIREWIRE_MOTU Say Y here to enable support for FireWire devices which MOTU produced: * 828mk2 * Traveler - * 828mk3 + * Ultralite + * 8pre + * 828mk3 (FireWire only) + * 828mk3 (Hybrid) * Audio Express + * 4pre To compile this driver as a module, choose M here: the module will be called snd-firewire-motu. @@ -164,6 +168,8 @@ config SND_FIREFACE Say Y here to include support for RME fireface series. * Fireface 400 * Fireface 800 + * Fireface UFX * Fireface UCX + * Fireface 802 endif # SND_FIREWIRE diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c index 67d735e9a6a4..fea92e148790 100644 --- a/sound/firewire/amdtp-am824.c +++ b/sound/firewire/amdtp-am824.c @@ -82,7 +82,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate, if (err < 0) return err; - s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc; + if (s->direction == AMDTP_OUT_STREAM) + s->ctx_data.rx.fdf = AMDTP_FDF_AM824 | s->sfc; p->pcm_channels = pcm_channels; p->midi_ports = midi_ports; diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index 37d38efb4c87..f8586f75441d 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -20,6 +20,8 @@ #define CYCLES_PER_SECOND 8000 #define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND) +#define OHCI_MAX_SECOND 8 + /* Always support Linux tracing subsystem. */ #define CREATE_TRACE_POINTS #include "amdtp-stream-trace.h" @@ -337,25 +339,26 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) } EXPORT_SYMBOL(amdtp_stream_pcm_prepare); -static unsigned int calculate_data_blocks(struct amdtp_stream *s, - unsigned int syt) +static unsigned int calculate_data_blocks(unsigned int *data_block_state, + bool is_blocking, bool is_no_info, + unsigned int syt_interval, enum cip_sfc sfc) { - unsigned int phase, data_blocks; + unsigned int data_blocks; /* Blocking mode. */ - if (s->flags & CIP_BLOCKING) { + if (is_blocking) { /* This module generate empty packet for 'no data'. */ - if (syt == CIP_SYT_NO_INFO) + if (is_no_info) data_blocks = 0; else - data_blocks = s->syt_interval; + data_blocks = syt_interval; /* Non-blocking mode. */ } else { - if (!cip_sfc_is_base_44100(s->sfc)) { + if (!cip_sfc_is_base_44100(sfc)) { // Sample_rate / 8000 is an integer, and precomputed. - data_blocks = s->ctx_data.rx.data_block_state; + data_blocks = *data_block_state; } else { - phase = s->ctx_data.rx.data_block_state; + unsigned int phase = *data_block_state; /* * This calculates the number of data blocks per packet so that @@ -365,31 +368,30 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s, * as possible in the sequence (to prevent underruns of the * device's buffer). */ - if (s->sfc == CIP_SFC_44100) + if (sfc == CIP_SFC_44100) /* 6 6 5 6 5 6 5 ... */ data_blocks = 5 + ((phase & 1) ^ (phase == 0 || phase >= 40)); else /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */ - data_blocks = 11 * (s->sfc >> 1) + (phase == 0); - if (++phase >= (80 >> (s->sfc >> 1))) + data_blocks = 11 * (sfc >> 1) + (phase == 0); + if (++phase >= (80 >> (sfc >> 1))) phase = 0; - s->ctx_data.rx.data_block_state = phase; + *data_block_state = phase; } } return data_blocks; } -static unsigned int calculate_syt(struct amdtp_stream *s, - unsigned int cycle) +static unsigned int calculate_syt_offset(unsigned int *last_syt_offset, + unsigned int *syt_offset_state, enum cip_sfc sfc) { - unsigned int syt_offset, phase, index, syt; + unsigned int syt_offset; - if (s->ctx_data.rx.last_syt_offset < TICKS_PER_CYCLE) { - if (!cip_sfc_is_base_44100(s->sfc)) - syt_offset = s->ctx_data.rx.last_syt_offset + - s->ctx_data.rx.syt_offset_state; + if (*last_syt_offset < TICKS_PER_CYCLE) { + if (!cip_sfc_is_base_44100(sfc)) + syt_offset = *last_syt_offset + *syt_offset_state; else { /* * The time, in ticks, of the n'th SYT_INTERVAL sample is: @@ -401,28 +403,24 @@ static unsigned int calculate_syt(struct amdtp_stream *s, * 1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ... * This code generates _exactly_ the same sequence. */ - phase = s->ctx_data.rx.syt_offset_state; - index = phase % 13; - syt_offset = s->ctx_data.rx.last_syt_offset; + unsigned int phase = *syt_offset_state; + unsigned int index = phase % 13; + + syt_offset = *last_syt_offset; syt_offset += 1386 + ((index && !(index & 3)) || phase == 146); if (++phase >= 147) phase = 0; - s->ctx_data.rx.syt_offset_state = phase; + *syt_offset_state = phase; } } else - syt_offset = s->ctx_data.rx.last_syt_offset - TICKS_PER_CYCLE; - s->ctx_data.rx.last_syt_offset = syt_offset; + syt_offset = *last_syt_offset - TICKS_PER_CYCLE; + *last_syt_offset = syt_offset; - if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += s->ctx_data.rx.transfer_delay; - syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; - syt += syt_offset % TICKS_PER_CYCLE; + if (syt_offset >= TICKS_PER_CYCLE) + syt_offset = CIP_SYT_NO_INFO; - return syt & CIP_SYT_MASK; - } else { - return CIP_SYT_NO_INFO; - } + return syt_offset; } static void update_pcm_pointers(struct amdtp_stream *s, @@ -680,8 +678,8 @@ static inline u32 compute_cycle_count(__be32 ctx_header_tstamp) static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) { cycle += addend; - if (cycle >= 8 * CYCLES_PER_SECOND) - cycle -= 8 * CYCLES_PER_SECOND; + if (cycle >= OHCI_MAX_SECOND * CYCLES_PER_SECOND) + cycle -= OHCI_MAX_SECOND * CYCLES_PER_SECOND; return cycle; } @@ -738,21 +736,41 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, return 0; } -static void generate_ideal_pkt_descs(struct amdtp_stream *s, - struct pkt_desc *descs, - const __be32 *ctx_header, - unsigned int packets) +static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle, + unsigned int transfer_delay) +{ + unsigned int syt; + + syt_offset += transfer_delay; + syt = ((cycle + syt_offset / TICKS_PER_CYCLE) << 12) | + (syt_offset % TICKS_PER_CYCLE); + return syt & CIP_SYT_MASK; +} + +static void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs, + const __be32 *ctx_header, unsigned int packets, + const struct seq_desc *seq_descs, + unsigned int seq_size) { unsigned int dbc = s->data_block_counter; + unsigned int seq_index = s->ctx_data.rx.seq_index; int i; for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; unsigned int index = (s->packet_index + i) % s->queue_size; + const struct seq_desc *seq = seq_descs + seq_index; + unsigned int syt; desc->cycle = compute_it_cycle(*ctx_header, s->queue_size); - desc->syt = calculate_syt(s, desc->cycle); - desc->data_blocks = calculate_data_blocks(s, desc->syt); + + syt = seq->syt_offset; + if (syt != CIP_SYT_NO_INFO) { + syt = compute_syt(syt, desc->cycle, + s->ctx_data.rx.transfer_delay); + } + desc->syt = syt; + desc->data_blocks = seq->data_blocks; if (s->flags & CIP_DBC_IS_END_EVENT) dbc = (dbc + desc->data_blocks) & 0xff; @@ -764,10 +782,13 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s, desc->ctx_payload = s->buffer.packets[index].buffer; + seq_index = (seq_index + 1) % seq_size; + ++ctx_header; } s->data_block_counter = dbc; + s->ctx_data.rx.seq_index = seq_index; } static inline void cancel_stream(struct amdtp_stream *s) @@ -791,24 +812,16 @@ static void process_ctx_payloads(struct amdtp_stream *s, update_pcm_pointers(s, pcm, pcm_frames); } -static void amdtp_stream_master_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data); - -static void amdtp_stream_master_first_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data); - static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) { struct amdtp_stream *s = private_data; + const struct amdtp_domain *d = s->domain; const __be32 *ctx_header = header; unsigned int events_per_period = s->ctx_data.rx.events_per_period; unsigned int event_count = s->ctx_data.rx.event_count; unsigned int packets; - bool is_irq_target; int i; if (s->packet_index < 0) @@ -817,14 +830,11 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, // Calculate the number of packets in buffer and check XRUN. packets = header_length / sizeof(*ctx_header); - generate_ideal_pkt_descs(s, s->pkt_descs, ctx_header, packets); + generate_pkt_descs(s, s->pkt_descs, ctx_header, packets, d->seq_descs, + d->seq_size); process_ctx_payloads(s, s->pkt_descs, packets); - is_irq_target = - !!(context->callback.sc == amdtp_stream_master_callback || - context->callback.sc == amdtp_stream_master_first_callback); - for (i = 0; i < packets; ++i) { const struct pkt_desc *desc = s->pkt_descs + i; unsigned int syt; @@ -843,7 +853,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, desc->data_blocks, desc->data_block_counter, syt, i); - if (is_irq_target) { + if (s == s->domain->irq_target) { event_count += desc->data_blocks; if (event_count >= events_per_period) { event_count -= events_per_period; @@ -896,14 +906,63 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, } } -static void amdtp_stream_master_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data) +static void pool_ideal_seq_descs(struct amdtp_domain *d, unsigned int packets) { - struct amdtp_domain *d = private_data; struct amdtp_stream *irq_target = d->irq_target; + unsigned int seq_tail = d->seq_tail; + unsigned int seq_size = d->seq_size; + unsigned int min_avail; struct amdtp_stream *s; + min_avail = d->seq_size; + list_for_each_entry(s, &d->streams, list) { + unsigned int seq_index; + unsigned int avail; + + if (s->direction == AMDTP_IN_STREAM) + continue; + + seq_index = s->ctx_data.rx.seq_index; + avail = d->seq_tail; + if (seq_index > avail) + avail += d->seq_size; + avail -= seq_index; + + if (avail < min_avail) + min_avail = avail; + } + + while (min_avail < packets) { + struct seq_desc *desc = d->seq_descs + seq_tail; + + desc->syt_offset = calculate_syt_offset(&d->last_syt_offset, + &d->syt_offset_state, irq_target->sfc); + desc->data_blocks = calculate_data_blocks(&d->data_block_state, + !!(irq_target->flags & CIP_BLOCKING), + desc->syt_offset == CIP_SYT_NO_INFO, + irq_target->syt_interval, irq_target->sfc); + + ++seq_tail; + seq_tail %= seq_size; + + ++min_avail; + } + + d->seq_tail = seq_tail; +} + +static void irq_target_callback(struct fw_iso_context *context, u32 tstamp, + size_t header_length, void *header, + void *private_data) +{ + struct amdtp_stream *irq_target = private_data; + struct amdtp_domain *d = irq_target->domain; + unsigned int packets = header_length / sizeof(__be32); + struct amdtp_stream *s; + + // Record enough entries with extra 3 cycles at least. + pool_ideal_seq_descs(d, packets + 3); + out_stream_callback(context, tstamp, header_length, header, irq_target); if (amdtp_streaming_error(irq_target)) goto error; @@ -950,7 +1009,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, } else { cycle = compute_it_cycle(*ctx_header, s->queue_size); - context->callback.sc = out_stream_callback; + if (s == s->domain->irq_target) + context->callback.sc = irq_target_callback; + else + context->callback.sc = out_stream_callback; } s->start_cycle = cycle; @@ -958,64 +1020,29 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, context->callback.sc(context, tstamp, header_length, header, s); } -static void amdtp_stream_master_first_callback(struct fw_iso_context *context, - u32 tstamp, size_t header_length, - void *header, void *private_data) -{ - struct amdtp_domain *d = private_data; - struct amdtp_stream *s = d->irq_target; - const __be32 *ctx_header = header; - - s->callbacked = true; - wake_up(&s->callback_wait); - - s->start_cycle = compute_it_cycle(*ctx_header, s->queue_size); - - context->callback.sc = amdtp_stream_master_callback; - - context->callback.sc(context, tstamp, header_length, header, d); -} - /** * amdtp_stream_start - start transferring packets * @s: the AMDTP stream to start * @channel: the isochronous channel on the bus * @speed: firewire speed code - * @d: the AMDTP domain to which the AMDTP stream belongs - * @is_irq_target: whether isoc context for the AMDTP stream is used to generate - * hardware IRQ. * @start_cycle: the isochronous cycle to start the context. Start immediately * if negative value is given. + * @queue_size: The number of packets in the queue. + * @idle_irq_interval: the interval to queue packet during initial state. * * The stream cannot be started until it has been configured with * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI * device can be started. */ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, - struct amdtp_domain *d, bool is_irq_target, - int start_cycle) + int start_cycle, unsigned int queue_size, + unsigned int idle_irq_interval) { - static const struct { - unsigned int data_block; - unsigned int syt_offset; - } *entry, initial_state[] = { - [CIP_SFC_32000] = { 4, 3072 }, - [CIP_SFC_48000] = { 6, 1024 }, - [CIP_SFC_96000] = { 12, 1024 }, - [CIP_SFC_192000] = { 24, 1024 }, - [CIP_SFC_44100] = { 0, 67 }, - [CIP_SFC_88200] = { 0, 67 }, - [CIP_SFC_176400] = { 0, 67 }, - }; - unsigned int events_per_buffer = d->events_per_buffer; - unsigned int events_per_period = d->events_per_period; - unsigned int idle_irq_interval; + bool is_irq_target = (s == s->domain->irq_target); unsigned int ctx_header_size; unsigned int max_ctx_payload_size; enum dma_data_direction dir; int type, tag, err; - fw_iso_callback_t ctx_cb; - void *ctx_data; mutex_lock(&s->mutex); @@ -1034,12 +1061,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, s->data_block_counter = UINT_MAX; } else { - entry = &initial_state[s->sfc]; - s->data_block_counter = 0; - s->ctx_data.rx.data_block_state = entry->data_block; - s->ctx_data.rx.syt_offset_state = entry->syt_offset; - s->ctx_data.rx.last_syt_offset = TICKS_PER_CYCLE; } /* initialize packet buffer */ @@ -1063,37 +1085,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; } - // This is a case that AMDTP streams in domain run just for MIDI - // substream. Use the number of events equivalent to 10 msec as - // interval of hardware IRQ. - if (events_per_period == 0) - events_per_period = amdtp_rate_table[s->sfc] / 100; - if (events_per_buffer == 0) - events_per_buffer = events_per_period * 3; - - idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, - amdtp_rate_table[s->sfc]); - s->queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, - amdtp_rate_table[s->sfc]); - - err = iso_packets_buffer_init(&s->buffer, s->unit, s->queue_size, + err = iso_packets_buffer_init(&s->buffer, s->unit, queue_size, max_ctx_payload_size, dir); if (err < 0) goto err_unlock; - - if (is_irq_target) { - s->ctx_data.rx.events_per_period = events_per_period; - s->ctx_data.rx.event_count = 0; - ctx_cb = amdtp_stream_master_first_callback; - ctx_data = d; - } else { - ctx_cb = amdtp_stream_first_callback; - ctx_data = s; - } + s->queue_size = queue_size; s->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, ctx_header_size, - ctx_cb, ctx_data); + amdtp_stream_first_callback, s); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -1302,6 +1302,8 @@ int amdtp_domain_init(struct amdtp_domain *d) d->events_per_period = 0; + d->seq_descs = NULL; + return 0; } EXPORT_SYMBOL_GPL(amdtp_domain_init); @@ -1338,6 +1340,7 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, s->channel = channel; s->speed = speed; + s->domain = d; return 0; } @@ -1374,6 +1377,22 @@ static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle) */ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) { + static const struct { + unsigned int data_block; + unsigned int syt_offset; + } *entry, initial_state[] = { + [CIP_SFC_32000] = { 4, 3072 }, + [CIP_SFC_48000] = { 6, 1024 }, + [CIP_SFC_96000] = { 12, 1024 }, + [CIP_SFC_192000] = { 24, 1024 }, + [CIP_SFC_44100] = { 0, 67 }, + [CIP_SFC_88200] = { 0, 67 }, + [CIP_SFC_176400] = { 0, 67 }, + }; + unsigned int events_per_buffer = d->events_per_buffer; + unsigned int events_per_period = d->events_per_period; + unsigned int idle_irq_interval; + unsigned int queue_size; struct amdtp_stream *s; int cycle; int err; @@ -1387,12 +1406,34 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) return -ENXIO; d->irq_target = s; + // This is a case that AMDTP streams in domain run just for MIDI + // substream. Use the number of events equivalent to 10 msec as + // interval of hardware IRQ. + if (events_per_period == 0) + events_per_period = amdtp_rate_table[d->irq_target->sfc] / 100; + if (events_per_buffer == 0) + events_per_buffer = events_per_period * 3; + + queue_size = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_buffer, + amdtp_rate_table[d->irq_target->sfc]); + + d->seq_descs = kcalloc(queue_size, sizeof(*d->seq_descs), GFP_KERNEL); + if (!d->seq_descs) + return -ENOMEM; + d->seq_size = queue_size; + d->seq_tail = 0; + + entry = &initial_state[s->sfc]; + d->data_block_state = entry->data_block; + d->syt_offset_state = entry->syt_offset; + d->last_syt_offset = TICKS_PER_CYCLE; + if (ir_delay_cycle > 0) { struct fw_card *fw_card = fw_parent_device(s->unit)->card; err = get_current_cycle_time(fw_card, &cycle); if (err < 0) - return err; + goto error; // No need to care overflow in cycle field because of enough // width. @@ -1423,18 +1464,26 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) } else { // IT context starts immediately. cycle_match = -1; + s->ctx_data.rx.seq_index = 0; } if (s != d->irq_target) { - err = amdtp_stream_start(s, s->channel, s->speed, d, - false, cycle_match); + err = amdtp_stream_start(s, s->channel, s->speed, + cycle_match, queue_size, 0); if (err < 0) goto error; } } s = d->irq_target; - err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1); + s->ctx_data.rx.events_per_period = events_per_period; + s->ctx_data.rx.event_count = 0; + s->ctx_data.rx.seq_index = 0; + + idle_irq_interval = DIV_ROUND_UP(CYCLES_PER_SECOND * events_per_period, + amdtp_rate_table[d->irq_target->sfc]); + err = amdtp_stream_start(s, s->channel, s->speed, -1, queue_size, + idle_irq_interval); if (err < 0) goto error; @@ -1442,6 +1491,8 @@ int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) error: list_for_each_entry(s, &d->streams, list) amdtp_stream_stop(s); + kfree(d->seq_descs); + d->seq_descs = NULL; return err; } EXPORT_SYMBOL_GPL(amdtp_domain_start); @@ -1466,5 +1517,8 @@ void amdtp_domain_stop(struct amdtp_domain *d) d->events_per_period = 0; d->irq_target = NULL; + + kfree(d->seq_descs); + d->seq_descs = NULL; } EXPORT_SYMBOL_GPL(amdtp_domain_stop); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index f2d44e2dc3c8..703b710aaf7f 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -108,6 +108,8 @@ typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)( const struct pkt_desc *desc, unsigned int packets, struct snd_pcm_substream *pcm); + +struct amdtp_domain; struct amdtp_stream { struct fw_unit *unit; enum cip_flags flags; @@ -136,9 +138,7 @@ struct amdtp_stream { struct { // To calculate CIP data blocks and tstamp. unsigned int transfer_delay; - unsigned int data_block_state; - unsigned int last_syt_offset; - unsigned int syt_offset_state; + unsigned int seq_index; // To generate CIP header. unsigned int fdf; @@ -180,6 +180,7 @@ struct amdtp_stream { int channel; int speed; struct list_head list; + struct amdtp_domain *domain; }; int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, @@ -273,6 +274,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, msecs_to_jiffies(timeout)) > 0; } +struct seq_desc { + unsigned int syt_offset; + unsigned int data_blocks; +}; + struct amdtp_domain { struct list_head streams; @@ -280,6 +286,14 @@ struct amdtp_domain { unsigned int events_per_buffer; struct amdtp_stream *irq_target; + + struct seq_desc *seq_descs; + unsigned int seq_size; + unsigned int seq_tail; + + unsigned int data_block_state; + unsigned int syt_offset_state; + unsigned int last_syt_offset; }; int amdtp_domain_init(struct amdtp_domain *d); diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c index 0e4c3a9ed5e4..8d3b23778eb2 100644 --- a/sound/firewire/fireface/ff-protocol-latter.c +++ b/sound/firewire/fireface/ff-protocol-latter.c @@ -16,7 +16,8 @@ #define LATTER_SYNC_STATUS 0x0000801c0000ULL static int parse_clock_bits(u32 data, unsigned int *rate, - enum snd_ff_clock_src *src) + enum snd_ff_clock_src *src, + enum snd_ff_unit_version unit_version) { static const struct { unsigned int rate; @@ -43,6 +44,11 @@ static int parse_clock_bits(u32 data, unsigned int *rate, }; int i; + if (unit_version != SND_FF_UNIT_VERSION_UCX) { + // e.g. 0x00fe0f20 but expected 0x00eff002. + data = ((data & 0xf0f0f0f0) >> 4) | ((data & 0x0f0f0f0f) << 4); + } + for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) { rate_entry = rate_entries + i; if ((data & 0x0f000000) == rate_entry->flag) { @@ -79,7 +85,7 @@ static int latter_get_clock(struct snd_ff *ff, unsigned int *rate, return err; data = le32_to_cpu(reg); - return parse_clock_bits(data, rate, src); + return parse_clock_bits(data, rate, src, ff->unit_version); } static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable) @@ -107,18 +113,18 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate) int err; // Set the number of data blocks transferred in a second. - if (rate % 32000 == 0) - code = 0x00; + if (rate % 48000 == 0) + code = 0x04; else if (rate % 44100 == 0) code = 0x02; - else if (rate % 48000 == 0) - code = 0x04; + else if (rate % 32000 == 0) + code = 0x00; else return -EINVAL; if (rate >= 64000 && rate < 128000) code |= 0x08; - else if (rate >= 128000 && rate < 192000) + else if (rate >= 128000) code |= 0x10; reg = cpu_to_le32(code); @@ -140,7 +146,7 @@ static int latter_allocate_resources(struct snd_ff *ff, unsigned int rate) if (curr_rate == rate) break; } - if (count == 10) + if (count > 10) return -ETIMEDOUT; for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); ++i) { @@ -181,14 +187,30 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate) __le32 reg; int err; - if (rate >= 32000 && rate <= 48000) - flag = 0x92; - else if (rate >= 64000 && rate <= 96000) - flag = 0x8e; - else if (rate >= 128000 && rate <= 192000) - flag = 0x8c; - else - return -EINVAL; + if (ff->unit_version == SND_FF_UNIT_VERSION_UCX) { + // For Fireface UCX. Always use the maximum number of data + // channels in data block of packet. + if (rate >= 32000 && rate <= 48000) + flag = 0x92; + else if (rate >= 64000 && rate <= 96000) + flag = 0x8e; + else if (rate >= 128000 && rate <= 192000) + flag = 0x8c; + else + return -EINVAL; + } else { + // For Fireface UFX and 802. Due to bandwidth limitation on + // IEEE 1394a (400 Mbps), Analog 1-12 and AES are available + // without any ADAT at quadruple speed. + if (rate >= 32000 && rate <= 48000) + flag = 0x9e; + else if (rate >= 64000 && rate <= 96000) + flag = 0x96; + else if (rate >= 128000 && rate <= 192000) + flag = 0x8e; + else + return -EINVAL; + } if (generation != fw_parent_device(ff->unit)->card->generation) { err = fw_iso_resources_update(&ff->tx_resources); @@ -207,8 +229,6 @@ static int latter_begin_session(struct snd_ff *ff, unsigned int rate) if (err < 0) return err; - // Always use the maximum number of data channels in data block of - // packet. reg = cpu_to_le32(flag); return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST, LATTER_ISOC_START, ®, sizeof(reg), 0); @@ -263,7 +283,7 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer } } - err = parse_clock_bits(data, &rate, &src); + err = parse_clock_bits(data, &rate, &src, ff->unit_version); if (err < 0) return; label = snd_ff_proc_get_clk_label(src); diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index 63b79c4a5405..5452115c0ef9 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -184,7 +184,6 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) */ if (!amdtp_stream_running(&ff->rx_stream)) { int spd = fw_parent_device(ff->unit)->max_speed; - unsigned int ir_delay_cycle; err = ff->spec->protocol->begin_session(ff, rate); if (err < 0) @@ -200,14 +199,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) goto error; - // The device postpones start of transmission mostly for several - // cycles after receiving packets firstly. - if (ff->spec->protocol == &snd_ff_protocol_ff800) - ir_delay_cycle = 800; // = 100 msec - else - ir_delay_cycle = 16; // = 2 msec - - err = amdtp_domain_start(&ff->domain, ir_delay_cycle); + err = amdtp_domain_start(&ff->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c index b62a4fd22407..bc39269415d2 100644 --- a/sound/firewire/fireface/ff.c +++ b/sound/firewire/fireface/ff.c @@ -16,12 +16,22 @@ MODULE_LICENSE("GPL v2"); static void name_card(struct snd_ff *ff) { struct fw_device *fw_dev = fw_parent_device(ff->unit); + const char *const names[] = { + [SND_FF_UNIT_VERSION_FF800] = "Fireface800", + [SND_FF_UNIT_VERSION_FF400] = "Fireface400", + [SND_FF_UNIT_VERSION_UFX] = "FirefaceUFX", + [SND_FF_UNIT_VERSION_UCX] = "FirefaceUCX", + [SND_FF_UNIT_VERSION_802] = "Fireface802", + }; + const char *name; + + name = names[ff->unit_version]; strcpy(ff->card->driver, "Fireface"); - strcpy(ff->card->shortname, ff->spec->name); - strcpy(ff->card->mixername, ff->spec->name); + strcpy(ff->card->shortname, name); + strcpy(ff->card->mixername, name); snprintf(ff->card->longname, sizeof(ff->card->longname), - "RME %s, GUID %08x%08x at %s, S%d", ff->spec->name, + "RME %s, GUID %08x%08x at %s, S%d", name, fw_dev->config_rom[3], fw_dev->config_rom[4], dev_name(&ff->unit->device), 100 << fw_dev->max_speed); } @@ -101,6 +111,7 @@ static int snd_ff_probe(struct fw_unit *unit, spin_lock_init(&ff->lock); init_waitqueue_head(&ff->hwdep_wait); + ff->unit_version = entry->version; ff->spec = (const struct snd_ff_spec *)entry->driver_data; /* Register this sound card later. */ @@ -145,7 +156,6 @@ static void snd_ff_remove(struct fw_unit *unit) } static const struct snd_ff_spec spec_ff800 = { - .name = "Fireface800", .pcm_capture_channels = {28, 20, 12}, .pcm_playback_channels = {28, 20, 12}, .midi_in_ports = 1, @@ -157,7 +167,6 @@ static const struct snd_ff_spec spec_ff800 = { }; static const struct snd_ff_spec spec_ff400 = { - .name = "Fireface400", .pcm_capture_channels = {18, 14, 10}, .pcm_playback_channels = {18, 14, 10}, .midi_in_ports = 2, @@ -169,7 +178,6 @@ static const struct snd_ff_spec spec_ff400 = { }; static const struct snd_ff_spec spec_ucx = { - .name = "FirefaceUCX", .pcm_capture_channels = {18, 14, 12}, .pcm_playback_channels = {18, 14, 12}, .midi_in_ports = 2, @@ -180,6 +188,17 @@ static const struct snd_ff_spec spec_ucx = { .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, }; +static const struct snd_ff_spec spec_ufx_802 = { + .pcm_capture_channels = {30, 22, 14}, + .pcm_playback_channels = {30, 22, 14}, + .midi_in_ports = 1, + .midi_out_ports = 1, + .protocol = &snd_ff_protocol_latter, + .midi_high_addr = 0xffff00000034ull, + .midi_addr_range = 0x80, + .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, +}; + static const struct ieee1394_device_id snd_ff_id_table[] = { /* Fireface 800 */ { @@ -189,7 +208,7 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, .specifier_id = OUI_RME, - .version = 0x000001, + .version = SND_FF_UNIT_VERSION_FF800, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff800, }, @@ -201,10 +220,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, .specifier_id = OUI_RME, - .version = 0x000002, + .version = SND_FF_UNIT_VERSION_FF400, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, }, + // Fireface UFX. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = SND_FF_UNIT_VERSION_UFX, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ufx_802, + }, // Fireface UCX. { .match_flags = IEEE1394_MATCH_VENDOR_ID | @@ -213,10 +244,22 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { IEEE1394_MATCH_MODEL_ID, .vendor_id = OUI_RME, .specifier_id = OUI_RME, - .version = 0x000004, + .version = SND_FF_UNIT_VERSION_UCX, .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ucx, }, + // Fireface 802. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = SND_FF_UNIT_VERSION_802, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ufx_802, + }, {} }; MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index dc7a20f75983..705e7df4f929 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -34,6 +34,14 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2 +enum snd_ff_unit_version { + SND_FF_UNIT_VERSION_FF800 = 0x000001, + SND_FF_UNIT_VERSION_FF400 = 0x000002, + SND_FF_UNIT_VERSION_UFX = 0x000003, + SND_FF_UNIT_VERSION_UCX = 0x000004, + SND_FF_UNIT_VERSION_802 = 0x000005, +}; + enum snd_ff_stream_mode { SND_FF_STREAM_MODE_LOW = 0, SND_FF_STREAM_MODE_MID, @@ -43,8 +51,6 @@ enum snd_ff_stream_mode { struct snd_ff_protocol; struct snd_ff_spec { - const char *const name; - const unsigned int pcm_capture_channels[SND_FF_STREAM_MODE_COUNT]; const unsigned int pcm_playback_channels[SND_FF_STREAM_MODE_COUNT]; @@ -66,6 +72,7 @@ struct snd_ff { bool registered; struct delayed_work dwork; + enum snd_ff_unit_version unit_version; const struct snd_ff_spec *spec; /* To handle MIDI tx. */ diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index dda797209a27..654e28a6669f 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -177,7 +177,7 @@ struct snd_efw_phys_meters { u32 in_meters; u32 reserved4; u32 reserved5; - u32 values[0]; + u32 values[]; } __packed; enum snd_efw_clock_source { SND_EFW_CLOCK_SOURCE_INTERNAL = 0, diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c index 0fd36e469ad0..edb31ac26868 100644 --- a/sound/firewire/motu/amdtp-motu.c +++ b/sound/firewire/motu/amdtp-motu.c @@ -76,15 +76,11 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, if (i == ARRAY_SIZE(snd_motu_clock_rates)) return -EINVAL; - pcm_chunks = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + // Each data block includes SPH in its head. Data chunks follow with + // 3 byte alignment. Padding follows with zero to conform to quadlet + // alignment. + pcm_chunks = formats->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); @@ -440,7 +436,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s, int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, - const struct snd_motu_protocol *const protocol) + const struct snd_motu_spec *spec) { amdtp_stream_process_ctx_payloads_t process_ctx_payloads; int fmt = CIP_FMT_MOTU; @@ -454,14 +450,15 @@ int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, * Units of version 3 transmits packets with invalid CIP header * against IEC 61883-1. */ - if (protocol == &snd_motu_protocol_v3) { + if (spec->protocol_version == SND_MOTU_PROTOCOL_V3) { flags |= CIP_WRONG_DBS | CIP_SKIP_DBC_ZERO_CHECK | CIP_HEADER_WITHOUT_EOH; fmt = CIP_FMT_MOTU_TX_V3; } - if (protocol == &snd_motu_protocol_v2) { + if (spec == &snd_motu_spec_8pre || + spec == &snd_motu_spec_ultralite) { // 8pre has some quirks. flags |= CIP_WRONG_DBS | CIP_SKIP_DBC_ZERO_CHECK; diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index 2d41a1a4052c..8e1437371263 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -26,8 +26,7 @@ static int motu_rate_constraint(struct snd_pcm_hw_params *params, rate = snd_motu_clock_rates[i]; mode = i / 2; - pcm_channels = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + pcm_channels = formats->pcm_chunks[mode]; if (!snd_interval_test(c, pcm_channels)) continue; @@ -59,8 +58,7 @@ static int motu_channels_constraint(struct snd_pcm_hw_params *params, if (!snd_interval_test(r, rate)) continue; - pcm_channels = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + pcm_channels = formats->pcm_chunks[mode]; channels.min = min(channels.min, pcm_channels); channels.max = max(channels.max, pcm_channels); } @@ -82,8 +80,7 @@ static void limit_channels_and_rates(struct snd_motu *motu, rate = snd_motu_clock_rates[i]; mode = i / 2; - pcm_channels = formats->fixed_part_pcm_chunks[mode] + - formats->differed_part_pcm_chunks[mode]; + pcm_channels = formats->pcm_chunks[mode]; if (pcm_channels == 0) continue; @@ -133,7 +130,6 @@ static int init_hw_info(struct snd_motu *motu, 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; struct amdtp_domain *d = &motu->domain; enum snd_motu_clock_source src; int err; @@ -152,7 +148,7 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - err = protocol->get_clock_source(motu, &src); + err = snd_motu_protocol_get_clock_source(motu, &src); if (err < 0) goto err_locked; @@ -166,7 +162,7 @@ static int pcm_open(struct snd_pcm_substream *substream) unsigned int frames_per_buffer = d->events_per_buffer; unsigned int rate; - err = protocol->get_clock_rate(motu, &rate); + err = snd_motu_protocol_get_clock_rate(motu, &rate); if (err < 0) goto err_locked; diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c index 187f6abd878c..f009cf7aa074 100644 --- a/sound/firewire/motu/motu-proc.c +++ b/sound/firewire/motu/motu-proc.c @@ -28,13 +28,12 @@ static void proc_read_clock(struct snd_info_entry *entry, { 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) + if (snd_motu_protocol_get_clock_rate(motu, &rate) < 0) return; - if (protocol->get_clock_source(motu, &source) < 0) + if (snd_motu_protocol_get_clock_source(motu, &source) < 0) return; snd_iprintf(buffer, "Rate:\t%d\n", rate); @@ -45,15 +44,14 @@ 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) + if (snd_motu_protocol_cache_packet_formats(motu) < 0) return; - snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n"); + snd_iprintf(buffer, "tx:\tmsg\tfixed\ttotal\n"); for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) { mode = i >> 1; @@ -62,11 +60,11 @@ static void proc_read_format(struct snd_info_entry *entry, "%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]); + motu->spec->tx_fixed_pcm_chunks[mode], + formats->pcm_chunks[mode]); } - snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n"); + snd_iprintf(buffer, "rx:\tmsg\tfixed\ttotal\n"); for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) { mode = i >> 1; @@ -75,8 +73,8 @@ static void proc_read_format(struct snd_info_entry *entry, "%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]); + motu->spec->rx_fixed_pcm_chunks[mode], + formats->pcm_chunks[mode]); } } diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c index 619b6ae73f62..e59e69ab1538 100644 --- a/sound/firewire/motu/motu-protocol-v2.c +++ b/sound/firewire/motu/motu-protocol-v2.c @@ -35,7 +35,8 @@ static int get_clock_rate(u32 data, unsigned int *rate) return 0; } -static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate) +int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, + unsigned int *rate) { __be32 reg; int err; @@ -48,7 +49,8 @@ static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate) return get_clock_rate(be32_to_cpu(reg), rate); } -static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate) +int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, + unsigned int rate) { __be32 reg; u32 data; @@ -76,14 +78,10 @@ static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate) sizeof(reg)); } -static int get_clock_source(struct snd_motu *motu, u32 data, - enum snd_motu_clock_source *src) +static int detect_clock_source_optical_model(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) { - unsigned int index = data & V2_CLOCK_SRC_MASK; - if (index > 5) - return -EIO; - - switch (index) { + switch (data) { case 0: *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; break; @@ -116,14 +114,50 @@ static int get_clock_source(struct snd_motu *motu, u32 data, *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB; break; default: - return -EIO; + *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; } return 0; } -static int v2_get_clock_source(struct snd_motu *motu, - enum snd_motu_clock_source *src) +static int v2_detect_clock_source(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) +{ + switch (data) { + case 0: + *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; + break; + case 2: + *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; + break; + case 3: + *src = SND_MOTU_CLOCK_SOURCE_SPH; + break; + case 4: + *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; + break; + default: + *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; + } + + return 0; +} + +static int get_clock_source(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) +{ + data &= V2_CLOCK_SRC_MASK; + if (motu->spec == &snd_motu_spec_828mk2 || + motu->spec == &snd_motu_spec_traveler) + return detect_clock_source_optical_model(motu, data, src); + else + return v2_detect_clock_source(motu, data, src); +} + +int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src) { __be32 reg; int err; @@ -136,167 +170,189 @@ static int v2_get_clock_source(struct snd_motu *motu, return get_clock_source(motu, be32_to_cpu(reg), src); } -static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable) +// Expected for Traveler and 896HD, which implements Altera Cyclone EP1C3. +static int switch_fetching_mode_cyclone(struct snd_motu *motu, u32 *data, + bool enable) { - enum snd_motu_clock_source src; - __be32 reg; - u32 data; - int err = 0; + *data |= V2_CLOCK_MODEL_SPECIFIC; - // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. - if (motu->spec == &snd_motu_spec_828mk2) - return 0; + return 0; +} - err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, ®, - sizeof(reg)); +// For UltraLite and 8pre, which implements Xilinx Spartan XC3S200. +static int switch_fetching_mode_spartan(struct snd_motu *motu, u32 *data, + bool enable) +{ + unsigned int rate; + enum snd_motu_clock_source src; + int err; + + err = get_clock_source(motu, *data, &src); if (err < 0) return err; - data = be32_to_cpu(reg); - err = get_clock_source(motu, data, &src); + err = get_clock_rate(*data, &rate); if (err < 0) return err; - data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); - if (enable) - data |= V2_CLOCK_FETCH_ENABLE; + if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) + *data |= V2_CLOCK_MODEL_SPECIFIC; + + return 0; +} - if (motu->spec->flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4) { - // Expected for Traveler and 896HD, which implements Altera - // Cyclone EP1C3. - data |= V2_CLOCK_MODEL_SPECIFIC; +int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu, + bool enable) +{ + if (motu->spec == &snd_motu_spec_828mk2) { + // 828mkII implements Altera ACEX 1K EP1K30. Nothing to do. + return 0; } else { - // For UltraLite and 8pre, which implements Xilinx Spartan - // XC3S200. - unsigned int rate; + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, + ®, sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg); - err = get_clock_rate(data, &rate); + data &= ~(V2_CLOCK_FETCH_ENABLE | V2_CLOCK_MODEL_SPECIFIC); + if (enable) + data |= V2_CLOCK_FETCH_ENABLE; + + if (motu->spec == &snd_motu_spec_traveler) + err = switch_fetching_mode_cyclone(motu, &data, enable); + else + err = switch_fetching_mode_spartan(motu, &data, enable); if (err < 0) return err; - if (src == SND_MOTU_CLOCK_SOURCE_SPH && rate > 48000) - data |= V2_CLOCK_MODEL_SPECIFIC; + reg = cpu_to_be32(data); + return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, + ®, sizeof(reg)); } +} - reg = cpu_to_be32(data); - return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, ®, - sizeof(reg)); +static int detect_packet_formats_828mk2(struct snd_motu *motu, u32 data) +{ + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 4; + } + + if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 4; + } + + return 0; } -static 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) +static int detect_packet_formats_traveler(struct snd_motu *motu, u32 data) { - 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 { - if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; - } - - // Packets to v2 units include 2 chunks for phone 1/2, except - // for 176.4/192.0 kHz. - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 4; } - if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 4; } - /* - * 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; - - formats->fixed_part_pcm_chunks[0] = pcm_chunks[0]; - formats->fixed_part_pcm_chunks[1] = pcm_chunks[1]; - formats->fixed_part_pcm_chunks[2] = pcm_chunks[2]; + return 0; } -static void calculate_differed_part(struct snd_motu_packet_format *formats, - enum snd_motu_spec_flags flags, - u32 data, u32 mask, u32 shift) +static int detect_packet_formats_8pre(struct snd_motu *motu, u32 data) { - unsigned char pcm_chunks[2] = {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 (data == V2_OPT_IFACE_MODE_ADAT) { - if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) { - pcm_chunks[0] += 8; - pcm_chunks[1] += 4; - } - // 8pre has two sets of optical interface and doesn't reduce - // chunks for ADAT signals. - if (flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) { - pcm_chunks[1] += 4; - } + if (((data & V2_OPT_IN_IFACE_MASK) >> V2_OPT_IN_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 8; } - /* 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]; + if (((data & V2_OPT_OUT_IFACE_MASK) >> V2_OPT_OUT_IFACE_SHIFT) == + V2_OPT_IFACE_MODE_ADAT) { + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.pcm_chunks[1] += 8; + } + + return 0; } -static int v2_cache_packet_formats(struct snd_motu *motu) +int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu) { __be32 reg; u32 data; int err; + motu->tx_packet_formats.pcm_byte_offset = 10; + motu->rx_packet_formats.pcm_byte_offset = 10; + + motu->tx_packet_formats.msg_chunks = 2; + motu->rx_packet_formats.msg_chunks = 2; + err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, ®, 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); + memcpy(motu->tx_packet_formats.pcm_chunks, + motu->spec->tx_fixed_pcm_chunks, + sizeof(motu->tx_packet_formats.pcm_chunks)); + memcpy(motu->rx_packet_formats.pcm_chunks, + motu->spec->rx_fixed_pcm_chunks, + sizeof(motu->rx_packet_formats.pcm_chunks)); - 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); + if (motu->spec == &snd_motu_spec_828mk2) + return detect_packet_formats_828mk2(motu, data); + else if (motu->spec == &snd_motu_spec_traveler) + return detect_packet_formats_traveler(motu, data); + else if (motu->spec == &snd_motu_spec_8pre) + return detect_packet_formats_8pre(motu, data); + else + return 0; +} - motu->tx_packet_formats.pcm_byte_offset = 10; - motu->rx_packet_formats.pcm_byte_offset = 10; +const struct snd_motu_spec snd_motu_spec_828mk2 = { + .name = "828mk2", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q, + .tx_fixed_pcm_chunks = {14, 14, 0}, + .rx_fixed_pcm_chunks = {14, 14, 0}, +}; - return 0; -} +const struct snd_motu_spec snd_motu_spec_traveler = { + .name = "Traveler", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q, + .tx_fixed_pcm_chunks = {14, 14, 8}, + .rx_fixed_pcm_chunks = {14, 14, 8}, +}; + +const struct snd_motu_spec snd_motu_spec_ultralite = { + .name = "UltraLite", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q, + .tx_fixed_pcm_chunks = {14, 14, 0}, + .rx_fixed_pcm_chunks = {14, 14, 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, +const struct snd_motu_spec snd_motu_spec_8pre = { + .name = "8pre", + .protocol_version = SND_MOTU_PROTOCOL_V2, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_2ND_Q, + .tx_fixed_pcm_chunks = {10, 6, 0}, + .rx_fixed_pcm_chunks = {10, 6, 0}, }; diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c index d1545e2b5caa..01a47ac7bb2d 100644 --- a/sound/firewire/motu/motu-protocol-v3.c +++ b/sound/firewire/motu/motu-protocol-v3.c @@ -24,7 +24,8 @@ #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) +int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu, + unsigned int *rate) { __be32 reg; u32 data; @@ -45,7 +46,8 @@ static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate) return 0; } -static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate) +int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu, + unsigned int rate) { __be32 reg; u32 data; @@ -85,55 +87,102 @@ static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate) return 0; } -static int v3_get_clock_source(struct snd_motu *motu, - enum snd_motu_clock_source *src) +static int detect_clock_source_828mk3(struct snd_motu *motu, u32 data, + 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, ®, - sizeof(reg)); - if (err < 0) - return err; - data = be32_to_cpu(reg); - - val = data & V3_CLOCK_SOURCE_MASK; - if (val == 0x00) { + switch (data) { + case 0x00: *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; - } else if (val == 0x01) { + break; + case 0x01: *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; - } else if (val == 0x02) { + break; + case 0x02: *src = SND_MOTU_CLOCK_SOURCE_SPH; - } else if (val == 0x10) { + break; + case 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, - ®, sizeof(reg)); + break; + case 0x18: + case 0x19: + { + __be32 reg; + u32 options; + int err; + + err = snd_motu_transaction_read(motu, + V3_OPT_IFACE_MODE_OFFSET, ®, sizeof(reg)); if (err < 0) return err; - data = be32_to_cpu(reg); + options = be32_to_cpu(reg); - if (val == 0x18) { - if (data & V3_NO_ADAT_OPT_IN_IFACE_A) + if (data == 0x18) { + if (options & V3_NO_ADAT_OPT_IN_IFACE_A) *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A; else *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A; } else { - if (data & V3_NO_ADAT_OPT_IN_IFACE_B) + if (options & V3_NO_ADAT_OPT_IN_IFACE_B) *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B; else *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B; } - } else { + + break; + } + default: *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; } return 0; } -static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable) +static int v3_detect_clock_source(struct snd_motu *motu, u32 data, + enum snd_motu_clock_source *src) +{ + switch (data) { + case 0x00: + *src = SND_MOTU_CLOCK_SOURCE_INTERNAL; + break; + case 0x01: + *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC; + break; + case 0x02: + *src = SND_MOTU_CLOCK_SOURCE_SPH; + break; + case 0x10: + *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX; + break; + default: + *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN; + break; + } + + return 0; +} + +int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src) +{ + __be32 reg; + u32 data; + int err; + + err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, ®, + sizeof(reg)); + if (err < 0) + return err; + data = be32_to_cpu(reg) & V3_CLOCK_SOURCE_MASK; + + if (motu->spec == &snd_motu_spec_828mk3) + return detect_clock_source_828mk3(motu, data, src); + else + return v3_detect_clock_source(motu, data, src); +} + +int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu, + bool enable) { __be32 reg; u32 data; @@ -155,162 +204,113 @@ static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable) 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) +static int detect_packet_formats_828mk3(struct snd_motu *motu, u32 data) { - 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 { - if (flags & SND_MOTU_SPEC_RX_SEPARATED_MAIN) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (data & V3_ENABLE_OPT_IN_IFACE_A) { + if (data & V3_NO_ADAT_OPT_IN_IFACE_A) { + motu->tx_packet_formats.pcm_chunks[0] += 4; + motu->tx_packet_formats.pcm_chunks[1] += 4; + } else { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.pcm_chunks[1] += 4; } - - // Packets to v3 units include 2 chunks for phone 1/2, except - // for 176.4/192.0 kHz. - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; } - if (flags & SND_MOTU_SPEC_HAS_AESEBU_IFACE) { - pcm_chunks[0] += 2; - pcm_chunks[1] += 2; + if (data & V3_ENABLE_OPT_IN_IFACE_B) { + if (data & V3_NO_ADAT_OPT_IN_IFACE_B) { + motu->tx_packet_formats.pcm_chunks[0] += 4; + motu->tx_packet_formats.pcm_chunks[1] += 4; + } else { + motu->tx_packet_formats.pcm_chunks[0] += 8; + motu->tx_packet_formats.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; + if (data & V3_ENABLE_OPT_OUT_IFACE_A) { + if (data & V3_NO_ADAT_OPT_OUT_IFACE_A) { + motu->rx_packet_formats.pcm_chunks[0] += 4; + motu->rx_packet_formats.pcm_chunks[1] += 4; } else { - /* - * Additional data chunks for ADAT on optical interface - * A. - */ - pcm_chunks[0] += 8; - pcm_chunks[1] += 4; + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.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; + if (data & V3_ENABLE_OPT_OUT_IFACE_B) { + if (data & V3_NO_ADAT_OPT_OUT_IFACE_B) { + motu->rx_packet_formats.pcm_chunks[0] += 4; + motu->rx_packet_formats.pcm_chunks[1] += 4; } else { - /* - * Additional data chunks for ADAT on optical interface - * B. - */ - pcm_chunks[0] += 8; - pcm_chunks[1] += 4; + motu->rx_packet_formats.pcm_chunks[0] += 8; + motu->rx_packet_formats.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]; - } + return 0; } -static int v3_cache_packet_formats(struct snd_motu *motu) +int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu) { __be32 reg; u32 data; int err; + motu->tx_packet_formats.pcm_byte_offset = 10; + motu->rx_packet_formats.pcm_byte_offset = 10; + + motu->tx_packet_formats.msg_chunks = 2; + motu->rx_packet_formats.msg_chunks = 2; + err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, ®, 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); + memcpy(motu->tx_packet_formats.pcm_chunks, + motu->spec->tx_fixed_pcm_chunks, + sizeof(motu->tx_packet_formats.pcm_chunks)); + memcpy(motu->rx_packet_formats.pcm_chunks, + motu->spec->rx_fixed_pcm_chunks, + sizeof(motu->rx_packet_formats.pcm_chunks)); - 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); + if (motu->spec == &snd_motu_spec_828mk3) + return detect_packet_formats_828mk3(motu, data); + else + return 0; +} - motu->tx_packet_formats.pcm_byte_offset = 10; - motu->rx_packet_formats.pcm_byte_offset = 10; - return 0; -} +const struct snd_motu_spec snd_motu_spec_828mk3 = { + .name = "828mk3", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | + SND_MOTU_SPEC_TX_MIDI_3RD_Q, + .tx_fixed_pcm_chunks = {18, 18, 14}, + .rx_fixed_pcm_chunks = {14, 14, 10}, +}; + +const struct snd_motu_spec snd_motu_spec_ultralite_mk3 = { + .name = "UltraLiteMk3", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_RX_MIDI_3RD_Q | + SND_MOTU_SPEC_TX_MIDI_3RD_Q, + .tx_fixed_pcm_chunks = {18, 14, 10}, + .rx_fixed_pcm_chunks = {14, 14, 14}, +}; + +const struct snd_motu_spec snd_motu_spec_audio_express = { + .name = "AudioExpress", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .flags = SND_MOTU_SPEC_RX_MIDI_2ND_Q | + SND_MOTU_SPEC_TX_MIDI_3RD_Q, + .tx_fixed_pcm_chunks = {10, 10, 0}, + .rx_fixed_pcm_chunks = {10, 10, 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, +const struct snd_motu_spec snd_motu_spec_4pre = { + .name = "4pre", + .protocol_version = SND_MOTU_PROTOCOL_V3, + .tx_fixed_pcm_chunks = {10, 10, 0}, + .rx_fixed_pcm_chunks = {10, 10, 0}, }; diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index a17ddceb1bec..2028c5419f6f 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -88,7 +88,7 @@ static void finish_session(struct snd_motu *motu) u32 data; int err; - err = motu->spec->protocol->switch_fetching_mode(motu, false); + err = snd_motu_protocol_switch_fetching_mode(motu, false); if (err < 0) return; @@ -110,7 +110,7 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) { int err; - err = motu->spec->protocol->cache_packet_formats(motu); + err = snd_motu_protocol_cache_packet_formats(motu); if (err < 0) return err; @@ -140,7 +140,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, unsigned int curr_rate; int err; - err = motu->spec->protocol->get_clock_rate(motu, &curr_rate); + err = snd_motu_protocol_get_clock_rate(motu, &curr_rate); if (err < 0) return err; if (rate == 0) @@ -153,7 +153,7 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, fw_iso_resources_free(&motu->tx_resources); fw_iso_resources_free(&motu->rx_resources); - err = motu->spec->protocol->set_clock_rate(motu, rate); + err = snd_motu_protocol_set_clock_rate(motu, rate); if (err < 0) { dev_err(&motu->unit->device, "fail to set sampling rate: %d\n", err); @@ -201,9 +201,9 @@ static int ensure_packet_formats(struct snd_motu *motu) 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) + if (motu->spec->tx_fixed_pcm_chunks[0] == motu->tx_packet_formats.pcm_chunks[0]) data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; - if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0) + if (motu->spec->rx_fixed_pcm_chunks[0] == motu->rx_packet_formats.pcm_chunks[0]) data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS; data |= fw_parent_device(motu->unit)->max_speed; @@ -272,7 +272,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) goto stop_streams; } - err = motu->spec->protocol->switch_fetching_mode(motu, true); + err = snd_motu_protocol_switch_fetching_mode(motu, true); if (err < 0) { dev_err(&motu->unit->device, "fail to enable frame fetching: %d\n", err); @@ -317,7 +317,7 @@ static int init_stream(struct snd_motu *motu, struct amdtp_stream *s) if (err < 0) return err; - err = amdtp_motu_init(s, motu->unit, dir, motu->spec->protocol); + err = amdtp_motu_init(s, motu->unit, dir, motu->spec); if (err < 0) fw_iso_resources_destroy(resources); diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c index f2080d720aa9..a4929c1302dc 100644 --- a/sound/firewire/motu/motu.c +++ b/sound/firewire/motu/motu.c @@ -172,105 +172,6 @@ static void motu_bus_update(struct fw_unit *unit) snd_motu_transaction_reregister(motu); } -const struct snd_motu_spec snd_motu_spec_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_RX_SEPARATED_MAIN | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, - - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_traveler = { - .name = "Traveler", - .protocol = &snd_motu_protocol_v2, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_SUPPORT_CLOCK_X4 | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_HAS_AESEBU_IFACE | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, - - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_ultralite = { - .name = "UltraLite", - .protocol = &snd_motu_protocol_v2, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | // padding. - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q | - SND_MOTU_SPEC_RX_SEPARATED_MAIN, - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_8pre = { - .name = "8pre", - .protocol = &snd_motu_protocol_v2, - // In tx, use coax chunks for mix-return 1/2. In rx, use coax chunks for - // dummy 1/2. - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_HAS_OPT_IFACE_B | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_2ND_Q, - .analog_in_ports = 8, - .analog_out_ports = 2, -}; - -static const 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_RX_SEPARATED_MAIN | - SND_MOTU_SPEC_HAS_OPT_IFACE_A | - SND_MOTU_SPEC_HAS_OPT_IFACE_B | - SND_MOTU_SPEC_RX_MIDI_3RD_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, - - .analog_in_ports = 8, - .analog_out_ports = 8, -}; - -static const struct snd_motu_spec motu_audio_express = { - .name = "AudioExpress", - .protocol = &snd_motu_protocol_v3, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_RX_SEPARATED_MAIN | - SND_MOTU_SPEC_RX_MIDI_2ND_Q | - SND_MOTU_SPEC_TX_MIDI_3RD_Q, - .analog_in_ports = 2, - .analog_out_ports = 4, -}; - -static const struct snd_motu_spec motu_4pre = { - .name = "4pre", - .protocol = &snd_motu_protocol_v3, - .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 | - SND_MOTU_SPEC_TX_MICINST_CHUNK | - SND_MOTU_SPEC_TX_RETURN_CHUNK | - SND_MOTU_SPEC_RX_SEPARATED_MAIN, - .analog_in_ports = 2, - .analog_out_ports = 2, -}; - #define SND_MOTU_DEV_ENTRY(model, data) \ { \ .match_flags = IEEE1394_MATCH_VENDOR_ID | \ @@ -284,13 +185,14 @@ static const struct snd_motu_spec motu_4pre = { static const struct ieee1394_device_id motu_id_table[] = { SND_MOTU_DEV_ENTRY(0x000003, &snd_motu_spec_828mk2), - SND_MOTU_DEV_ENTRY(0x000009, &motu_traveler), - SND_MOTU_DEV_ENTRY(0x00000d, &motu_ultralite), - SND_MOTU_DEV_ENTRY(0x00000f, &motu_8pre), - SND_MOTU_DEV_ENTRY(0x000015, &motu_828mk3), /* FireWire only. */ - SND_MOTU_DEV_ENTRY(0x000035, &motu_828mk3), /* Hybrid. */ - SND_MOTU_DEV_ENTRY(0x000033, &motu_audio_express), - SND_MOTU_DEV_ENTRY(0x000045, &motu_4pre), + SND_MOTU_DEV_ENTRY(0x000009, &snd_motu_spec_traveler), + SND_MOTU_DEV_ENTRY(0x00000d, &snd_motu_spec_ultralite), + SND_MOTU_DEV_ENTRY(0x00000f, &snd_motu_spec_8pre), + SND_MOTU_DEV_ENTRY(0x000015, &snd_motu_spec_828mk3), // FireWire only. + SND_MOTU_DEV_ENTRY(0x000019, &snd_motu_spec_ultralite_mk3), // FireWire only. + SND_MOTU_DEV_ENTRY(0x000035, &snd_motu_spec_828mk3), // Hybrid. + SND_MOTU_DEV_ENTRY(0x000033, &snd_motu_spec_audio_express), + SND_MOTU_DEV_ENTRY(0x000045, &snd_motu_spec_4pre), { } }; MODULE_DEVICE_TABLE(ieee1394, motu_id_table); diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 6efbde405a0d..3d0236ee6716 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -36,8 +36,7 @@ struct snd_motu_packet_format { unsigned char pcm_byte_offset; unsigned char msg_chunks; - unsigned char fixed_part_pcm_chunks[3]; - unsigned char differed_part_pcm_chunks[3]; + unsigned char pcm_chunks[3]; }; struct snd_motu { @@ -74,19 +73,10 @@ struct snd_motu { }; 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_HAS_AESEBU_IFACE = 0x0020, - SND_MOTU_SPEC_HAS_OPT_IFACE_A = 0x0040, - SND_MOTU_SPEC_HAS_OPT_IFACE_B = 0x0080, - SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0100, - SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0200, - SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0400, - SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0800, - SND_MOTU_SPEC_RX_SEPARATED_MAIN = 0x1000, + SND_MOTU_SPEC_RX_MIDI_2ND_Q = 0x0001, + SND_MOTU_SPEC_RX_MIDI_3RD_Q = 0x0002, + SND_MOTU_SPEC_TX_MIDI_2ND_Q = 0x0004, + SND_MOTU_SPEC_TX_MIDI_3RD_Q = 0x0008, }; #define SND_MOTU_CLOCK_RATE_COUNT 6 @@ -108,33 +98,33 @@ enum snd_motu_clock_source { 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); +enum snd_motu_protocol_version { + SND_MOTU_PROTOCOL_V2, + SND_MOTU_PROTOCOL_V3, }; struct snd_motu_spec { const char *const name; + enum snd_motu_protocol_version protocol_version; enum snd_motu_spec_flags flags; - unsigned char analog_in_ports; - unsigned char analog_out_ports; - - const struct snd_motu_protocol *const protocol; + unsigned char tx_fixed_pcm_chunks[3]; + unsigned char rx_fixed_pcm_chunks[3]; }; -extern const struct snd_motu_protocol snd_motu_protocol_v2; -extern const struct snd_motu_protocol snd_motu_protocol_v3; - extern const struct snd_motu_spec snd_motu_spec_828mk2; +extern const struct snd_motu_spec snd_motu_spec_traveler; +extern const struct snd_motu_spec snd_motu_spec_ultralite; +extern const struct snd_motu_spec snd_motu_spec_8pre; + +extern const struct snd_motu_spec snd_motu_spec_828mk3; +extern const struct snd_motu_spec snd_motu_spec_ultralite_mk3; +extern const struct snd_motu_spec snd_motu_spec_audio_express; +extern const struct snd_motu_spec snd_motu_spec_4pre; int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit, enum amdtp_stream_direction dir, - const struct snd_motu_protocol *const protocol); + const struct snd_motu_spec *spec); int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate, unsigned int midi_ports, struct snd_motu_packet_format *formats); @@ -169,4 +159,79 @@ 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); + +int snd_motu_protocol_v2_get_clock_rate(struct snd_motu *motu, + unsigned int *rate); +int snd_motu_protocol_v2_set_clock_rate(struct snd_motu *motu, + unsigned int rate); +int snd_motu_protocol_v2_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src); +int snd_motu_protocol_v2_switch_fetching_mode(struct snd_motu *motu, + bool enable); +int snd_motu_protocol_v2_cache_packet_formats(struct snd_motu *motu); + +int snd_motu_protocol_v3_get_clock_rate(struct snd_motu *motu, + unsigned int *rate); +int snd_motu_protocol_v3_set_clock_rate(struct snd_motu *motu, + unsigned int rate); +int snd_motu_protocol_v3_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *src); +int snd_motu_protocol_v3_switch_fetching_mode(struct snd_motu *motu, + bool enable); +int snd_motu_protocol_v3_cache_packet_formats(struct snd_motu *motu); + +static inline int snd_motu_protocol_get_clock_rate(struct snd_motu *motu, + unsigned int *rate) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_get_clock_rate(motu, rate); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_get_clock_rate(motu, rate); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_set_clock_rate(struct snd_motu *motu, + unsigned int rate) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_set_clock_rate(motu, rate); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_set_clock_rate(motu, rate); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_get_clock_source(struct snd_motu *motu, + enum snd_motu_clock_source *source) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_get_clock_source(motu, source); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_get_clock_source(motu, source); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_switch_fetching_mode(struct snd_motu *motu, + bool enable) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_switch_fetching_mode(motu, enable); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_switch_fetching_mode(motu, enable); + else + return -ENXIO; +} + +static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu) +{ + if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V2) + return snd_motu_protocol_v2_cache_packet_formats(motu); + else if (motu->spec->protocol_version == SND_MOTU_PROTOCOL_V3) + return snd_motu_protocol_v3_cache_packet_formats(motu); + else + return -ENXIO; +} + #endif diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index 73bfa71845f6..d0a604c939df 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); static void default_release(struct device *dev) { - snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev)); + snd_hdac_ext_bus_device_exit(dev_to_hdac_dev(dev)); } /** diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 3fe62be1cbcc..09ddab5f5cae 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -81,7 +81,6 @@ int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, mutex_unlock(&bus->cmd_mutex); return err; } -EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb); /** * snd_hdac_bus_exec_verb_unlocked - unlocked version @@ -150,7 +149,6 @@ void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex) schedule_work(&bus->unsol_work); } -EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); /* * process queued unsolicited events @@ -162,6 +160,7 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work) struct hdac_driver *drv; unsigned int rp, caddr, res; + spin_lock_irq(&bus->reg_lock); while (bus->unsol_rp != bus->unsol_wp) { rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE; bus->unsol_rp = rp; @@ -173,10 +172,13 @@ static void snd_hdac_bus_process_unsol_events(struct work_struct *work) codec = bus->caddr_tbl[caddr & 0x0f]; if (!codec || !codec->dev.driver) continue; + spin_unlock_irq(&bus->reg_lock); drv = drv_to_hdac_driver(codec->dev.driver); if (drv->unsol_event) drv->unsol_event(codec, res); + spin_lock_irq(&bus->reg_lock); } + spin_unlock_irq(&bus->reg_lock); } /** diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index bc4a8b606020..011b17cc1efa 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -9,6 +9,7 @@ #include <sound/core.h> #include <sound/hdaudio.h> #include <sound/hda_register.h> +#include "local.h" /* clear CORB read pointer properly */ static void azx_clear_corbrp(struct hdac_bus *bus) @@ -527,6 +528,18 @@ bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) } bus->chip_init = true; + + /* + * Default value of '8' is as per the HD audio specification (Rev 1.0a). + * Following relation is used to derive STRIPE control value. + * For sample rate <= 48K: + * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 } + * For sample rate > 48K: + * { ((num_channels * bits_per_sample * rate/48000) / + * number of SDOs) >= 8 } + */ + bus->sdo_limit = 8; + return true; } EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index e3119f5cb0d5..333220f0f8af 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -20,7 +20,7 @@ static int get_codec_vendor_name(struct hdac_device *codec); static void default_release(struct device *dev) { - snd_hdac_device_exit(container_of(dev, struct hdac_device, dev)); + snd_hdac_device_exit(dev_to_hdac_dev(dev)); } /** diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index a314b03b4a4c..a38a2af1654f 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -38,7 +38,7 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus, else value = (channels * bits_per_sample) / sdo_line; - if (value >= 8) + if (value >= bus->sdo_limit) break; } diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c index be1df80ed013..20b8f6cb3ff8 100644 --- a/sound/hda/intel-dsp-config.c +++ b/sound/hda/intel-dsp-config.c @@ -1,10 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz> +#include <linux/acpi.h> #include <linux/bits.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_intel.h> #include <sound/core.h> #include <sound/intel-dsp-config.h> #include <sound/intel-nhlt.h> @@ -14,9 +17,14 @@ static int dsp_driver; module_param(dsp_driver, int, 0444); MODULE_PARM_DESC(dsp_driver, "Force the DSP driver for Intel DSP (0=auto, 1=legacy, 2=SST, 3=SOF)"); -#define FLAG_SST BIT(0) -#define FLAG_SOF BIT(1) -#define FLAG_SOF_ONLY_IF_DMIC BIT(16) +#define FLAG_SST BIT(0) +#define FLAG_SOF BIT(1) +#define FLAG_SST_ONLY_IF_DMIC BIT(15) +#define FLAG_SOF_ONLY_IF_DMIC BIT(16) +#define FLAG_SOF_ONLY_IF_SOUNDWIRE BIT(17) + +#define FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE (FLAG_SOF_ONLY_IF_DMIC | \ + FLAG_SOF_ONLY_IF_SOUNDWIRE) struct config_entry { u32 flags; @@ -100,6 +108,10 @@ static const struct config_entry config_table[] = { {} } }, + { + .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, + .device = 0x9d70, + }, #endif /* Kabylake-LP */ #if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) @@ -116,6 +128,10 @@ static const struct config_entry config_table[] = { {} } }, + { + .flags = FLAG_SST | FLAG_SST_ONLY_IF_DMIC, + .device = 0x9d71, + }, #endif /* @@ -166,7 +182,7 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x9dc8, }, #endif @@ -187,7 +203,7 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0xa348, }, #endif @@ -204,18 +220,50 @@ static const struct config_entry config_table[] = { DMI_MATCH(DMI_SYS_VENDOR, "Google"), } }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "09C6") + }, + }, + { + /* early version of SKU 09C6 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0983") + }, + }, {} } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x02c8, }, #endif /* Cometlake-H */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H) { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF, + .device = 0x06c8, + .dmi_table = (const struct dmi_system_id []) { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "098F"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0990"), + }, + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x06c8, }, #endif @@ -236,7 +284,7 @@ static const struct config_entry config_table[] = { } }, { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0x34c8, }, #endif @@ -256,9 +304,8 @@ static const struct config_entry config_table[] = { {} } }, - { - .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE, .device = 0xa0c8, }, #endif @@ -303,6 +350,28 @@ static int snd_intel_dsp_check_dmic(struct pci_dev *pci) return ret; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) +static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) +{ + struct sdw_intel_acpi_info info; + acpi_handle handle; + int ret; + + handle = ACPI_HANDLE(&pci->dev); + + ret = sdw_intel_acpi_scan(handle, &info); + if (ret < 0) + return ret; + + return info.link_mask; +} +#else +static int snd_intel_dsp_check_soundwire(struct pci_dev *pci) +{ + return 0; +} +#endif + int snd_intel_dsp_driver_probe(struct pci_dev *pci) { const struct config_entry *cfg; @@ -336,22 +405,36 @@ int snd_intel_dsp_driver_probe(struct pci_dev *pci) return SND_INTEL_DSP_DRIVER_ANY; if (cfg->flags & FLAG_SOF) { - if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC) { + if (cfg->flags & FLAG_SOF_ONLY_IF_SOUNDWIRE && + snd_intel_dsp_check_soundwire(pci) > 0) { + dev_info(&pci->dev, "SoundWire enabled on CannonLake+ platform, using SOF driver\n"); + return SND_INTEL_DSP_DRIVER_SOF; + } + if (cfg->flags & FLAG_SOF_ONLY_IF_DMIC && + snd_intel_dsp_check_dmic(pci)) { + dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); + return SND_INTEL_DSP_DRIVER_SOF; + } + if (!(cfg->flags & FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE)) + return SND_INTEL_DSP_DRIVER_SOF; + } + + + if (cfg->flags & FLAG_SST) { + if (cfg->flags & FLAG_SST_ONLY_IF_DMIC) { if (snd_intel_dsp_check_dmic(pci)) { - dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SOF driver\n"); - return SND_INTEL_DSP_DRIVER_SOF; + dev_info(&pci->dev, "Digital mics found on Skylake+ platform, using SST driver\n"); + return SND_INTEL_DSP_DRIVER_SST; } } else { - return SND_INTEL_DSP_DRIVER_SOF; + return SND_INTEL_DSP_DRIVER_SST; } } - if (cfg->flags & FLAG_SST) - return SND_INTEL_DSP_DRIVER_SST; - return SND_INTEL_DSP_DRIVER_LEGACY; } EXPORT_SYMBOL_GPL(snd_intel_dsp_driver_probe); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Intel DSP config driver"); +MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index 73d4f8c4f4c7..059aaf04f536 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -4,58 +4,25 @@ #include <linux/acpi.h> #include <sound/intel-nhlt.h> -#define NHLT_ACPI_HEADER_SIG "NHLT" - -/* Unique identification for getting NHLT blobs */ -static const guid_t osc_guid = - GUID_INIT(0xA69F886E, 0x6CEB, 0x4594, - 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53); - struct nhlt_acpi_table *intel_nhlt_init(struct device *dev) { - acpi_handle handle; - union acpi_object *obj; - struct nhlt_resource_desc *nhlt_ptr; - struct nhlt_acpi_table *nhlt_table = NULL; - - handle = ACPI_HANDLE(dev); - if (!handle) { - dev_err(dev, "Didn't find ACPI_HANDLE\n"); - return NULL; - } + struct nhlt_acpi_table *nhlt; + acpi_status status; - obj = acpi_evaluate_dsm(handle, &osc_guid, 1, 1, NULL); - - if (!obj) - return NULL; - - if (obj->type != ACPI_TYPE_BUFFER) { - dev_dbg(dev, "No NHLT table found\n"); - ACPI_FREE(obj); + status = acpi_get_table(ACPI_SIG_NHLT, 0, + (struct acpi_table_header **)&nhlt); + if (ACPI_FAILURE(status)) { + dev_warn(dev, "NHLT table not found\n"); return NULL; } - nhlt_ptr = (struct nhlt_resource_desc *)obj->buffer.pointer; - if (nhlt_ptr->length) - nhlt_table = (struct nhlt_acpi_table *) - memremap(nhlt_ptr->min_addr, nhlt_ptr->length, - MEMREMAP_WB); - ACPI_FREE(obj); - if (nhlt_table && - (strncmp(nhlt_table->header.signature, - NHLT_ACPI_HEADER_SIG, - strlen(NHLT_ACPI_HEADER_SIG)) != 0)) { - memunmap(nhlt_table); - dev_err(dev, "NHLT ACPI header signature incorrect\n"); - return NULL; - } - return nhlt_table; + return nhlt; } EXPORT_SYMBOL_GPL(intel_nhlt_init); void intel_nhlt_free(struct nhlt_acpi_table *nhlt) { - memunmap((void *)nhlt); + acpi_put_table((struct acpi_table_header *)nhlt); } EXPORT_SYMBOL_GPL(intel_nhlt_free); diff --git a/sound/hda/local.h b/sound/hda/local.h index 5b935219352f..896ba142e8bc 100644 --- a/sound/hda/local.h +++ b/sound/hda/local.h @@ -36,6 +36,9 @@ void hda_widget_sysfs_exit(struct hdac_device *codec); int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); void snd_hdac_bus_remove_device(struct hdac_bus *bus, struct hdac_device *codec); +void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); +int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res); int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, unsigned int flags, unsigned int *res); diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index ce4c8ba2fa98..ca18fe3ff8a5 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -54,7 +54,7 @@ MODULE_PARM_DESC(clockfreq, "Clock frequency for ad1816a driver (default = 0).") static const struct pnp_card_device_id snd_ad1816a_pnpids[] = { /* Analog Devices AD1815 */ { .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } }, - /* Analog Device AD1816? */ + /* Analog Devices AD1816? */ { .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, /* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */ { .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } }, diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index c5b1d5900eed..d6420d224d09 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -1171,7 +1171,10 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header) "alias for %d\n", header->number, header->hdr.a.OriginalSample); - + + if (header->number >= WF_MAX_SAMPLE) + return -EINVAL; + munge_int32 (header->number, &alias_hdr[0], 2); munge_int32 (header->hdr.a.OriginalSample, &alias_hdr[2], 2); munge_int32 (*((unsigned int *)&header->hdr.a.sampleStartOffset), @@ -1202,6 +1205,9 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header) int num_samples; unsigned char *msample_hdr; + if (header->number >= WF_MAX_SAMPLE) + return -EINVAL; + msample_hdr = kmalloc(WF_MSAMPLE_BYTES, GFP_KERNEL); if (! msample_hdr) return -ENOMEM; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index ebf926728c5f..45ef0f52ec55 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -1356,7 +1356,7 @@ static int patch_cx20551(struct snd_ac97 *ac97) } /* - * Analog Device AD18xx, AD19xx codecs + * Analog Devices AD18xx, AD19xx codecs */ #ifdef CONFIG_PM static void ad18xx_resume(struct snd_ac97 *ac97) diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index e1d3082a4fe9..7ba542e45a3d 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -99,10 +99,10 @@ comment "Set to Y if you want auto-loading the codec driver" depends on SND_HDA=y && SND_HDA_CODEC_REALTEK=m config SND_HDA_CODEC_ANALOG - tristate "Build Analog Device HD-audio codec support" + tristate "Build Analog Devices HD-audio codec support" select SND_HDA_GENERIC help - Say Y or M here to include Analog Device HD-audio codec support in + Say Y or M here to include Analog Devices HD-audio codec support in snd-hda-intel driver, such as AD1986A. comment "Set to Y if you want auto-loading the codec driver" diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 773992a07efa..0cc5fad1af8a 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -52,10 +52,21 @@ #define HDA_IPFS_INTR_MASK 0x188 #define HDA_IPFS_EN_INTR (1 << 16) +/* FPCI */ +#define FPCI_DBG_CFG_2 0x10F4 +#define FPCI_GCAP_NSDO_SHIFT 18 +#define FPCI_GCAP_NSDO_MASK (0x3 << FPCI_GCAP_NSDO_SHIFT) + /* max number of SDs */ #define NUM_CAPTURE_SD 1 #define NUM_PLAYBACK_SD 1 +/* + * Tegra194 does not reflect correct number of SDO lines. Below macro + * is used to update the GCAP register to workaround the issue. + */ +#define TEGRA194_NUM_SDO_LINES 4 + struct hda_tegra { struct azx chip; struct device *dev; @@ -275,6 +286,7 @@ static int hda_tegra_init_clk(struct hda_tegra *hda) static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) { + struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); struct hdac_bus *bus = azx_bus(chip); struct snd_card *card = chip->card; int err; @@ -298,6 +310,26 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) bus->irq = irq_id; card->sync_irq = bus->irq; + /* + * Tegra194 has 4 SDO lines and the STRIPE can be used to + * indicate how many of the SDO lines the stream should be + * striped. But GCAP register does not reflect the true + * capability of HW. Below workaround helps to fix this. + * + * GCAP_NSDO is bits 19:18 in T_AZA_DBG_CFG_2, + * 0 for 1 SDO, 1 for 2 SDO, 2 for 4 SDO lines. + */ + if (of_device_is_compatible(np, "nvidia,tegra194-hda")) { + u32 val; + + dev_info(card->dev, "Override SDO lines to %u\n", + TEGRA194_NUM_SDO_LINES); + + val = readl(hda->regs + FPCI_DBG_CFG_2) & ~FPCI_GCAP_NSDO_MASK; + val |= (TEGRA194_NUM_SDO_LINES >> 1) << FPCI_GCAP_NSDO_SHIFT; + writel(val, hda->regs + FPCI_DBG_CFG_2); + } + gcap = azx_readw(chip, GCAP); dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); @@ -332,6 +364,23 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) /* initialize chip */ azx_init_chip(chip, 1); + /* + * Playback (for 44.1K/48K, 2-channel, 16-bps) fails with + * 4 SDO lines due to legacy design limitation. Following + * is, from HD Audio Specification (Revision 1.0a), used to + * control striping of the stream across multiple SDO lines + * for sample rates <= 48K. + * + * { ((num_channels * bits_per_sample) / number of SDOs) >= 8 } + * + * Due to legacy design issue it is recommended that above + * ratio must be greater than 8. Since number of SDO lines is + * in powers of 2, next available ratio is 16 which can be + * used as a limiting factor here. + */ + if (of_device_is_compatible(np, "nvidia,tegra194-hda")) + chip->bus.core.sdo_limit = 16; + /* codec detection */ if (!bus->codec_mask) { dev_err(card->dev, "no codecs found!\n"); @@ -408,6 +457,7 @@ static int hda_tegra_create(struct snd_card *card, static const struct of_device_id hda_tegra_match[] = { { .compatible = "nvidia,tegra30-hda" }, + { .compatible = "nvidia,tegra194-hda" }, {}, }; MODULE_DEVICE_TABLE(of, hda_tegra_match); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 07be1b197566..fbd7cc6026d8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2024,7 +2024,7 @@ static const struct hda_pcm_ops generic_ops = { static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -2037,7 +2037,7 @@ static int hdmi_get_spk_alloc(struct hdac_device *hdac, int pcm_idx) static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -2051,7 +2051,7 @@ static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx, static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, unsigned char *chmap, int prepared) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -2067,7 +2067,7 @@ static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx, static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); struct hdmi_spec *spec = codec->spec; struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx); @@ -3787,7 +3787,7 @@ static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap, static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, hda_nid_t pin_nid, int hdmi_slot, int stream_channel) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); int verb; int ati_channel_setup = 0; @@ -3823,7 +3823,7 @@ static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac, static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac, hda_nid_t pin_nid, int asp_slot) { - struct hda_codec *codec = container_of(hdac, struct hda_codec, core); + struct hda_codec *codec = hdac_to_hda_codec(hdac); bool was_odd = false; int ati_asp_slot = asp_slot; int verb; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 041d2a32059b..0aa778ff7f2b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -17,6 +17,7 @@ #include <linux/dmi.h> #include <linux/module.h> #include <linux/input.h> +#include <linux/leds.h> #include <sound/core.h> #include <sound/jack.h> #include <sound/hda_codec.h> @@ -81,6 +82,7 @@ struct alc_spec { /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ int mute_led_polarity; + int micmute_led_polarity; hda_nid_t mute_led_nid; hda_nid_t cap_mute_led_nid; @@ -384,6 +386,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0282: case 0x10ec0283: case 0x10ec0286: + case 0x10ec0287: case 0x10ec0288: case 0x10ec0285: case 0x10ec0298: @@ -4079,11 +4082,9 @@ static void alc269_fixup_hp_mute_led_mic3(struct hda_codec *codec, /* update LED status via GPIO */ static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask, - bool enabled) + int polarity, bool enabled) { - struct alc_spec *spec = codec->spec; - - if (spec->mute_led_polarity) + if (polarity) enabled = !enabled; alc_update_gpio_data(codec, mask, !enabled); /* muted -> LED on */ } @@ -4094,7 +4095,8 @@ static void alc_fixup_gpio_mute_hook(void *private_data, int enabled) struct hda_codec *codec = private_data; struct alc_spec *spec = codec->spec; - alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled); + alc_update_gpio_led(codec, spec->gpio_mute_led_mask, + spec->mute_led_polarity, enabled); } /* turn on/off mic-mute LED via GPIO per capture hook */ @@ -4103,9 +4105,30 @@ static void alc_gpio_micmute_update(struct hda_codec *codec) struct alc_spec *spec = codec->spec; alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->micmute_led_polarity, spec->gen.micmute_led.led_value); } +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) +static int micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct hda_codec *codec = dev_to_hda_codec(led_cdev->dev->parent); + struct alc_spec *spec = codec->spec; + + alc_update_gpio_led(codec, spec->gpio_mic_led_mask, + spec->micmute_led_polarity, !!brightness); + return 0; +} + +static struct led_classdev micmute_led_cdev = { + .name = "hda::micmute", + .max_brightness = 1, + .brightness_set_blocking = micmute_led_set, + .default_trigger = "audio-micmute", +}; +#endif + /* setup mute and mic-mute GPIO bits, add hooks appropriately */ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, int action, @@ -4113,6 +4136,9 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, unsigned int micmute_mask) { struct alc_spec *spec = codec->spec; +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + int err; +#endif alc_fixup_gpio(codec, action, mute_mask | micmute_mask); @@ -4125,6 +4151,13 @@ static void alc_fixup_hp_gpio_led(struct hda_codec *codec, if (micmute_mask) { spec->gpio_mic_led_mask = micmute_mask; snd_hda_gen_add_micmute_led(codec, alc_gpio_micmute_update); + +#if IS_REACHABLE(CONFIG_LEDS_TRIGGER_AUDIO) + micmute_led_cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); + err = devm_led_classdev_register(&codec->core.dev, &micmute_led_cdev); + if (err) + codec_warn(codec, "failed to register micmute LED\n"); +#endif } } @@ -4137,7 +4170,11 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, static void alc285_fixup_hp_gpio_led(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - alc_fixup_hp_gpio_led(codec, action, 0x04, 0x00); + struct alc_spec *spec = codec->spec; + + spec->micmute_led_polarity = 1; + + alc_fixup_hp_gpio_led(codec, action, 0x04, 0x01); } static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, @@ -5484,18 +5521,9 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec, { 0x19, 0x21a11010 }, /* dock mic */ { } }; - /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise - * the speaker output becomes too low by some reason on Thinkpads with - * ALC298 codec - */ - static const hda_nid_t preferred_pairs[] = { - 0x14, 0x03, 0x17, 0x02, 0x21, 0x02, - 0 - }; struct alc_spec *spec = codec->spec; if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->gen.preferred_dacs = preferred_pairs; spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; snd_hda_apply_pincfgs(codec, pincfgs); } else if (action == HDA_FIXUP_ACT_INIT) { @@ -5508,6 +5536,23 @@ static void alc_fixup_tpt470_dock(struct hda_codec *codec, } } +static void alc_fixup_tpt470_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + /* Assure the speaker pin to be coupled with DAC NID 0x03; otherwise + * the speaker output becomes too low by some reason on Thinkpads with + * ALC298 codec + */ + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x03, 0x17, 0x02, 0x21, 0x02, + 0 + }; + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) + spec->gen.preferred_dacs = preferred_pairs; +} + static void alc_shutup_dell_xps13(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5799,7 +5844,8 @@ static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec, snd_hda_gen_hp_automute(codec, jack); /* mute_led_polarity is set to 0, so we pass inverted value here */ - alc_update_gpio_led(codec, 0x10, !spec->gen.hp_jack_present); + alc_update_gpio_led(codec, 0x10, spec->mute_led_polarity, + !spec->gen.hp_jack_present); } /* Manage GPIOs for HP EliteBook Folio 9480m. @@ -6063,6 +6109,7 @@ enum { ALC700_FIXUP_INTEL_REFERENCE, ALC274_FIXUP_DELL_BIND_DACS, ALC274_FIXUP_DELL_AIO_LINEOUT_VERB, + ALC298_FIXUP_TPT470_DOCK_FIX, ALC298_FIXUP_TPT470_DOCK, ALC255_FIXUP_DUMMY_LINEOUT_VERB, ALC255_FIXUP_DELL_HEADSET_MIC, @@ -6994,12 +7041,18 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC274_FIXUP_DELL_BIND_DACS }, - [ALC298_FIXUP_TPT470_DOCK] = { + [ALC298_FIXUP_TPT470_DOCK_FIX] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_tpt470_dock, .chained = true, .chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE }, + [ALC298_FIXUP_TPT470_DOCK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_tpt470_dacs, + .chained = true, + .chain_id = ALC298_FIXUP_TPT470_DOCK_FIX + }, [ALC255_FIXUP_DUMMY_LINEOUT_VERB] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -7638,6 +7691,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"}, {.id = ALC292_FIXUP_TPT440, .name = "tpt440"}, {.id = ALC292_FIXUP_TPT460, .name = "tpt460"}, + {.id = ALC298_FIXUP_TPT470_DOCK_FIX, .name = "tpt470-dock-fix"}, {.id = ALC298_FIXUP_TPT470_DOCK, .name = "tpt470-dock"}, {.id = ALC233_FIXUP_LENOVO_MULTI_CODECS, .name = "dual-codecs"}, {.id = ALC700_FIXUP_INTEL_REFERENCE, .name = "alc700-ref"}, @@ -8276,6 +8330,7 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0215: case 0x10ec0245: case 0x10ec0285: + case 0x10ec0287: case 0x10ec0289: spec->codec_variant = ALC269_TYPE_ALC215; spec->shutup = alc225_shutup; @@ -9554,6 +9609,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269), HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269), HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0287, "ALC287", patch_alc269), HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269), HDA_CODEC_ENTRY(0x10ec0289, "ALC289", patch_alc269), HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 6a0520c4fb5a..cf801a235df9 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -460,7 +460,7 @@ static void xonar_st_init(struct oxygen *chip) data->generic.anti_pop_delay = 100; data->h6 = chip->model.dac_channels_mixer > 2; - data->has_cs2000 = 1; + data->has_cs2000 = true; data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1; data->broken_i2c = true; @@ -502,7 +502,7 @@ static void xonar_xense_init(struct oxygen *chip) xonar_init_ext_power(chip); data->generic.anti_pop_delay = 100; - data->has_cs2000 = 1; + data->has_cs2000 = true; data->cs2000_regs[CS2000_FUN_CFG_1] = CS2000_REF_CLK_DIV_1; oxygen_write16(chip, OXYGEN_I2S_A_FORMAT, diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 592532c09a82..2e750b317be1 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -226,7 +226,7 @@ static int snd_pmac_pcm_prepare(struct snd_pmac *chip, struct pmac_stream *rec, offset += rec->period_size; } /* make loop */ - cp->command = cpu_to_le16(DBDMA_NOP + BR_ALWAYS); + cp->command = cpu_to_le16(DBDMA_NOP | BR_ALWAYS); cp->cmd_dep = cpu_to_le32(rec->cmd.addr); snd_pmac_dma_stop(rec); @@ -726,7 +726,7 @@ void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long add chip->extra_dma.cmds->xfer_status = cpu_to_le16(0); chip->extra_dma.cmds->cmd_dep = cpu_to_le32(chip->extra_dma.addr); chip->extra_dma.cmds->phy_addr = cpu_to_le32(addr); - chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE + BR_ALWAYS); + chip->extra_dma.cmds->command = cpu_to_le16(OUTPUT_MORE | BR_ALWAYS); out_le32(&chip->awacs->control, (in_le32(&chip->awacs->control) & ~0x1f00) | (speed << 8)); diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h index 598b07d9b6fe..d0efc5e254ae 100644 --- a/sound/soc/codecs/hdac_hda.h +++ b/sound/soc/codecs/hdac_hda.h @@ -28,10 +28,6 @@ struct hdac_hda_priv { bool need_display_power; }; -#define hdac_to_hda_priv(_hdac) \ - container_of(_hdac, struct hdac_hda_priv, codec.core) -#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core) - struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void); #endif /* __HDAC_HDA_H__ */ diff --git a/sound/usb/card.h b/sound/usb/card.h index 395403a2d33f..d6219fba9699 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -84,6 +84,10 @@ struct snd_usb_endpoint { dma_addr_t sync_dma; /* DMA address of syncbuf */ unsigned int pipe; /* the data i/o pipe */ + unsigned int framesize[2]; /* small/large frame sizes in samples */ + unsigned int sample_rem; /* remainder from division fs/fps */ + unsigned int sample_accum; /* sample accumulator */ + unsigned int fps; /* frames per second */ unsigned int freqn; /* nominal sampling rate in fs/fps in Q16.16 format */ unsigned int freqm; /* momentary sampling rate in fs/fps in Q16.16 format */ int freqshift; /* how much to shift the feedback value to get Q16.16 */ @@ -104,6 +108,7 @@ struct snd_usb_endpoint { int iface, altsetting; int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ + bool is_implicit_feedback; /* This endpoint is used as implicit feedback */ spinlock_t lock; struct list_head list; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 4a9a2f6ef5a4..9bea7d3f99f8 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -124,12 +124,12 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep) /* * For streaming based on information derived from sync endpoints, - * prepare_outbound_urb_sizes() will call next_packet_size() to + * prepare_outbound_urb_sizes() will call slave_next_packet_size() to * determine the number of samples to be sent in the next packet. * - * For implicit feedback, next_packet_size() is unused. + * For implicit feedback, slave_next_packet_size() is unused. */ -int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) +int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep) { unsigned long flags; int ret; @@ -146,6 +146,29 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) return ret; } +/* + * For adaptive and synchronous endpoints, prepare_outbound_urb_sizes() + * will call next_packet_size() to determine the number of samples to be + * sent in the next packet. + */ +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) +{ + int ret; + + if (ep->fill_max) + return ep->maxframesize; + + ep->sample_accum += ep->sample_rem; + if (ep->sample_accum >= ep->fps) { + ep->sample_accum -= ep->fps; + ret = ep->framesize[1]; + } else { + ret = ep->framesize[0]; + } + + return ret; +} + static void retire_outbound_urb(struct snd_usb_endpoint *ep, struct snd_urb_ctx *urb_ctx) { @@ -190,6 +213,8 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, if (ctx->packet_size[i]) counts = ctx->packet_size[i]; + else if (ep->sync_master) + counts = snd_usb_endpoint_slave_next_packet_size(ep); else counts = snd_usb_endpoint_next_packet_size(ep); @@ -321,17 +346,17 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) ep->next_packet_read_pos %= MAX_URBS; /* take URB out of FIFO */ - if (!list_empty(&ep->ready_playback_urbs)) + if (!list_empty(&ep->ready_playback_urbs)) { ctx = list_first_entry(&ep->ready_playback_urbs, struct snd_urb_ctx, ready_list); + list_del_init(&ctx->ready_list); + } } spin_unlock_irqrestore(&ep->lock, flags); if (ctx == NULL) return; - list_del_init(&ctx->ready_list); - /* copy over the length information */ for (i = 0; i < packet->packets; i++) ctx->packet_size[i] = packet->packet_size[i]; @@ -497,6 +522,8 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, list_add_tail(&ep->list, &chip->ep_list); + ep->is_implicit_feedback = 0; + __exit_unlock: mutex_unlock(&chip->mutex); @@ -597,6 +624,178 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) } /* + * Check data endpoint for format differences + */ +static bool check_ep_params(struct snd_usb_endpoint *ep, + snd_pcm_format_t pcm_format, + unsigned int channels, + unsigned int period_bytes, + unsigned int frames_per_period, + unsigned int periods_per_buffer, + struct audioformat *fmt, + struct snd_usb_endpoint *sync_ep) +{ + unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb; + unsigned int max_packs_per_period, urbs_per_period, urb_packs; + unsigned int max_urbs; + int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; + int tx_length_quirk = (ep->chip->tx_length_quirk && + usb_pipeout(ep->pipe)); + bool ret = 1; + + if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { + /* + * When operating in DSD DOP mode, the size of a sample frame + * in hardware differs from the actual physical format width + * because we need to make room for the DOP markers. + */ + frame_bits += channels << 3; + } + + ret = ret && (ep->datainterval == fmt->datainterval); + ret = ret && (ep->stride == frame_bits >> 3); + + switch (pcm_format) { + case SNDRV_PCM_FORMAT_U8: + ret = ret && (ep->silence_value == 0x80); + break; + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + ret = ret && (ep->silence_value == 0x69); + break; + default: + ret = ret && (ep->silence_value == 0); + } + + /* assume max. frequency is 50% higher than nominal */ + ret = ret && (ep->freqmax == ep->freqn + (ep->freqn >> 1)); + /* Round up freqmax to nearest integer in order to calculate maximum + * packet size, which must represent a whole number of frames. + * This is accomplished by adding 0x0.ffff before converting the + * Q16.16 format into integer. + * In order to accurately calculate the maximum packet size when + * the data interval is more than 1 (i.e. ep->datainterval > 0), + * multiply by the data interval prior to rounding. For instance, + * a freqmax of 41 kHz will result in a max packet size of 6 (5.125) + * frames with a data interval of 1, but 11 (10.25) frames with a + * data interval of 2. + * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the + * maximum datainterval value of 3, at USB full speed, higher for + * USB high speed, noting that ep->freqmax is in units of + * frames per packet in Q16.16 format.) + */ + maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) * + (frame_bits >> 3); + if (tx_length_quirk) + maxsize += sizeof(__le32); /* Space for length descriptor */ + /* but wMaxPacketSize might reduce this */ + if (ep->maxpacksize && ep->maxpacksize < maxsize) { + /* whatever fits into a max. size packet */ + unsigned int data_maxsize = maxsize = ep->maxpacksize; + + if (tx_length_quirk) + /* Need to remove the length descriptor to calc freq */ + data_maxsize -= sizeof(__le32); + ret = ret && (ep->freqmax == (data_maxsize / (frame_bits >> 3)) + << (16 - ep->datainterval)); + } + + if (ep->fill_max) + ret = ret && (ep->curpacksize == ep->maxpacksize); + else + ret = ret && (ep->curpacksize == maxsize); + + if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) { + packs_per_ms = 8 >> ep->datainterval; + max_packs_per_urb = MAX_PACKS_HS; + } else { + packs_per_ms = 1; + max_packs_per_urb = MAX_PACKS; + } + if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) + max_packs_per_urb = min(max_packs_per_urb, + 1U << sync_ep->syncinterval); + max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); + + /* + * Capture endpoints need to use small URBs because there's no way + * to tell in advance where the next period will end, and we don't + * want the next URB to complete much after the period ends. + * + * Playback endpoints with implicit sync much use the same parameters + * as their corresponding capture endpoint. + */ + if (usb_pipein(ep->pipe) || + snd_usb_endpoint_implicit_feedback_sink(ep)) { + + urb_packs = packs_per_ms; + /* + * Wireless devices can poll at a max rate of once per 4ms. + * For dataintervals less than 5, increase the packet count to + * allow the host controller to use bursting to fill in the + * gaps. + */ + if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_WIRELESS) { + int interval = ep->datainterval; + + while (interval < 5) { + urb_packs <<= 1; + ++interval; + } + } + /* make capture URBs <= 1 ms and smaller than a period */ + urb_packs = min(max_packs_per_urb, urb_packs); + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; + ret = ret && (ep->nurbs == MAX_URBS); + + /* + * Playback endpoints without implicit sync are adjusted so that + * a period fits as evenly as possible in the smallest number of + * URBs. The total number of URBs is adjusted to the size of the + * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits. + */ + } else { + /* determine how small a packet can be */ + minsize = (ep->freqn >> (16 - ep->datainterval)) * + (frame_bits >> 3); + /* with sync from device, assume it can be 12% lower */ + if (sync_ep) + minsize -= minsize >> 3; + minsize = max(minsize, 1u); + + /* how many packets will contain an entire ALSA period? */ + max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize); + + /* how many URBs will contain a period? */ + urbs_per_period = DIV_ROUND_UP(max_packs_per_period, + max_packs_per_urb); + /* how many packets are needed in each URB? */ + urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period); + + /* limit the number of frames in a single URB */ + ret = ret && (ep->max_urb_frames == + DIV_ROUND_UP(frames_per_period, urbs_per_period)); + + /* try to use enough URBs to contain an entire ALSA buffer */ + max_urbs = min((unsigned) MAX_URBS, + MAX_QUEUE * packs_per_ms / urb_packs); + ret = ret && (ep->nurbs == min(max_urbs, + urbs_per_period * periods_per_buffer)); + } + + ret = ret && (ep->datainterval == fmt->datainterval); + ret = ret && (ep->maxpacksize == fmt->maxpacksize); + ret = ret && + (ep->fill_max == !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)); + + return ret; +} + +/* * configure a data endpoint */ static int data_ep_set_params(struct snd_usb_endpoint *ep, @@ -861,10 +1060,23 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, int err; if (ep->use_count != 0) { - usb_audio_warn(ep->chip, - "Unable to change format on ep #%x: already in use\n", - ep->ep_num); - return -EBUSY; + bool check = ep->is_implicit_feedback && + check_ep_params(ep, pcm_format, + channels, period_bytes, + period_frames, buffer_periods, + fmt, sync_ep); + + if (!check) { + usb_audio_warn(ep->chip, + "Unable to change format on ep #%x: already in use\n", + ep->ep_num); + return -EBUSY; + } + + usb_audio_dbg(ep->chip, + "Ep #%x already in use as implicit feedback but format not changed\n", + ep->ep_num); + return 0; } /* release old buffers, if any */ @@ -874,10 +1086,17 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, ep->maxpacksize = fmt->maxpacksize; ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX); - if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) + if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) { ep->freqn = get_usb_full_speed_rate(rate); - else + ep->fps = 1000; + } else { ep->freqn = get_usb_high_speed_rate(rate); + ep->fps = 8000; + } + + ep->sample_rem = rate % ep->fps; + ep->framesize[0] = rate / ep->fps; + ep->framesize[1] = (rate + (ep->fps - 1)) / ep->fps; /* calculate the frequency in 16.16 format */ ep->freqm = ep->freqn; @@ -936,6 +1155,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) ep->active_mask = 0; ep->unlink_mask = 0; ep->phase = 0; + ep->sample_accum = 0; snd_usb_endpoint_start_quirk(ep); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 63a39d4fa8d8..d23fa0a8c11b 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -28,6 +28,7 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free(struct snd_usb_endpoint *ep); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); +int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep); int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index 4f096685ed65..7629116f570e 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -550,6 +550,7 @@ static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file) /* NOTE: hwdep layer provides atomicity here */ line6->messages.active = 1; + line6->messages.nonblock = file->f_flags & O_NONBLOCK ? 1 : 0; return 0; } @@ -579,6 +580,9 @@ line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, while (kfifo_len(&line6->messages.fifo) == 0) { mutex_unlock(&line6->messages.read_lock); + if (line6->messages.nonblock) + return -EAGAIN; + rv = wait_event_interruptible( line6->messages.wait_queue, kfifo_len(&line6->messages.fifo) != 0); @@ -626,11 +630,27 @@ line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count, return rv; } +static __poll_t +line6_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait) +{ + __poll_t rv; + struct usb_line6 *line6 = hwdep->private_data; + + poll_wait(file, &line6->messages.wait_queue, wait); + + mutex_lock(&line6->messages.read_lock); + rv = kfifo_len(&line6->messages.fifo) == 0 ? 0 : EPOLLIN | EPOLLRDNORM; + mutex_unlock(&line6->messages.read_lock); + + return rv; +} + static const struct snd_hwdep_ops hwdep_ops = { .open = line6_hwdep_open, .release = line6_hwdep_release, .read = line6_hwdep_read, .write = line6_hwdep_write, + .poll = line6_hwdep_poll, }; /* Insert into circular buffer */ diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index e5e572ed5f30..1a4e3700c80c 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -163,6 +163,7 @@ struct usb_line6 { struct mutex read_lock; wait_queue_head_t wait_queue; unsigned int active:1; + unsigned int nonblock:1; STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT) fifo; } messages; diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index a88d7854513b..15769f266790 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1182,6 +1182,14 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, cval->res = 384; } break; + case USB_ID(0x0495, 0x3042): /* ESS Technology Asus USB DAC */ + if ((strstr(kctl->id.name, "Playback Volume") != NULL) || + strstr(kctl->id.name, "Capture Volume") != NULL) { + cval->min >>= 8; + cval->max = 0; + cval->res = 1; + } + break; } } diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index bfdc6ad52785..9af7aa93f6fa 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -397,6 +397,21 @@ static const struct usbmix_connector_map trx40_mobo_connector_map[] = { {} }; +/* Rear panel + front mic on Gigabyte TRX40 Aorus Master with ALC1220-VB */ +static const struct usbmix_name_map aorus_master_alc1220vb_map[] = { + { 17, NULL }, /* OT, IEC958?, disabled */ + { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */ + { 16, "Line Out" }, /* OT */ + { 22, "Line Out Playback" }, /* FU */ + { 7, "Line" }, /* IT */ + { 19, "Line Capture" }, /* FU */ + { 8, "Mic" }, /* IT */ + { 20, "Mic Capture" }, /* FU */ + { 9, "Front Mic" }, /* IT */ + { 21, "Front Mic Capture" }, /* FU */ + {} +}; + /* * Control map entries */ @@ -526,6 +541,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x1b1c, 0x0a42), .map = corsair_virtuoso_map, }, + { /* Gigabyte TRX40 Aorus Master (rear panel + front mic) */ + .id = USB_ID(0x0414, 0xa001), + .map = aorus_master_alc1220vb_map, + }, { /* Gigabyte TRX40 Aorus Pro WiFi */ .id = USB_ID(0x0414, 0xa002), .map = trx40_mobo_map, diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index a5f65a9a0254..b6bcf2f92383 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -2185,6 +2185,421 @@ static int snd_rme_controls_create(struct usb_mixer_interface *mixer) return 0; } +/* + * RME Babyface Pro (FS) + * + * These devices exposes a couple of DSP functions via request to EP0. + * Switches are available via control registers, while routing is controlled + * by controlling the volume on each possible crossing point. + * Volume control is linear, from -inf (dec. 0) to +6dB (dec. 65536) with + * 0dB being at dec. 32768. + */ +enum { + SND_BBFPRO_CTL_REG1 = 0, + SND_BBFPRO_CTL_REG2 +}; + +#define SND_BBFPRO_CTL_REG_MASK 1 +#define SND_BBFPRO_CTL_IDX_MASK 0xff +#define SND_BBFPRO_CTL_IDX_SHIFT 1 +#define SND_BBFPRO_CTL_VAL_MASK 1 +#define SND_BBFPRO_CTL_VAL_SHIFT 9 +#define SND_BBFPRO_CTL_REG1_CLK_MASTER 0 +#define SND_BBFPRO_CTL_REG1_CLK_OPTICAL 1 +#define SND_BBFPRO_CTL_REG1_SPDIF_PRO 7 +#define SND_BBFPRO_CTL_REG1_SPDIF_EMPH 8 +#define SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL 10 +#define SND_BBFPRO_CTL_REG2_48V_AN1 0 +#define SND_BBFPRO_CTL_REG2_48V_AN2 1 +#define SND_BBFPRO_CTL_REG2_SENS_IN3 2 +#define SND_BBFPRO_CTL_REG2_SENS_IN4 3 +#define SND_BBFPRO_CTL_REG2_PAD_AN1 4 +#define SND_BBFPRO_CTL_REG2_PAD_AN2 5 + +#define SND_BBFPRO_MIXER_IDX_MASK 0x1ff +#define SND_BBFPRO_MIXER_VAL_MASK 0x3ffff +#define SND_BBFPRO_MIXER_VAL_SHIFT 9 +#define SND_BBFPRO_MIXER_VAL_MIN 0 // -inf +#define SND_BBFPRO_MIXER_VAL_MAX 65536 // +6dB + +#define SND_BBFPRO_USBREQ_CTL_REG1 0x10 +#define SND_BBFPRO_USBREQ_CTL_REG2 0x17 +#define SND_BBFPRO_USBREQ_MIXER 0x12 + +static int snd_bbfpro_ctl_update(struct usb_mixer_interface *mixer, u8 reg, + u8 index, u8 value) +{ + int err; + u16 usb_req, usb_idx, usb_val; + struct snd_usb_audio *chip = mixer->chip; + + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + + if (reg == SND_BBFPRO_CTL_REG1) { + usb_req = SND_BBFPRO_USBREQ_CTL_REG1; + if (index == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) { + usb_idx = 3; + usb_val = value ? 3 : 0; + } else { + usb_idx = 1 << index; + usb_val = value ? usb_idx : 0; + } + } else { + usb_req = SND_BBFPRO_USBREQ_CTL_REG2; + usb_idx = 1 << index; + usb_val = value ? usb_idx : 0; + } + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), usb_req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + usb_val, usb_idx, NULL, 0); + + snd_usb_unlock_shutdown(chip); + return err; +} + +static int snd_bbfpro_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 reg, idx, val; + int pv; + + pv = kcontrol->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + val = kcontrol->private_value >> SND_BBFPRO_CTL_VAL_SHIFT; + + if ((reg == SND_BBFPRO_CTL_REG1 && + idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) || + (reg == SND_BBFPRO_CTL_REG2 && + (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 || + idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) { + ucontrol->value.enumerated.item[0] = val; + } else { + ucontrol->value.integer.value[0] = val; + } + return 0; +} + +static int snd_bbfpro_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + u8 reg, idx; + int pv; + + pv = kcontrol->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + + if (reg == SND_BBFPRO_CTL_REG1 && + idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) { + static const char * const texts[2] = { + "AutoSync", + "Internal" + }; + return snd_ctl_enum_info(uinfo, 1, 2, texts); + } else if (reg == SND_BBFPRO_CTL_REG2 && + (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 || + idx == SND_BBFPRO_CTL_REG2_SENS_IN4)) { + static const char * const texts[2] = { + "-10dBV", + "+4dBu" + }; + return snd_ctl_enum_info(uinfo, 1, 2, texts); + } + + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + return 0; +} + +static int snd_bbfpro_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int err; + u8 reg, idx; + int old_value, pv, val; + + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + + pv = kcontrol->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + old_value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK; + + if ((reg == SND_BBFPRO_CTL_REG1 && + idx == SND_BBFPRO_CTL_REG1_CLK_OPTICAL) || + (reg == SND_BBFPRO_CTL_REG2 && + (idx == SND_BBFPRO_CTL_REG2_SENS_IN3 || + idx == SND_BBFPRO_CTL_REG2_SENS_IN4))) { + val = ucontrol->value.enumerated.item[0]; + } else { + val = ucontrol->value.integer.value[0]; + } + + if (val > 1) + return -EINVAL; + + if (val == old_value) + return 0; + + kcontrol->private_value = reg + | ((idx & SND_BBFPRO_CTL_IDX_MASK) << SND_BBFPRO_CTL_IDX_SHIFT) + | ((val & SND_BBFPRO_CTL_VAL_MASK) << SND_BBFPRO_CTL_VAL_SHIFT); + + err = snd_bbfpro_ctl_update(mixer, reg, idx, val); + return err < 0 ? err : 1; +} + +static int snd_bbfpro_ctl_resume(struct usb_mixer_elem_list *list) +{ + u8 reg, idx; + int value, pv; + + pv = list->kctl->private_value; + reg = pv & SND_BBFPRO_CTL_REG_MASK; + idx = (pv >> SND_BBFPRO_CTL_IDX_SHIFT) & SND_BBFPRO_CTL_IDX_MASK; + value = (pv >> SND_BBFPRO_CTL_VAL_SHIFT) & SND_BBFPRO_CTL_VAL_MASK; + + return snd_bbfpro_ctl_update(list->mixer, reg, idx, value); +} + +static int snd_bbfpro_vol_update(struct usb_mixer_interface *mixer, u16 index, + u32 value) +{ + struct snd_usb_audio *chip = mixer->chip; + int err; + u16 idx; + u16 usb_idx, usb_val; + u32 v; + + err = snd_usb_lock_shutdown(chip); + if (err < 0) + return err; + + idx = index & SND_BBFPRO_MIXER_IDX_MASK; + // 18 bit linear volume, split so 2 bits end up in index. + v = value & SND_BBFPRO_MIXER_VAL_MASK; + usb_idx = idx | (v & 0x3) << 14; + usb_val = (v >> 2) & 0xffff; + + err = snd_usb_ctl_msg(chip->dev, + usb_sndctrlpipe(chip->dev, 0), + SND_BBFPRO_USBREQ_MIXER, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, + usb_val, usb_idx, NULL, 0); + + snd_usb_unlock_shutdown(chip); + return err; +} + +static int snd_bbfpro_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT; + return 0; +} + +static int snd_bbfpro_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = SND_BBFPRO_MIXER_VAL_MIN; + uinfo->value.integer.max = SND_BBFPRO_MIXER_VAL_MAX; + return 0; +} + +static int snd_bbfpro_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int err; + u16 idx; + u32 new_val, old_value, uvalue; + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); + struct usb_mixer_interface *mixer = list->mixer; + + uvalue = ucontrol->value.integer.value[0]; + idx = kcontrol->private_value & SND_BBFPRO_MIXER_IDX_MASK; + old_value = kcontrol->private_value >> SND_BBFPRO_MIXER_VAL_SHIFT; + + if (uvalue > SND_BBFPRO_MIXER_VAL_MAX) + return -EINVAL; + + if (uvalue == old_value) + return 0; + + new_val = uvalue & SND_BBFPRO_MIXER_VAL_MASK; + + kcontrol->private_value = idx + | (new_val << SND_BBFPRO_MIXER_VAL_SHIFT); + + err = snd_bbfpro_vol_update(mixer, idx, new_val); + return err < 0 ? err : 1; +} + +static int snd_bbfpro_vol_resume(struct usb_mixer_elem_list *list) +{ + int pv = list->kctl->private_value; + u16 idx = pv & SND_BBFPRO_MIXER_IDX_MASK; + u32 val = (pv >> SND_BBFPRO_MIXER_VAL_SHIFT) + & SND_BBFPRO_MIXER_VAL_MASK; + return snd_bbfpro_vol_update(list->mixer, idx, val); +} + +// Predfine elements +static const struct snd_kcontrol_new snd_bbfpro_ctl_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .index = 0, + .info = snd_bbfpro_ctl_info, + .get = snd_bbfpro_ctl_get, + .put = snd_bbfpro_ctl_put +}; + +static const struct snd_kcontrol_new snd_bbfpro_vol_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .index = 0, + .info = snd_bbfpro_vol_info, + .get = snd_bbfpro_vol_get, + .put = snd_bbfpro_vol_put +}; + +static int snd_bbfpro_ctl_add(struct usb_mixer_interface *mixer, u8 reg, + u8 index, char *name) +{ + struct snd_kcontrol_new knew = snd_bbfpro_ctl_control; + + knew.name = name; + knew.private_value = (reg & SND_BBFPRO_CTL_REG_MASK) + | ((index & SND_BBFPRO_CTL_IDX_MASK) + << SND_BBFPRO_CTL_IDX_SHIFT); + + return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_ctl_resume, + &knew, NULL); +} + +static int snd_bbfpro_vol_add(struct usb_mixer_interface *mixer, u16 index, + char *name) +{ + struct snd_kcontrol_new knew = snd_bbfpro_vol_control; + + knew.name = name; + knew.private_value = index & SND_BBFPRO_MIXER_IDX_MASK; + + return add_single_ctl_with_resume(mixer, 0, snd_bbfpro_vol_resume, + &knew, NULL); +} + +static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i, o; + char name[48]; + + static const char * const input[] = { + "AN1", "AN2", "IN3", "IN4", "AS1", "AS2", "ADAT3", + "ADAT4", "ADAT5", "ADAT6", "ADAT7", "ADAT8"}; + + static const char * const output[] = { + "AN1", "AN2", "PH3", "PH4", "AS1", "AS2", "ADAT3", "ADAT4", + "ADAT5", "ADAT6", "ADAT7", "ADAT8"}; + + for (o = 0 ; o < 12 ; ++o) { + for (i = 0 ; i < 12 ; ++i) { + // Line routing + snprintf(name, sizeof(name), + "%s-%s-%s Playback Volume", + (i < 2 ? "Mic" : "Line"), + input[i], output[o]); + err = snd_bbfpro_vol_add(mixer, (26 * o + i), name); + if (err < 0) + return err; + + // PCM routing... yes, it is output remapping + snprintf(name, sizeof(name), + "PCM-%s-%s Playback Volume", + output[i], output[o]); + err = snd_bbfpro_vol_add(mixer, (26 * o + 12 + i), + name); + if (err < 0) + return err; + } + } + + // Control Reg 1 + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_CLK_OPTICAL, + "Sample Clock Source"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_SPDIF_PRO, + "IEC958 Pro Mask"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_SPDIF_EMPH, + "IEC958 Emphasis"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG1, + SND_BBFPRO_CTL_REG1_SPDIF_OPTICAL, + "IEC958 Switch"); + if (err < 0) + return err; + + // Control Reg 2 + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_48V_AN1, + "Mic-AN1 48V"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_48V_AN2, + "Mic-AN2 48V"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_SENS_IN3, + "Line-IN3 Sens."); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_SENS_IN4, + "Line-IN4 Sens."); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_PAD_AN1, + "Mic-AN1 PAD"); + if (err < 0) + return err; + + err = snd_bbfpro_ctl_add(mixer, SND_BBFPRO_CTL_REG2, + SND_BBFPRO_CTL_REG2_PAD_AN2, + "Mic-AN2 PAD"); + if (err < 0) + return err; + + return 0; +} + int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { int err = 0; @@ -2286,6 +2701,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x0194f, 0x010c): /* Presonus Studio 1810c */ err = snd_sc1810_init_mixer(mixer); break; + case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */ + err = snd_bbfpro_controls_create(mixer); + break; } return err; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index a4e4064f9aee..d61c2f1095b5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -404,6 +404,8 @@ add_sync_ep: if (!subs->sync_endpoint) return -EINVAL; + subs->sync_endpoint->is_implicit_feedback = 1; + subs->data_endpoint->sync_master = subs->sync_endpoint; return 1; @@ -502,12 +504,15 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, implicit_fb ? SND_USB_ENDPOINT_TYPE_DATA : SND_USB_ENDPOINT_TYPE_SYNC); + if (!subs->sync_endpoint) { if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) return 0; return -EINVAL; } + subs->sync_endpoint->is_implicit_feedback = implicit_fb; + subs->data_endpoint->sync_master = subs->sync_endpoint; return 0; @@ -1579,6 +1584,8 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, for (i = 0; i < ctx->packets; i++) { if (ctx->packet_size[i]) counts = ctx->packet_size[i]; + else if (ep->sync_master) + counts = snd_usb_endpoint_slave_next_packet_size(ep); else counts = snd_usb_endpoint_next_packet_size(ep); diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 4174ad11fca6..889c550c9f29 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -54,6 +54,38 @@ void snd_usb_audio_create_proc(struct snd_usb_audio *chip) proc_audio_usbid_read); } +static const char * const channel_labels[] = { + [SNDRV_CHMAP_NA] = "N/A", + [SNDRV_CHMAP_MONO] = "MONO", + [SNDRV_CHMAP_FL] = "FL", + [SNDRV_CHMAP_FR] = "FR", + [SNDRV_CHMAP_FC] = "FC", + [SNDRV_CHMAP_LFE] = "LFE", + [SNDRV_CHMAP_RL] = "RL", + [SNDRV_CHMAP_RR] = "RR", + [SNDRV_CHMAP_FLC] = "FLC", + [SNDRV_CHMAP_FRC] = "FRC", + [SNDRV_CHMAP_RC] = "RC", + [SNDRV_CHMAP_SL] = "SL", + [SNDRV_CHMAP_SR] = "SR", + [SNDRV_CHMAP_TC] = "TC", + [SNDRV_CHMAP_TFL] = "TFL", + [SNDRV_CHMAP_TFC] = "TFC", + [SNDRV_CHMAP_TFR] = "TFR", + [SNDRV_CHMAP_TRL] = "TRL", + [SNDRV_CHMAP_TRC] = "TRC", + [SNDRV_CHMAP_TRR] = "TRR", + [SNDRV_CHMAP_TFLC] = "TFLC", + [SNDRV_CHMAP_TFRC] = "TFRC", + [SNDRV_CHMAP_LLFE] = "LLFE", + [SNDRV_CHMAP_RLFE] = "RLFE", + [SNDRV_CHMAP_TSL] = "TSL", + [SNDRV_CHMAP_TSR] = "TSR", + [SNDRV_CHMAP_BC] = "BC", + [SNDRV_CHMAP_RLC] = "RLC", + [SNDRV_CHMAP_RRC] = "RRC", +}; + /* * proc interface for list the supported pcm formats */ @@ -97,6 +129,27 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s snd_iprintf(buffer, " Data packet interval: %d us\n", 125 * (1 << fp->datainterval)); snd_iprintf(buffer, " Bits: %d\n", fp->fmt_bits); + + if (fp->dsd_raw) + snd_iprintf(buffer, " DSD raw: DOP=%d, bitrev=%d\n", + fp->dsd_dop, fp->dsd_bitrev); + + if (fp->chmap) { + const struct snd_pcm_chmap_elem *map = fp->chmap; + int c; + + snd_iprintf(buffer, " Channel map:"); + for (c = 0; c < map->channels; c++) { + if (map->map[c] >= ARRAY_SIZE(channel_labels) || + !channel_labels[map->map[c]]) + snd_iprintf(buffer, " --"); + else + snd_iprintf(buffer, " %s", + channel_labels[map->map[c]]); + } + snd_iprintf(buffer, "\n"); + } + // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); // snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes); } diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 6313c30f5c85..5236f4d015c4 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -25,6 +25,21 @@ .idProduct = prod, \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC +#define QUIRK_RENAME_DEVICE(_vendor, _device) \ + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ + .vendor_name = _vendor, \ + .product_name = _device, \ + .ifnum = QUIRK_NO_INTERFACE \ + } + +#define QUIRK_DEVICE_PROFILE(_vendor, _device, _profile) \ + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ + .vendor_name = _vendor, \ + .product_name = _device, \ + .profile_name = _profile, \ + .ifnum = QUIRK_NO_INTERFACE \ + } + /* FTDI devices */ { USB_DEVICE(0x0403, 0xb8d8), @@ -61,20 +76,12 @@ /* Creative/E-Mu devices */ { USB_DEVICE(0x041e, 0x3010), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Creative Labs", - .product_name = "Sound Blaster MP3+", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Creative Labs", "Sound Blaster MP3+") }, /* Creative/Toshiba Multimedia Center SB-0500 */ { USB_DEVICE(0x041e, 0x3048), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Toshiba", - .product_name = "SB-0500", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Toshiba", "SB-0500") }, { /* E-Mu 0202 USB */ @@ -207,11 +214,7 @@ .idProduct = 0x0990, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Logitech, Inc.", - .product_name = "QuickCam Pro 9000", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Logitech, Inc.", "QuickCam Pro 9000") }, /* @@ -2596,11 +2599,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, { USB_DEVICE(0x0ccd, 0x0028), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "TerraTec", - .product_name = "Aureon5.1MkII", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("TerraTec", "Aureon5.1MkII") }, { USB_DEVICE(0x0ccd, 0x0035), @@ -2615,19 +2614,11 @@ YAMAHA_DEVICE(0x7010, "UB99"), /* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ { USB_DEVICE(0x103d, 0x0100), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Stanton", - .product_name = "ScratchAmp", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp") }, { USB_DEVICE(0x103d, 0x0101), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Stanton", - .product_name = "ScratchAmp", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp") }, /* Novation EMS devices */ @@ -2788,11 +2779,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), { /* aka. Serato Scratch Live DJ Box */ USB_DEVICE(0x13e5, 0x0001), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Rane", - .product_name = "SL-1", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE("Rane", "SL-1") }, /* Native Instruments MK2 series */ @@ -3259,10 +3246,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), * is also used by the CM106 based cards, so make it unique. */ USB_DEVICE(0x0d8c, 0x0103), - .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { - .product_name = "Audio Advantage MicroII", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_RENAME_DEVICE(NULL, "Audio Advantage MicroII") }, /* disabled due to regression for other devices; @@ -3368,12 +3352,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), /* Dell WD15 Dock */ { USB_DEVICE(0x0bda, 0x4014), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Dell", - .product_name = "WD15 Dock", - .profile_name = "Dell-WD15-Dock", - .ifnum = QUIRK_NO_INTERFACE - } + QUIRK_DEVICE_PROFILE("Dell", "WD15 Dock", "Dell-WD15-Dock") }, /* Dell WD19 Dock */ { @@ -3553,12 +3532,8 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), #define ALC1220_VB_DESKTOP(vend, prod) { \ USB_DEVICE(vend, prod), \ - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ - .vendor_name = "Realtek", \ - .product_name = "ALC1220-VB-DT", \ - .profile_name = "Realtek-ALC1220-VB-Desktop", \ - .ifnum = QUIRK_NO_INTERFACE \ - } \ + QUIRK_DEVICE_PROFILE("Realtek", "ALC1220-VB-DT", \ + "Realtek-ALC1220-VB-Desktop") \ } ALC1220_VB_DESKTOP(0x0414, 0xa002), /* Gigabyte TRX40 Aorus Pro WiFi */ ALC1220_VB_DESKTOP(0x0db0, 0x0d64), /* MSI TRX40 Creator */ @@ -3566,4 +3541,21 @@ ALC1220_VB_DESKTOP(0x0db0, 0x543d), /* MSI TRX40 */ ALC1220_VB_DESKTOP(0x26ce, 0x0a01), /* Asrock TRX40 Creator */ #undef ALC1220_VB_DESKTOP +/* Two entries for Gigabyte TRX40 Aorus Master: + * TRX40 Aorus Master has two USB-audio devices, one for the front headphone + * with ESS SABRE9218 DAC chip, while another for the rest I/O (the rear + * panel and the front mic) with Realtek ALC1220-VB. + * Here we provide two distinct names for making UCM profiles easier. + */ +{ + USB_DEVICE(0x0414, 0xa000), + QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Front Headphone", + "Gigabyte-Aorus-Master-Front-Headphone") +}, +{ + USB_DEVICE(0x0414, 0xa001), + QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Main Audio", + "Gigabyte-Aorus-Master-Main-Audio") +}, + #undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index d8a765be5dfe..c0e3bc4afec6 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1807,20 +1807,6 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, */ fp->attributes &= ~UAC_EP_CS_ATTR_FILL_MAX; break; - case USB_ID(0x1235, 0x8200): /* Focusrite Scarlett 2i4 2nd gen */ - case USB_ID(0x1235, 0x8202): /* Focusrite Scarlett 2i2 2nd gen */ - case USB_ID(0x1235, 0x8205): /* Focusrite Scarlett Solo 2nd gen */ - /* - * Reports that playback should use Synch: Synchronous - * while still providing a feedback endpoint. - * Synchronous causes snapping on some sample rates. - * Force it to use Synch: Asynchronous. - */ - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE; - fp->ep_attr |= USB_ENDPOINT_SYNC_ASYNC; - } - break; } } diff --git a/sound/usb/usx2y/usbusx2y.h b/sound/usb/usx2y/usbusx2y.h index e0f77172ce8f..144b85f57bd2 100644 --- a/sound/usb/usx2y/usbusx2y.h +++ b/sound/usb/usx2y/usbusx2y.h @@ -18,7 +18,7 @@ struct snd_usX2Y_AsyncSeq { struct snd_usX2Y_urbSeq { int submitted; int len; - struct urb *urb[0]; + struct urb *urb[]; }; #include "usx2yhwdeppcm.h" |