diff options
62 files changed, 1479 insertions, 469 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e7982af9a5d8..1df99d9e350e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5856,6 +5856,24 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode, return 0; } +#ifdef CONFIG_ACPI +bool pci_pr3_present(struct pci_dev *pdev) +{ + struct acpi_device *adev; + + if (acpi_disabled) + return false; + + adev = ACPI_COMPANION(&pdev->dev); + if (!adev) + return false; + + return adev->power.flags.power_resources && + acpi_has_method(adev->handle, "_PR3"); +} +EXPORT_SYMBOL_GPL(pci_pr3_present); +#endif + /** * pci_add_dma_alias - Add a DMA devfn alias for a device * @dev: the PCI device for which alias is added diff --git a/include/linux/pci.h b/include/linux/pci.h index f9088c89a534..be529d311122 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2310,9 +2310,11 @@ struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus); void pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *)); +bool pci_pr3_present(struct pci_dev *pdev); #else static inline struct irq_domain * pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; } +static inline bool pci_pr3_present(struct pci_dev *pdev) { return false; } #endif #ifdef CONFIG_EEH diff --git a/include/sound/intel-dsp-config.h b/include/sound/intel-dsp-config.h new file mode 100644 index 000000000000..c36622bee3f8 --- /dev/null +++ b/include/sound/intel-dsp-config.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * intel-dsp-config.h - Intel DSP config + * + * Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz> + */ + +#ifndef __INTEL_DSP_CONFIG_H__ +#define __INTEL_DSP_CONFIG_H__ + +struct pci_dev; + +enum { + SND_INTEL_DSP_DRIVER_ANY = 0, + SND_INTEL_DSP_DRIVER_LEGACY, + SND_INTEL_DSP_DRIVER_SST, + SND_INTEL_DSP_DRIVER_SOF, + SND_INTEL_DSP_DRIVER_LAST = SND_INTEL_DSP_DRIVER_SOF +}; + +#if IS_ENABLED(CONFIG_SND_INTEL_DSP_CONFIG) + +int snd_intel_dsp_driver_probe(struct pci_dev *pci); + +#else + +static inline int snd_intel_dsp_driver_probe(struct pci_dev *pci) +{ + return SND_INTEL_DSP_DRIVER_ANY; +} + +#endif + +#endif diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 4ee79ad6ae22..4044c42d8595 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -72,11 +72,11 @@ config SND_PCM_OSS config SND_PCM_OSS_PLUGINS bool "OSS PCM (digital audio) API - Include plugin system" depends on SND_PCM_OSS - default y + default y help - If you disable this option, the ALSA's OSS PCM API will not - support conversion of channels, formats and rates. It will - behave like most of new OSS/Free drivers in 2.4/2.6 kernels. + If you disable this option, the ALSA's OSS PCM API will not + support conversion of channels, formats and rates. It will + behave like most of new OSS/Free drivers in 2.4/2.6 kernels. config SND_PCM_TIMER bool "PCM timer interface" if EXPERT @@ -128,13 +128,13 @@ config SND_SUPPORT_OLD_API or older). config SND_PROC_FS - bool "Sound Proc FS Support" if EXPERT - depends on PROC_FS - default y - help - Say 'N' to disable Sound proc FS, which may reduce code size about - 9KB on x86_64 platform. - If unsure say Y. + bool "Sound Proc FS Support" if EXPERT + depends on PROC_FS + default y + help + Say 'N' to disable Sound proc FS, which may reduce code size about + 9KB on x86_64 platform. + If unsure say Y. config SND_VERBOSE_PROCFS bool "Verbose procfs contents" @@ -142,8 +142,8 @@ config SND_VERBOSE_PROCFS default y help Say Y here to include code for verbose procfs contents (provides - useful information to developers when a problem occurs). On the - other side, it makes the ALSA subsystem larger. + useful information to developers when a problem occurs). On the + other side, it makes the ALSA subsystem larger. config SND_VERBOSE_PRINTK bool "Verbose printk" @@ -164,7 +164,7 @@ config SND_DEBUG_VERBOSE depends on SND_DEBUG help Say Y here to enable extra-verbose debugging messages. - + Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages. So, say Y only if you are ready to be annoyed. diff --git a/sound/drivers/Kconfig b/sound/drivers/Kconfig index 09932cc98e9d..15d6d46acf9c 100644 --- a/sound/drivers/Kconfig +++ b/sound/drivers/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config SND_MPU401_UART - tristate - select SND_RAWMIDI + tristate + select SND_RAWMIDI config SND_OPL3_LIB tristate @@ -90,16 +90,16 @@ config SND_DUMMY will be called snd-dummy. config SND_ALOOP - tristate "Generic loopback driver (PCM)" - select SND_PCM - help - Say 'Y' or 'M' to include support for the PCM loopback device. + tristate "Generic loopback driver (PCM)" + select SND_PCM + help + Say 'Y' or 'M' to include support for the PCM loopback device. This module returns played samples back to the user space using the standard ALSA PCM device. The devices are routed 0->1 and - 1->0, where first number is the playback PCM device and second + 1->0, where first number is the playback PCM device and second number is the capture device. Module creates two PCM devices and configured number of substreams (see the pcm_substreams module - parameter). + parameter). The loopback device allows time sychronization with an external timing source using the time shift universal control (+-20% @@ -142,12 +142,12 @@ config SND_MTS64 select SND_RAWMIDI help The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with - additional SMPTE Timecode capabilities for the parallel port. + additional SMPTE Timecode capabilities for the parallel port. Say 'Y' to include support for this device. To compile this driver as a module, chose 'M' here: the module - will be called snd-mts64. + will be called snd-mts64. config SND_SERIAL_U16550 tristate "UART16550 serial MIDI driver" diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index b0a904cdb932..995c2cefc222 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -77,7 +77,7 @@ config SND_BEBOB tristate "BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware" select SND_FIREWIRE_LIB select SND_HWDEP - help + help Say Y here to include support for FireWire devices based on BridgeCo DM1000/DM1100/DM1500 with BeBoB firmware: * Edirol FA-66/FA-101 @@ -111,8 +111,8 @@ config SND_BEBOB * M-Audio FireWire 1814/ProjectMix IO * Digidesign Mbox 2 Pro - To compile this driver as a module, choose M here: the module - will be called snd-bebob. + To compile this driver as a module, choose M here: the module + will be called snd-bebob. config SND_FIREWIRE_DIGI00X tristate "Digidesign Digi 002/003 family support" diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index e50e28f77e74..37d38efb4c87 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -9,6 +9,7 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/firewire.h> +#include <linux/firewire-constants.h> #include <linux/module.h> #include <linux/slab.h> #include <sound/pcm.h> @@ -52,10 +53,6 @@ #define CIP_FMT_AM 0x10 #define AMDTP_FDF_NO_DATA 0xff -/* TODO: make these configurable */ -#define INTERRUPT_INTERVAL 16 -#define QUEUE_LENGTH 48 - // For iso header, tstamp and 2 CIP header. #define IR_CTX_HEADER_SIZE_CIP 16 // For iso header and tstamp. @@ -180,6 +177,8 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime) { struct snd_pcm_hardware *hw = &runtime->hw; + unsigned int ctx_header_size; + unsigned int maximum_usec_per_period; int err; hw->info = SNDRV_PCM_INFO_BATCH | @@ -200,19 +199,36 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, hw->period_bytes_max = hw->period_bytes_min * 2048; hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min; - /* - * Currently firewire-lib processes 16 packets in one software - * interrupt callback. This equals to 2msec but actually the - * interval of the interrupts has a jitter. - * Additionally, even if adding a constraint to fit period size to - * 2msec, actual calculated frames per period doesn't equal to 2msec, - * depending on sampling rate. - * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec. - * Here let us use 5msec for safe period interrupt. - */ + // Linux driver for 1394 OHCI controller voluntarily flushes isoc + // context when total size of accumulated context header reaches + // PAGE_SIZE. This kicks tasklet for the isoc context and brings + // callback in the middle of scheduled interrupts. + // Although AMDTP streams in the same domain use the same events per + // IRQ, use the largest size of context header between IT/IR contexts. + // Here, use the value of context header in IR context is for both + // contexts. + if (!(s->flags & CIP_NO_HEADER)) + ctx_header_size = IR_CTX_HEADER_SIZE_CIP; + else + ctx_header_size = IR_CTX_HEADER_SIZE_NO_CIP; + maximum_usec_per_period = USEC_PER_SEC * PAGE_SIZE / + CYCLES_PER_SECOND / ctx_header_size; + + // In IEC 61883-6, one isoc packet can transfer events up to the value + // of syt interval. This comes from the interval of isoc cycle. As 1394 + // OHCI controller can generate hardware IRQ per isoc packet, the + // interval is 125 usec. + // However, there are two ways of transmission in IEC 61883-6; blocking + // and non-blocking modes. In blocking mode, the sequence of isoc packet + // includes 'empty' or 'NODATA' packets which include no event. In + // non-blocking mode, the number of events per packet is variable up to + // the syt interval. + // Due to the above protocol design, the minimum PCM frames per + // interrupt should be double of the value of syt interval, thus it is + // 250 usec. err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - 5000, UINT_MAX); + 250, maximum_usec_per_period); if (err < 0) goto end; @@ -436,11 +452,12 @@ static void pcm_period_tasklet(unsigned long data) snd_pcm_period_elapsed(pcm); } -static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params) +static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params, + bool sched_irq) { int err; - params->interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); + params->interrupt = sched_irq; params->tag = s->tag; params->sy = 0; @@ -451,18 +468,18 @@ static int queue_packet(struct amdtp_stream *s, struct fw_iso_packet *params) goto end; } - if (++s->packet_index >= QUEUE_LENGTH) + if (++s->packet_index >= s->queue_size) s->packet_index = 0; end: return err; } static inline int queue_out_packet(struct amdtp_stream *s, - struct fw_iso_packet *params) + struct fw_iso_packet *params, bool sched_irq) { params->skip = !!(params->header_length == 0 && params->payload_length == 0); - return queue_packet(s, params); + return queue_packet(s, params, sched_irq); } static inline int queue_in_packet(struct amdtp_stream *s, @@ -472,7 +489,7 @@ static inline int queue_in_packet(struct amdtp_stream *s, params->header_length = s->ctx_data.tx.ctx_header_size; params->payload_length = s->ctx_data.tx.max_ctx_payload_length; params->skip = false; - return queue_packet(s, params); + return queue_packet(s, params, false); } static void generate_cip_header(struct amdtp_stream *s, __be32 cip_header[2], @@ -669,13 +686,14 @@ static inline u32 increment_cycle_count(u32 cycle, unsigned int addend) } // Align to actual cycle count for the packet which is going to be scheduled. -// This module queued the same number of isochronous cycle as QUEUE_LENGTH to -// skip isochronous cycle, therefore it's OK to just increment the cycle by -// QUEUE_LENGTH for scheduled cycle. -static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp) +// This module queued the same number of isochronous cycle as the size of queue +// to kip isochronous cycle, therefore it's OK to just increment the cycle by +// the size of queue for scheduled cycle. +static inline u32 compute_it_cycle(const __be32 ctx_header_tstamp, + unsigned int queue_size) { u32 cycle = compute_cycle_count(ctx_header_tstamp); - return increment_cycle_count(cycle, QUEUE_LENGTH); + return increment_cycle_count(cycle, queue_size); } static int generate_device_pkt_descs(struct amdtp_stream *s, @@ -689,7 +707,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s, for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; - unsigned int index = (s->packet_index + i) % QUEUE_LENGTH; + unsigned int index = (s->packet_index + i) % s->queue_size; unsigned int cycle; unsigned int payload_length; unsigned int data_blocks; @@ -730,9 +748,9 @@ static void generate_ideal_pkt_descs(struct amdtp_stream *s, for (i = 0; i < packets; ++i) { struct pkt_desc *desc = descs + i; - unsigned int index = (s->packet_index + i) % QUEUE_LENGTH; + unsigned int index = (s->packet_index + i) % s->queue_size; - desc->cycle = compute_it_cycle(*ctx_header); + 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); @@ -773,22 +791,40 @@ 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 __be32 *ctx_header = header; - unsigned int packets = header_length / sizeof(*ctx_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) return; + // 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); 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; @@ -796,6 +832,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, struct fw_iso_packet params; __be32 header[IT_PKT_HEADER_SIZE_CIP / sizeof(__be32)]; } template = { {0}, {0} }; + bool sched_irq = false; if (s->ctx_data.rx.syt_override < 0) syt = desc->syt; @@ -806,13 +843,21 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, desc->data_blocks, desc->data_block_counter, syt, i); - if (queue_out_packet(s, &template.params) < 0) { + if (is_irq_target) { + event_count += desc->data_blocks; + if (event_count >= events_per_period) { + event_count -= events_per_period; + sched_irq = true; + } + } + + if (queue_out_packet(s, &template.params, sched_irq) < 0) { cancel_stream(s); return; } } - fw_iso_context_queue_flush(s->context); + s->ctx_data.rx.event_count = event_count; } static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, @@ -820,15 +865,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, void *private_data) { struct amdtp_stream *s = private_data; - unsigned int packets; __be32 *ctx_header = header; + unsigned int packets; int i; int err; if (s->packet_index < 0) return; - // The number of packets in buffer. + // Calculate the number of packets in buffer and check XRUN. packets = header_length / s->ctx_data.tx.ctx_header_size; err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets); @@ -849,11 +894,40 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, return; } } +} + +static void amdtp_stream_master_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 *irq_target = d->irq_target; + struct amdtp_stream *s; + + out_stream_callback(context, tstamp, header_length, header, irq_target); + if (amdtp_streaming_error(irq_target)) + goto error; + + list_for_each_entry(s, &d->streams, list) { + if (s != irq_target && amdtp_stream_running(s)) { + fw_iso_context_flush_completions(s->context); + if (amdtp_streaming_error(s)) + goto error; + } + } - fw_iso_context_queue_flush(s->context); + return; +error: + if (amdtp_stream_running(irq_target)) + cancel_stream(irq_target); + + list_for_each_entry(s, &d->streams, list) { + if (amdtp_stream_running(s)) + cancel_stream(s); + } } -/* this is executed one time */ +// this is executed one time. static void amdtp_stream_first_callback(struct fw_iso_context *context, u32 tstamp, size_t header_length, void *header, void *private_data) @@ -874,7 +948,7 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, context->callback.sc = in_stream_callback; } else { - cycle = compute_it_cycle(*ctx_header); + cycle = compute_it_cycle(*ctx_header, s->queue_size); context->callback.sc = out_stream_callback; } @@ -884,17 +958,42 @@ 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. * * 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) +static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed, + struct amdtp_domain *d, bool is_irq_target, + int start_cycle) { static const struct { unsigned int data_block; @@ -908,10 +1007,15 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) [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 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); @@ -922,6 +1026,12 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) } if (s->direction == AMDTP_IN_STREAM) { + // NOTE: IT context should be used for constant IRQ. + if (is_irq_target) { + err = -EINVAL; + goto err_unlock; + } + s->data_block_counter = UINT_MAX; } else { entry = &initial_state[s->sfc]; @@ -953,14 +1063,37 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) max_ctx_payload_size -= IT_PKT_HEADER_SIZE_CIP; } - err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH, + // 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, 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->context = fw_iso_context_create(fw_parent_device(s->unit)->card, type, channel, speed, ctx_header_size, - amdtp_stream_first_callback, s); + ctx_cb, ctx_data); if (IS_ERR(s->context)) { err = PTR_ERR(s->context); if (err == -EBUSY) @@ -981,7 +1114,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) else s->tag = TAG_CIP; - s->pkt_descs = kcalloc(INTERRUPT_INTERVAL, sizeof(*s->pkt_descs), + s->pkt_descs = kcalloc(s->queue_size, sizeof(*s->pkt_descs), GFP_KERNEL); if (!s->pkt_descs) { err = -ENOMEM; @@ -991,12 +1124,21 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) s->packet_index = 0; do { struct fw_iso_packet params; + if (s->direction == AMDTP_IN_STREAM) { err = queue_in_packet(s, ¶ms); } else { + bool sched_irq = false; + params.header_length = 0; params.payload_length = 0; - err = queue_out_packet(s, ¶ms); + + if (is_irq_target) { + sched_irq = !((s->packet_index + 1) % + idle_irq_interval); + } + + err = queue_out_packet(s, ¶ms, sched_irq); } if (err < 0) goto err_pkt_descs; @@ -1008,7 +1150,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) tag |= FW_ISO_CONTEXT_MATCH_TAG0; s->callbacked = false; - err = fw_iso_context_start(s->context, -1, 0, tag); + err = fw_iso_context_start(s->context, start_cycle, 0, tag); if (err < 0) goto err_pkt_descs; @@ -1029,54 +1171,69 @@ err_unlock: } /** - * amdtp_stream_pcm_pointer - get the PCM buffer position + * amdtp_domain_stream_pcm_pointer - get the PCM buffer position + * @d: the AMDTP domain. * @s: the AMDTP stream that transports the PCM data * * Returns the current buffer position, in frames. */ -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s) +unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, + struct amdtp_stream *s) { - /* - * This function is called in software IRQ context of period_tasklet or - * process context. - * - * When the software IRQ context was scheduled by software IRQ context - * of IR/IT contexts, queued packets were already handled. Therefore, - * no need to flush the queue in buffer anymore. - * - * When the process context reach here, some packets will be already - * queued in the buffer. These packets should be handled immediately - * to keep better granularity of PCM pointer. - * - * Later, the process context will sometimes schedules software IRQ - * context of the period_tasklet. Then, no need to flush the queue by - * the same reason as described for IR/IT contexts. - */ - if (!in_interrupt() && amdtp_stream_running(s)) - fw_iso_context_flush_completions(s->context); + struct amdtp_stream *irq_target = d->irq_target; + + if (irq_target && amdtp_stream_running(irq_target)) { + // This function is called in software IRQ context of + // period_tasklet or process context. + // + // When the software IRQ context was scheduled by software IRQ + // context of IT contexts, queued packets were already handled. + // Therefore, no need to flush the queue in buffer furthermore. + // + // When the process context reach here, some packets will be + // already queued in the buffer. These packets should be handled + // immediately to keep better granularity of PCM pointer. + // + // Later, the process context will sometimes schedules software + // IRQ context of the period_tasklet. Then, no need to flush the + // queue by the same reason as described in the above + if (!in_interrupt()) { + // Queued packet should be processed without any kernel + // preemption to keep latency against bus cycle. + preempt_disable(); + fw_iso_context_flush_completions(irq_target->context); + preempt_enable(); + } + } return READ_ONCE(s->pcm_buffer_pointer); } -EXPORT_SYMBOL(amdtp_stream_pcm_pointer); +EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_pointer); /** - * amdtp_stream_pcm_ack - acknowledge queued PCM frames + * amdtp_domain_stream_pcm_ack - acknowledge queued PCM frames + * @d: the AMDTP domain. * @s: the AMDTP stream that transfers the PCM frames * * Returns zero always. */ -int amdtp_stream_pcm_ack(struct amdtp_stream *s) +int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s) { - /* - * Process isochronous packets for recent isochronous cycle to handle - * queued PCM frames. - */ - if (amdtp_stream_running(s)) - fw_iso_context_flush_completions(s->context); + struct amdtp_stream *irq_target = d->irq_target; + + // Process isochronous packets for recent isochronous cycle to handle + // queued PCM frames. + if (irq_target && amdtp_stream_running(irq_target)) { + // Queued packet should be processed without any kernel + // preemption to keep latency against bus cycle. + preempt_disable(); + fw_iso_context_flush_completions(irq_target->context); + preempt_enable(); + } return 0; } -EXPORT_SYMBOL(amdtp_stream_pcm_ack); +EXPORT_SYMBOL_GPL(amdtp_domain_stream_pcm_ack); /** * amdtp_stream_update - update the stream after a bus reset @@ -1143,6 +1300,8 @@ int amdtp_domain_init(struct amdtp_domain *d) { INIT_LIST_HEAD(&d->streams); + d->events_per_period = 0; + return 0; } EXPORT_SYMBOL_GPL(amdtp_domain_init); @@ -1184,26 +1343,105 @@ int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, } EXPORT_SYMBOL_GPL(amdtp_domain_add_stream); +static int get_current_cycle_time(struct fw_card *fw_card, int *cur_cycle) +{ + int generation; + int rcode; + __be32 reg; + u32 data; + + // This is a request to local 1394 OHCI controller and expected to + // complete without any event waiting. + generation = fw_card->generation; + smp_rmb(); // node_id vs. generation. + rcode = fw_run_transaction(fw_card, TCODE_READ_QUADLET_REQUEST, + fw_card->node_id, generation, SCODE_100, + CSR_REGISTER_BASE + CSR_CYCLE_TIME, + ®, sizeof(reg)); + if (rcode != RCODE_COMPLETE) + return -EIO; + + data = be32_to_cpu(reg); + *cur_cycle = data >> 12; + + return 0; +} + /** * amdtp_domain_start - start sending packets for isoc context in the domain. * @d: the AMDTP domain. + * @ir_delay_cycle: the cycle delay to start all IR contexts. */ -int amdtp_domain_start(struct amdtp_domain *d) +int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle) { struct amdtp_stream *s; - int err = 0; + int cycle; + int err; + // Select an IT context as IRQ target. list_for_each_entry(s, &d->streams, list) { - err = amdtp_stream_start(s, s->channel, s->speed); - if (err < 0) + if (s->direction == AMDTP_OUT_STREAM) break; } + if (!s) + return -ENXIO; + d->irq_target = s; - if (err < 0) { - list_for_each_entry(s, &d->streams, list) - amdtp_stream_stop(s); + 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; + + // No need to care overflow in cycle field because of enough + // width. + cycle += ir_delay_cycle; + + // Round up to sec field. + if ((cycle & 0x00001fff) >= CYCLES_PER_SECOND) { + unsigned int sec; + + // The sec field can overflow. + sec = (cycle & 0xffffe000) >> 13; + cycle = (++sec << 13) | + ((cycle & 0x00001fff) / CYCLES_PER_SECOND); + } + + // In OHCI 1394 specification, lower 2 bits are available for + // sec field. + cycle &= 0x00007fff; + } else { + cycle = -1; + } + + list_for_each_entry(s, &d->streams, list) { + int cycle_match; + + if (s->direction == AMDTP_IN_STREAM) { + cycle_match = cycle; + } else { + // IT context starts immediately. + cycle_match = -1; + } + + if (s != d->irq_target) { + err = amdtp_stream_start(s, s->channel, s->speed, d, + false, cycle_match); + if (err < 0) + goto error; + } } + s = d->irq_target; + err = amdtp_stream_start(s, s->channel, s->speed, d, true, -1); + if (err < 0) + goto error; + + return 0; +error: + list_for_each_entry(s, &d->streams, list) + amdtp_stream_stop(s); return err; } EXPORT_SYMBOL_GPL(amdtp_domain_start); @@ -1216,10 +1454,17 @@ void amdtp_domain_stop(struct amdtp_domain *d) { struct amdtp_stream *s, *next; + if (d->irq_target) + amdtp_stream_stop(d->irq_target); + list_for_each_entry_safe(s, next, &d->streams, list) { list_del(&s->list); - amdtp_stream_stop(s); + if (s != d->irq_target) + amdtp_stream_stop(s); } + + d->events_per_period = 0; + d->irq_target = NULL; } EXPORT_SYMBOL_GPL(amdtp_domain_stop); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index bbbca964b9b4..f2d44e2dc3c8 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -117,6 +117,7 @@ struct amdtp_stream { /* For packet processing. */ struct fw_iso_context *context; struct iso_packets_buffer buffer; + unsigned int queue_size; int packet_index; struct pkt_desc *pkt_descs; int tag; @@ -142,6 +143,10 @@ struct amdtp_stream { // To generate CIP header. unsigned int fdf; int syt_override; + + // To generate constant hardware IRQ. + unsigned int event_count; + unsigned int events_per_period; } rx; } ctx_data; @@ -194,8 +199,6 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, struct snd_pcm_runtime *runtime); void amdtp_stream_pcm_prepare(struct amdtp_stream *s); -unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s); -int amdtp_stream_pcm_ack(struct amdtp_stream *s); void amdtp_stream_pcm_abort(struct amdtp_stream *s); extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; @@ -272,6 +275,11 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, struct amdtp_domain { struct list_head streams; + + unsigned int events_per_period; + unsigned int events_per_buffer; + + struct amdtp_stream *irq_target; }; int amdtp_domain_init(struct amdtp_domain *d); @@ -280,7 +288,21 @@ void amdtp_domain_destroy(struct amdtp_domain *d); int amdtp_domain_add_stream(struct amdtp_domain *d, struct amdtp_stream *s, int channel, int speed); -int amdtp_domain_start(struct amdtp_domain *d); +int amdtp_domain_start(struct amdtp_domain *d, unsigned int ir_delay_cycle); void amdtp_domain_stop(struct amdtp_domain *d); +static inline int amdtp_domain_set_events_per_period(struct amdtp_domain *d, + unsigned int events_per_period, + unsigned int events_per_buffer) +{ + d->events_per_period = events_per_period; + d->events_per_buffer = events_per_buffer; + + return 0; +} + +unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, + struct amdtp_stream *s); +int amdtp_domain_stream_pcm_ack(struct amdtp_domain *d, struct amdtp_stream *s); + #endif diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index 356d6ba60959..d1ad9a8451bc 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -217,7 +217,9 @@ int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob, enum snd_bebob_clock_type *src); int snd_bebob_stream_discover(struct snd_bebob *bebob); int snd_bebob_stream_init_duplex(struct snd_bebob *bebob); -int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate); +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_bebob_stream_start_duplex(struct snd_bebob *bebob); void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob); void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob); diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 4d8805fa8a00..6f597d03e7c1 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) return err; mutex_lock(&bebob->mutex); - err = snd_bebob_stream_reserve_duplex(bebob, 0); + err = snd_bebob_stream_reserve_duplex(bebob, 0, 0, 0); if (err >= 0) { ++bebob->substreams_counter; err = snd_bebob_stream_start_duplex(bebob); diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 0fb9eed46837..1b100159f4c5 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -129,18 +129,17 @@ end: return err; } -static int -pcm_open(struct snd_pcm_substream *substream) +static int pcm_open(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; const struct snd_bebob_rate_spec *spec = bebob->spec->rate; - unsigned int sampling_rate; + struct amdtp_domain *d = &bebob->domain; enum snd_bebob_clock_type src; int err; err = snd_bebob_stream_lock_try(bebob); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(bebob, substream); if (err < 0) @@ -150,15 +149,20 @@ pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - /* - * When source of clock is internal or any PCM stream are running, - * the available sampling rate is limited at current sampling rate. - */ + mutex_lock(&bebob->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (src == SND_BEBOB_CLOCK_TYPE_EXTERNAL || - amdtp_stream_pcm_running(&bebob->tx_stream) || - amdtp_stream_pcm_running(&bebob->rx_stream)) { + (bebob->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int sampling_rate; + err = spec->get(bebob, &sampling_rate); if (err < 0) { + mutex_unlock(&bebob->mutex); dev_err(&bebob->unit->device, "fail to get sampling rate: %d\n", err); goto err_locked; @@ -166,11 +170,31 @@ pcm_open(struct snd_pcm_substream *substream) substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&bebob->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&bebob->mutex); + goto err_locked; + } + } } + mutex_unlock(&bebob->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_bebob_stream_lock_release(bebob); return err; @@ -197,9 +221,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&bebob->mutex); - err = snd_bebob_stream_reserve_duplex(bebob, rate); + err = snd_bebob_stream_reserve_duplex(bebob, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++bebob->substreams_counter; mutex_unlock(&bebob->mutex); @@ -286,31 +313,33 @@ pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) return 0; } -static snd_pcm_uframes_t -pcm_capture_pointer(struct snd_pcm_substream *sbstrm) +static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&bebob->tx_stream); + + return amdtp_domain_stream_pcm_pointer(&bebob->domain, + &bebob->tx_stream); } -static snd_pcm_uframes_t -pcm_playback_pointer(struct snd_pcm_substream *sbstrm) +static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_bebob *bebob = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&bebob->rx_stream); + + return amdtp_domain_stream_pcm_pointer(&bebob->domain, + &bebob->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - return amdtp_stream_pcm_ack(&bebob->tx_stream); + return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_bebob *bebob = substream->private_data; - return amdtp_stream_pcm_ack(&bebob->rx_stream); + return amdtp_domain_stream_pcm_ack(&bebob->domain, &bebob->rx_stream); } int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index 6c1497d9f52b..f7f0db5aa811 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -553,7 +553,9 @@ static int keep_resources(struct snd_bebob *bebob, struct amdtp_stream *stream, return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); } -int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate) +int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -606,6 +608,14 @@ int snd_bebob_stream_reserve_duplex(struct snd_bebob *bebob, unsigned int rate) cmp_connection_release(&bebob->out_conn); return err; } + + err = amdtp_domain_set_events_per_period(&bebob->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + cmp_connection_release(&bebob->out_conn); + cmp_connection_release(&bebob->in_conn); + return err; + } } return 0; @@ -647,7 +657,15 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob) if (err < 0) goto error; - err = amdtp_domain_start(&bebob->domain); + // The device postpones start of transmission mostly for 1 sec + // after receives packets firstly. For safe, IR context starts + // 1.5 sec (=12000 cycles) later. This is within 2.0 sec + // (=CALLBACK_TIMEOUT). + // Furthermore, some devices transfer isoc packets with + // discontinuous counter in the beginning of packet streaming. + // The delay has an effect to avoid detection of this + // discontinuity. + err = amdtp_domain_start(&bebob->domain, 12000); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index c9e19bddfc09..4c2998034313 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) mutex_lock(&dice->mutex); - err = snd_dice_stream_reserve_duplex(dice, 0); + err = snd_dice_stream_reserve_duplex(dice, 0, 0, 0); if (err >= 0) { ++dice->substreams_counter; err = snd_dice_stream_start_duplex(dice); diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c index 94a4dccfc381..f1848fb39bd0 100644 --- a/sound/firewire/dice/dice-pcm.c +++ b/sound/firewire/dice/dice-pcm.c @@ -164,13 +164,14 @@ static int init_hw_info(struct snd_dice *dice, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; + struct amdtp_domain *d = &dice->domain; unsigned int source; bool internal; int err; err = snd_dice_stream_lock_try(dice); if (err < 0) - goto end; + return err; err = init_hw_info(dice, substream); if (err < 0) @@ -195,27 +196,56 @@ static int pcm_open(struct snd_pcm_substream *substream) break; } - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ + mutex_lock(&dice->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (!internal || - amdtp_stream_pcm_running(&dice->tx_stream[0]) || - amdtp_stream_pcm_running(&dice->tx_stream[1]) || - amdtp_stream_pcm_running(&dice->rx_stream[0]) || - amdtp_stream_pcm_running(&dice->rx_stream[1])) { + (dice->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; unsigned int rate; err = snd_dice_transaction_get_rate(dice, &rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&dice->mutex); goto err_locked; + } + substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + if (frames_per_period > 0) { + // For double_pcm_frame quirk. + if (rate > 96000) { + frames_per_period *= 2; + frames_per_buffer *= 2; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&dice->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&dice->mutex); + goto err_locked; + } + } } + mutex_unlock(&dice->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_dice_stream_lock_release(dice); return err; @@ -243,9 +273,17 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int events_per_period = params_period_size(hw_params); + unsigned int events_per_buffer = params_buffer_size(hw_params); mutex_lock(&dice->mutex); - err = snd_dice_stream_reserve_duplex(dice, rate); + // For double_pcm_frame quirk. + if (rate > 96000) { + events_per_period /= 2; + events_per_buffer /= 2; + } + err = snd_dice_stream_reserve_duplex(dice, rate, + events_per_period, events_per_buffer); if (err >= 0) ++dice->substreams_counter; mutex_unlock(&dice->mutex); @@ -341,14 +379,14 @@ static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; - return amdtp_stream_pcm_pointer(stream); + return amdtp_domain_stream_pcm_pointer(&dice->domain, stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; - return amdtp_stream_pcm_pointer(stream); + return amdtp_domain_stream_pcm_pointer(&dice->domain, stream); } static int capture_ack(struct snd_pcm_substream *substream) @@ -356,7 +394,7 @@ static int capture_ack(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device]; - return amdtp_stream_pcm_ack(stream); + return amdtp_domain_stream_pcm_ack(&dice->domain, stream); } static int playback_ack(struct snd_pcm_substream *substream) @@ -364,7 +402,7 @@ static int playback_ack(struct snd_pcm_substream *substream) struct snd_dice *dice = substream->private_data; struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device]; - return amdtp_stream_pcm_ack(stream); + return amdtp_domain_stream_pcm_ack(&dice->domain, stream); } int snd_dice_create_pcm(struct snd_dice *dice) diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index f6a8627ae5a2..6a3d60913e10 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -278,7 +278,9 @@ static void finish_session(struct snd_dice *dice, struct reg_params *tx_params, snd_dice_transaction_clear_enable(dice); } -int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate) +int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, + unsigned int events_per_period, + unsigned int events_per_buffer) { unsigned int curr_rate; int err; @@ -324,6 +326,11 @@ int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate) &rx_params); if (err < 0) goto error; + + err = amdtp_domain_set_events_per_period(&dice->domain, + events_per_period, events_per_buffer); + if (err < 0) + goto error; } return 0; @@ -455,7 +462,7 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice) goto error; } - err = amdtp_domain_start(&dice->domain); + err = amdtp_domain_start(&dice->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h index fa6d74303f54..16366773e22e 100644 --- a/sound/firewire/dice/dice.h +++ b/sound/firewire/dice/dice.h @@ -210,7 +210,9 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice); void snd_dice_stream_stop_duplex(struct snd_dice *dice); int snd_dice_stream_init_duplex(struct snd_dice *dice); void snd_dice_stream_destroy_duplex(struct snd_dice *dice); -int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate); +int snd_dice_stream_reserve_duplex(struct snd_dice *dice, unsigned int rate, + unsigned int events_per_period, + unsigned int events_per_buffer); void snd_dice_stream_update_duplex(struct snd_dice *dice); int snd_dice_stream_detect_current_formats(struct snd_dice *dice); diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 2b57ece89101..68eb8c39afa6 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) return err; mutex_lock(&dg00x->mutex); - err = snd_dg00x_stream_reserve_duplex(dg00x, 0); + err = snd_dg00x_stream_reserve_duplex(dg00x, 0, 0, 0); if (err >= 0) { ++dg00x->substreams_counter; err = snd_dg00x_stream_start_duplex(dg00x); diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 18e561b26625..8befc5d2ef22 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -100,14 +100,14 @@ static int pcm_init_hw_params(struct snd_dg00x *dg00x, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; + struct amdtp_domain *d = &dg00x->domain; enum snd_dg00x_clock clock; bool detect; - unsigned int rate; int err; err = snd_dg00x_stream_lock_try(dg00x); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(dg00x, substream); if (err < 0) @@ -127,19 +127,49 @@ static int pcm_open(struct snd_pcm_substream *substream) } } + mutex_lock(&dg00x->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if ((clock != SND_DG00X_CLOCK_INTERNAL) || - amdtp_stream_pcm_running(&dg00x->rx_stream) || - amdtp_stream_pcm_running(&dg00x->tx_stream)) { + (dg00x->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int rate; + err = snd_dg00x_stream_get_external_rate(dg00x, &rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&dg00x->mutex); goto err_locked; + } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&dg00x->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&dg00x->mutex); + goto err_locked; + } + } } + mutex_unlock(&dg00x->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_dg00x_stream_lock_release(dg00x); return err; @@ -167,9 +197,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&dg00x->mutex); - err = snd_dg00x_stream_reserve_duplex(dg00x, rate); + err = snd_dg00x_stream_reserve_duplex(dg00x, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++dg00x->substreams_counter; mutex_unlock(&dg00x->mutex); @@ -268,28 +301,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&dg00x->tx_stream); + return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_dg00x *dg00x = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&dg00x->rx_stream); + return amdtp_domain_stream_pcm_pointer(&dg00x->domain, &dg00x->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; - return amdtp_stream_pcm_ack(&dg00x->tx_stream); + return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_dg00x *dg00x = substream->private_data; - return amdtp_stream_pcm_ack(&dg00x->rx_stream); + return amdtp_domain_stream_pcm_ack(&dg00x->domain, &dg00x->rx_stream); } int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c index d6a92460060f..405d6903bfbc 100644 --- a/sound/firewire/digi00x/digi00x-stream.c +++ b/sound/firewire/digi00x/digi00x-stream.c @@ -283,7 +283,9 @@ void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x) destroy_stream(dg00x, &dg00x->tx_stream); } -int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) +int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -315,6 +317,14 @@ int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate) fw_iso_resources_free(&dg00x->rx_resources); return err; } + + err = amdtp_domain_set_events_per_period(&dg00x->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&dg00x->rx_resources); + fw_iso_resources_free(&dg00x->tx_resources); + return err; + } } return 0; @@ -365,7 +375,7 @@ int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x) if (err < 0) goto error; - err = amdtp_domain_start(&dg00x->domain); + err = amdtp_domain_start(&dg00x->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h index 8041c65f2736..129de8edd5ea 100644 --- a/sound/firewire/digi00x/digi00x.h +++ b/sound/firewire/digi00x/digi00x.h @@ -141,7 +141,9 @@ int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x, int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect); int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x); -int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate); +int snd_dg00x_stream_reserve_duplex(struct snd_dg00x *dg00x, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x); void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x); diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c index 9eab3ad283ce..c29f87a65c0f 100644 --- a/sound/firewire/fireface/ff-pcm.c +++ b/sound/firewire/fireface/ff-pcm.c @@ -139,6 +139,7 @@ static int pcm_init_hw_params(struct snd_ff *ff, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; + struct amdtp_domain *d = &ff->domain; unsigned int rate; enum snd_ff_clock_src src; int i, err; @@ -155,16 +156,21 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto release_lock; + mutex_lock(&ff->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (src != SND_FF_CLOCK_SRC_INTERNAL) { for (i = 0; i < CIP_SFC_COUNT; ++i) { if (amdtp_rate_table[i] == rate) break; } - /* - * The unit is configured at sampling frequency which packet - * streaming engine can't support. - */ + + // The unit is configured at sampling frequency which packet + // streaming engine can't support. if (i >= CIP_SFC_COUNT) { + mutex_unlock(&ff->mutex); err = -EIO; goto release_lock; } @@ -172,14 +178,34 @@ static int pcm_open(struct snd_pcm_substream *substream) substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; } else { - if (amdtp_stream_pcm_running(&ff->rx_stream) || - amdtp_stream_pcm_running(&ff->tx_stream)) { + if (ff->substreams_counter > 0) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + rate = amdtp_rate_table[ff->rx_stream.sfc]; substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&ff->mutex); + goto release_lock; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&ff->mutex); + goto release_lock; + } } } + mutex_unlock(&ff->mutex); + snd_pcm_set_sync(substream); return 0; @@ -211,9 +237,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&ff->mutex); - err = snd_ff_stream_reserve_duplex(ff, rate); + err = snd_ff_stream_reserve_duplex(ff, rate, frames_per_period, + frames_per_buffer); if (err >= 0) ++ff->substreams_counter; mutex_unlock(&ff->mutex); @@ -312,28 +341,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&ff->tx_stream); + return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_ff *ff = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&ff->rx_stream); + return amdtp_domain_stream_pcm_pointer(&ff->domain, &ff->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; - return amdtp_stream_pcm_ack(&ff->tx_stream); + return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_ff *ff = substream->private_data; - return amdtp_stream_pcm_ack(&ff->rx_stream); + return amdtp_domain_stream_pcm_ack(&ff->domain, &ff->rx_stream); } int snd_ff_create_pcm_devices(struct snd_ff *ff) diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c index e8e6f9fd6433..63b79c4a5405 100644 --- a/sound/firewire/fireface/ff-stream.c +++ b/sound/firewire/fireface/ff-stream.c @@ -106,7 +106,9 @@ void snd_ff_stream_destroy_duplex(struct snd_ff *ff) destroy_stream(ff, &ff->tx_stream); } -int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; enum snd_ff_clock_src src; @@ -150,6 +152,14 @@ int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate) err = ff->spec->protocol->allocate_resources(ff, rate); if (err < 0) return err; + + err = amdtp_domain_set_events_per_period(&ff->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&ff->tx_resources); + fw_iso_resources_free(&ff->rx_resources); + return err; + } } return 0; @@ -174,6 +184,7 @@ 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) @@ -189,7 +200,14 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (err < 0) goto error; - err = amdtp_domain_start(&ff->domain); + // 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); if (err < 0) goto error; diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h index b4c22ca6079e..dc7a20f75983 100644 --- a/sound/firewire/fireface/ff.h +++ b/sound/firewire/fireface/ff.h @@ -139,7 +139,9 @@ int snd_ff_stream_get_multiplier_mode(enum cip_sfc sfc, enum snd_ff_stream_mode *mode); int snd_ff_stream_init_duplex(struct snd_ff *ff); void snd_ff_stream_destroy_duplex(struct snd_ff *ff); -int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate); +int snd_ff_stream_reserve_duplex(struct snd_ff *ff, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate); void snd_ff_stream_stop_duplex(struct snd_ff *ff); void snd_ff_stream_update_duplex(struct snd_ff *ff); diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h index 4cda297f8438..dda797209a27 100644 --- a/sound/firewire/fireworks/fireworks.h +++ b/sound/firewire/fireworks/fireworks.h @@ -207,7 +207,9 @@ int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate); int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate); int snd_efw_stream_init_duplex(struct snd_efw *efw); -int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate); +int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_efw_stream_start_duplex(struct snd_efw *efw); void snd_efw_stream_stop_duplex(struct snd_efw *efw); void snd_efw_stream_update_duplex(struct snd_efw *efw); diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index a9f4a9630d15..84621e356848 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) goto end; mutex_lock(&efw->mutex); - err = snd_efw_stream_reserve_duplex(efw, 0); + err = snd_efw_stream_reserve_duplex(efw, 0, 0, 0); if (err >= 0) { ++efw->substreams_counter; err = snd_efw_stream_start_duplex(efw); diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index a7025dccc754..64c1bcf28dfa 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -173,13 +173,13 @@ end: static int pcm_open(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - unsigned int sampling_rate; + struct amdtp_domain *d = &efw->domain; enum snd_efw_clock_source clock_source; int err; err = snd_efw_stream_lock_try(efw); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(efw, substream); if (err < 0) @@ -189,23 +189,49 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ + mutex_lock(&efw->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) || - amdtp_stream_pcm_running(&efw->tx_stream) || - amdtp_stream_pcm_running(&efw->rx_stream)) { + (efw->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int sampling_rate; + err = snd_efw_command_get_sampling_rate(efw, &sampling_rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&efw->mutex); goto err_locked; + } substream->runtime->hw.rate_min = sampling_rate; substream->runtime->hw.rate_max = sampling_rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&efw->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&efw->mutex); + goto err_locked; + } + } } + mutex_unlock(&efw->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_efw_stream_lock_release(efw); return err; @@ -231,9 +257,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&efw->mutex); - err = snd_efw_stream_reserve_duplex(efw, rate); + err = snd_efw_stream_reserve_duplex(efw, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++efw->substreams_counter; mutex_unlock(&efw->mutex); @@ -319,26 +348,28 @@ static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_efw *efw = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&efw->tx_stream); + + return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_efw *efw = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&efw->rx_stream); + + return amdtp_domain_stream_pcm_pointer(&efw->domain, &efw->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - return amdtp_stream_pcm_ack(&efw->tx_stream); + return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_efw *efw = substream->private_data; - return amdtp_stream_pcm_ack(&efw->rx_stream); + return amdtp_domain_stream_pcm_ack(&efw->domain, &efw->rx_stream); } int snd_efw_create_pcm_devices(struct snd_efw *efw) diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c index f2de304d2f26..2206af0fef42 100644 --- a/sound/firewire/fireworks/fireworks_stream.c +++ b/sound/firewire/fireworks/fireworks_stream.c @@ -181,7 +181,9 @@ static int keep_resources(struct snd_efw *efw, struct amdtp_stream *stream, return cmp_connection_reserve(conn, amdtp_stream_get_max_payload(stream)); } -int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate) +int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -228,6 +230,14 @@ int snd_efw_stream_reserve_duplex(struct snd_efw *efw, unsigned int rate) cmp_connection_release(&efw->in_conn); return err; } + + err = amdtp_domain_set_events_per_period(&efw->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + cmp_connection_release(&efw->in_conn); + cmp_connection_release(&efw->out_conn); + return err; + } } return 0; @@ -262,7 +272,7 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw) if (err < 0) goto error; - err = amdtp_domain_start(&efw->domain); + err = amdtp_domain_start(&efw->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c index 46a0035df31e..2365f7dfde26 100644 --- a/sound/firewire/motu/motu-midi.c +++ b/sound/firewire/motu/motu-midi.c @@ -17,7 +17,7 @@ static int midi_open(struct snd_rawmidi_substream *substream) mutex_lock(&motu->mutex); - err = snd_motu_stream_reserve_duplex(motu, 0); + err = snd_motu_stream_reserve_duplex(motu, 0, 0, 0); if (err >= 0) { ++motu->substreams_counter; err = snd_motu_stream_start_duplex(motu); diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c index aa2e584da6fe..55d3d6661731 100644 --- a/sound/firewire/motu/motu-pcm.c +++ b/sound/firewire/motu/motu-pcm.c @@ -134,8 +134,8 @@ 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; - unsigned int rate; int err; err = snd_motu_stream_lock_try(motu); @@ -152,28 +152,50 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - /* - * When source of clock is not internal or any PCM streams are running, - * available sampling rate is limited at current sampling rate. - */ err = protocol->get_clock_source(motu, &src); if (err < 0) goto err_locked; + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL || - amdtp_stream_pcm_running(&motu->tx_stream) || - amdtp_stream_pcm_running(&motu->rx_stream)) { + (motu->substreams_counter > 0 && d->events_per_period > 0)) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int rate; + err = protocol->get_clock_rate(motu, &rate); if (err < 0) goto err_locked; + substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&motu->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&motu->mutex); + goto err_locked; + } + } } snd_pcm_set_sync(substream); mutex_unlock(&motu->mutex); - return err; + return 0; err_locked: mutex_unlock(&motu->mutex); snd_motu_stream_lock_release(motu); @@ -202,9 +224,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&motu->mutex); - err = snd_motu_stream_reserve_duplex(motu, rate); + err = snd_motu_stream_reserve_duplex(motu, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++motu->substreams_counter; mutex_unlock(&motu->mutex); @@ -295,27 +320,27 @@ static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_pointer(&motu->tx_stream); + return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->tx_stream); } static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_pointer(&motu->rx_stream); + return amdtp_domain_stream_pcm_pointer(&motu->domain, &motu->rx_stream); } static int capture_ack(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_ack(&motu->tx_stream); + return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->tx_stream); } static int playback_ack(struct snd_pcm_substream *substream) { struct snd_motu *motu = substream->private_data; - return amdtp_stream_pcm_ack(&motu->rx_stream); + return amdtp_domain_stream_pcm_ack(&motu->domain, &motu->rx_stream); } int snd_motu_create_pcm_devices(struct snd_motu *motu) diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c index 813e38e6a86e..a17ddceb1bec 100644 --- a/sound/firewire/motu/motu-stream.c +++ b/sound/firewire/motu/motu-stream.c @@ -133,7 +133,9 @@ int snd_motu_stream_cache_packet_formats(struct snd_motu *motu) return 0; } -int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate) +int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -171,6 +173,14 @@ int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate) fw_iso_resources_free(&motu->tx_resources); return err; } + + err = amdtp_domain_set_events_per_period(&motu->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&motu->tx_resources); + fw_iso_resources_free(&motu->rx_resources); + return err; + } } return 0; @@ -250,7 +260,7 @@ int snd_motu_stream_start_duplex(struct snd_motu *motu) if (err < 0) goto stop_streams; - err = amdtp_domain_start(&motu->domain); + err = amdtp_domain_start(&motu->domain, 0); if (err < 0) goto stop_streams; diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h index 350ee2c16f4a..35ed8b6bb8e8 100644 --- a/sound/firewire/motu/motu.h +++ b/sound/firewire/motu/motu.h @@ -154,7 +154,9 @@ void snd_motu_transaction_unregister(struct snd_motu *motu); int snd_motu_stream_init_duplex(struct snd_motu *motu); void snd_motu_stream_destroy_duplex(struct snd_motu *motu); int snd_motu_stream_cache_packet_formats(struct snd_motu *motu); -int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate); +int snd_motu_stream_reserve_duplex(struct snd_motu *motu, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_motu_stream_start_duplex(struct snd_motu *motu); void snd_motu_stream_stop_duplex(struct snd_motu *motu); int snd_motu_stream_lock_try(struct snd_motu *motu); diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 9bdec08cb8ea..775cba3f1f02 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -18,7 +18,7 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, 0, 0, 0, 0); if (err >= 0) { ++oxfw->substreams_count; err = snd_oxfw_stream_start_duplex(oxfw); @@ -45,7 +45,7 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream) mutex_lock(&oxfw->mutex); - err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0); + err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, 0, 0, 0, 0); if (err >= 0) { ++oxfw->substreams_count; err = snd_oxfw_stream_start_duplex(oxfw); diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c index 7c6d1c277d4d..74bd1811cec2 100644 --- a/sound/firewire/oxfw/oxfw-pcm.c +++ b/sound/firewire/oxfw/oxfw-pcm.c @@ -170,30 +170,56 @@ end: static int pcm_open(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; + struct amdtp_domain *d = &oxfw->domain; int err; err = snd_oxfw_stream_lock_try(oxfw); if (err < 0) - goto end; + return err; err = init_hw_params(oxfw, substream); if (err < 0) goto err_locked; - /* - * When any PCM streams are already running, the available sampling - * rate is limited at current value. - */ - if (amdtp_stream_pcm_running(&oxfw->tx_stream) || - amdtp_stream_pcm_running(&oxfw->rx_stream)) { + mutex_lock(&oxfw->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. + if (oxfw->substreams_count > 0 && d->events_per_period > 0) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + err = limit_to_current_params(substream); - if (err < 0) - goto end; + if (err < 0) { + mutex_unlock(&oxfw->mutex); + goto err_locked; + } + + if (frames_per_period > 0) { + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&oxfw->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&oxfw->mutex); + goto err_locked; + } + } } + mutex_unlock(&oxfw->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_oxfw_stream_lock_release(oxfw); return err; @@ -221,10 +247,13 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); unsigned int channels = params_channels(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&oxfw->mutex); err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->tx_stream, - rate, channels); + rate, channels, frames_per_period, + frames_per_buffer); if (err >= 0) ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); @@ -246,10 +275,13 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); unsigned int channels = params_channels(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&oxfw->mutex); err = snd_oxfw_stream_reserve_duplex(oxfw, &oxfw->rx_stream, - rate, channels); + rate, channels, frames_per_period, + frames_per_buffer); if (err >= 0) ++oxfw->substreams_count; mutex_unlock(&oxfw->mutex); @@ -361,27 +393,27 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstm) { struct snd_oxfw *oxfw = sbstm->private_data; - return amdtp_stream_pcm_pointer(&oxfw->tx_stream); + return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm) { struct snd_oxfw *oxfw = sbstm->private_data; - return amdtp_stream_pcm_pointer(&oxfw->rx_stream); + return amdtp_domain_stream_pcm_pointer(&oxfw->domain, &oxfw->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - return amdtp_stream_pcm_ack(&oxfw->tx_stream); + return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_oxfw *oxfw = substream->private_data; - return amdtp_stream_pcm_ack(&oxfw->rx_stream); + return amdtp_domain_stream_pcm_ack(&oxfw->domain, &oxfw->rx_stream); } int snd_oxfw_create_pcm(struct snd_oxfw *oxfw) diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c index 3c9a796b6526..501a80094bf7 100644 --- a/sound/firewire/oxfw/oxfw-stream.c +++ b/sound/firewire/oxfw/oxfw-stream.c @@ -244,7 +244,9 @@ static int keep_resources(struct snd_oxfw *oxfw, struct amdtp_stream *stream) int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels) + unsigned int rate, unsigned int pcm_channels, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { struct snd_oxfw_stream_formation formation; enum avc_general_plug_dir dir; @@ -305,6 +307,15 @@ int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, return err; } } + + err = amdtp_domain_set_events_per_period(&oxfw->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + cmp_connection_release(&oxfw->in_conn); + if (oxfw->has_output) + cmp_connection_release(&oxfw->out_conn); + return err; + } } return 0; @@ -344,7 +355,7 @@ int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw) } } - err = amdtp_domain_start(&oxfw->domain); + err = amdtp_domain_start(&oxfw->domain, 0); if (err < 0) goto error; diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h index c9627b8c5d6e..c30e537087b0 100644 --- a/sound/firewire/oxfw/oxfw.h +++ b/sound/firewire/oxfw/oxfw.h @@ -103,7 +103,9 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate, int snd_oxfw_stream_init_duplex(struct snd_oxfw *oxfw); int snd_oxfw_stream_reserve_duplex(struct snd_oxfw *oxfw, struct amdtp_stream *stream, - unsigned int rate, unsigned int pcm_channels); + unsigned int rate, unsigned int pcm_channels, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_oxfw_stream_start_duplex(struct snd_oxfw *oxfw); void snd_oxfw_stream_stop_duplex(struct snd_oxfw *oxfw); void snd_oxfw_stream_destroy_duplex(struct snd_oxfw *oxfw); diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index 2377732caa52..cd45f20ba515 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -43,13 +43,13 @@ static int pcm_init_hw_params(struct snd_tscm *tscm, static int pcm_open(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; + struct amdtp_domain *d = &tscm->domain; enum snd_tscm_clock clock; - unsigned int rate; int err; err = snd_tscm_stream_lock_try(tscm); if (err < 0) - goto end; + return err; err = pcm_init_hw_params(tscm, substream); if (err < 0) @@ -59,19 +59,46 @@ static int pcm_open(struct snd_pcm_substream *substream) if (err < 0) goto err_locked; - if (clock != SND_TSCM_CLOCK_INTERNAL || - amdtp_stream_pcm_running(&tscm->rx_stream) || - amdtp_stream_pcm_running(&tscm->tx_stream)) { + mutex_lock(&tscm->mutex); + + // When source of clock is not internal or any stream is reserved for + // transmission of PCM frames, the available sampling rate is limited + // at current one. + if (clock != SND_TSCM_CLOCK_INTERNAL || tscm->substreams_counter > 0) { + unsigned int frames_per_period = d->events_per_period; + unsigned int frames_per_buffer = d->events_per_buffer; + unsigned int rate; + err = snd_tscm_stream_get_rate(tscm, &rate); - if (err < 0) + if (err < 0) { + mutex_unlock(&tscm->mutex); goto err_locked; + } substream->runtime->hw.rate_min = rate; substream->runtime->hw.rate_max = rate; + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + frames_per_period, frames_per_period); + if (err < 0) { + mutex_unlock(&tscm->mutex); + goto err_locked; + } + + err = snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + frames_per_buffer, frames_per_buffer); + if (err < 0) { + mutex_unlock(&tscm->mutex); + goto err_locked; + } } + mutex_unlock(&tscm->mutex); + snd_pcm_set_sync(substream); -end: - return err; + + return 0; err_locked: snd_tscm_stream_lock_release(tscm); return err; @@ -99,9 +126,12 @@ static int pcm_hw_params(struct snd_pcm_substream *substream, if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) { unsigned int rate = params_rate(hw_params); + unsigned int frames_per_period = params_period_size(hw_params); + unsigned int frames_per_buffer = params_buffer_size(hw_params); mutex_lock(&tscm->mutex); - err = snd_tscm_stream_reserve_duplex(tscm, rate); + err = snd_tscm_stream_reserve_duplex(tscm, rate, + frames_per_period, frames_per_buffer); if (err >= 0) ++tscm->substreams_counter; mutex_unlock(&tscm->mutex); @@ -200,28 +230,28 @@ static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm) { struct snd_tscm *tscm = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&tscm->tx_stream); + return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->tx_stream); } static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) { struct snd_tscm *tscm = sbstrm->private_data; - return amdtp_stream_pcm_pointer(&tscm->rx_stream); + return amdtp_domain_stream_pcm_pointer(&tscm->domain, &tscm->rx_stream); } static int pcm_capture_ack(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; - return amdtp_stream_pcm_ack(&tscm->tx_stream); + return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->tx_stream); } static int pcm_playback_ack(struct snd_pcm_substream *substream) { struct snd_tscm *tscm = substream->private_data; - return amdtp_stream_pcm_ack(&tscm->rx_stream); + return amdtp_domain_stream_pcm_ack(&tscm->domain, &tscm->rx_stream); } int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c index adf69a520b80..eb07e1decf9b 100644 --- a/sound/firewire/tascam/tascam-stream.c +++ b/sound/firewire/tascam/tascam-stream.c @@ -383,7 +383,9 @@ void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm) destroy_stream(tscm, &tscm->tx_stream); } -int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) +int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer) { unsigned int curr_rate; int err; @@ -413,6 +415,14 @@ int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate) fw_iso_resources_free(&tscm->tx_resources); return err; } + + err = amdtp_domain_set_events_per_period(&tscm->domain, + frames_per_period, frames_per_buffer); + if (err < 0) { + fw_iso_resources_free(&tscm->tx_resources); + fw_iso_resources_free(&tscm->rx_resources); + return err; + } } return 0; @@ -463,7 +473,7 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate) if (err < 0) goto error; - err = amdtp_domain_start(&tscm->domain); + err = amdtp_domain_start(&tscm->domain, 0); if (err < 0) return err; diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h index 15bd335fa07f..78b7a08986a1 100644 --- a/sound/firewire/tascam/tascam.h +++ b/sound/firewire/tascam/tascam.h @@ -168,7 +168,9 @@ int snd_tscm_stream_get_clock(struct snd_tscm *tscm, int snd_tscm_stream_init_duplex(struct snd_tscm *tscm); void snd_tscm_stream_update_duplex(struct snd_tscm *tscm); void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm); -int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate); +int snd_tscm_stream_reserve_duplex(struct snd_tscm *tscm, unsigned int rate, + unsigned int frames_per_period, + unsigned int frames_per_buffer); int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate); void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm); diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index 3d33fc1757ba..b0c88fe040ee 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -34,6 +34,12 @@ config SND_HDA_PREALLOC_SIZE via a proc file (/proc/asound/card*/pcm*/sub*/prealloc), too. config SND_INTEL_NHLT - tristate + bool # this config should be selected only for Intel ACPI platforms. - # A fallback is provided so that the code compiles in all cases.
\ No newline at end of file + # A fallback is provided so that the code compiles in all cases. + +config SND_INTEL_DSP_CONFIG + tristate + select SND_INTEL_NHLT if ACPI + # this config should be selected only for Intel DSP platforms. + # A fallback is provided so that the code compiles in all cases. diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 8560f6ef1b19..601e617918b8 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o #extended hda obj-$(CONFIG_SND_HDA_EXT_CORE) += ext/ -snd-intel-nhlt-objs := intel-nhlt.o -obj-$(CONFIG_SND_INTEL_NHLT) += snd-intel-nhlt.o +snd-intel-dspcfg-objs := intel-dsp-config.o +snd-intel-dspcfg-$(CONFIG_SND_INTEL_NHLT) += intel-nhlt.o +obj-$(CONFIG_SND_INTEL_DSP_CONFIG) += snd-intel-dspcfg.o diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c new file mode 100644 index 000000000000..0b2a7201a46a --- /dev/null +++ b/sound/hda/intel-dsp-config.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Jaroslav Kysela <perex@perex.cz> + +#include <linux/bits.h> +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <sound/core.h> +#include <sound/intel-dsp-config.h> +#include <sound/intel-nhlt.h> + +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) + +struct config_entry { + u32 flags; + u16 device; + const struct dmi_system_id *dmi_table; +}; + +/* + * configuration table + * - the order of similar PCI ID entries is important! + * - the first successful match will win + */ +static const struct config_entry config_table[] = { +/* Merrifield */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_MERRIFIELD) + { + .flags = FLAG_SOF, + .device = 0x119a, + }, +#endif +/* Broxton-T */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + { + .flags = FLAG_SOF, + .device = 0x1a98, + }, +#endif +/* + * Apollolake (Broxton-P) + * the legacy HDaudio driver is used except on Up Squared (SOF) and + * Chromebooks (SST) + */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) + { + .flags = FLAG_SOF, + .device = 0x5a98, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Up Squared", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "AAEON"), + DMI_MATCH(DMI_BOARD_NAME, "UP-APL01"), + } + }, + {} + } + }, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) + { + .flags = FLAG_SST, + .device = 0x5a98, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif +/* + * Skylake and Kabylake use legacy HDaudio driver except for Google + * Chromebooks (SST) + */ + +/* Sunrise Point-LP */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) + { + .flags = FLAG_SST, + .device = 0x9d70, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif +/* Kabylake-LP */ +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) + { + .flags = FLAG_SST, + .device = 0x9d71, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif + +/* + * Geminilake uses legacy HDaudio driver except for Google + * Chromebooks + */ +/* Geminilake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) + { + .flags = FLAG_SOF, + .device = 0x3198, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, +#endif + +/* + * CoffeeLake, CannonLake, CometLake, IceLake, TigerLake use legacy + * HDaudio driver except for Google Chromebooks and when DMICs are + * present. Two cases are required since Coreboot does not expose NHLT + * tables. + * + * When the Chromebook quirk is not present, it's based on information + * that no such device exists. When the quirk is present, it could be + * either based on product information or a placeholder. + */ + +/* Cannonlake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_CANNONLAKE) + { + .flags = FLAG_SOF, + .device = 0x9dc8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x9dc8, + }, +#endif + +/* Coffelake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COFFEELAKE) + { + .flags = FLAG_SOF, + .device = 0xa348, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0xa348, + }, +#endif + +/* Cometlake-LP */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_LP) + { + .flags = FLAG_SOF, + .device = 0x02c8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x02c8, + }, +#endif +/* Cometlake-H */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMETLAKE_H) + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x06c8, + }, +#endif + +/* Icelake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_ICELAKE) + { + .flags = FLAG_SOF, + .device = 0x34c8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x34c8, + }, +#endif + +/* Tigerlake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_TIGERLAKE) + { + .flags = FLAG_SOF, + .device = 0xa0c8, + .dmi_table = (const struct dmi_system_id []) { + { + .ident = "Google Chromebooks", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + } + }, + {} + } + }, + + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0xa0c8, + }, +#endif + +/* Elkhart Lake */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) + { + .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC, + .device = 0x4b55, + }, +#endif + +}; + +static const struct config_entry *snd_intel_dsp_find_config + (struct pci_dev *pci, const struct config_entry *table, u32 len) +{ + u16 device; + + device = pci->device; + for (; len > 0; len--, table++) { + if (table->device != device) + continue; + if (table->dmi_table && !dmi_check_system(table->dmi_table)) + continue; + return table; + } + return NULL; +} + +static int snd_intel_dsp_check_dmic(struct pci_dev *pci) +{ + struct nhlt_acpi_table *nhlt; + int ret = 0; + + nhlt = intel_nhlt_init(&pci->dev); + if (nhlt) { + if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) + ret = 1; + intel_nhlt_free(nhlt); + } + return ret; +} + +int snd_intel_dsp_driver_probe(struct pci_dev *pci) +{ + const struct config_entry *cfg; + + if (dsp_driver > 0 && dsp_driver <= SND_INTEL_DSP_DRIVER_LAST) + return dsp_driver; + + /* Intel vendor only */ + if (snd_BUG_ON(pci->vendor != 0x8086)) + return SND_INTEL_DSP_DRIVER_ANY; + + /* + * detect DSP by checking class/subclass/prog-id information + * class=04 subclass 03 prog-if 00: no DSP, use legacy driver + * class=04 subclass 01 prog-if 00: DSP is present + * (and may be required e.g. for DMIC or SSP support) + * class=04 subclass 03 prog-if 80: use DSP or legacy mode + */ + if (pci->class == 0x040300) + return SND_INTEL_DSP_DRIVER_LEGACY; + if (pci->class != 0x040100 && pci->class != 0x040380) { + dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, selecting HDA legacy driver\n", pci->class); + return SND_INTEL_DSP_DRIVER_LEGACY; + } + + dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); + + /* find the configuration for the specific device */ + cfg = snd_intel_dsp_find_config(pci, config_table, ARRAY_SIZE(config_table)); + if (!cfg) + return SND_INTEL_DSP_DRIVER_ANY; + + if (cfg->flags & FLAG_SOF) { + if (cfg->flags & FLAG_SOF_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; + } + } else { + return SND_INTEL_DSP_DRIVER_SOF; + } + } + + 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"); diff --git a/sound/hda/intel-nhlt.c b/sound/hda/intel-nhlt.c index daede96f28ee..097ff6c10099 100644 --- a/sound/hda/intel-nhlt.c +++ b/sound/hda/intel-nhlt.c @@ -102,6 +102,3 @@ int intel_nhlt_get_dmic_geo(struct device *dev, struct nhlt_acpi_table *nhlt) return dmic_geo; } EXPORT_SYMBOL_GPL(intel_nhlt_get_dmic_geo); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel NHLT driver"); diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig index b690ed937cbe..6ffa48dd5983 100644 --- a/sound/isa/Kconfig +++ b/sound/isa/Kconfig @@ -2,22 +2,22 @@ # ALSA ISA drivers config SND_WSS_LIB - tristate - select SND_PCM + tristate + select SND_PCM select SND_TIMER config SND_SB_COMMON - tristate + tristate config SND_SB8_DSP - tristate - select SND_PCM - select SND_SB_COMMON + tristate + select SND_PCM + select SND_SB_COMMON config SND_SB16_DSP - tristate - select SND_PCM - select SND_SB_COMMON + tristate + select SND_PCM + select SND_SB_COMMON menuconfig SND_ISA bool "ISA sound devices" diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig index 8a33402fd415..b497b803c834 100644 --- a/sound/mips/Kconfig +++ b/sound/mips/Kconfig @@ -14,15 +14,15 @@ config SND_SGI_O2 tristate "SGI O2 Audio" depends on SGI_IP32 select SND_PCM - help - Sound support for the SGI O2 Workstation. + help + Sound support for the SGI O2 Workstation. config SND_SGI_HAL2 - tristate "SGI HAL2 Audio" - depends on SGI_HAS_HAL2 + tristate "SGI HAL2 Audio" + depends on SGI_HAS_HAL2 select SND_PCM - help - Sound support for the SGI Indy and Indigo2 Workstation. + help + Sound support for the SGI Indy and Indigo2 Workstation. endif # SND_MIPS diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index 7630f808d087..93bc9bef7641 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -217,7 +217,7 @@ config SND_CMIPCI will be called snd-cmipci. config SND_OXYGEN_LIB - tristate + tristate config SND_OXYGEN tristate "C-Media 8786, 8787, 8788 (Oxygen)" diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index dae47a45b2b8..bd48335d09d7 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -12,7 +12,7 @@ config SND_HDA_INTEL tristate "HD Audio PCI" depends on SND_PCI select SND_HDA - select SND_INTEL_NHLT if ACPI + select SND_INTEL_DSP_CONFIG help Say Y here to include support for Intel "High Definition Audio" (Azalia) and its compatible devices. @@ -23,15 +23,6 @@ config SND_HDA_INTEL To compile this driver as a module, choose M here: the module will be called snd-hda-intel. -config SND_HDA_INTEL_DETECT_DMIC - bool "DMIC detection and probe abort" - depends on SND_HDA_INTEL - help - Say Y to detect digital microphones on SKL+ devices. DMICs - cannot be handled by the HDaudio legacy driver and are - currently only supported by the SOF driver. - If unsure say N. - config SND_HDA_TEGRA tristate "NVIDIA Tegra HD Audio" depends on ARCH_TEGRA diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index cf53fbd872ee..2a9d87ff2e1c 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -46,7 +46,7 @@ #include <sound/initval.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> -#include <sound/intel-nhlt.h> +#include <sound/intel-dsp-config.h> #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> #include <linux/firmware.h> @@ -124,7 +124,7 @@ static char *patch[SNDRV_CARDS]; static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = CONFIG_SND_HDA_INPUT_BEEP_MODE}; #endif -static bool dmic_detect = IS_ENABLED(CONFIG_SND_HDA_INTEL_DETECT_DMIC); +static bool dsp_driver = 1; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); @@ -159,8 +159,9 @@ module_param_array(beep_mode, bool, NULL, 0444); MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " "(0=off, 1=on) (default=1)."); #endif -module_param(dmic_detect, bool, 0444); -MODULE_PARM_DESC(dmic_detect, "DMIC detect on SKL+ platforms"); +module_param(dsp_driver, bool, 0444); +MODULE_PARM_DESC(dsp_driver, "Allow DSP driver selection (bypass this driver) " + "(0=off, 1=on) (default=1)"); #ifdef CONFIG_PM static int param_set_xint(const char *val, const struct kernel_param *kp); @@ -1280,11 +1281,17 @@ static void init_vga_switcheroo(struct azx *chip) { struct hda_intel *hda = container_of(chip, struct hda_intel, chip); struct pci_dev *p = get_bound_vga(chip->pci); + struct pci_dev *parent; if (p) { dev_info(chip->card->dev, "Handle vga_switcheroo audio client\n"); hda->use_vga_switcheroo = 1; - chip->bus.keep_power = 1; /* cleared in either gpu_bound op or codec probe */ + + /* cleared in either gpu_bound op or codec probe, or when its + * upstream port has _PR3 (i.e. dGPU). + */ + parent = pci_upstream_bridge(p); + chip->bus.keep_power = parent ? !pci_pr3_present(parent) : 1; chip->driver_caps |= AZX_DCAPS_PM_RUNTIME; pci_dev_put(p); } @@ -2020,25 +2027,6 @@ static const struct hda_controller_ops pci_hda_ops = { .position_check = azx_position_check, }; -static int azx_check_dmic(struct pci_dev *pci, struct azx *chip) -{ - struct nhlt_acpi_table *nhlt; - int ret = 0; - - if (chip->driver_type == AZX_DRIVER_SKL && - pci->class != 0x040300) { - nhlt = intel_nhlt_init(&pci->dev); - if (nhlt) { - if (intel_nhlt_get_dmic_geo(&pci->dev, nhlt)) { - ret = -ENODEV; - dev_info(&pci->dev, "Digital mics found on Skylake+ platform, aborting probe\n"); - } - intel_nhlt_free(nhlt); - } - } - return ret; -} - static int azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -2056,6 +2044,16 @@ static int azx_probe(struct pci_dev *pci, return -ENOENT; } + /* + * stop probe if another Intel's DSP driver should be activated + */ + if (dsp_driver) { + err = snd_intel_dsp_driver_probe(pci); + if (err != SND_INTEL_DSP_DRIVER_ANY && + err != SND_INTEL_DSP_DRIVER_LEGACY) + return -ENODEV; + } + err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); if (err < 0) { @@ -2069,17 +2067,6 @@ static int azx_probe(struct pci_dev *pci, card->private_data = chip; hda = container_of(chip, struct hda_intel, chip); - /* - * stop probe if digital microphones detected on Skylake+ platform - * with the DSP enabled. This is an opt-in behavior defined at build - * time or at run-time with a module parameter - */ - if (dmic_detect) { - err = azx_check_dmic(pci, chip); - if (err < 0) - goto out_free; - } - pci_set_drvdata(pci, card); err = register_vga_switcheroo(chip); diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig index 2333efac758a..8039a8febefa 100644 --- a/sound/soc/cirrus/Kconfig +++ b/sound/soc/cirrus/Kconfig @@ -33,13 +33,13 @@ config SND_EP93XX_SOC_AC97 select SND_SOC_AC97_BUS config SND_EP93XX_SOC_SNAPPERCL15 - tristate "SoC Audio support for Bluewater Systems Snapper CL15 module" - depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15 && I2C - select SND_EP93XX_SOC_I2S - select SND_SOC_TLV320AIC23_I2C - help - Say Y or M here if you want to add support for I2S audio on the - Bluewater Systems Snapper CL15 module. + tristate "SoC Audio support for Bluewater Systems Snapper CL15 module" + depends on SND_EP93XX_SOC && MACH_SNAPPER_CL15 && I2C + select SND_EP93XX_SOC_I2S + select SND_SOC_TLV320AIC23_I2C + help + Say Y or M here if you want to add support for I2S audio on the + Bluewater Systems Snapper CL15 module. config SND_EP93XX_SOC_SIMONE tristate "SoC Audio support for Simplemachines Sim.One board" diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 229cc89f8c5a..ef9d73b89623 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -257,16 +257,16 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM9705 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9712 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) select SND_SOC_WM9713 if (SND_SOC_AC97_BUS || SND_SOC_AC97_BUS_NEW) - help - Normally ASoC codec drivers are only built if a machine driver which - uses them is also built since they are only usable with a machine - driver. Selecting this option will allow these drivers to be built - without an explicit machine driver for test and development purposes. + help + Normally ASoC codec drivers are only built if a machine driver which + uses them is also built since they are only usable with a machine + driver. Selecting this option will allow these drivers to be built + without an explicit machine driver for test and development purposes. Support for the bus types used to access the codecs to be built must be selected separately. - If unsure select "N". + If unsure select "N". config SND_SOC_88PM860X tristate @@ -570,8 +570,8 @@ config SND_SOC_CS42XX8_I2C # Cirrus Logic CS43130 HiFi DAC config SND_SOC_CS43130 - tristate "Cirrus Logic CS43130 CODEC" - depends on I2C + tristate "Cirrus Logic CS43130 CODEC" + depends on I2C config SND_SOC_CS4341 tristate "Cirrus Logic CS4341 CODEC" @@ -643,19 +643,19 @@ config SND_SOC_L3 tristate config SND_SOC_DA7210 - tristate + tristate config SND_SOC_DA7213 - tristate + tristate config SND_SOC_DA7218 tristate config SND_SOC_DA7219 - tristate + tristate config SND_SOC_DA732X - tristate + tristate config SND_SOC_DA9055 tristate @@ -717,7 +717,7 @@ config SND_SOC_INNO_RK3036 select REGMAP_MMIO config SND_SOC_ISABELLE - tristate + tristate config SND_SOC_LM49453 tristate @@ -988,7 +988,7 @@ config SND_SOC_RT5640 tristate config SND_SOC_RT5645 - tristate + tristate config SND_SOC_RT5651 tristate @@ -1220,7 +1220,7 @@ config SND_SOC_UDA134X tristate config SND_SOC_UDA1380 - tristate + tristate depends on I2C config SND_SOC_WCD9335 @@ -1348,7 +1348,7 @@ config SND_SOC_WM8904 depends on I2C config SND_SOC_WM8940 - tristate + tristate config SND_SOC_WM8955 tristate diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 01c99750212a..3f9e65c10d5b 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -113,7 +113,7 @@ config SND_SOC_INTEL_SKYLAKE select SND_SOC_INTEL_CNL select SND_SOC_INTEL_CFL help - This is a backwards-compatible option to select all devices + This is a backwards-compatible option to select all devices supported by the Intel SST/Skylake driver. This option is no longer recommended and will be deprecated when the SOF driver is introduced. Distributions should explicitly @@ -215,7 +215,7 @@ config SND_SOC_INTEL_SKYLAKE_COMMON select SND_SOC_INTEL_SST select SND_SOC_HDAC_HDA if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC select SND_SOC_ACPI_INTEL_MATCH - select SND_INTEL_NHLT if ACPI + select SND_INTEL_DSP_CONFIG help If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ GeminiLake or CannonLake platform with the DSP enabled in the BIOS diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 5c27f7ab4a5f..882ff36a7c9c 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -3,13 +3,13 @@ menuconfig SND_SOC_INTEL_MACH bool "Intel Machine drivers" depends on SND_SOC_INTEL_SST_TOPLEVEL || SND_SOC_SOF_INTEL_TOPLEVEL help - Intel ASoC Machine Drivers. If you have a Intel machine that - has an audio controller with a DSP and I2S or DMIC port, then - enable this option by saying Y + Intel ASoC Machine Drivers. If you have a Intel machine that + has an audio controller with a DSP and I2S or DMIC port, then + enable this option by saying Y - Note that the answer to this question doesn't directly affect the - kernel: saying N will just cause the configurator to skip all - the questions about Intel ASoC machine drivers. + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Intel ASoC machine drivers. if SND_SOC_INTEL_MACH @@ -114,11 +114,11 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH depends on X86_INTEL_LPSS || COMPILE_TEST select SND_SOC_ACPI select SND_SOC_RT5670 - help - This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell - platforms with RT5672 audio codec. - Say Y or m if you have such a device. This is a recommended option. - If unsure select "N". + help + This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell + platforms with RT5672 audio codec. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". config SND_SOC_INTEL_CHT_BSW_RT5645_MACH tristate "Cherrytrail & Braswell with RT5645/5650 codec" @@ -311,20 +311,20 @@ config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH If unsure select "N". config SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH - tristate "KBL with RT5663, RT5514 and MAX98927 in I2S Mode" + tristate "KBL with RT5663, RT5514 and MAX98927 in I2S Mode" depends on I2C && ACPI depends on MFD_INTEL_LPSS || COMPILE_TEST - depends on SPI - select SND_SOC_RT5663 - select SND_SOC_RT5514 - select SND_SOC_RT5514_SPI - select SND_SOC_MAX98927 - select SND_SOC_HDAC_HDMI - help - This adds support for ASoC Onboard Codec I2S machine driver. This will - create an alsa sound card for RT5663 + RT5514 + MAX98927. - Say Y or m if you have such a device. This is a recommended option. - If unsure select "N". + depends on SPI + select SND_SOC_RT5663 + select SND_SOC_RT5514 + select SND_SOC_RT5514_SPI + select SND_SOC_MAX98927 + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5663 + RT5514 + MAX98927. + Say Y or m if you have such a device. This is a recommended option. + If unsure select "N". config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH tristate "KBL with DA7219 and MAX98357A in I2S Mode" @@ -393,7 +393,7 @@ config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH help This adds support for ASoC machine driver for Intel platforms SKL/KBL/BXT/APL with iDisp, HDA audio codecs. - Say Y or m if you have such a device. This is a recommended option. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". endif ## SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC || SND_SOC_SOF_HDA_AUDIO_CODEC diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 141dbbf975ac..58ba3e9469ba 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -27,6 +27,7 @@ #include <sound/hda_i915.h> #include <sound/hda_codec.h> #include <sound/intel-nhlt.h> +#include <sound/intel-dsp-config.h> #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" @@ -987,22 +988,10 @@ static int skl_probe(struct pci_dev *pci, switch (skl_pci_binding) { case SND_SKL_PCI_BIND_AUTO: - /* - * detect DSP by checking class/subclass/prog-id information - * class=04 subclass 03 prog-if 00: no DSP, use legacy driver - * class=04 subclass 01 prog-if 00: DSP is present - * (and may be required e.g. for DMIC or SSP support) - * class=04 subclass 03 prog-if 80: use DSP or legacy mode - */ - if (pci->class == 0x040300) { - dev_info(&pci->dev, "The DSP is not enabled on this platform, aborting probe\n"); + err = snd_intel_dsp_driver_probe(pci); + if (err != SND_INTEL_DSP_DRIVER_ANY && + err != SND_INTEL_DSP_DRIVER_SST) return -ENODEV; - } - if (pci->class != 0x040100 && pci->class != 0x040380) { - dev_err(&pci->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, aborting probe\n", pci->class); - return -ENODEV; - } - dev_info(&pci->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); break; case SND_SKL_PCI_BIND_LEGACY: dev_info(&pci->dev, "Module parameter forced binding with HDaudio legacy, aborting probe\n"); diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 213d4dab0346..295cfffa4646 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -190,14 +190,14 @@ config SND_PXA2XX_SOC_MAGICIAN HTC Magician. config SND_PXA2XX_SOC_MIOA701 - tristate "SoC Audio support for MIO A701" - depends on SND_PXA2XX_SOC && MACH_MIOA701 + tristate "SoC Audio support for MIO A701" + depends on SND_PXA2XX_SOC && MACH_MIOA701 depends on AC97_BUS=n - select SND_PXA2XX_SOC_AC97 - select SND_SOC_WM9713 - help - Say Y if you want to add support for SoC audio on the - MIO A701. + select SND_PXA2XX_SOC_AC97 + select SND_SOC_WM9713 + help + Say Y if you want to add support for SoC audio on the + MIO A701. config SND_PXA2XX_SOC_IMOTE2 tristate "SoC Audio support for IMote 2" @@ -205,7 +205,7 @@ config SND_PXA2XX_SOC_IMOTE2 select SND_PXA2XX_SOC_I2S select SND_SOC_WM8940 help - Say Y if you want to add support for SoC audio on the + Say Y if you want to add support for SoC audio on the IMote 2. config SND_MMP_SOC_BROWNSTONE diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 60086858e920..6530d2462a9e 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -3,8 +3,8 @@ config SND_SOC_QCOM tristate "ASoC support for QCOM platforms" depends on ARCH_QCOM || COMPILE_TEST help - Say Y or M if you want to add support to use audio devices - in Qualcomm Technologies SOC-based platforms. + Say Y or M if you want to add support to use audio devices + in Qualcomm Technologies SOC-based platforms. config SND_SOC_LPASS_CPU tristate @@ -30,17 +30,17 @@ config SND_SOC_STORM select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help - Say Y or M if you want add support for SoC audio on the - Qualcomm Technologies IPQ806X-based Storm board. + Say Y or M if you want add support for SoC audio on the + Qualcomm Technologies IPQ806X-based Storm board. config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" depends on SND_SOC_QCOM select SND_SOC_LPASS_APQ8016 help - Support for Qualcomm Technologies LPASS audio block in - APQ8016 SOC-based systems. - Say Y if you want to use audio devices on MI2S. + Support for Qualcomm Technologies LPASS audio block in + APQ8016 SOC-based systems. + Say Y if you want to use audio devices on MI2S. config SND_SOC_QCOM_COMMON tristate @@ -93,9 +93,9 @@ config SND_SOC_MSM8996 select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON help - Support for Qualcomm Technologies LPASS audio block in - APQ8096 SoC-based systems. - Say Y if you want to use audio device on this SoCs + Support for Qualcomm Technologies LPASS audio block in + APQ8096 SoC-based systems. + Say Y if you want to use audio device on this SoCs config SND_SOC_SDM845 tristate "SoC Machine driver for SDM845 boards" diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 638983123d8f..9304177de78a 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -195,10 +195,10 @@ config SND_SOC_ODROID Say Y here to enable audio support for the Odroid XU3/XU4. config SND_SOC_ARNDALE_RT5631_ALC5631 - tristate "Audio support for RT5631(ALC5631) on Arndale Board" - depends on I2C - select SND_SAMSUNG_I2S - select SND_SOC_RT5631 + tristate "Audio support for RT5631(ALC5631) on Arndale Board" + depends on I2C + select SND_SAMSUNG_I2S + select SND_SOC_RT5631 config SND_SOC_SAMSUNG_TM2_WM5110 tristate "SoC I2S Audio support for WM5110 on TM2 board" diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 5acae75f5750..6315fba8ce71 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -5,9 +5,9 @@ config SND_SOC_SOF_IMX_TOPLEVEL depends on ARM64|| COMPILE_TEST depends on SND_SOC_SOF_OF help - This adds support for Sound Open Firmware for NXP i.MX platforms. - Say Y if you have such a device. - If unsure select "N". + This adds support for Sound Open Firmware for NXP i.MX platforms. + Say Y if you have such a device. + If unsure select "N". if SND_SOC_SOF_IMX_TOPLEVEL @@ -16,8 +16,8 @@ config SND_SOC_SOF_IMX8 depends on IMX_SCU depends on IMX_DSP help - This adds support for Sound Open Firmware for NXP i.MX8 platforms - Say Y if you have such a device. - If unsure select "N". + This adds support for Sound Open Firmware for NXP i.MX8 platforms + Say Y if you have such a device. + If unsure select "N". endif ## SND_SOC_SOF_IMX_IMX_TOPLEVEL diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index d62f51d33be1..05f4aed13af9 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -36,7 +36,7 @@ config SND_SOC_SOF_INTEL_PCI config SND_SOC_SOF_INTEL_HIFI_EP_IPC tristate help - This option is not user-selectable but automagically handled by + This option is not user-selectable but automagically handled by 'select' statements at a higher level config SND_SOC_SOF_INTEL_ATOM_HIFI_EP @@ -217,31 +217,31 @@ config SND_SOC_SOF_COMETLAKE_H_SUPPORT config SND_SOC_SOF_TIGERLAKE_SUPPORT bool "SOF support for Tigerlake" help - This adds support for Sound Open Firmware for Intel(R) platforms - using the Tigerlake processors. - Say Y if you have such a device. - If unsure select "N". + This adds support for Sound Open Firmware for Intel(R) platforms + using the Tigerlake processors. + Say Y if you have such a device. + If unsure select "N". config SND_SOC_SOF_TIGERLAKE tristate select SND_SOC_SOF_HDA_COMMON help - This option is not user-selectable but automagically handled by + This option is not user-selectable but automagically handled by 'select' statements at a higher level config SND_SOC_SOF_ELKHARTLAKE_SUPPORT bool "SOF support for ElkhartLake" help - This adds support for Sound Open Firmware for Intel(R) platforms - using the ElkhartLake processors. - Say Y if you have such a device. - If unsure select "N". + This adds support for Sound Open Firmware for Intel(R) platforms + using the ElkhartLake processors. + Say Y if you have such a device. + If unsure select "N". config SND_SOC_SOF_ELKHARTLAKE tristate select SND_SOC_SOF_HDA_COMMON help - This option is not user-selectable but automagically handled by + This option is not user-selectable but automagically handled by 'select' statements at a higher level config SND_SOC_SOF_HDA_COMMON @@ -296,7 +296,7 @@ config SND_SOC_SOF_HDA tristate select SND_HDA_EXT_CORE if SND_SOC_SOF_HDA_LINK select SND_SOC_HDAC_HDA if SND_SOC_SOF_HDA_AUDIO_CODEC - select SND_INTEL_NHLT if ACPI + select SND_INTEL_DSP_CONFIG help This option is not user-selectable but automagically handled by 'select' statements at a higher level diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index d66412a77873..3a9e0e2a150d 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/pm_runtime.h> +#include <sound/intel-dsp-config.h> #include <sound/soc-acpi.h> #include <sound/soc-acpi-intel-match.h> #include <sound/sof.h> @@ -277,6 +278,11 @@ static int sof_pci_probe(struct pci_dev *pci, const struct snd_sof_dsp_ops *ops; int ret; + ret = snd_intel_dsp_driver_probe(pci); + if (ret != SND_INTEL_DSP_DRIVER_ANY && + ret != SND_INTEL_DSP_DRIVER_SOF) + return -ENODEV; + dev_dbg(&pci->dev, "PCI DSP detected"); /* get ops for platform */ diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig index 69973179ef15..1d3586b68db7 100644 --- a/sound/soc/xilinx/Kconfig +++ b/sound/soc/xilinx/Kconfig @@ -9,15 +9,15 @@ config SND_SOC_XILINX_I2S encapsulates PCM in AES format and sends AES data. config SND_SOC_XILINX_AUDIO_FORMATTER - tristate "Audio support for the the Xilinx audio formatter" - help - Select this option to enable Xilinx audio formatter - support. This provides DMA platform device support for - audio functionality. + tristate "Audio support for the the Xilinx audio formatter" + help + Select this option to enable Xilinx audio formatter + support. This provides DMA platform device support for + audio functionality. config SND_SOC_XILINX_SPDIF - tristate "Audio support for the the Xilinx SPDIF" - help - Select this option to enable Xilinx SPDIF Audio. - This provides playback and capture of SPDIF audio in - AES format. + tristate "Audio support for the the Xilinx SPDIF" + help + Select this option to enable Xilinx SPDIF Audio. + This provides playback and capture of SPDIF audio in + AES format. diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig index a7842e4b791c..a23d4f13ca19 100644 --- a/sound/soc/zte/Kconfig +++ b/sound/soc/zte/Kconfig @@ -18,9 +18,9 @@ config ZX_I2S ZTE ZX I2S interface config ZX_TDM - tristate "ZTE ZX TDM Driver Support" - depends on COMMON_CLK - select SND_SOC_GENERIC_DMAENGINE_PCM - help - Say Y or M if you want to add support for codecs attached to the - ZTE ZX TDM interface + tristate "ZTE ZX TDM Driver Support" + depends on COMMON_CLK + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for codecs attached to the + ZTE ZX TDM interface diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index e2c53a0841da..059242f15d75 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -107,24 +107,24 @@ config SND_USB_US122L will be called snd-usb-us122l. config SND_USB_6FIRE - tristate "TerraTec DMX 6Fire USB" - select FW_LOADER - select BITREVERSE - select SND_RAWMIDI - select SND_PCM - select SND_VMASTER - help - Say Y here to include support for TerraTec 6fire DMX USB interface. - - You will need firmware files in order to be able to use the device - after it has been coldstarted. An install script for the firmware - and further help can be found at - http://sixfireusb.sourceforge.net + tristate "TerraTec DMX 6Fire USB" + select FW_LOADER + select BITREVERSE + select SND_RAWMIDI + select SND_PCM + select SND_VMASTER + help + Say Y here to include support for TerraTec 6fire DMX USB interface. + + You will need firmware files in order to be able to use the device + after it has been coldstarted. An install script for the firmware + and further help can be found at + http://sixfireusb.sourceforge.net config SND_USB_HIFACE - tristate "M2Tech hiFace USB-SPDIF driver" - select SND_PCM - help + tristate "M2Tech hiFace USB-SPDIF driver" + select SND_PCM + help Select this option to include support for M2Tech hiFace USB-SPDIF interface. diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c index 5fd4e32247a6..cd389d21219a 100644 --- a/sound/x86/intel_hdmi_audio.c +++ b/sound/x86/intel_hdmi_audio.c @@ -1708,10 +1708,8 @@ static int hdmi_lpe_audio_probe(struct platform_device *pdev) /* get resources */ irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "Could not get irq resource: %d\n", irq); + if (irq < 0) return irq; - } res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_mmio) { |