diff options
Diffstat (limited to 'sound')
75 files changed, 2489 insertions, 1062 deletions
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.c b/sound/core/oss/pcm_plugin.c index 59d62f05658f..1545f8fdb4db 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -205,13 +205,14 @@ static snd_pcm_sframes_t calc_dst_frames(struct snd_pcm_substream *plug, plugin = snd_pcm_plug_first(plug); while (plugin && frames > 0) { plugin_next = plugin->next; + if (check_size && plugin->buf_frames && + frames > plugin->buf_frames) + frames = plugin->buf_frames; if (plugin->dst_frames) { frames = plugin->dst_frames(plugin, frames); if (frames < 0) return frames; } - if (check_size && frames > plugin->buf_frames) - frames = plugin->buf_frames; plugin = plugin_next; } return frames; @@ -225,14 +226,15 @@ static snd_pcm_sframes_t calc_src_frames(struct snd_pcm_substream *plug, plugin = snd_pcm_plug_last(plug); while (plugin && frames > 0) { - if (check_size && frames > plugin->buf_frames) - frames = plugin->buf_frames; plugin_prev = plugin->prev; if (plugin->src_frames) { frames = plugin->src_frames(plugin, frames); if (frames < 0) return frames; } + if (check_size && plugin->buf_frames && + frames > plugin->buf_frames) + frames = plugin->buf_frames; plugin = plugin_prev; } return frames; 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/pcm_lib.c b/sound/core/pcm_lib.c index 872a852de75c..d531e1bc2b81 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -433,6 +433,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, no_delta_check: if (runtime->status->hw_ptr == new_hw_ptr) { + runtime->hw_ptr_jiffies = curr_jiffies; update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return 0; } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index aef860256278..eeab8850ed76 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -138,6 +138,16 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream) } EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq); +static void snd_pcm_stream_lock_nested(struct snd_pcm_substream *substream) +{ + struct snd_pcm_group *group = &substream->self_group; + + if (substream->pcm->nonatomic) + mutex_lock_nested(&group->mutex, SINGLE_DEPTH_NESTING); + else + spin_lock_nested(&group->lock, SINGLE_DEPTH_NESTING); +} + /** * snd_pcm_stream_unlock_irq - Unlock the PCM stream * @substream: PCM substream @@ -2166,6 +2176,12 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) } pcm_file = f.file->private_data; substream1 = pcm_file->substream; + + if (substream == substream1) { + res = -EINVAL; + goto _badf; + } + group = kzalloc(sizeof(*group), GFP_KERNEL); if (!group) { res = -ENOMEM; @@ -2194,7 +2210,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) snd_pcm_stream_unlock_irq(substream); snd_pcm_group_lock_irq(target_group, nonatomic); - snd_pcm_stream_lock(substream1); + snd_pcm_stream_lock_nested(substream1); snd_pcm_group_assign(substream1, target_group); refcount_inc(&target_group->refs); snd_pcm_stream_unlock(substream1); @@ -2210,7 +2226,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) static void relink_to_local(struct snd_pcm_substream *substream) { - snd_pcm_stream_lock(substream); + snd_pcm_stream_lock_nested(substream); snd_pcm_group_assign(substream, &substream->self_group); snd_pcm_stream_unlock(substream); } diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 20dd08e1f675..2a688b711a9a 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -120,6 +120,17 @@ static void snd_rawmidi_input_event_work(struct work_struct *work) runtime->event(runtime->substream); } +/* buffer refcount management: call with runtime->lock held */ +static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime) +{ + runtime->buffer_ref++; +} + +static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime) +{ + runtime->buffer_ref--; +} + static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) { struct snd_rawmidi_runtime *runtime; @@ -669,6 +680,11 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime, if (!newbuf) return -ENOMEM; spin_lock_irq(&runtime->lock); + if (runtime->buffer_ref) { + spin_unlock_irq(&runtime->lock); + kvfree(newbuf); + return -EBUSY; + } oldbuf = runtime->buffer; runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; @@ -1019,8 +1035,10 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, long result = 0, count1; struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; + int err = 0; spin_lock_irqsave(&runtime->lock, flags); + snd_rawmidi_buffer_ref(runtime); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) @@ -1039,16 +1057,19 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, if (userbuf) { spin_unlock_irqrestore(&runtime->lock, flags); if (copy_to_user(userbuf + result, - runtime->buffer + appl_ptr, count1)) { - return result > 0 ? result : -EFAULT; - } + runtime->buffer + appl_ptr, count1)) + err = -EFAULT; spin_lock_irqsave(&runtime->lock, flags); + if (err) + goto out; } result += count1; count -= count1; } + out: + snd_rawmidi_buffer_unref(runtime); spin_unlock_irqrestore(&runtime->lock, flags); - return result; + return result > 0 ? result : err; } long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, @@ -1342,6 +1363,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, return -EAGAIN; } } + snd_rawmidi_buffer_ref(runtime); while (count > 0 && runtime->avail > 0) { count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) @@ -1373,6 +1395,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, } __end: count1 = runtime->avail < runtime->buffer_size; + snd_rawmidi_buffer_unref(runtime); spin_unlock_irqrestore(&runtime->lock, flags); if (count1) snd_rawmidi_output_trigger(substream, 1); 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-trace.h b/sound/firewire/amdtp-stream-trace.h index 16c7f6605511..26e7cb555d3c 100644 --- a/sound/firewire/amdtp-stream-trace.h +++ b/sound/firewire/amdtp-stream-trace.h @@ -66,8 +66,7 @@ TRACE_EVENT(amdtp_packet, __entry->irq, __entry->index, __print_array(__get_dynamic_array(cip_header), - __get_dynamic_array_len(cip_header), - sizeof(u8))) + __get_dynamic_array_len(cip_header), 1)) ); #endif 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/Kconfig b/sound/hda/Kconfig index 4ca6b09056f3..3bc9224d5e4f 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -21,16 +21,17 @@ config SND_HDA_EXT_CORE select SND_HDA_CORE config SND_HDA_PREALLOC_SIZE - int "Pre-allocated buffer size for HD-audio driver" if !SND_DMA_SGBUF + int "Pre-allocated buffer size for HD-audio driver" range 0 32768 - default 0 if SND_DMA_SGBUF + default 2048 if SND_DMA_SGBUF default 64 if !SND_DMA_SGBUF help Specifies the default pre-allocated buffer-size in kB for the HD-audio driver. A larger buffer (e.g. 2048) is preferred for systems using PulseAudio. The default 64 is chosen just for compatibility reasons. - On x86 systems, the default is zero as we need no preallocation. + On x86 systems, the default is 2048 as a reasonable value for + most of modern systems. Note that the pre-allocation size can be changed dynamically via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. 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/es1688/es1688.c b/sound/isa/es1688/es1688.c index ff3a05ad99c0..64610571a5e1 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -267,8 +267,10 @@ static int snd_es968_pnp_detect(struct pnp_card_link *pcard, return error; } error = snd_es1688_probe(card, dev); - if (error < 0) + if (error < 0) { + snd_card_free(card); return error; + } pnp_set_card_drvdata(pcard, card); snd_es968_pnp_is_probed = 1; return 0; diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index e764816a8f7a..b039429e6871 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -867,10 +867,13 @@ static void snd_miro_write(struct snd_miro *chip, unsigned char reg, spin_unlock_irqrestore(&chip->lock, flags); } +static inline void snd_miro_write_mask(struct snd_miro *chip, + unsigned char reg, unsigned char value, unsigned char mask) +{ + unsigned char oldval = snd_miro_read(chip, reg); -#define snd_miro_write_mask(chip, reg, value, mask) \ - snd_miro_write(chip, reg, \ - (snd_miro_read(chip, reg) & ~(mask)) | ((value) & (mask))) + snd_miro_write(chip, reg, (oldval & ~mask) | (value & mask)); +} /* * Proc Interface diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index d06b29693c85..0e6d20e49158 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -317,10 +317,13 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg, } -#define snd_opti9xx_write_mask(chip, reg, value, mask) \ - snd_opti9xx_write(chip, reg, \ - (snd_opti9xx_read(chip, reg) & ~(mask)) | ((value) & (mask))) +static inline void snd_opti9xx_write_mask(struct snd_opti9xx *chip, + unsigned char reg, unsigned char value, unsigned char mask) +{ + unsigned char oldval = snd_opti9xx_read(chip, reg); + snd_opti9xx_write(chip, reg, (oldval & ~mask) | (value & mask)); +} static int snd_opti9xx_configure(struct snd_opti9xx *chip, long port, 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/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c index 6e3177bcc709..015c0d676897 100644 --- a/sound/pci/ctxfi/cthw20k1.c +++ b/sound/pci/ctxfi/cthw20k1.c @@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk) static int src_put_rsc_ctrl_blk(void *blk) { - kfree((struct src_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk) static int src_mgr_put_ctrl_blk(void *blk) { - kfree((struct src_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk) static int srcimp_mgr_put_ctrl_blk(void *blk) { - kfree((struct srcimp_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk) static int amixer_rsc_put_ctrl_blk(void *blk) { - kfree((struct amixer_rsc_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk) static int dai_put_ctrl_blk(void *blk) { - kfree((struct dai_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk) static int dao_put_ctrl_blk(void *blk) { - kfree((struct dao_ctrl_blk *)blk); + kfree(blk); return 0; } @@ -1156,7 +1156,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk) static int daio_mgr_put_ctrl_blk(void *blk) { - kfree((struct daio_mgr_ctrl_blk *)blk); + kfree(blk); return 0; } diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index ddb7c2ce3f7c..def8161cde4c 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1040,7 +1040,7 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3) continue; - if (reg < 0x49 && val <= 0xffffffff && channel_id <= 2) + if (reg < 0x49 && channel_id <= 2) snd_emu10k1x_ptr_write(emu, reg, channel_id, val); } } 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_codec.c b/sound/pci/hda/hda_codec.c index a34a2c9f4bcf..7e3ae4534df9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -641,8 +641,18 @@ static void hda_jackpoll_work(struct work_struct *work) struct hda_codec *codec = container_of(work, struct hda_codec, jackpoll_work.work); - snd_hda_jack_set_dirty_all(codec); - snd_hda_jack_poll_all(codec); + /* for non-polling trigger: we need nothing if already powered on */ + if (!codec->jackpoll_interval && snd_hdac_is_power_on(&codec->core)) + return; + + /* the power-up/down sequence triggers the runtime resume */ + snd_hda_power_up_pm(codec); + /* update jacks manually if polling is required, too */ + if (codec->jackpoll_interval) { + snd_hda_jack_set_dirty_all(codec); + snd_hda_jack_poll_all(codec); + } + snd_hda_power_down_pm(codec); if (!codec->jackpoll_interval) return; @@ -2951,18 +2961,14 @@ static int hda_codec_runtime_resume(struct device *dev) static int hda_codec_force_resume(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); - bool forced_resume = !codec->relaxed_resume && codec->jacktbl.used; int ret; - /* The get/put pair below enforces the runtime resume even if the - * device hasn't been used at suspend time. This trick is needed to - * update the jack state change during the sleep. - */ - if (forced_resume) - pm_runtime_get_noresume(dev); ret = pm_runtime_force_resume(dev); - if (forced_resume) - pm_runtime_put(dev); + /* schedule jackpoll work for jack detection update */ + if (codec->jackpoll_interval || + (pm_runtime_suspended(dev) && hda_codec_need_resume(codec))) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); return ret; } diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index bd093593f8fb..41a03c61a74b 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1004,7 +1004,8 @@ static void __azx_runtime_resume(struct azx *chip, bool from_rt) if (status && from_rt) { list_for_each_codec(codec, &chip->bus) - if (status & (1 << codec->addr)) + if (!codec->relaxed_resume && + (status & (1 << codec->addr))) schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval); } @@ -1027,7 +1028,7 @@ static int azx_suspend(struct device *dev) chip = card->private_data; bus = azx_bus(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - __azx_runtime_suspend(chip); + pm_runtime_force_suspend(dev); if (bus->irq >= 0) { free_irq(bus->irq, chip); bus->irq = -1; @@ -1055,7 +1056,8 @@ static int azx_resume(struct device *dev) chip->msi = 0; if (azx_acquire_irq(chip, 1) < 0) return -EIO; - __azx_runtime_resume(chip, false); + + pm_runtime_force_resume(dev); snd_power_change_state(card, SNDRV_CTL_POWER_D0); trace_azx_resume(chip); @@ -1071,6 +1073,8 @@ static int azx_freeze_noirq(struct device *dev) struct azx *chip = card->private_data; struct pci_dev *pci = to_pci_dev(dev); + if (!azx_is_pm_ready(card)) + return 0; if (chip->driver_type == AZX_DRIVER_SKL) pci_set_power_state(pci, PCI_D3hot); @@ -1083,6 +1087,8 @@ static int azx_thaw_noirq(struct device *dev) struct azx *chip = card->private_data; struct pci_dev *pci = to_pci_dev(dev); + if (!azx_is_pm_ready(card)) + return 0; if (chip->driver_type == AZX_DRIVER_SKL) pci_set_power_state(pci, PCI_D0); @@ -1098,12 +1104,12 @@ static int azx_runtime_suspend(struct device *dev) if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - if (!azx_has_pm_runtime(chip)) - return 0; /* enable controller wake up event */ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | - STATESTS_INT_MASK); + if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) { + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | + STATESTS_INT_MASK); + } __azx_runtime_suspend(chip); trace_azx_runtime_suspend(chip); @@ -1114,17 +1120,18 @@ static int azx_runtime_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; + bool from_rt = snd_power_get_state(card) == SNDRV_CTL_POWER_D0; if (!azx_is_pm_ready(card)) return 0; chip = card->private_data; - if (!azx_has_pm_runtime(chip)) - return 0; - __azx_runtime_resume(chip, true); + __azx_runtime_resume(chip, from_rt); /* disable controller Wake Up event*/ - azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & - ~STATESTS_INT_MASK); + if (from_rt) { + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & + ~STATESTS_INT_MASK); + } trace_azx_runtime_resume(chip); return 0; @@ -1199,10 +1206,8 @@ static void azx_vs_set_state(struct pci_dev *pci, if (!disabled) { dev_info(chip->card->dev, "Start delayed initialization\n"); - if (azx_probe_continue(chip) < 0) { + if (azx_probe_continue(chip) < 0) dev_err(chip->card->dev, "initialization error\n"); - hda->init_failed = true; - } } } else { dev_info(chip->card->dev, "%s via vga_switcheroo\n", @@ -1335,12 +1340,15 @@ static int register_vga_switcheroo(struct azx *chip) /* * destructor */ -static int azx_free(struct azx *chip) +static void azx_free(struct azx *chip) { struct pci_dev *pci = chip->pci; struct hda_intel *hda = container_of(chip, struct hda_intel, chip); struct hdac_bus *bus = azx_bus(chip); + if (hda->freed) + return; + if (azx_has_pm_runtime(chip) && chip->running) pm_runtime_get_noresume(&pci->dev); chip->running = 0; @@ -1384,9 +1392,8 @@ static int azx_free(struct azx *chip) if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) snd_hdac_i915_exit(bus); - kfree(hda); - return 0; + hda->freed = 1; } static int azx_dev_disconnect(struct snd_device *device) @@ -1402,7 +1409,8 @@ static int azx_dev_disconnect(struct snd_device *device) static int azx_dev_free(struct snd_device *device) { - return azx_free(device->device_data); + azx_free(device->device_data); + return 0; } #ifdef SUPPORT_VGA_SWITCHEROO @@ -1769,7 +1777,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, if (err < 0) return err; - hda = kzalloc(sizeof(*hda), GFP_KERNEL); + hda = devm_kzalloc(&pci->dev, sizeof(*hda), GFP_KERNEL); if (!hda) { pci_disable_device(pci); return -ENOMEM; @@ -1810,7 +1818,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, err = azx_bus_init(chip, model[dev]); if (err < 0) { - kfree(hda); pci_disable_device(pci); return err; } @@ -2005,7 +2012,7 @@ static int azx_first_init(struct azx *chip) /* codec detection */ if (!azx_bus(chip)->codec_mask) { dev_err(card->dev, "no codecs found!\n"); - return -ENODEV; + /* keep running the rest for the runtime PM */ } if (azx_acquire_irq(chip, 0) < 0) @@ -2027,24 +2034,15 @@ static void azx_firmware_cb(const struct firmware *fw, void *context) { struct snd_card *card = context; struct azx *chip = card->private_data; - struct pci_dev *pci = chip->pci; - if (!fw) { - dev_err(card->dev, "Cannot load firmware, aborting\n"); - goto error; - } - - chip->fw = fw; + if (fw) + chip->fw = fw; + else + dev_err(card->dev, "Cannot load firmware, continue without patching\n"); if (!chip->disabled) { /* continue probing */ - if (azx_probe_continue(chip)) - goto error; + azx_probe_continue(chip); } - return; /* OK */ - - error: - snd_card_free(card); - pci_set_drvdata(pci, NULL); } #endif @@ -2080,10 +2078,10 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream, * some HD-audio PCI entries are exposed without any codecs, and such devices * should be ignored from the beginning. */ -static const struct snd_pci_quirk driver_blacklist[] = { - SND_PCI_QUIRK(0x1043, 0x874f, "ASUS ROG Zenith II / Strix", 0), - SND_PCI_QUIRK(0x1462, 0xcb59, "MSI TRX40 Creator", 0), - SND_PCI_QUIRK(0x1462, 0xcb60, "MSI TRX40", 0), +static const struct pci_device_id driver_blacklist[] = { + { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1043, 0x874f) }, /* ASUS ROG Zenith II / Strix */ + { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb59) }, /* MSI TRX40 Creator */ + { PCI_DEVICE_SUB(0x1022, 0x1487, 0x1462, 0xcb60) }, /* MSI TRX40 */ {} }; @@ -2103,7 +2101,7 @@ static int azx_probe(struct pci_dev *pci, bool schedule_probe; int err; - if (snd_pci_quirk_lookup(pci, driver_blacklist)) { + if (pci_match_id(driver_blacklist, pci)) { dev_info(&pci->dev, "Skipping the blacklisted device\n"); return -ENODEV; } @@ -2308,9 +2306,11 @@ static int azx_probe_continue(struct azx *chip) #endif /* create codec instances */ - err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); - if (err < 0) - goto out_free; + if (bus->codec_mask) { + err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); + if (err < 0) + goto out_free; + } #ifdef CONFIG_SND_HDA_PATCH_LOADER if (chip->fw) { @@ -2324,7 +2324,7 @@ static int azx_probe_continue(struct azx *chip) #endif } #endif - if ((probe_only[dev] & 1) == 0) { + if (bus->codec_mask && !(probe_only[dev] & 1)) { err = azx_codec_configure(chip); if (err < 0) goto out_free; @@ -2341,17 +2341,23 @@ static int azx_probe_continue(struct azx *chip) set_default_power_save(chip); - if (azx_has_pm_runtime(chip)) + if (azx_has_pm_runtime(chip)) { + pm_runtime_use_autosuspend(&pci->dev); + pm_runtime_allow(&pci->dev); pm_runtime_put_autosuspend(&pci->dev); + } out_free: - if (err < 0 || !hda->need_i915_power) + if (err < 0) { + azx_free(chip); + return err; + } + + if (!hda->need_i915_power) display_power(chip, false); - if (err < 0) - hda->init_failed = 1; complete_all(&hda->probe_wait); to_hda_bus(bus)->bus_probing = 0; - return err; + return 0; } static void azx_remove(struct pci_dev *pci) @@ -2656,6 +2662,9 @@ static const struct pci_device_id azx_ids[] = { { PCI_DEVICE(0x1002, 0xab20), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, + { PCI_DEVICE(0x1002, 0xab28), + .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | + AZX_DCAPS_PM_RUNTIME }, { PCI_DEVICE(0x1002, 0xab38), .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS | AZX_DCAPS_PM_RUNTIME }, diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index 2acfff3da1a0..3fb119f09040 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -27,6 +27,7 @@ struct hda_intel { unsigned int use_vga_switcheroo:1; unsigned int vga_switcheroo_registered:1; unsigned int init_failed:1; /* delayed init failed */ + unsigned int freed:1; /* resources already released */ bool need_i915_power:1; /* the hda controller needs i915 power */ }; 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 403baca89452..fbd7cc6026d8 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -38,6 +38,10 @@ static bool static_hdmi_pcm; module_param(static_hdmi_pcm, bool, 0644); MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); +static bool enable_acomp = true; +module_param(enable_acomp, bool, 0444); +MODULE_PARM_DESC(enable_acomp, "Enable audio component binding (default=yes)"); + struct hdmi_spec_per_cvt { hda_nid_t cvt_nid; int assigned; @@ -1844,8 +1848,10 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) /* Add sanity check to pass klockwork check. * This should never happen. */ - if (WARN_ON(spdif == NULL)) + if (WARN_ON(spdif == NULL)) { + mutex_unlock(&codec->spdif_mutex); return true; + } non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO); mutex_unlock(&codec->spdif_mutex); return non_pcm; @@ -2018,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); @@ -2031,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); @@ -2045,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); @@ -2061,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); @@ -2194,7 +2200,9 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + struct hdmi_eld *pin_eld = &per_pin->sink_eld; + pin_eld->eld_valid = false; hdmi_present_sense(per_pin, 0); } @@ -2505,6 +2513,11 @@ static void generic_acomp_init(struct hda_codec *codec, { struct hdmi_spec *spec = codec->spec; + if (!enable_acomp) { + codec_info(codec, "audio component disabled by module option\n"); + return; + } + spec->port2pin = port2pin; setup_drm_audio_ops(codec, ops); if (!snd_hdac_acomp_init(&codec->bus->core, &spec->drm_audio_ops, @@ -3774,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; @@ -3810,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 de2826f90d34..6d73f8beadb6 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; @@ -377,12 +379,14 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0233: case 0x10ec0235: case 0x10ec0236: + case 0x10ec0245: case 0x10ec0255: case 0x10ec0256: case 0x10ec0257: case 0x10ec0282: case 0x10ec0283: case 0x10ec0286: + case 0x10ec0287: case 0x10ec0288: case 0x10ec0285: case 0x10ec0298: @@ -797,9 +801,11 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports) { if (!alc_subsystem_id(codec, ports)) { struct alc_spec *spec = codec->spec; - codec_dbg(codec, - "realtek: Enable default setup for auto mode as fallback\n"); - spec->init_amp = ALC_INIT_DEFAULT; + if (spec->init_amp == ALC_INIT_UNDEFINED) { + codec_dbg(codec, + "realtek: Enable default setup for auto mode as fallback\n"); + spec->init_amp = ALC_INIT_DEFAULT; + } } } @@ -2454,6 +2460,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), SND_PCI_QUIRK(0x1458, 0xa0cd, "Gigabyte X570 Aorus Master", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), @@ -2469,6 +2476,9 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Y530", ALC882_FIXUP_LENOVO_Y530), @@ -4072,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 */ } @@ -4087,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 */ @@ -4096,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, @@ -4106,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); @@ -4118,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 } } @@ -4130,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, @@ -5477,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) { @@ -5501,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; @@ -5792,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. @@ -5853,6 +5906,15 @@ static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec, } } +static void alc225_fixup_s3_pop_noise(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + codec->power_save_node = 1; +} + /* Forcibly assign NID 0x03 to HP/LO while NID 0x02 to SPK for EQ */ static void alc274_fixup_bind_dacs(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -5957,6 +6019,7 @@ enum { ALC269_FIXUP_HP_LINE1_MIC1_LED, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, + ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, ALC269_FIXUP_NO_SHUTUP, ALC286_FIXUP_SONY_MIC_NO_PRESENCE, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, @@ -6042,9 +6105,11 @@ enum { ALC233_FIXUP_ACER_HEADSET_MIC, ALC294_FIXUP_LENOVO_MIC_LOCATION, ALC225_FIXUP_DELL_WYSE_MIC_NO_PRESENCE, + ALC225_FIXUP_S3_POP_NOISE, 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, @@ -6077,9 +6142,12 @@ enum { ALC294_FIXUP_ASUS_DUAL_SPK, ALC285_FIXUP_THINKPAD_HEADSET_JACK, ALC294_FIXUP_ASUS_HPE, + ALC294_FIXUP_ASUS_COEF_1B, ALC285_FIXUP_HP_GPIO_LED, ALC285_FIXUP_HP_MUTE_LED, ALC236_FIXUP_HP_MUTE_LED, + ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, + ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, }; static const struct hda_fixup alc269_fixups[] = { @@ -6277,6 +6345,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT }, + [ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc269_fixup_limit_int_mic_boost, + .chained = true, + .chain_id = ALC269_FIXUP_LENOVO_DOCK, + }, [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_no_hp_to_lineout, @@ -6929,6 +7003,12 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, + .chain_id = ALC225_FIXUP_S3_POP_NOISE + }, + [ALC225_FIXUP_S3_POP_NOISE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc225_fixup_s3_pop_noise, + .chained = true, .chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC }, [ALC700_FIXUP_INTEL_REFERENCE] = { @@ -6961,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[]) { @@ -7201,6 +7287,17 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC }, + [ALC294_FIXUP_ASUS_COEF_1B] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + /* Set bit 10 to correct noisy output after reboot from + * Windows 10 (due to pop noise reduction?) + */ + { 0x20, AC_VERB_SET_COEF_INDEX, 0x1b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x4e4b }, + { } + }, + }, [ALC285_FIXUP_HP_GPIO_LED] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_hp_gpio_led, @@ -7213,6 +7310,22 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc236_fixup_hp_mute_led, }, + [ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc5 }, + { } + }, + }, + [ALC295_FIXUP_ASUS_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7378,9 +7491,12 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x17d1, "ASUS UX431FL", ALC294_FIXUP_ASUS_DUAL_SPK), SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC), + SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC), SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE), + SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), SND_PCI_QUIRK(0x1043, 0x1a30, "ASUS X705UD", ALC256_FIXUP_ASUS_MIC), + SND_PCI_QUIRK(0x1043, 0x1b11, "ASUS UX431DA", ALC294_FIXUP_ASUS_COEF_1B), SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7406,6 +7522,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), + SND_PCI_QUIRK(0x144d, 0xc176, "Samsung Notebook 9 Pro (NP930MBE-K04US)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET), SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), @@ -7416,12 +7534,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x8560, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x8561, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), + SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE), - SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK), + SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST), SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), @@ -7560,6 +7679,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_HEADSET_MODE, .name = "headset-mode"}, {.id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, .name = "headset-mode-no-hp-mic"}, {.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, + {.id = ALC269_FIXUP_LENOVO_DOCK_LIMIT_BOOST, .name = "lenovo-dock-limit-boost"}, {.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"}, {.id = ALC269_FIXUP_HP_DOCK_GPIO_MIC1_LED, .name = "hp-dock-gpio-mic1-led"}, {.id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, .name = "dell-headset-multi"}, @@ -7571,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"}, @@ -7988,6 +8109,18 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60130}, {0x17, 0x90170110}, {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60120}, + {0x17, 0x90170110}, + {0x21, 0x04211030}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0295, 0x1043, "ASUS", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, + {0x21, 0x03211020}), SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL4_MIC_NO_PRESENCE, {0x14, 0x90170110}, {0x21, 0x04211020}), @@ -8028,6 +8161,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { ALC225_STANDARD_PINS, {0x12, 0xb7a60130}, {0x17, 0x90170110}), + SND_HDA_PIN_QUIRK(0x10ec0623, 0x17aa, "Lenovo", ALC283_FIXUP_HEADSET_MIC, + {0x14, 0x01014010}, + {0x17, 0x90170120}, + {0x18, 0x02a11030}, + {0x19, 0x02a1103f}, + {0x21, 0x0221101f}), {} }; @@ -8195,7 +8334,9 @@ static int patch_alc269(struct hda_codec *codec) spec->gen.mixer_nid = 0; break; case 0x10ec0215: + case 0x10ec0245: case 0x10ec0285: + case 0x10ec0287: case 0x10ec0289: spec->codec_variant = ALC269_TYPE_ALC215; spec->shutup = alc225_shutup; @@ -8203,8 +8344,6 @@ static int patch_alc269(struct hda_codec *codec) spec->gen.mixer_nid = 0; break; case 0x10ec0225: - codec->power_save_node = 1; - /* fall through */ case 0x10ec0295: case 0x10ec0299: spec->codec_variant = ALC269_TYPE_ALC225; @@ -9456,6 +9595,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269), HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0245, "ALC245", patch_alc269), HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), HDA_CODEC_ENTRY(0x10ec0257, "ALC257", patch_alc269), @@ -9475,6 +9615,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/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index 884d0cdec08c..73e1e5400506 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -2332,7 +2332,8 @@ static int snd_ice1712_chip_init(struct snd_ice1712 *ice) pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]); pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]); pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]); - if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) { + if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24 && + ice->eeprom.subvendor != ICE1712_SUBDEVICE_STAUDIO_ADCIII) { ice->gpio.write_mask = ice->eeprom.gpiomask; ice->gpio.direction = ice->eeprom.gpiodir; snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 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/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index dc05c57db03a..c72cb2888c21 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -59,19 +59,19 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv, static const struct snd_kcontrol_new max9867_snd_controls[] = { SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL, - MAX9867_RIGHTVOL, 0, 41, 1, max9867_master_tlv), + MAX9867_RIGHTVOL, 0, 40, 1, max9867_master_tlv), SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv), SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN, MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv), SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, 5, 4, 0, max9867_micboost_tlv), + MAX9867_RIGHTMICGAIN, 5, 3, 0, max9867_micboost_tlv), SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1), SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1, max9867_dac_tlv), SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0, max9867_dacboost_tlv), - SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 0, 4, 15, 1, + SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 4, 0, 15, 1, max9867_adc_tlv), SOC_ENUM("Speaker Mode", max9867_spkmode), SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 653a58c96e24..2491e1ce16d3 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -4338,16 +4338,16 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, codec = codec_dai->playback_widget; if (playback_cpu && codec) { - if (dai_link->params && !dai_link->playback_widget) { + if (dai_link->params && !rtd->playback_widget) { substream = streams[SNDRV_PCM_STREAM_PLAYBACK].substream; dai = snd_soc_dapm_new_dai(card, substream, "playback"); if (IS_ERR(dai)) goto capture; - dai_link->playback_widget = dai; + rtd->playback_widget = dai; } dapm_connect_dai_routes(&card->dapm, cpu_dai, playback_cpu, - dai_link->playback_widget, + rtd->playback_widget, codec_dai, codec); } @@ -4356,16 +4356,16 @@ capture: codec = codec_dai->capture_widget; if (codec && capture_cpu) { - if (dai_link->params && !dai_link->capture_widget) { + if (dai_link->params && !rtd->capture_widget) { substream = streams[SNDRV_PCM_STREAM_CAPTURE].substream; dai = snd_soc_dapm_new_dai(card, substream, "capture"); if (IS_ERR(dai)) return; - dai_link->capture_widget = dai; + rtd->capture_widget = dai; } dapm_connect_dai_routes(&card->dapm, codec_dai, codec, - dai_link->capture_widget, + rtd->capture_widget, cpu_dai, capture_cpu); } } diff --git a/sound/usb/card.c b/sound/usb/card.c index fd6fd1726ea0..162bdd6eb4d4 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -634,7 +634,6 @@ static int usb_audio_probe(struct usb_interface *intf, id, &chip); if (err < 0) goto __error; - chip->pm_intf = intf; break; } else if (vid[i] != -1 || pid[i] != -1) { dev_info(&dev->dev, @@ -651,6 +650,13 @@ static int usb_audio_probe(struct usb_interface *intf, goto __error; } } + + if (chip->num_interfaces >= MAX_CARD_INTERFACES) { + dev_info(&dev->dev, "Too many interfaces assigned to the single USB-audio card\n"); + err = -EINVAL; + goto __error; + } + dev_set_drvdata(&dev->dev, chip); /* @@ -703,6 +709,7 @@ static int usb_audio_probe(struct usb_interface *intf, } usb_chip[chip->index] = chip; + chip->intf[chip->num_interfaces] = intf; chip->num_interfaces++; usb_set_intfdata(intf, chip); atomic_dec(&chip->active); @@ -818,19 +825,37 @@ void snd_usb_unlock_shutdown(struct snd_usb_audio *chip) int snd_usb_autoresume(struct snd_usb_audio *chip) { + int i, err; + if (atomic_read(&chip->shutdown)) return -EIO; - if (atomic_inc_return(&chip->active) == 1) - return usb_autopm_get_interface(chip->pm_intf); + if (atomic_inc_return(&chip->active) != 1) + return 0; + + for (i = 0; i < chip->num_interfaces; i++) { + err = usb_autopm_get_interface(chip->intf[i]); + if (err < 0) { + /* rollback */ + while (--i >= 0) + usb_autopm_put_interface(chip->intf[i]); + atomic_dec(&chip->active); + return err; + } + } return 0; } void snd_usb_autosuspend(struct snd_usb_audio *chip) { + int i; + if (atomic_read(&chip->shutdown)) return; - if (atomic_dec_and_test(&chip->active)) - usb_autopm_put_interface(chip->pm_intf); + if (!atomic_dec_and_test(&chip->active)) + return; + + for (i = 0; i < chip->num_interfaces; i++) + usb_autopm_put_interface(chip->intf[i]); } static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) @@ -843,9 +868,6 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) if (chip == (void *)-1L) return 0; - chip->autosuspended = !!PMSG_IS_AUTO(message); - if (!chip->autosuspended) - snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); if (!chip->num_suspended_intf++) { list_for_each_entry(as, &chip->pcm_list, list) { snd_usb_pcm_suspend(as); @@ -858,6 +880,11 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) snd_usb_mixer_suspend(mixer); } + if (!PMSG_IS_AUTO(message) && !chip->system_suspend) { + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); + chip->system_suspend = chip->num_suspended_intf; + } + return 0; } @@ -871,10 +898,10 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) if (chip == (void *)-1L) return 0; - if (--chip->num_suspended_intf) - return 0; atomic_inc(&chip->active); /* avoid autopm */ + if (chip->num_suspended_intf > 1) + goto out; list_for_each_entry(as, &chip->pcm_list, list) { err = snd_usb_pcm_resume(as); @@ -896,9 +923,12 @@ static int __usb_audio_resume(struct usb_interface *intf, bool reset_resume) snd_usbmidi_resume(p); } - if (!chip->autosuspended) + out: + if (chip->num_suspended_intf == chip->system_suspend) { snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); - chip->autosuspended = 0; + chip->system_suspend = 0; + } + chip->num_suspended_intf--; err_out: atomic_dec(&chip->active); /* allow autopm after this point */ 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/format.c b/sound/usb/format.c index 50e1874c847c..5ffb457cc88c 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -278,6 +278,52 @@ static bool s1810c_valid_sample_rate(struct audioformat *fp, } /* + * Many Focusrite devices supports a limited set of sampling rates per + * altsetting. Maximum rate is exposed in the last 4 bytes of Format Type + * descriptor which has a non-standard bLength = 10. + */ +static bool focusrite_valid_sample_rate(struct snd_usb_audio *chip, + struct audioformat *fp, + unsigned int rate) +{ + struct usb_interface *iface; + struct usb_host_interface *alts; + unsigned char *fmt; + unsigned int max_rate; + + iface = usb_ifnum_to_if(chip->dev, fp->iface); + if (!iface) + return true; + + alts = &iface->altsetting[fp->altset_idx]; + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, + NULL, UAC_FORMAT_TYPE); + if (!fmt) + return true; + + if (fmt[0] == 10) { /* bLength */ + max_rate = combine_quad(&fmt[6]); + + /* Validate max rate */ + if (max_rate != 48000 && + max_rate != 96000 && + max_rate != 192000 && + max_rate != 384000) { + + usb_audio_info(chip, + "%u:%d : unexpected max rate: %u\n", + fp->iface, fp->altsetting, max_rate); + + return true; + } + + return rate <= max_rate; + } + + return true; +} + +/* * Helper function to walk the array of sample rate triplets reported by * the device. The problem is that we need to parse whole array first to * get to know how many sample rates we have to expect. @@ -319,6 +365,11 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, !s1810c_valid_sample_rate(fp, rate)) goto skip_rate; + /* Filter out invalid rates on Focusrite devices */ + if (USB_ID_VENDOR(chip->usb_id) == 0x1235 && + !focusrite_valid_sample_rate(chip, fp, rate)) + goto skip_rate; + if (fp->rate_table) fp->rate_table[nr_rates] = rate; if (!fp->rate_min || rate < fp->rate_min) 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/line6/podhd.c b/sound/usb/line6/podhd.c index d37db32ecd3b..e39dc85c355a 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -21,8 +21,7 @@ enum { LINE6_PODHD300, LINE6_PODHD400, - LINE6_PODHD500_0, - LINE6_PODHD500_1, + LINE6_PODHD500, LINE6_PODX3, LINE6_PODX3LIVE, LINE6_PODHD500X, @@ -318,8 +317,7 @@ static const struct usb_device_id podhd_id_table[] = { /* TODO: no need to alloc data interfaces when only audio is used */ { LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 }, { LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 }, - { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 }, - { LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 }, + { LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500 }, { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 }, { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE }, { LINE6_IF_NUM(0x4159, 0), .driver_info = LINE6_PODHD500X }, @@ -352,23 +350,13 @@ static const struct line6_properties podhd_properties_table[] = { .ep_audio_r = 0x82, .ep_audio_w = 0x01, }, - [LINE6_PODHD500_0] = { + [LINE6_PODHD500] = { .id = "PODHD500", .name = "POD HD500", - .capabilities = LINE6_CAP_PCM + .capabilities = LINE6_CAP_PCM | LINE6_CAP_CONTROL | LINE6_CAP_HWMON, .altsetting = 1, - .ep_ctrl_r = 0x81, - .ep_ctrl_w = 0x01, - .ep_audio_r = 0x86, - .ep_audio_w = 0x02, - }, - [LINE6_PODHD500_1] = { - .id = "PODHD500", - .name = "POD HD500", - .capabilities = LINE6_CAP_PCM - | LINE6_CAP_HWMON, - .altsetting = 0, + .ctrl_if = 1, .ep_ctrl_r = 0x81, .ep_ctrl_w = 0x01, .ep_audio_r = 0x86, diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 721d12130d0c..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; } } @@ -1457,7 +1465,7 @@ error: usb_audio_err(chip, "cannot get connectors status: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n", UAC_GET_CUR, validx, idx, cval->val_type); - return ret; + return filter_error(cval, ret); } ucontrol->value.integer.value[0] = val; @@ -1771,10 +1779,16 @@ static void get_connector_control_name(struct usb_mixer_interface *mixer, /* Build a mixer control for a UAC connector control (jack-detect) */ static void build_connector_control(struct usb_mixer_interface *mixer, + const struct usbmix_name_map *imap, struct usb_audio_term *term, bool is_input) { struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; + const struct usbmix_name_map *map; + + map = find_map(imap, term->id, 0); + if (check_ignored_ctl(map)) + return; cval = kzalloc(sizeof(*cval), GFP_KERNEL); if (!cval) @@ -1805,8 +1819,12 @@ static void build_connector_control(struct usb_mixer_interface *mixer, usb_mixer_elem_info_free(cval); return; } - get_connector_control_name(mixer, term, is_input, kctl->id.name, - sizeof(kctl->id.name)); + + if (check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name))) + strlcat(kctl->id.name, " Jack", sizeof(kctl->id.name)); + else + get_connector_control_name(mixer, term, is_input, kctl->id.name, + sizeof(kctl->id.name)); kctl->private_free = snd_usb_mixer_elem_free; snd_usb_mixer_add_control(&cval->head, kctl); } @@ -2109,8 +2127,9 @@ static int parse_audio_input_terminal(struct mixer_build *state, int unitid, check_input_term(state, term_id, &iterm); /* Check for jack detection. */ - if (uac_v2v3_control_is_readable(bmctls, control)) - build_connector_control(state->mixer, &iterm, true); + if ((iterm.type & 0xff00) != 0x0100 && + uac_v2v3_control_is_readable(bmctls, control)) + build_connector_control(state->mixer, state->map, &iterm, true); return 0; } @@ -3071,13 +3090,13 @@ static int snd_usb_mixer_controls_badd(struct usb_mixer_interface *mixer, memset(&iterm, 0, sizeof(iterm)); iterm.id = UAC3_BADD_IT_ID4; iterm.type = UAC_BIDIR_TERMINAL_HEADSET; - build_connector_control(mixer, &iterm, true); + build_connector_control(mixer, map->map, &iterm, true); /* Output Term - Insertion control */ memset(&oterm, 0, sizeof(oterm)); oterm.id = UAC3_BADD_OT_ID3; oterm.type = UAC_BIDIR_TERMINAL_HEADSET; - build_connector_control(mixer, &oterm, false); + build_connector_control(mixer, map->map, &oterm, false); } return 0; @@ -3106,7 +3125,8 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) if (map->id == state.chip->usb_id) { state.map = map->map; state.selector_map = map->selector_map; - mixer->ignore_ctl_error = map->ignore_ctl_error; + mixer->connector_map = map->connector_map; + mixer->ignore_ctl_error |= map->ignore_ctl_error; break; } } @@ -3149,10 +3169,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) if (err < 0 && err != -EINVAL) return err; - if (uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls), + if ((state.oterm.type & 0xff00) != 0x0100 && + uac_v2v3_control_is_readable(le16_to_cpu(desc->bmControls), UAC2_TE_CONNECTOR)) { - build_connector_control(state.mixer, &state.oterm, - false); + build_connector_control(state.mixer, state.map, + &state.oterm, false); } } else { /* UAC_VERSION_3 */ struct uac3_output_terminal_descriptor *desc = p; @@ -3174,10 +3195,11 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) if (err < 0 && err != -EINVAL) return err; - if (uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls), + if ((state.oterm.type & 0xff00) != 0x0100 && + uac_v2v3_control_is_readable(le32_to_cpu(desc->bmControls), UAC3_TE_INSERTION)) { - build_connector_control(state.mixer, &state.oterm, - false); + build_connector_control(state.mixer, state.map, + &state.oterm, false); } } } @@ -3185,10 +3207,32 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) return 0; } +static int delegate_notify(struct usb_mixer_interface *mixer, int unitid, + u8 *control, u8 *channel) +{ + const struct usbmix_connector_map *map = mixer->connector_map; + + if (!map) + return unitid; + + for (; map->id; map++) { + if (map->id == unitid) { + if (control && map->control) + *control = map->control; + if (channel && map->channel) + *channel = map->channel; + return map->delegated_id; + } + } + return unitid; +} + void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer, int unitid) { struct usb_mixer_elem_list *list; + unitid = delegate_notify(mixer, unitid, NULL, NULL); + for_each_mixer_elem(list, mixer, unitid) { struct usb_mixer_elem_info *info = mixer_elem_list_to_info(list); @@ -3258,6 +3302,8 @@ static void snd_usb_mixer_interrupt_v2(struct usb_mixer_interface *mixer, return; } + unitid = delegate_notify(mixer, unitid, &control, &channel); + for_each_mixer_elem(list, mixer, unitid) count++; diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 65d6d08c96f5..41ec9dc4139b 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -6,6 +6,13 @@ struct media_mixer_ctl; +struct usbmix_connector_map { + u8 id; + u8 delegated_id; + u8 control; + u8 channel; +}; + struct usb_mixer_interface { struct snd_usb_audio *chip; struct usb_host_interface *hostif; @@ -18,6 +25,9 @@ struct usb_mixer_interface { /* the usb audio specification version this interface complies to */ int protocol; + /* optional connector delegation map */ + const struct usbmix_connector_map *connector_map; + /* Sound Blaster remote control stuff */ const struct rc_config *rc_cfg; u32 rc_code; diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 72b575c34860..9af7aa93f6fa 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -27,6 +27,7 @@ struct usbmix_ctl_map { u32 id; const struct usbmix_name_map *map; const struct usbmix_selector_map *selector_map; + const struct usbmix_connector_map *connector_map; int ignore_ctl_error; }; @@ -360,13 +361,57 @@ static const struct usbmix_name_map corsair_virtuoso_map[] = { }; /* Some mobos shipped with a dummy HD-audio show the invalid GET_MIN/GET_MAX - * response for Input Gain Pad (id=19, control=12). Skip it. + * response for Input Gain Pad (id=19, control=12) and the connector status + * for SPDIF terminal (id=18). Skip them. */ static const struct usbmix_name_map asus_rog_map[] = { + { 18, NULL }, /* OT, connector control */ { 19, NULL, 12 }, /* FU, Input Gain Pad */ {} }; +/* TRX40 mobos with Realtek ALC1220-VB */ +static const struct usbmix_name_map trx40_mobo_map[] = { + { 18, NULL }, /* OT, IEC958 - broken response, disabled */ + { 19, NULL, 12 }, /* FU, Input Gain Pad - broken response, disabled */ + { 16, "Speaker" }, /* OT */ + { 22, "Speaker Playback" }, /* FU */ + { 7, "Line" }, /* IT */ + { 19, "Line Capture" }, /* FU */ + { 17, "Front Headphone" }, /* OT */ + { 23, "Front Headphone Playback" }, /* FU */ + { 8, "Mic" }, /* IT */ + { 20, "Mic Capture" }, /* FU */ + { 9, "Front Mic" }, /* IT */ + { 21, "Front Mic Capture" }, /* FU */ + { 24, "IEC958 Playback" }, /* FU */ + {} +}; + +static const struct usbmix_connector_map trx40_mobo_connector_map[] = { + { 10, 16 }, /* (Back) Speaker */ + { 11, 17 }, /* Front Headphone */ + { 13, 7 }, /* Line */ + { 14, 8 }, /* Mic */ + { 15, 9 }, /* Front Mic */ + {} +}; + +/* 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 */ @@ -496,9 +541,14 @@ 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 = asus_rog_map, + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, }, { /* ASUS ROG Zenith II */ .id = USB_ID(0x0b05, 0x1916), @@ -510,11 +560,18 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { }, { /* MSI TRX40 Creator */ .id = USB_ID(0x0db0, 0x0d64), - .map = asus_rog_map, + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, }, { /* MSI TRX40 */ .id = USB_ID(0x0db0, 0x543d), - .map = asus_rog_map, + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, + }, + { /* Asrock TRX40 Creator */ + .id = USB_ID(0x26ce, 0x0a01), + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, }, { 0 } /* terminator */ }; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 02b036b2aefb..b6bcf2f92383 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -1509,11 +1509,15 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol, /* use known values for that card: interface#1 altsetting#1 */ iface = usb_ifnum_to_if(chip->dev, 1); - if (!iface || iface->num_altsetting < 2) - return -EINVAL; + if (!iface || iface->num_altsetting < 2) { + err = -EINVAL; + goto end; + } alts = &iface->altsetting[1]; - if (get_iface_desc(alts)->bNumEndpoints < 1) - return -EINVAL; + if (get_iface_desc(alts)->bNumEndpoints < 1) { + err = -EINVAL; + goto end; + } ep = get_endpoint(alts, 0)->bEndpointAddress; err = snd_usb_ctl_msg(chip->dev, @@ -2181,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; @@ -2282,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..8a05dcb1344f 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -370,6 +370,10 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ep = 0x81; ifnum = 2; goto add_sync_ep_from_ifnum; + case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */ + ep = 0x82; + ifnum = 0; + goto add_sync_ep_from_ifnum; case USB_ID(0x0582, 0x01d8): /* BOSS Katana */ /* BOSS Katana amplifiers do not need quirks */ return 0; @@ -404,6 +408,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 +508,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 +1588,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 e009d584e7d0..4ec491011b19 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -25,6 +25,33 @@ .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 \ + } + +/* HP Thunderbolt Dock Audio Headset */ +{ + USB_DEVICE(0x03f0, 0x0269), + QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Headset", + "HP-Thunderbolt-Dock-Audio-Headset"), +}, +/* HP Thunderbolt Dock Audio Module */ +{ + USB_DEVICE(0x03f0, 0x0567), + QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Module", + "HP-Thunderbolt-Dock-Audio-Module"), +}, /* FTDI devices */ { USB_DEVICE(0x0403, 0xb8d8), @@ -61,20 +88,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 +226,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 +2611,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 +2626,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 */ @@ -2756,90 +2759,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_MIDI_NOVATION } }, -{ - /* - * Focusrite Scarlett Solo 2nd generation - * 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. - */ - USB_DEVICE(0x1235, 0x8205), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_COMPOSITE, - .data = (const struct snd_usb_audio_quirk[]) { - { - .ifnum = 1, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .channels = 2, - .iface = 1, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x01, - .ep_attr = USB_ENDPOINT_XFER_ISOC | - USB_ENDPOINT_SYNC_ASYNC, - .protocol = UAC_VERSION_2, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000, - .rate_min = 44100, - .rate_max = 192000, - .nr_rates = 6, - .rate_table = (unsigned int[]) { - 44100, 48000, 88200, - 96000, 176400, 192000 - }, - .clock = 41 - } - }, - { - .ifnum = 2, - .type = QUIRK_AUDIO_FIXED_ENDPOINT, - .data = & (const struct audioformat) { - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .channels = 2, - .iface = 2, - .altsetting = 1, - .altset_idx = 1, - .attributes = 0, - .endpoint = 0x82, - .ep_attr = USB_ENDPOINT_XFER_ISOC | - USB_ENDPOINT_SYNC_ASYNC | - USB_ENDPOINT_USAGE_IMPLICIT_FB, - .protocol = UAC_VERSION_2, - .rates = SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000, - .rate_min = 44100, - .rate_max = 192000, - .nr_rates = 6, - .rate_table = (unsigned int[]) { - 44100, 48000, 88200, - 96000, 176400, 192000 - }, - .clock = 41 - } - }, - { - .ifnum = 3, - .type = QUIRK_IGNORE_INTERFACE - }, - { - .ifnum = -1 - } - } - } -}, /* Access Music devices */ { @@ -2872,11 +2791,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 */ @@ -3343,10 +3258,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; @@ -3452,12 +3364,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 */ { @@ -3635,4 +3542,95 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } }, +#define ALC1220_VB_DESKTOP(vend, prod) { \ + USB_DEVICE(vend, prod), \ + 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 */ +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") +}, +{ + /* + * Pioneer DJ DJM-900NXS2 + * 10 channels playback & 12 channels capture @ 44.1/48/96kHz S24LE + */ + USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000a), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 10, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_44100| + SNDRV_PCM_RATE_48000| + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 3, + .rate_table = (unsigned int[]) { + 44100, 48000, 96000 + } + } + }, + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 12, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC| + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_44100| + SNDRV_PCM_RATE_48000| + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 3, + .rate_table = (unsigned int[]) { + 44100, 48000, 96000 + } + } + }, + { + .ifnum = -1 + } + } + } +}, + #undef USB_DEVICE_VENDOR_SPEC diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index a8ece1701068..bca0179a0ef8 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1458,6 +1458,30 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs, subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0; } + +/* + * Pioneer DJ DJM-900NXS2 + * Device needs to know the sample rate each time substream is started + */ +static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs) +{ + + /* Convert sample rate value to little endian */ + u8 sr[3]; + + sr[0] = subs->cur_rate & 0xff; + sr[1] = (subs->cur_rate >> 8) & 0xff; + sr[2] = (subs->cur_rate >> 16) & 0xff; + + /* Configure device */ + usb_set_interface(subs->dev, 0, 1); + snd_usb_ctl_msg(subs->stream->chip->dev, + usb_rcvctrlpipe(subs->stream->chip->dev, 0), + 0x01, 0x22, 0x0100, 0x0082, &sr, 0x0003); + + return 0; +} + void snd_usb_set_format_quirk(struct snd_usb_substream *subs, struct audioformat *fmt) { @@ -1468,6 +1492,9 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs, case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */ set_format_emu_quirk(subs, fmt); break; + case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */ + pioneer_djm_set_format_quirk(subs); + break; } } @@ -1636,13 +1663,14 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) msleep(20); - /* Zoom R16/24, Logitech H650e, Jabra 550a needs a tiny delay here, - * otherwise requests like get/set frequency return as failed despite - * actually succeeding. + /* Zoom R16/24, Logitech H650e, Jabra 550a, Kingston HyperX needs a tiny + * delay here, otherwise requests like get/set frequency return as + * failed despite actually succeeding. */ if ((chip->usb_id == USB_ID(0x1686, 0x00dd) || chip->usb_id == USB_ID(0x046d, 0x0a46) || - chip->usb_id == USB_ID(0x0b0e, 0x0349)) && + chip->usb_id == USB_ID(0x0b0e, 0x0349) || + chip->usb_id == USB_ID(0x0951, 0x16ad)) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) usleep_range(1000, 2000); } @@ -1687,7 +1715,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, case USB_ID(0x0d8c, 0x0316): /* Hegel HD12 DSD */ case USB_ID(0x10cb, 0x0103): /* The Bit Opus #3; with fp->dsd_raw */ - case USB_ID(0x16b0, 0x06b2): /* NuPrime DAC-10 */ + case USB_ID(0x16d0, 0x06b2): /* NuPrime DAC-10 */ case USB_ID(0x16d0, 0x09dd): /* Encore mDSD */ case USB_ID(0x16d0, 0x0733): /* Furutech ADL Stratos */ case USB_ID(0x16d0, 0x09db): /* NuPrime Audio DAC-9 */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 1c892c7f14d7..b91c4c0807ec 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -19,14 +19,16 @@ struct media_device; struct media_intf_devnode; +#define MAX_CARD_INTERFACES 16 + struct snd_usb_audio { int index; struct usb_device *dev; struct snd_card *card; - struct usb_interface *pm_intf; + struct usb_interface *intf[MAX_CARD_INTERFACES]; u32 usb_id; struct mutex mutex; - unsigned int autosuspended:1; + unsigned int system_suspend; atomic_t active; atomic_t shutdown; atomic_t usage_count; 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" diff --git a/sound/usb/usx2y/usbusx2yaudio.c b/sound/usb/usx2y/usbusx2yaudio.c index 37d290fe9d43..ecaf41265dcd 100644 --- a/sound/usb/usx2y/usbusx2yaudio.c +++ b/sound/usb/usx2y/usbusx2yaudio.c @@ -681,6 +681,8 @@ static int usX2Y_rate_set(struct usX2Ydev *usX2Y, int rate) us->submitted = 2*NOOF_SETRATE_URBS; for (i = 0; i < NOOF_SETRATE_URBS; ++i) { struct urb *urb = us->urb[i]; + if (!urb) + continue; if (urb->status) { if (!err) err = -ENODEV; |