diff options
Diffstat (limited to 'sound')
54 files changed, 3566 insertions, 607 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index bea523a5d852..3eb47d0006a7 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -123,6 +123,7 @@ static int snd_compr_open(struct inode *inode, struct file *f) } runtime->state = SNDRV_PCM_STATE_OPEN; init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->wait); data->stream.runtime = runtime; f->private_data = (void *)data; mutex_lock(&compr->lock); @@ -682,12 +683,34 @@ static int snd_compr_stop(struct snd_compr_stream *stream) if (!retval) { stream->runtime->state = SNDRV_PCM_STATE_SETUP; wake_up(&stream->runtime->sleep); + snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; } return retval; } +static int snd_compress_wait_for_drain(struct snd_compr_stream *stream) +{ + /* + * We are called with lock held. So drop the lock while we wait for + * drain complete notfication from the driver + * + * It is expected that driver will notify the drain completion and then + * stream will be moved to SETUP state, even if draining resulted in an + * error. We can trigger next track after this. + */ + stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + mutex_unlock(&stream->device->lock); + + wait_event(stream->runtime->wait, stream->runtime->drain_wake); + + wake_up(&stream->runtime->sleep); + mutex_lock(&stream->device->lock); + + return 0; +} + static int snd_compr_drain(struct snd_compr_stream *stream) { int retval; @@ -695,11 +718,17 @@ static int snd_compr_drain(struct snd_compr_stream *stream) if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED || stream->runtime->state == SNDRV_PCM_STATE_SETUP) return -EPERM; + + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN); - if (!retval) { - stream->runtime->state = SNDRV_PCM_STATE_DRAINING; + if (retval) { + pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval); wake_up(&stream->runtime->sleep); + return retval; } + + retval = snd_compress_wait_for_drain(stream); + stream->runtime->state = SNDRV_PCM_STATE_SETUP; return retval; } @@ -735,10 +764,16 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream) if (stream->next_track == false) return -EPERM; + stream->runtime->drain_wake = 0; retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN); + if (retval) { + pr_err("Partial drain returned failure\n"); + wake_up(&stream->runtime->sleep); + return retval; + } stream->next_track = false; - return retval; + return snd_compress_wait_for_drain(stream); } static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg) diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index bdf826f4fe0c..51a79218815b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -30,6 +30,7 @@ #include <linux/seq_file.h> #include <asm/uaccess.h> #include <linux/dma-mapping.h> +#include <linux/genalloc.h> #include <linux/moduleparam.h> #include <linux/mutex.h> #include <sound/memalloc.h> @@ -157,6 +158,46 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, dec_snd_pages(pg); dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); } + +/** + * snd_malloc_dev_iram - allocate memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + * @size: number of bytes to allocate from the iram + * + * This function requires iram phandle provided via of_node + */ +void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) +{ + struct device *dev = dmab->dev.dev; + struct gen_pool *pool = NULL; + + if (dev->of_node) + pool = of_get_named_gen_pool(dev->of_node, "iram", 0); + + if (!pool) + return; + + /* Assign the pool into private_data field */ + dmab->private_data = pool; + + dmab->area = (void *)gen_pool_alloc(pool, size); + if (!dmab->area) + return; + + dmab->addr = gen_pool_virt_to_phys(pool, (unsigned long)dmab->area); +} + +/** + * snd_free_dev_iram - free allocated specific memory from on-chip internal ram + * @dmab: buffer allocation record to store the allocated data + */ +void snd_free_dev_iram(struct snd_dma_buffer *dmab) +{ + struct gen_pool *pool = dmab->private_data; + + if (pool && dmab->area) + gen_pool_free(pool, (unsigned long)dmab->area, dmab->bytes); +} #endif /* CONFIG_HAS_DMA */ /* @@ -197,6 +238,16 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->addr = 0; break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_malloc_dev_iram(dmab, size); + if (dmab->area) + break; + /* Internal memory might have limited size and no enough space, + * so if we fail to malloc, try to fetch memory traditionally. + */ + dmab->dev.type = SNDRV_DMA_TYPE_DEV; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); break; @@ -269,6 +320,11 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) snd_free_pages(dmab->area, dmab->bytes); break; #ifdef CONFIG_HAS_DMA +#ifdef CONFIG_GENERIC_ALLOCATOR + case SNDRV_DMA_TYPE_DEV_IRAM: + snd_free_dev_iram(dmab); + break; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); break; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a68d4c6d702c..b71be579c6ec 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3199,6 +3199,14 @@ int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *area) { area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; +#ifdef CONFIG_GENERIC_ALLOCATOR + if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_IRAM) { + area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); + return remap_pfn_range(area, area->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + area->vm_end - area->vm_start, area->vm_page_prot); + } +#endif /* CONFIG_GENERIC_ALLOCATOR */ #ifdef ARCH_HAS_DMA_MMAP_COHERENT if (!substream->ops->page && substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV) diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index ea063e1f8722..b3e274fe4a77 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -11,6 +11,21 @@ config SND_FIREWIRE_LIB tristate depends on SND_PCM +config SND_DICE + tristate "DICE-based DACs (EXPERIMENTAL)" + select SND_HWDEP + select SND_PCM + select SND_FIREWIRE_LIB + help + Say Y here to include support for many DACs based on the DICE + chip family (DICE-II/Jr/Mini) from TC Applied Technologies. + + At the moment, this driver supports playback only. If you + want to use devices that support capturing, use FFADO instead. + + To compile this driver as a module, choose M here: the module + will be called snd-dice. + config SND_FIREWIRE_SPEAKERS tristate "FireWire speakers" select SND_PCM diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile index 460179df5bb5..509955061d30 100644 --- a/sound/firewire/Makefile +++ b/sound/firewire/Makefile @@ -1,10 +1,12 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \ fcp.o cmp.o amdtp.o +snd-dice-objs := dice.o snd-firewire-speakers-objs := speakers.o snd-isight-objs := isight.o snd-scs1x-objs := scs1x.o obj-$(CONFIG_SND_FIREWIRE_LIB) += snd-firewire-lib.o +obj-$(CONFIG_SND_DICE) += snd-dice.o obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o obj-$(CONFIG_SND_ISIGHT) += snd-isight.o obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index ea995af6d049..d3226892ad6b 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -42,9 +42,6 @@ static void pcm_period_tasklet(unsigned long data); int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, enum cip_out_flags flags) { - if (flags != CIP_NONBLOCKING) - return -EINVAL; - s->unit = fw_unit_get(unit); s->flags = flags; s->context = ERR_PTR(-1); @@ -62,73 +59,91 @@ EXPORT_SYMBOL(amdtp_out_stream_init); */ void amdtp_out_stream_destroy(struct amdtp_out_stream *s) { - WARN_ON(!IS_ERR(s->context)); + WARN_ON(amdtp_out_stream_running(s)); mutex_destroy(&s->mutex); fw_unit_put(s->unit); } EXPORT_SYMBOL(amdtp_out_stream_destroy); +const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = { + [CIP_SFC_32000] = 8, + [CIP_SFC_44100] = 8, + [CIP_SFC_48000] = 8, + [CIP_SFC_88200] = 16, + [CIP_SFC_96000] = 16, + [CIP_SFC_176400] = 32, + [CIP_SFC_192000] = 32, +}; +EXPORT_SYMBOL(amdtp_syt_intervals); + /** - * amdtp_out_stream_set_rate - set the sample rate + * amdtp_out_stream_set_parameters - set stream parameters * @s: the AMDTP output stream to configure * @rate: the sample rate + * @pcm_channels: the number of PCM samples in each data block, to be encoded + * as AM824 multi-bit linear audio + * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) * - * The sample rate must be set before the stream is started, and must not be + * The parameters must be set before the stream is started, and must not be * changed while the stream is running. */ -void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate) +void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports) { - static const struct { - unsigned int rate; - unsigned int syt_interval; - } rate_info[] = { - [CIP_SFC_32000] = { 32000, 8, }, - [CIP_SFC_44100] = { 44100, 8, }, - [CIP_SFC_48000] = { 48000, 8, }, - [CIP_SFC_88200] = { 88200, 16, }, - [CIP_SFC_96000] = { 96000, 16, }, - [CIP_SFC_176400] = { 176400, 32, }, - [CIP_SFC_192000] = { 192000, 32, }, + static const unsigned int rates[] = { + [CIP_SFC_32000] = 32000, + [CIP_SFC_44100] = 44100, + [CIP_SFC_48000] = 48000, + [CIP_SFC_88200] = 88200, + [CIP_SFC_96000] = 96000, + [CIP_SFC_176400] = 176400, + [CIP_SFC_192000] = 192000, }; unsigned int sfc; - if (WARN_ON(!IS_ERR(s->context))) + if (WARN_ON(amdtp_out_stream_running(s))) return; - for (sfc = 0; sfc < ARRAY_SIZE(rate_info); ++sfc) - if (rate_info[sfc].rate == rate) { - s->sfc = sfc; - s->syt_interval = rate_info[sfc].syt_interval; - return; - } + for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc) + if (rates[sfc] == rate) + goto sfc_found; WARN_ON(1); + return; + +sfc_found: + s->dual_wire = (s->flags & CIP_HI_DUALWIRE) && sfc > CIP_SFC_96000; + if (s->dual_wire) { + sfc -= 2; + rate /= 2; + pcm_channels *= 2; + } + s->sfc = sfc; + s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8); + s->pcm_channels = pcm_channels; + s->midi_ports = midi_ports; + + s->syt_interval = amdtp_syt_intervals[sfc]; + + /* default buffering in the device */ + s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + if (s->flags & CIP_BLOCKING) + /* additional buffering needed to adjust for no-data packets */ + s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate; } -EXPORT_SYMBOL(amdtp_out_stream_set_rate); +EXPORT_SYMBOL(amdtp_out_stream_set_parameters); /** * amdtp_out_stream_get_max_payload - get the stream's packet size * @s: the AMDTP output stream * * This function must not be called before the stream has been configured - * with amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(). + * with amdtp_out_stream_set_parameters(). */ unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s) { - static const unsigned int max_data_blocks[] = { - [CIP_SFC_32000] = 4, - [CIP_SFC_44100] = 6, - [CIP_SFC_48000] = 6, - [CIP_SFC_88200] = 12, - [CIP_SFC_96000] = 12, - [CIP_SFC_176400] = 23, - [CIP_SFC_192000] = 24, - }; - - s->data_block_quadlets = s->pcm_channels; - s->data_block_quadlets += DIV_ROUND_UP(s->midi_ports, 8); - - return 8 + max_data_blocks[s->sfc] * 4 * s->data_block_quadlets; + return 8 + s->syt_interval * s->data_block_quadlets * 4; } EXPORT_SYMBOL(amdtp_out_stream_get_max_payload); @@ -138,19 +153,26 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, static void amdtp_write_s32(struct amdtp_out_stream *s, struct snd_pcm_substream *pcm, __be32 *buffer, unsigned int frames); +static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); +static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames); /** * amdtp_out_stream_set_pcm_format - set the PCM format * @s: the AMDTP output stream to configure * @format: the format of the ALSA PCM device * - * The sample format must be set before the stream is started, and must not be - * changed while the stream is running. + * The sample format must be set after the other paramters (rate/PCM channels/ + * MIDI) and before the stream is started, and must not be changed while the + * stream is running. */ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, snd_pcm_format_t format) { - if (WARN_ON(!IS_ERR(s->context))) + if (WARN_ON(amdtp_out_stream_running(s))) return; switch (format) { @@ -158,10 +180,16 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s, WARN_ON(1); /* fall through */ case SNDRV_PCM_FORMAT_S16: - s->transfer_samples = amdtp_write_s16; + if (s->dual_wire) + s->transfer_samples = amdtp_write_s16_dualwire; + else + s->transfer_samples = amdtp_write_s16; break; case SNDRV_PCM_FORMAT_S32: - s->transfer_samples = amdtp_write_s32; + if (s->dual_wire) + s->transfer_samples = amdtp_write_s32_dualwire; + else + s->transfer_samples = amdtp_write_s32; break; } } @@ -248,7 +276,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s, s->last_syt_offset = syt_offset; if (syt_offset < TICKS_PER_CYCLE) { - syt_offset += TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE; + syt_offset += s->transfer_delay; syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12; syt += syt_offset % TICKS_PER_CYCLE; @@ -268,7 +296,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s, channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; frame_step = s->data_block_quadlets - channels; @@ -294,7 +322,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, channels = s->pcm_channels; src = (void *)runtime->dma_area + - s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frames_to_bytes(runtime, s->pcm_buffer_pointer); remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer; frame_step = s->data_block_quadlets - channels; @@ -310,6 +338,68 @@ static void amdtp_write_s16(struct amdtp_out_stream *s, } } +static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + const u32 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frame_adjust_1 = channels - 1; + frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + + channels /= 2; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_1; + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src >> 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_2; + } +} + +static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s, + struct snd_pcm_substream *pcm, + __be32 *buffer, unsigned int frames) +{ + struct snd_pcm_runtime *runtime = pcm->runtime; + unsigned int channels, frame_adjust_1, frame_adjust_2, i, c; + const u16 *src; + + channels = s->pcm_channels; + src = (void *)runtime->dma_area + + s->pcm_buffer_pointer * (runtime->frame_bits / 8); + frame_adjust_1 = channels - 1; + frame_adjust_2 = 1 - (s->data_block_quadlets - channels); + + channels /= 2; + for (i = 0; i < frames; ++i) { + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src << 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_1; + for (c = 0; c < channels; ++c) { + *buffer = cpu_to_be32((*src << 8) | 0x40000000); + src++; + buffer += 2; + } + buffer -= frame_adjust_2; + } +} + static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s, __be32 *buffer, unsigned int frames) { @@ -344,8 +434,17 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) return; index = s->packet_index; - data_blocks = calculate_data_blocks(s); syt = calculate_syt(s, cycle); + if (!(s->flags & CIP_BLOCKING)) { + data_blocks = calculate_data_blocks(s); + } else { + if (syt != 0xffff) { + data_blocks = s->syt_interval; + } else { + data_blocks = 0; + syt = 0xffffff; + } + } buffer = s->buffer.packets[index].buffer; buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) | @@ -386,6 +485,9 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle) s->packet_index = index; if (pcm) { + if (s->dual_wire) + data_blocks *= 2; + ptr = s->pcm_buffer_pointer + data_blocks; if (ptr >= pcm->runtime->buffer_size) ptr -= pcm->runtime->buffer_size; @@ -455,9 +557,8 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s) * @speed: firewire speed code * * The stream cannot be started until it has been configured with - * amdtp_out_stream_set_hw_params(), amdtp_out_stream_set_pcm(), and - * amdtp_out_stream_set_midi(); and it must be started before any - * PCM or MIDI device can be started. + * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(), + * and it must be started before any PCM or MIDI device can be started. */ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) { @@ -477,7 +578,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed) mutex_lock(&s->mutex); - if (WARN_ON(!IS_ERR(s->context) || + if (WARN_ON(amdtp_out_stream_running(s) || (!s->pcm_channels && !s->midi_ports))) { err = -EBADFD; goto err_unlock; @@ -573,7 +674,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s) { mutex_lock(&s->mutex); - if (IS_ERR(s->context)) { + if (!amdtp_out_stream_running(s)) { mutex_unlock(&s->mutex); return; } diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h index f6103d68c4b1..839ebf812d79 100644 --- a/sound/firewire/amdtp.h +++ b/sound/firewire/amdtp.h @@ -1,6 +1,7 @@ #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED #define SOUND_FIREWIRE_AMDTP_H_INCLUDED +#include <linux/err.h> #include <linux/interrupt.h> #include <linux/mutex.h> #include "packets-buffer.h" @@ -11,9 +12,18 @@ * sample_rate/8000 samples, with rounding up or down to adjust * for clock skew and left-over fractional samples. This should * be used if supported by the device. + * @CIP_BLOCKING: In blocking mode, each packet contains either zero or + * SYT_INTERVAL samples, with these two types alternating so that + * the overall sample rate comes out right. + * @CIP_HI_DUALWIRE: At rates above 96 kHz, pretend that the stream runs + * at half the actual sample rate with twice the number of channels; + * two samples of a channel are stored consecutively in the packet. + * Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size. */ enum cip_out_flags { - CIP_NONBLOCKING = 0, + CIP_NONBLOCKING = 0x00, + CIP_BLOCKING = 0x01, + CIP_HI_DUALWIRE = 0x02, }; /** @@ -27,6 +37,7 @@ enum cip_sfc { CIP_SFC_96000 = 4, CIP_SFC_176400 = 5, CIP_SFC_192000 = 6, + CIP_SFC_COUNT }; #define AMDTP_OUT_PCM_FORMAT_BITS (SNDRV_PCM_FMTBIT_S16 | \ @@ -43,6 +54,7 @@ struct amdtp_out_stream { struct mutex mutex; enum cip_sfc sfc; + bool dual_wire; unsigned int data_block_quadlets; unsigned int pcm_channels; unsigned int midi_ports; @@ -51,6 +63,7 @@ struct amdtp_out_stream { __be32 *buffer, unsigned int frames); unsigned int syt_interval; + unsigned int transfer_delay; unsigned int source_node_id_field; struct iso_packets_buffer buffer; @@ -74,7 +87,10 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit, enum cip_out_flags flags); void amdtp_out_stream_destroy(struct amdtp_out_stream *s); -void amdtp_out_stream_set_rate(struct amdtp_out_stream *s, unsigned int rate); +void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s, + unsigned int rate, + unsigned int pcm_channels, + unsigned int midi_ports); unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s); int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed); @@ -87,31 +103,11 @@ void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s); unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s); void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s); -/** - * amdtp_out_stream_set_pcm - configure format of PCM samples - * @s: the AMDTP output stream to be configured - * @pcm_channels: the number of PCM samples in each data block, to be encoded - * as AM824 multi-bit linear audio - * - * This function must not be called while the stream is running. - */ -static inline void amdtp_out_stream_set_pcm(struct amdtp_out_stream *s, - unsigned int pcm_channels) -{ - s->pcm_channels = pcm_channels; -} +extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT]; -/** - * amdtp_out_stream_set_midi - configure format of MIDI data - * @s: the AMDTP output stream to be configured - * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels) - * - * This function must not be called while the stream is running. - */ -static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s, - unsigned int midi_ports) +static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s) { - s->midi_ports = midi_ports; + return !IS_ERR(s->context); } /** diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c index 645cb0ba4293..efdbf585e404 100644 --- a/sound/firewire/cmp.c +++ b/sound/firewire/cmp.c @@ -48,9 +48,6 @@ static int pcr_modify(struct cmp_connection *c, int (*check)(struct cmp_connection *c, __be32 pcr), enum bus_reset_handling bus_reset_handling) { - struct fw_device *device = fw_parent_device(c->resources.unit); - int generation = c->resources.generation; - int rcode, errors = 0; __be32 old_arg, buffer[2]; int err; @@ -59,36 +56,31 @@ static int pcr_modify(struct cmp_connection *c, old_arg = buffer[0]; buffer[1] = modify(c, buffer[0]); - rcode = fw_run_transaction( - device->card, TCODE_LOCK_COMPARE_SWAP, - device->node_id, generation, device->max_speed, + err = snd_fw_transaction( + c->resources.unit, TCODE_LOCK_COMPARE_SWAP, CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index), - buffer, 8); - - if (rcode == RCODE_COMPLETE) { - if (buffer[0] == old_arg) /* success? */ - break; - - if (check) { - err = check(c, buffer[0]); - if (err < 0) - return err; - } - } else if (rcode == RCODE_GENERATION) - goto bus_reset; - else if (rcode_is_permanent_error(rcode) || ++errors >= 3) - goto io_error; + buffer, 8, + FW_FIXED_GENERATION | c->resources.generation); + + if (err < 0) { + if (err == -EAGAIN && + bus_reset_handling == SUCCEED_ON_BUS_RESET) + err = 0; + return err; + } + + if (buffer[0] == old_arg) /* success? */ + break; + + if (check) { + err = check(c, buffer[0]); + if (err < 0) + return err; + } } c->last_pcr_value = buffer[1]; return 0; - -io_error: - cmp_error(c, "transaction failed: %s\n", fw_rcode_string(rcode)); - return -EIO; - -bus_reset: - return bus_reset_handling == ABORT_ON_BUS_RESET ? -EAGAIN : 0; } @@ -108,7 +100,7 @@ int cmp_connection_init(struct cmp_connection *c, err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, CSR_REGISTER_BASE + CSR_IMPR, - &impr_be, 4); + &impr_be, 4, 0); if (err < 0) return err; impr = be32_to_cpu(impr_be); diff --git a/sound/firewire/dice-interface.h b/sound/firewire/dice-interface.h new file mode 100644 index 000000000000..27b044f84c81 --- /dev/null +++ b/sound/firewire/dice-interface.h @@ -0,0 +1,371 @@ +#ifndef SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED +#define SOUND_FIREWIRE_DICE_INTERFACE_H_INCLUDED + +/* + * DICE device interface definitions + */ + +/* + * Generally, all registers can be read like memory, i.e., with quadlet read or + * block read transactions with at least quadlet-aligned offset and length. + * Writes are not allowed except where noted; quadlet-sized registers must be + * written with a quadlet write transaction. + * + * All values are in big endian. The DICE firmware runs on a little-endian CPU + * and just byte-swaps _all_ quadlets on the bus, so values without endianness + * (e.g. strings) get scrambled and must be byte-swapped again by the driver. + */ + +/* + * Streaming is handled by the "DICE driver" interface. Its registers are + * located in this private address space. + */ +#define DICE_PRIVATE_SPACE 0xffffe0000000uLL + +/* + * The registers are organized in several sections, which are organized + * separately to allow them to be extended individually. Whether a register is + * supported can be detected by checking its offset against its section's size. + * + * The section offset values are relative to DICE_PRIVATE_SPACE; the offset/ + * size values are measured in quadlets. Read-only. + */ +#define DICE_GLOBAL_OFFSET 0x00 +#define DICE_GLOBAL_SIZE 0x04 +#define DICE_TX_OFFSET 0x08 +#define DICE_TX_SIZE 0x0c +#define DICE_RX_OFFSET 0x10 +#define DICE_RX_SIZE 0x14 +#define DICE_EXT_SYNC_OFFSET 0x18 +#define DICE_EXT_SYNC_SIZE 0x1c +#define DICE_UNUSED2_OFFSET 0x20 +#define DICE_UNUSED2_SIZE 0x24 + +/* + * Global settings. + */ + +/* + * Stores the full 64-bit address (node ID and offset in the node's address + * space) where the device will send notifications. Must be changed with + * a compare/swap transaction by the owner. This register is automatically + * cleared on a bus reset. + */ +#define GLOBAL_OWNER 0x000 +#define OWNER_NO_OWNER 0xffff000000000000uLL +#define OWNER_NODE_SHIFT 48 + +/* + * A bitmask with asynchronous events; read-only. When any event(s) happen, + * the bits of previous events are cleared, and the value of this register is + * also written to the address stored in the owner register. + */ +#define GLOBAL_NOTIFICATION 0x008 +/* Some registers in the Rx/Tx sections may have changed. */ +#define NOTIFY_RX_CFG_CHG 0x00000001 +#define NOTIFY_TX_CFG_CHG 0x00000002 +/* Lock status of the current clock source may have changed. */ +#define NOTIFY_LOCK_CHG 0x00000010 +/* Write to the clock select register has been finished. */ +#define NOTIFY_CLOCK_ACCEPTED 0x00000020 +/* Lock status of some clock source has changed. */ +#define NOTIFY_EXT_STATUS 0x00000040 +/* Other bits may be used for device-specific events. */ + +/* + * A name that can be customized for each device; read/write. Padded with zero + * bytes. Quadlets are byte-swapped. The encoding is whatever the host driver + * happens to be using. + */ +#define GLOBAL_NICK_NAME 0x00c +#define NICK_NAME_SIZE 64 + +/* + * The current sample rate and clock source; read/write. Whether a clock + * source or sample rate is supported is device-specific; the internal clock + * source is always available. Low/mid/high = up to 48/96/192 kHz. This + * register can be changed even while streams are running. + */ +#define GLOBAL_CLOCK_SELECT 0x04c +#define CLOCK_SOURCE_MASK 0x000000ff +#define CLOCK_SOURCE_AES1 0x00000000 +#define CLOCK_SOURCE_AES2 0x00000001 +#define CLOCK_SOURCE_AES3 0x00000002 +#define CLOCK_SOURCE_AES4 0x00000003 +#define CLOCK_SOURCE_AES_ANY 0x00000004 +#define CLOCK_SOURCE_ADAT 0x00000005 +#define CLOCK_SOURCE_TDIF 0x00000006 +#define CLOCK_SOURCE_WC 0x00000007 +#define CLOCK_SOURCE_ARX1 0x00000008 +#define CLOCK_SOURCE_ARX2 0x00000009 +#define CLOCK_SOURCE_ARX3 0x0000000a +#define CLOCK_SOURCE_ARX4 0x0000000b +#define CLOCK_SOURCE_INTERNAL 0x0000000c +#define CLOCK_RATE_MASK 0x0000ff00 +#define CLOCK_RATE_32000 0x00000000 +#define CLOCK_RATE_44100 0x00000100 +#define CLOCK_RATE_48000 0x00000200 +#define CLOCK_RATE_88200 0x00000300 +#define CLOCK_RATE_96000 0x00000400 +#define CLOCK_RATE_176400 0x00000500 +#define CLOCK_RATE_192000 0x00000600 +#define CLOCK_RATE_ANY_LOW 0x00000700 +#define CLOCK_RATE_ANY_MID 0x00000800 +#define CLOCK_RATE_ANY_HIGH 0x00000900 +#define CLOCK_RATE_NONE 0x00000a00 +#define CLOCK_RATE_SHIFT 8 + +/* + * Enable streaming; read/write. Writing a non-zero value (re)starts all + * streams that have a valid iso channel set; zero stops all streams. The + * streams' parameters must be configured before starting. This register is + * automatically cleared on a bus reset. + */ +#define GLOBAL_ENABLE 0x050 + +/* + * Status of the sample clock; read-only. + */ +#define GLOBAL_STATUS 0x054 +/* The current clock source is locked. */ +#define STATUS_SOURCE_LOCKED 0x00000001 +/* The actual sample rate; CLOCK_RATE_32000-_192000 or _NONE. */ +#define STATUS_NOMINAL_RATE_MASK 0x0000ff00 + +/* + * Status of all clock sources; read-only. + */ +#define GLOBAL_EXTENDED_STATUS 0x058 +/* + * The _LOCKED bits always show the current status; any change generates + * a notification. + */ +#define EXT_STATUS_AES1_LOCKED 0x00000001 +#define EXT_STATUS_AES2_LOCKED 0x00000002 +#define EXT_STATUS_AES3_LOCKED 0x00000004 +#define EXT_STATUS_AES4_LOCKED 0x00000008 +#define EXT_STATUS_ADAT_LOCKED 0x00000010 +#define EXT_STATUS_TDIF_LOCKED 0x00000020 +#define EXT_STATUS_ARX1_LOCKED 0x00000040 +#define EXT_STATUS_ARX2_LOCKED 0x00000080 +#define EXT_STATUS_ARX3_LOCKED 0x00000100 +#define EXT_STATUS_ARX4_LOCKED 0x00000200 +#define EXT_STATUS_WC_LOCKED 0x00000400 +/* + * The _SLIP bits do not generate notifications; a set bit indicates that an + * error occurred since the last time when this register was read with + * a quadlet read transaction. + */ +#define EXT_STATUS_AES1_SLIP 0x00010000 +#define EXT_STATUS_AES2_SLIP 0x00020000 +#define EXT_STATUS_AES3_SLIP 0x00040000 +#define EXT_STATUS_AES4_SLIP 0x00080000 +#define EXT_STATUS_ADAT_SLIP 0x00100000 +#define EXT_STATUS_TDIF_SLIP 0x00200000 +#define EXT_STATUS_ARX1_SLIP 0x00400000 +#define EXT_STATUS_ARX2_SLIP 0x00800000 +#define EXT_STATUS_ARX3_SLIP 0x01000000 +#define EXT_STATUS_ARX4_SLIP 0x02000000 +#define EXT_STATUS_WC_SLIP 0x04000000 + +/* + * The measured rate of the current clock source, in Hz; read-only. + */ +#define GLOBAL_SAMPLE_RATE 0x05c + +/* + * The version of the DICE driver specification that this device conforms to; + * read-only. + */ +#define GLOBAL_VERSION 0x060 + +/* Some old firmware versions do not have the following global registers: */ + +/* + * Supported sample rates and clock sources; read-only. + */ +#define GLOBAL_CLOCK_CAPABILITIES 0x064 +#define CLOCK_CAP_RATE_32000 0x00000001 +#define CLOCK_CAP_RATE_44100 0x00000002 +#define CLOCK_CAP_RATE_48000 0x00000004 +#define CLOCK_CAP_RATE_88200 0x00000008 +#define CLOCK_CAP_RATE_96000 0x00000010 +#define CLOCK_CAP_RATE_176400 0x00000020 +#define CLOCK_CAP_RATE_192000 0x00000040 +#define CLOCK_CAP_SOURCE_AES1 0x00010000 +#define CLOCK_CAP_SOURCE_AES2 0x00020000 +#define CLOCK_CAP_SOURCE_AES3 0x00040000 +#define CLOCK_CAP_SOURCE_AES4 0x00080000 +#define CLOCK_CAP_SOURCE_AES_ANY 0x00100000 +#define CLOCK_CAP_SOURCE_ADAT 0x00200000 +#define CLOCK_CAP_SOURCE_TDIF 0x00400000 +#define CLOCK_CAP_SOURCE_WC 0x00800000 +#define CLOCK_CAP_SOURCE_ARX1 0x01000000 +#define CLOCK_CAP_SOURCE_ARX2 0x02000000 +#define CLOCK_CAP_SOURCE_ARX3 0x04000000 +#define CLOCK_CAP_SOURCE_ARX4 0x08000000 +#define CLOCK_CAP_SOURCE_INTERNAL 0x10000000 + +/* + * Names of all clock sources; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. Unused clock sources are included. + */ +#define GLOBAL_CLOCK_SOURCE_NAMES 0x068 +#define CLOCK_SOURCE_NAMES_SIZE 256 + +/* + * Capture stream settings. This section includes the number/size registers + * and the registers of all streams. + */ + +/* + * The number of supported capture streams; read-only. + */ +#define TX_NUMBER 0x000 + +/* + * The size of one stream's register block, in quadlets; read-only. The + * registers of the first stream follow immediately afterwards; the registers + * of the following streams are offset by this register's value. + */ +#define TX_SIZE 0x004 + +/* + * The isochronous channel number on which packets are sent, or -1 if the + * stream is not to be used; read/write. + */ +#define TX_ISOCHRONOUS 0x008 + +/* + * The number of audio channels; read-only. There will be one quadlet per + * channel; the first channel is the first quadlet in a data block. + */ +#define TX_NUMBER_AUDIO 0x00c + +/* + * The number of MIDI ports, 0-8; read-only. If > 0, there will be one + * additional quadlet in each data block, following the audio quadlets. + */ +#define TX_NUMBER_MIDI 0x010 + +/* + * The speed at which the packets are sent, SCODE_100-_400; read/write. + */ +#define TX_SPEED 0x014 + +/* + * Names of all audio channels; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. + */ +#define TX_NAMES 0x018 +#define TX_NAMES_SIZE 256 + +/* + * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio + * channel. + */ +#define TX_AC3_CAPABILITIES 0x118 + +/* + * Send audio data with IEC60958 label; read/write. Bitmask with one bit per + * audio channel. This register can be changed even while the stream is + * running. + */ +#define TX_AC3_ENABLE 0x11c + +/* + * Playback stream settings. This section includes the number/size registers + * and the registers of all streams. + */ + +/* + * The number of supported playback streams; read-only. + */ +#define RX_NUMBER 0x000 + +/* + * The size of one stream's register block, in quadlets; read-only. The + * registers of the first stream follow immediately afterwards; the registers + * of the following streams are offset by this register's value. + */ +#define RX_SIZE 0x004 + +/* + * The isochronous channel number on which packets are received, or -1 if the + * stream is not to be used; read/write. + */ +#define RX_ISOCHRONOUS 0x008 + +/* + * Index of first quadlet to be interpreted; read/write. If > 0, that many + * quadlets at the beginning of each data block will be ignored, and all the + * audio and MIDI quadlets will follow. + */ +#define RX_SEQ_START 0x00c + +/* + * The number of audio channels; read-only. There will be one quadlet per + * channel. + */ +#define RX_NUMBER_AUDIO 0x010 + +/* + * The number of MIDI ports, 0-8; read-only. If > 0, there will be one + * additional quadlet in each data block, following the audio quadlets. + */ +#define RX_NUMBER_MIDI 0x014 + +/* + * Names of all audio channels; read-only. Quadlets are byte-swapped. Names + * are separated with one backslash, the list is terminated with two + * backslashes. + */ +#define RX_NAMES 0x018 +#define RX_NAMES_SIZE 256 + +/* + * Audio IEC60958 capabilities; read-only. Bitmask with one bit per audio + * channel. + */ +#define RX_AC3_CAPABILITIES 0x118 + +/* + * Receive audio data with IEC60958 label; read/write. Bitmask with one bit + * per audio channel. This register can be changed even while the stream is + * running. + */ +#define RX_AC3_ENABLE 0x11c + +/* + * Extended synchronization information. + * This section can be read completely with a block read request. + */ + +/* + * Current clock source; read-only. + */ +#define EXT_SYNC_CLOCK_SOURCE 0x000 + +/* + * Clock source is locked (boolean); read-only. + */ +#define EXT_SYNC_LOCKED 0x004 + +/* + * Current sample rate (CLOCK_RATE_* >> CLOCK_RATE_SHIFT), _32000-_192000 or + * _NONE; read-only. + */ +#define EXT_SYNC_RATE 0x008 + +/* + * ADAT user data bits; read-only. + */ +#define EXT_SYNC_ADAT_USER_DATA 0x00c +/* The data bits, if available. */ +#define ADAT_USER_DATA_MASK 0x0f +/* The data bits are not available. */ +#define ADAT_USER_DATA_NO_DATA 0x10 + +#endif diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c new file mode 100644 index 000000000000..6feee6614193 --- /dev/null +++ b/sound/firewire/dice.c @@ -0,0 +1,1494 @@ +/* + * TC Applied Technologies Digital Interface Communications Engine driver + * + * Copyright (c) Clemens Ladisch <clemens@ladisch.de> + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#include <linux/compat.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/firewire.h> +#include <sound/hwdep.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include "amdtp.h" +#include "iso-resources.h" +#include "lib.h" +#include "dice-interface.h" + + +struct dice { + struct snd_card *card; + struct fw_unit *unit; + spinlock_t lock; + struct mutex mutex; + unsigned int global_offset; + unsigned int rx_offset; + unsigned int clock_caps; + unsigned int rx_channels[3]; + unsigned int rx_midi_ports[3]; + struct fw_address_handler notification_handler; + int owner_generation; + int dev_lock_count; /* > 0 driver, < 0 userspace */ + bool dev_lock_changed; + bool global_enabled; + struct completion clock_accepted; + wait_queue_head_t hwdep_wait; + u32 notification_bits; + struct fw_iso_resources resources; + struct amdtp_out_stream stream; +}; + +MODULE_DESCRIPTION("DICE driver"); +MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); +MODULE_LICENSE("GPL v2"); + +static const unsigned int dice_rates[] = { + /* mode 0 */ + [0] = 32000, + [1] = 44100, + [2] = 48000, + /* mode 1 */ + [3] = 88200, + [4] = 96000, + /* mode 2 */ + [5] = 176400, + [6] = 192000, +}; + +static unsigned int rate_to_index(unsigned int rate) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if (dice_rates[i] == rate) + return i; + + return 0; +} + +static unsigned int rate_index_to_mode(unsigned int rate_index) +{ + return ((int)rate_index - 1) / 2; +} + +static void dice_lock_changed(struct dice *dice) +{ + dice->dev_lock_changed = true; + wake_up(&dice->hwdep_wait); +} + +static int dice_try_lock(struct dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count < 0) { + err = -EBUSY; + goto out; + } + + if (dice->dev_lock_count++ == 0) + dice_lock_changed(dice); + err = 0; + +out: + spin_unlock_irq(&dice->lock); + + return err; +} + +static void dice_unlock(struct dice *dice) +{ + spin_lock_irq(&dice->lock); + + if (WARN_ON(dice->dev_lock_count <= 0)) + goto out; + + if (--dice->dev_lock_count == 0) + dice_lock_changed(dice); + +out: + spin_unlock_irq(&dice->lock); +} + +static inline u64 global_address(struct dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->global_offset + offset; +} + +// TODO: rx index +static inline u64 rx_address(struct dice *dice, unsigned int offset) +{ + return DICE_PRIVATE_SPACE + dice->rx_offset + offset; +} + +static int dice_owner_set(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int err, errors = 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (;;) { + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, + TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | + dice->owner_generation); + + if (err == 0) { + if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + break; + } + if (err != -EAGAIN || ++errors >= 3) + break; + + msleep(20); + } + + kfree(buffer); + + return err; +} + +static int dice_owner_update(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + int err; + + if (dice->owner_generation == -1) + return 0; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + buffer[0] = cpu_to_be64(OWNER_NO_OWNER); + buffer[1] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + + dice->owner_generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, + FW_FIXED_GENERATION | dice->owner_generation); + + if (err == 0) { + if (buffer[0] != cpu_to_be64(OWNER_NO_OWNER)) { + dev_err(&dice->unit->device, + "device is already in use\n"); + err = -EBUSY; + } + } else if (err == -EAGAIN) { + err = 0; /* try again later */ + } + + kfree(buffer); + + if (err < 0) + dice->owner_generation = -1; + + return err; +} + +static void dice_owner_clear(struct dice *dice) +{ + struct fw_device *device = fw_parent_device(dice->unit); + __be64 *buffer; + + buffer = kmalloc(2 * 8, GFP_KERNEL); + if (!buffer) + return; + + buffer[0] = cpu_to_be64( + ((u64)device->card->node_id << OWNER_NODE_SHIFT) | + dice->notification_handler.offset); + buffer[1] = cpu_to_be64(OWNER_NO_OWNER); + snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, + global_address(dice, GLOBAL_OWNER), + buffer, 2 * 8, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + kfree(buffer); + + dice->owner_generation = -1; +} + +static int dice_enable_set(struct dice *dice) +{ + __be32 value; + int err; + + value = cpu_to_be32(1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_ENABLE), + &value, 4, + FW_FIXED_GENERATION | dice->owner_generation); + if (err < 0) + return err; + + dice->global_enabled = true; + + return 0; +} + +static void dice_enable_clear(struct dice *dice) +{ + __be32 value; + + if (!dice->global_enabled) + return; + + value = 0; + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_ENABLE), + &value, 4, FW_QUIET | + FW_FIXED_GENERATION | dice->owner_generation); + + dice->global_enabled = false; +} + +static void dice_notification(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, + int generation, unsigned long long offset, + void *data, size_t length, void *callback_data) +{ + struct dice *dice = callback_data; + u32 bits; + unsigned long flags; + + if (tcode != TCODE_WRITE_QUADLET_REQUEST) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + if ((offset & 3) != 0) { + fw_send_response(card, request, RCODE_ADDRESS_ERROR); + return; + } + + bits = be32_to_cpup(data); + + spin_lock_irqsave(&dice->lock, flags); + dice->notification_bits |= bits; + spin_unlock_irqrestore(&dice->lock, flags); + + fw_send_response(card, request, RCODE_COMPLETE); + + if (bits & NOTIFY_CLOCK_ACCEPTED) + complete(&dice->clock_accepted); + wake_up(&dice->hwdep_wait); +} + +static int dice_rate_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct dice *dice = rule->private; + const struct snd_interval *channels = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval *rate = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval allowed_rates = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, mode; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) { + mode = rate_index_to_mode(i); + if ((dice->clock_caps & (1 << i)) && + snd_interval_test(channels, dice->rx_channels[mode])) { + allowed_rates.min = min(allowed_rates.min, + dice_rates[i]); + allowed_rates.max = max(allowed_rates.max, + dice_rates[i]); + } + } + + return snd_interval_refine(rate, &allowed_rates); +} + +static int dice_channels_constraint(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct dice *dice = rule->private; + const struct snd_interval *rate = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_interval allowed_channels = { + .min = UINT_MAX, .max = 0, .integer = 1 + }; + unsigned int i, mode; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if ((dice->clock_caps & (1 << i)) && + snd_interval_test(rate, dice_rates[i])) { + mode = rate_index_to_mode(i); + allowed_channels.min = min(allowed_channels.min, + dice->rx_channels[mode]); + allowed_channels.max = max(allowed_channels.max, + dice->rx_channels[mode]); + } + + return snd_interval_refine(channels, &allowed_channels); +} + +static int dice_open(struct snd_pcm_substream *substream) +{ + static const struct snd_pcm_hardware hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = AMDTP_OUT_PCM_FORMAT_BITS, + .channels_min = UINT_MAX, + .channels_max = 0, + .buffer_bytes_max = 16 * 1024 * 1024, + .period_bytes_min = 1, + .period_bytes_max = UINT_MAX, + .periods_min = 1, + .periods_max = UINT_MAX, + }; + struct dice *dice = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int i; + int err; + + err = dice_try_lock(dice); + if (err < 0) + goto error; + + runtime->hw = hardware; + + for (i = 0; i < ARRAY_SIZE(dice_rates); ++i) + if (dice->clock_caps & (1 << i)) + runtime->hw.rates |= + snd_pcm_rate_to_rate_bit(dice_rates[i]); + snd_pcm_limit_hw_rates(runtime); + + for (i = 0; i < 3; ++i) + if (dice->rx_channels[i]) { + runtime->hw.channels_min = min(runtime->hw.channels_min, + dice->rx_channels[i]); + runtime->hw.channels_max = max(runtime->hw.channels_max, + dice->rx_channels[i]); + } + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + dice_rate_constraint, dice, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (err < 0) + goto err_lock; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + dice_channels_constraint, dice, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto err_lock; + + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); + if (err < 0) + goto err_lock; + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); + if (err < 0) + goto err_lock; + + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + 5000, UINT_MAX); + if (err < 0) + goto err_lock; + + err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (err < 0) + goto err_lock; + + return 0; + +err_lock: + dice_unlock(dice); +error: + return err; +} + +static int dice_close(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + + dice_unlock(dice); + + return 0; +} + +static int dice_stream_start_packets(struct dice *dice) +{ + int err; + + if (amdtp_out_stream_running(&dice->stream)) + return 0; + + err = amdtp_out_stream_start(&dice->stream, dice->resources.channel, + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + return err; + + err = dice_enable_set(dice); + if (err < 0) { + amdtp_out_stream_stop(&dice->stream); + return err; + } + + return 0; +} + +static int dice_stream_start(struct dice *dice) +{ + __be32 channel; + int err; + + if (!dice->resources.allocated) { + err = fw_iso_resources_allocate(&dice->resources, + amdtp_out_stream_get_max_payload(&dice->stream), + fw_parent_device(dice->unit)->max_speed); + if (err < 0) + goto error; + + channel = cpu_to_be32(dice->resources.channel); + err = snd_fw_transaction(dice->unit, + TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), + &channel, 4, 0); + if (err < 0) + goto err_resources; + } + + err = dice_stream_start_packets(dice); + if (err < 0) + goto err_rx_channel; + + return 0; + +err_rx_channel: + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); +err_resources: + fw_iso_resources_free(&dice->resources); +error: + return err; +} + +static void dice_stream_stop_packets(struct dice *dice) +{ + if (amdtp_out_stream_running(&dice->stream)) { + dice_enable_clear(dice); + amdtp_out_stream_stop(&dice->stream); + } +} + +static void dice_stream_stop(struct dice *dice) +{ + __be32 channel; + + dice_stream_stop_packets(dice); + + if (!dice->resources.allocated) + return; + + channel = cpu_to_be32((u32)-1); + snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + rx_address(dice, RX_ISOCHRONOUS), &channel, 4, 0); + + fw_iso_resources_free(&dice->resources); +} + +static int dice_change_rate(struct dice *dice, unsigned int clock_rate) +{ + __be32 value; + int err; + + INIT_COMPLETION(dice->clock_accepted); + + value = cpu_to_be32(clock_rate | CLOCK_SOURCE_ARX1); + err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &value, 4, 0); + if (err < 0) + return err; + + if (!wait_for_completion_timeout(&dice->clock_accepted, + msecs_to_jiffies(100))) + dev_warn(&dice->unit->device, "clock change timed out\n"); + + return 0; +} + +static int dice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct dice *dice = substream->private_data; + unsigned int rate_index, mode; + int err; + + mutex_lock(&dice->mutex); + dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + err = snd_pcm_lib_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + if (err < 0) + return err; + + rate_index = rate_to_index(params_rate(hw_params)); + err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + if (err < 0) + return err; + + mode = rate_index_to_mode(rate_index); + amdtp_out_stream_set_parameters(&dice->stream, + params_rate(hw_params), + params_channels(hw_params), + dice->rx_midi_ports[mode]); + amdtp_out_stream_set_pcm_format(&dice->stream, + params_format(hw_params)); + + return 0; +} + +static int dice_hw_free(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + + mutex_lock(&dice->mutex); + dice_stream_stop(dice); + mutex_unlock(&dice->mutex); + + return snd_pcm_lib_free_vmalloc_buffer(substream); +} + +static int dice_prepare(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + int err; + + mutex_lock(&dice->mutex); + + if (amdtp_out_streaming_error(&dice->stream)) + dice_stream_stop_packets(dice); + + err = dice_stream_start(dice); + if (err < 0) { + mutex_unlock(&dice->mutex); + return err; + } + + mutex_unlock(&dice->mutex); + + amdtp_out_stream_pcm_prepare(&dice->stream); + + return 0; +} + +static int dice_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct dice *dice = substream->private_data; + struct snd_pcm_substream *pcm; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pcm = substream; + break; + case SNDRV_PCM_TRIGGER_STOP: + pcm = NULL; + break; + default: + return -EINVAL; + } + amdtp_out_stream_pcm_trigger(&dice->stream, pcm); + + return 0; +} + +static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream) +{ + struct dice *dice = substream->private_data; + + return amdtp_out_stream_pcm_pointer(&dice->stream); +} + +static int dice_create_pcm(struct dice *dice) +{ + static struct snd_pcm_ops ops = { + .open = dice_open, + .close = dice_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = dice_hw_params, + .hw_free = dice_hw_free, + .prepare = dice_prepare, + .trigger = dice_trigger, + .pointer = dice_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(dice->card, "DICE", 0, 1, 0, &pcm); + if (err < 0) + return err; + pcm->private_data = dice; + strcpy(pcm->name, dice->card->shortname); + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->ops = &ops; + + return 0; +} + +static long dice_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, + long count, loff_t *offset) +{ + struct dice *dice = hwdep->private_data; + DEFINE_WAIT(wait); + union snd_firewire_event event; + + spin_lock_irq(&dice->lock); + + while (!dice->dev_lock_changed && dice->notification_bits == 0) { + prepare_to_wait(&dice->hwdep_wait, &wait, TASK_INTERRUPTIBLE); + spin_unlock_irq(&dice->lock); + schedule(); + finish_wait(&dice->hwdep_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + spin_lock_irq(&dice->lock); + } + + memset(&event, 0, sizeof(event)); + if (dice->dev_lock_changed) { + event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; + event.lock_status.status = dice->dev_lock_count > 0; + dice->dev_lock_changed = false; + + count = min(count, (long)sizeof(event.lock_status)); + } else { + event.dice_notification.type = SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION; + event.dice_notification.notification = dice->notification_bits; + dice->notification_bits = 0; + + count = min(count, (long)sizeof(event.dice_notification)); + } + + spin_unlock_irq(&dice->lock); + + if (copy_to_user(buf, &event, count)) + return -EFAULT; + + return count; +} + +static unsigned int dice_hwdep_poll(struct snd_hwdep *hwdep, struct file *file, + poll_table *wait) +{ + struct dice *dice = hwdep->private_data; + unsigned int events; + + poll_wait(file, &dice->hwdep_wait, wait); + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_changed || dice->notification_bits != 0) + events = POLLIN | POLLRDNORM; + else + events = 0; + spin_unlock_irq(&dice->lock); + + return events; +} + +static int dice_hwdep_get_info(struct dice *dice, void __user *arg) +{ + struct fw_device *dev = fw_parent_device(dice->unit); + struct snd_firewire_get_info info; + + memset(&info, 0, sizeof(info)); + info.type = SNDRV_FIREWIRE_TYPE_DICE; + info.card = dev->card->index; + *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]); + *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]); + strlcpy(info.device_name, dev_name(&dev->device), + sizeof(info.device_name)); + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int dice_hwdep_lock(struct dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == 0) { + dice->dev_lock_count = -1; + err = 0; + } else { + err = -EBUSY; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int dice_hwdep_unlock(struct dice *dice) +{ + int err; + + spin_lock_irq(&dice->lock); + + if (dice->dev_lock_count == -1) { + dice->dev_lock_count = 0; + err = 0; + } else { + err = -EBADFD; + } + + spin_unlock_irq(&dice->lock); + + return err; +} + +static int dice_hwdep_release(struct snd_hwdep *hwdep, struct file *file) +{ + struct dice *dice = hwdep->private_data; + + spin_lock_irq(&dice->lock); + if (dice->dev_lock_count == -1) + dice->dev_lock_count = 0; + spin_unlock_irq(&dice->lock); + + return 0; +} + +static int dice_hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct dice *dice = hwdep->private_data; + + switch (cmd) { + case SNDRV_FIREWIRE_IOCTL_GET_INFO: + return dice_hwdep_get_info(dice, (void __user *)arg); + case SNDRV_FIREWIRE_IOCTL_LOCK: + return dice_hwdep_lock(dice); + case SNDRV_FIREWIRE_IOCTL_UNLOCK: + return dice_hwdep_unlock(dice); + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static int dice_hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return dice_hwdep_ioctl(hwdep, file, cmd, + (unsigned long)compat_ptr(arg)); +} +#else +#define dice_hwdep_compat_ioctl NULL +#endif + +static int dice_create_hwdep(struct dice *dice) +{ + static const struct snd_hwdep_ops ops = { + .read = dice_hwdep_read, + .release = dice_hwdep_release, + .poll = dice_hwdep_poll, + .ioctl = dice_hwdep_ioctl, + .ioctl_compat = dice_hwdep_compat_ioctl, + }; + struct snd_hwdep *hwdep; + int err; + + err = snd_hwdep_new(dice->card, "DICE", 0, &hwdep); + if (err < 0) + return err; + strcpy(hwdep->name, "DICE"); + hwdep->iface = SNDRV_HWDEP_IFACE_FW_DICE; + hwdep->ops = ops; + hwdep->private_data = dice; + hwdep->exclusive = true; + + return 0; +} + +static int dice_proc_read_mem(struct dice *dice, void *buffer, + unsigned int offset_q, unsigned int quadlets) +{ + unsigned int i; + int err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + 4 * offset_q, + buffer, 4 * quadlets, 0); + if (err < 0) + return err; + + for (i = 0; i < quadlets; ++i) + be32_to_cpus(&((u32 *)buffer)[i]); + + return 0; +} + +static const char *str_from_array(const char *const strs[], unsigned int count, + unsigned int i) +{ + if (i < count) + return strs[i]; + else + return "(unknown)"; +} + +static void dice_proc_fixup_string(char *s, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < size; i += 4) + cpu_to_le32s((u32 *)(s + i)); + + for (i = 0; i < size - 2; ++i) { + if (s[i] == '\0') + return; + if (s[i] == '\\' && s[i + 1] == '\\') { + s[i + 2] = '\0'; + return; + } + } + s[size - 1] = '\0'; +} + +static void dice_proc_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + static const char *const section_names[5] = { + "global", "tx", "rx", "ext_sync", "unused2" + }; + static const char *const clock_sources[] = { + "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif", + "wc", "arx1", "arx2", "arx3", "arx4", "internal" + }; + static const char *const rates[] = { + "32000", "44100", "48000", "88200", "96000", "176400", "192000", + "any low", "any mid", "any high", "none" + }; + struct dice *dice = entry->private_data; + u32 sections[ARRAY_SIZE(section_names) * 2]; + struct { + u32 number; + u32 size; + } tx_rx_header; + union { + struct { + u32 owner_hi, owner_lo; + u32 notification; + char nick_name[NICK_NAME_SIZE]; + u32 clock_select; + u32 enable; + u32 status; + u32 extended_status; + u32 sample_rate; + u32 version; + u32 clock_caps; + char clock_source_names[CLOCK_SOURCE_NAMES_SIZE]; + } global; + struct { + u32 iso; + u32 number_audio; + u32 number_midi; + u32 speed; + char names[TX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } tx; + struct { + u32 iso; + u32 seq_start; + u32 number_audio; + u32 number_midi; + char names[RX_NAMES_SIZE]; + u32 ac3_caps; + u32 ac3_enable; + } rx; + struct { + u32 clock_source; + u32 locked; + u32 rate; + u32 adat_user_data; + } ext_sync; + } buf; + unsigned int quadlets, stream, i; + + if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0) + return; + snd_iprintf(buffer, "sections:\n"); + for (i = 0; i < ARRAY_SIZE(section_names); ++i) + snd_iprintf(buffer, " %s: offset %u, size %u\n", + section_names[i], + sections[i * 2], sections[i * 2 + 1]); + + quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4); + if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0) + return; + snd_iprintf(buffer, "global:\n"); + snd_iprintf(buffer, " owner: %04x:%04x%08x\n", + buf.global.owner_hi >> 16, + buf.global.owner_hi & 0xffff, buf.global.owner_lo); + snd_iprintf(buffer, " notification: %08x\n", buf.global.notification); + dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE); + snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name); + snd_iprintf(buffer, " clock select: %s %s\n", + str_from_array(clock_sources, ARRAY_SIZE(clock_sources), + buf.global.clock_select & CLOCK_SOURCE_MASK), + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.clock_select & CLOCK_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " enable: %u\n", buf.global.enable); + snd_iprintf(buffer, " status: %slocked %s\n", + buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un", + str_from_array(rates, ARRAY_SIZE(rates), + (buf.global.status & + STATUS_NOMINAL_RATE_MASK) + >> CLOCK_RATE_SHIFT)); + snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status); + snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate); + snd_iprintf(buffer, " version: %u.%u.%u.%u\n", + (buf.global.version >> 24) & 0xff, + (buf.global.version >> 16) & 0xff, + (buf.global.version >> 8) & 0xff, + (buf.global.version >> 0) & 0xff); + if (quadlets >= 90) { + snd_iprintf(buffer, " clock caps:"); + for (i = 0; i <= 6; ++i) + if (buf.global.clock_caps & (1 << i)) + snd_iprintf(buffer, " %s", rates[i]); + for (i = 0; i <= 12; ++i) + if (buf.global.clock_caps & (1 << (16 + i))) + snd_iprintf(buffer, " %s", clock_sources[i]); + snd_iprintf(buffer, "\n"); + dice_proc_fixup_string(buf.global.clock_source_names, + CLOCK_SOURCE_NAMES_SIZE); + snd_iprintf(buffer, " clock source names: %s\n", + buf.global.clock_source_names); + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx)); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "tx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso); + snd_iprintf(buffer, " audio channels: %u\n", + buf.tx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi); + snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.tx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.tx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.tx.ac3_enable); + } + } + + if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0) + return; + quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx)); + for (stream = 0; stream < tx_rx_header.number; ++stream) { + if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 + + stream * tx_rx_header.size, + quadlets) < 0) + break; + snd_iprintf(buffer, "rx %u:\n", stream); + snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso); + snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start); + snd_iprintf(buffer, " audio channels: %u\n", + buf.rx.number_audio); + snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi); + if (quadlets >= 68) { + dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE); + snd_iprintf(buffer, " names: %s\n", buf.rx.names); + } + if (quadlets >= 70) { + snd_iprintf(buffer, " ac3 caps: %08x\n", + buf.rx.ac3_caps); + snd_iprintf(buffer, " ac3 enable: %08x\n", + buf.rx.ac3_enable); + } + } + + quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4); + if (quadlets >= 4) { + if (dice_proc_read_mem(dice, &buf.ext_sync, + sections[6], 4) < 0) + return; + snd_iprintf(buffer, "ext status:\n"); + snd_iprintf(buffer, " clock source: %s\n", + str_from_array(clock_sources, + ARRAY_SIZE(clock_sources), + buf.ext_sync.clock_source)); + snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked); + snd_iprintf(buffer, " rate: %s\n", + str_from_array(rates, ARRAY_SIZE(rates), + buf.ext_sync.rate)); + snd_iprintf(buffer, " adat user data: "); + if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA) + snd_iprintf(buffer, "-\n"); + else + snd_iprintf(buffer, "%x\n", + buf.ext_sync.adat_user_data); + } +} + +static void dice_create_proc(struct dice *dice) +{ + struct snd_info_entry *entry; + + if (!snd_card_proc_new(dice->card, "dice", &entry)) + snd_info_set_text_ops(entry, dice, dice_proc_read); +} + +static void dice_card_free(struct snd_card *card) +{ + struct dice *dice = card->private_data; + + amdtp_out_stream_destroy(&dice->stream); + fw_core_remove_address_handler(&dice->notification_handler); + mutex_destroy(&dice->mutex); +} + +#define OUI_WEISS 0x001c6a + +#define DICE_CATEGORY_ID 0x04 +#define WEISS_CATEGORY_ID 0x00 + +static int dice_interface_check(struct fw_unit *unit) +{ + static const int min_values[10] = { + 10, 0x64 / 4, + 10, 0x18 / 4, + 10, 0x18 / 4, + 0, 0, + 0, 0, + }; + struct fw_device *device = fw_parent_device(unit); + struct fw_csr_iterator it; + int key, value, vendor = -1, model = -1, err; + unsigned int category, i; + __be32 pointers[ARRAY_SIZE(min_values)]; + __be32 tx_data[4]; + __be32 version; + + /* + * Check that GUID and unit directory are constructed according to DICE + * rules, i.e., that the specifier ID is the GUID's OUI, and that the + * GUID chip ID consists of the 8-bit category ID, the 10-bit product + * ID, and a 22-bit serial number. + */ + fw_csr_iterator_init(&it, unit->directory); + while (fw_csr_iterator_next(&it, &key, &value)) { + switch (key) { + case CSR_SPECIFIER_ID: + vendor = value; + break; + case CSR_MODEL: + model = value; + break; + } + } + if (vendor == OUI_WEISS) + category = WEISS_CATEGORY_ID; + else + category = DICE_CATEGORY_ID; + if (device->config_rom[3] != ((vendor << 8) | category) || + device->config_rom[4] >> 22 != model) + return -ENODEV; + + /* + * Check that the sub address spaces exist and are located inside the + * private address space. The minimum values are chosen so that all + * minimally required registers are included. + */ + err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(pointers), 0); + if (err < 0) + return -ENODEV; + for (i = 0; i < ARRAY_SIZE(pointers); ++i) { + value = be32_to_cpu(pointers[i]); + if (value < min_values[i] || value >= 0x40000) + return -ENODEV; + } + + /* We support playback only. Let capture devices be handled by FFADO. */ + err = snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[2]) * 4, + tx_data, sizeof(tx_data), 0); + if (err < 0 || (tx_data[0] && tx_data[3])) + return -ENODEV; + + /* + * Check that the implemented DICE driver specification major version + * number matches. + */ + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + DICE_PRIVATE_SPACE + + be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, + &version, 4, 0); + if (err < 0) + return -ENODEV; + if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { + dev_err(&unit->device, + "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); + return -ENODEV; + } + + return 0; +} + +static int highest_supported_mode_rate(struct dice *dice, unsigned int mode) +{ + int i; + + for (i = ARRAY_SIZE(dice_rates) - 1; i >= 0; --i) + if ((dice->clock_caps & (1 << i)) && + rate_index_to_mode(i) == mode) + return i; + + return -1; +} + +static int dice_read_mode_params(struct dice *dice, unsigned int mode) +{ + __be32 values[2]; + int rate_index, err; + + rate_index = highest_supported_mode_rate(dice, mode); + if (rate_index < 0) { + dice->rx_channels[mode] = 0; + dice->rx_midi_ports[mode] = 0; + return 0; + } + + err = dice_change_rate(dice, rate_index << CLOCK_RATE_SHIFT); + if (err < 0) + return err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + rx_address(dice, RX_NUMBER_AUDIO), + values, 2 * 4, 0); + if (err < 0) + return err; + + dice->rx_channels[mode] = be32_to_cpu(values[0]); + dice->rx_midi_ports[mode] = be32_to_cpu(values[1]); + + return 0; +} + +static int dice_read_params(struct dice *dice) +{ + __be32 pointers[6]; + __be32 value; + int mode, err; + + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + DICE_PRIVATE_SPACE, + pointers, sizeof(pointers), 0); + if (err < 0) + return err; + + dice->global_offset = be32_to_cpu(pointers[0]) * 4; + dice->rx_offset = be32_to_cpu(pointers[4]) * 4; + + /* some very old firmwares don't tell about their clock support */ + if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) { + err = snd_fw_transaction( + dice->unit, TCODE_READ_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_CAPABILITIES), + &value, 4, 0); + if (err < 0) + return err; + dice->clock_caps = be32_to_cpu(value); + } else { + /* this should be supported by any device */ + dice->clock_caps = CLOCK_CAP_RATE_44100 | + CLOCK_CAP_RATE_48000 | + CLOCK_CAP_SOURCE_ARX1 | + CLOCK_CAP_SOURCE_INTERNAL; + } + + for (mode = 2; mode >= 0; --mode) { + err = dice_read_mode_params(dice, mode); + if (err < 0) + return err; + } + + return 0; +} + +static void dice_card_strings(struct dice *dice) +{ + struct snd_card *card = dice->card; + struct fw_device *dev = fw_parent_device(dice->unit); + char vendor[32], model[32]; + unsigned int i; + int err; + + strcpy(card->driver, "DICE"); + + strcpy(card->shortname, "DICE"); + BUILD_BUG_ON(NICK_NAME_SIZE < sizeof(card->shortname)); + err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, + global_address(dice, GLOBAL_NICK_NAME), + card->shortname, sizeof(card->shortname), 0); + if (err >= 0) { + /* DICE strings are returned in "always-wrong" endianness */ + BUILD_BUG_ON(sizeof(card->shortname) % 4 != 0); + for (i = 0; i < sizeof(card->shortname); i += 4) + swab32s((u32 *)&card->shortname[i]); + card->shortname[sizeof(card->shortname) - 1] = '\0'; + } + + strcpy(vendor, "?"); + fw_csr_string(dev->config_rom + 5, CSR_VENDOR, vendor, sizeof(vendor)); + strcpy(model, "?"); + fw_csr_string(dice->unit->directory, CSR_MODEL, model, sizeof(model)); + snprintf(card->longname, sizeof(card->longname), + "%s %s (serial %u) at %s, S%d", + vendor, model, dev->config_rom[4] & 0x3fffff, + dev_name(&dice->unit->device), 100 << dev->max_speed); + + strcpy(card->mixername, "DICE"); +} + +static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id) +{ + struct snd_card *card; + struct dice *dice; + __be32 clock_sel; + int err; + + err = dice_interface_check(unit); + if (err < 0) + return err; + + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*dice), &card); + if (err < 0) + return err; + snd_card_set_dev(card, &unit->device); + + dice = card->private_data; + dice->card = card; + spin_lock_init(&dice->lock); + mutex_init(&dice->mutex); + dice->unit = unit; + init_completion(&dice->clock_accepted); + init_waitqueue_head(&dice->hwdep_wait); + + dice->notification_handler.length = 4; + dice->notification_handler.address_callback = dice_notification; + dice->notification_handler.callback_data = dice; + err = fw_core_add_address_handler(&dice->notification_handler, + &fw_high_memory_region); + if (err < 0) + goto err_mutex; + + err = dice_owner_set(dice); + if (err < 0) + goto err_notification_handler; + + err = dice_read_params(dice); + if (err < 0) + goto err_owner; + + err = fw_iso_resources_init(&dice->resources, unit); + if (err < 0) + goto err_owner; + dice->resources.channels_mask = 0x00000000ffffffffuLL; + + err = amdtp_out_stream_init(&dice->stream, unit, + CIP_BLOCKING | CIP_HI_DUALWIRE); + if (err < 0) + goto err_resources; + + card->private_free = dice_card_free; + + dice_card_strings(dice); + + err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock_sel, 4, 0); + if (err < 0) + goto error; + clock_sel &= cpu_to_be32(~CLOCK_SOURCE_MASK); + clock_sel |= cpu_to_be32(CLOCK_SOURCE_ARX1); + err = snd_fw_transaction(unit, TCODE_WRITE_QUADLET_REQUEST, + global_address(dice, GLOBAL_CLOCK_SELECT), + &clock_sel, 4, 0); + if (err < 0) + goto error; + + err = dice_create_pcm(dice); + if (err < 0) + goto error; + + err = dice_create_hwdep(dice); + if (err < 0) + goto error; + + dice_create_proc(dice); + + err = snd_card_register(card); + if (err < 0) + goto error; + + dev_set_drvdata(&unit->device, dice); + + return 0; + +err_resources: + fw_iso_resources_destroy(&dice->resources); +err_owner: + dice_owner_clear(dice); +err_notification_handler: + fw_core_remove_address_handler(&dice->notification_handler); +err_mutex: + mutex_destroy(&dice->mutex); +error: + snd_card_free(card); + return err; +} + +static void dice_remove(struct fw_unit *unit) +{ + struct dice *dice = dev_get_drvdata(&unit->device); + + amdtp_out_stream_pcm_abort(&dice->stream); + + snd_card_disconnect(dice->card); + + mutex_lock(&dice->mutex); + + dice_stream_stop(dice); + dice_owner_clear(dice); + + mutex_unlock(&dice->mutex); + + snd_card_free_when_closed(dice->card); +} + +static void dice_bus_reset(struct fw_unit *unit) +{ + struct dice *dice = dev_get_drvdata(&unit->device); + + /* + * On a bus reset, the DICE firmware disables streaming and then goes + * off contemplating its own navel for hundreds of milliseconds before + * it can react to any of our attempts to reenable streaming. This + * means that we lose synchronization anyway, so we force our streams + * to stop so that the application can restart them in an orderly + * manner. + */ + amdtp_out_stream_pcm_abort(&dice->stream); + + mutex_lock(&dice->mutex); + + dice->global_enabled = false; + dice_stream_stop_packets(dice); + + dice_owner_update(dice); + + fw_iso_resources_update(&dice->resources); + + mutex_unlock(&dice->mutex); +} + +#define DICE_INTERFACE 0x000001 + +static const struct ieee1394_device_id dice_id_table[] = { + { + .match_flags = IEEE1394_MATCH_VERSION, + .version = DICE_INTERFACE, + }, + { } +}; +MODULE_DEVICE_TABLE(ieee1394, dice_id_table); + +static struct fw_driver dice_driver = { + .driver = { + .owner = THIS_MODULE, + .name = KBUILD_MODNAME, + .bus = &fw_bus_type, + }, + .probe = dice_probe, + .update = dice_bus_reset, + .remove = dice_remove, + .id_table = dice_id_table, +}; + +static int __init alsa_dice_init(void) +{ + return driver_register(&dice_driver.driver); +} + +static void __exit alsa_dice_exit(void) +{ + driver_unregister(&dice_driver.driver); +} + +module_init(alsa_dice_init); +module_exit(alsa_dice_exit); diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c index ec578b5ad8da..860c08073c59 100644 --- a/sound/firewire/fcp.c +++ b/sound/firewire/fcp.c @@ -90,7 +90,7 @@ int fcp_avc_transaction(struct fw_unit *unit, : TCODE_WRITE_BLOCK_REQUEST; ret = snd_fw_transaction(t.unit, tcode, CSR_REGISTER_BASE + CSR_FCP_COMMAND, - (void *)command, command_size); + (void *)command, command_size, 0); if (ret < 0) break; diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 58a5afefdc69..fd42e6b315e6 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -217,7 +217,7 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, static int isight_connect(struct isight *isight) { - int ch, err, rcode, errors = 0; + int ch, err; __be32 value; retry_after_bus_reset: @@ -230,27 +230,19 @@ retry_after_bus_reset: } value = cpu_to_be32(ch | (isight->device->max_speed << SPEED_SHIFT)); - for (;;) { - rcode = fw_run_transaction( - isight->device->card, - TCODE_WRITE_QUADLET_REQUEST, - isight->device->node_id, - isight->resources.generation, - isight->device->max_speed, - isight->audio_base + REG_ISO_TX_CONFIG, - &value, 4); - if (rcode == RCODE_COMPLETE) { - return 0; - } else if (rcode == RCODE_GENERATION) { - fw_iso_resources_free(&isight->resources); - goto retry_after_bus_reset; - } else if (rcode_is_permanent_error(rcode) || ++errors >= 3) { - err = -EIO; - goto err_resources; - } - msleep(5); + err = snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_ISO_TX_CONFIG, + &value, 4, FW_FIXED_GENERATION | + isight->resources.generation); + if (err == -EAGAIN) { + fw_iso_resources_free(&isight->resources); + goto retry_after_bus_reset; + } else if (err < 0) { + goto err_resources; } + return 0; + err_resources: fw_iso_resources_free(&isight->resources); error: @@ -315,17 +307,19 @@ static int isight_hw_params(struct snd_pcm_substream *substream, static int reg_read(struct isight *isight, int offset, __be32 *value) { return snd_fw_transaction(isight->unit, TCODE_READ_QUADLET_REQUEST, - isight->audio_base + offset, value, 4); + isight->audio_base + offset, value, 4, 0); } static int reg_write(struct isight *isight, int offset, __be32 value) { return snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, - isight->audio_base + offset, &value, 4); + isight->audio_base + offset, &value, 4, 0); } static void isight_stop_streaming(struct isight *isight) { + __be32 value; + if (!isight->context) return; @@ -333,7 +327,10 @@ static void isight_stop_streaming(struct isight *isight) fw_iso_context_destroy(isight->context); isight->context = NULL; fw_iso_resources_free(&isight->resources); - reg_write(isight, REG_AUDIO_ENABLE, 0); + value = 0; + snd_fw_transaction(isight->unit, TCODE_WRITE_QUADLET_REQUEST, + isight->audio_base + REG_AUDIO_ENABLE, + &value, 4, FW_QUIET); } static int isight_hw_free(struct snd_pcm_substream *substream) diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c index 14eb41498372..7409edba9f06 100644 --- a/sound/firewire/lib.c +++ b/sound/firewire/lib.c @@ -11,7 +11,7 @@ #include <linux/module.h> #include "lib.h" -#define ERROR_RETRY_DELAY_MS 5 +#define ERROR_RETRY_DELAY_MS 20 /** * snd_fw_transaction - send a request and wait for its completion @@ -20,6 +20,9 @@ * @offset: the address in the target's address space * @buffer: input/output data * @length: length of @buffer + * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the + * request only in that generation; use %FW_QUIET to suppress error + * messages * * Submits an asynchronous request to the target device, and waits for the * response. The node ID and the current generation are derived from @unit. @@ -27,14 +30,18 @@ * Returns zero on success, or a negative error code. */ int snd_fw_transaction(struct fw_unit *unit, int tcode, - u64 offset, void *buffer, size_t length) + u64 offset, void *buffer, size_t length, + unsigned int flags) { struct fw_device *device = fw_parent_device(unit); int generation, rcode, tries = 0; + generation = flags & FW_GENERATION_MASK; for (;;) { - generation = device->generation; - smp_rmb(); /* node_id vs. generation */ + if (!(flags & FW_FIXED_GENERATION)) { + generation = device->generation; + smp_rmb(); /* node_id vs. generation */ + } rcode = fw_run_transaction(device->card, tcode, device->node_id, generation, device->max_speed, offset, @@ -43,9 +50,14 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode, if (rcode == RCODE_COMPLETE) return 0; + if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION)) + return -EAGAIN; + if (rcode_is_permanent_error(rcode) || ++tries >= 3) { - dev_err(&unit->device, "transaction failed: %s\n", - fw_rcode_string(rcode)); + if (!(flags & FW_QUIET)) + dev_err(&unit->device, + "transaction failed: %s\n", + fw_rcode_string(rcode)); return -EIO; } diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h index aef301476ea9..02cfabc9c3c4 100644 --- a/sound/firewire/lib.h +++ b/sound/firewire/lib.h @@ -6,8 +6,13 @@ struct fw_unit; +#define FW_GENERATION_MASK 0x00ff +#define FW_FIXED_GENERATION 0x0100 +#define FW_QUIET 0x0200 + int snd_fw_transaction(struct fw_unit *unit, int tcode, - u64 offset, void *buffer, size_t length); + u64 offset, void *buffer, size_t length, + unsigned int flags); /* returns true if retrying the transaction would not make sense */ static inline bool rcode_is_permanent_error(int rcode) diff --git a/sound/firewire/scs1x.c b/sound/firewire/scs1x.c index 505fc8123199..858023cf4298 100644 --- a/sound/firewire/scs1x.c +++ b/sound/firewire/scs1x.c @@ -369,7 +369,7 @@ static int scs_init_hss_address(struct scs *scs) data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | scs->hss_handler.offset); err = snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, - HSS1394_ADDRESS, &data, 8); + HSS1394_ADDRESS, &data, 8, 0); if (err < 0) dev_err(&scs->unit->device, "HSS1394 communication failed\n"); @@ -455,12 +455,16 @@ err_card: static void scs_update(struct fw_unit *unit) { struct scs *scs = dev_get_drvdata(&unit->device); + int generation; __be64 data; data = cpu_to_be64(((u64)HSS1394_TAG_CHANGE_ADDRESS << 56) | scs->hss_handler.offset); + generation = fw_parent_device(unit)->generation; + smp_rmb(); /* node_id vs. generation */ snd_fw_transaction(scs->unit, TCODE_WRITE_BLOCK_REQUEST, - HSS1394_ADDRESS, &data, 8); + HSS1394_ADDRESS, &data, 8, + FW_FIXED_GENERATION | generation); } static void scs_remove(struct fw_unit *unit) diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c index fe9e6e2f2c5b..cc8bc3a51bc1 100644 --- a/sound/firewire/speakers.c +++ b/sound/firewire/speakers.c @@ -52,7 +52,6 @@ struct fwspk { struct mutex mutex; struct cmp_connection connection; struct amdtp_out_stream stream; - bool stream_running; bool mute; s16 volume[6]; s16 volume_min; @@ -188,10 +187,9 @@ static int fwspk_close(struct snd_pcm_substream *substream) static void fwspk_stop_stream(struct fwspk *fwspk) { - if (fwspk->stream_running) { + if (amdtp_out_stream_running(&fwspk->stream)) { amdtp_out_stream_stop(&fwspk->stream); cmp_connection_break(&fwspk->connection); - fwspk->stream_running = false; } } @@ -246,8 +244,10 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto error; - amdtp_out_stream_set_rate(&fwspk->stream, params_rate(hw_params)); - amdtp_out_stream_set_pcm(&fwspk->stream, params_channels(hw_params)); + amdtp_out_stream_set_parameters(&fwspk->stream, + params_rate(hw_params), + params_channels(hw_params), + 0); amdtp_out_stream_set_pcm_format(&fwspk->stream, params_format(hw_params)); @@ -285,7 +285,7 @@ static int fwspk_prepare(struct snd_pcm_substream *substream) if (amdtp_out_streaming_error(&fwspk->stream)) fwspk_stop_stream(fwspk); - if (!fwspk->stream_running) { + if (!amdtp_out_stream_running(&fwspk->stream)) { err = cmp_connection_establish(&fwspk->connection, amdtp_out_stream_get_max_payload(&fwspk->stream)); if (err < 0) @@ -296,8 +296,6 @@ static int fwspk_prepare(struct snd_pcm_substream *substream) fwspk->connection.speed); if (err < 0) goto err_connection; - - fwspk->stream_running = true; } mutex_unlock(&fwspk->mutex); @@ -647,7 +645,7 @@ static u32 fwspk_read_firmware_version(struct fw_unit *unit) int err; err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST, - OXFORD_FIRMWARE_ID_ADDRESS, &data, 4); + OXFORD_FIRMWARE_ID_ADDRESS, &data, 4, 0); return err >= 0 ? be32_to_cpu(data) : 0; } diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c index c0be085e4a20..0e7254bde4c2 100644 --- a/sound/oss/sb_ess.c +++ b/sound/oss/sb_ess.c @@ -1544,7 +1544,7 @@ static int ess_has_rec_mixer (int submodel) return 1; default: return 0; - }; + } }; #ifdef FKS_LOGGING diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index dc632cdc3870..5f2acd35dcb9 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -1913,6 +1913,7 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); */ u32 h_control = kcontrol->private_value; + unsigned int idx; u16 band; u16 tuner_bands[HPI_TUNER_BAND_LAST]; u32 num_bands = 0; @@ -1920,7 +1921,10 @@ static int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, HPI_TUNER_BAND_LAST); - band = tuner_bands[ucontrol->value.enumerated.item[0]]; + idx = ucontrol->value.enumerated.item[0]; + if (idx >= ARRAY_SIZE(tuner_bands)) + idx = ARRAY_SIZE(tuner_bands) - 1; + band = tuner_bands[idx]; hpi_handle_error(hpi_tuner_set_band(h_control, band)); return 1; @@ -2383,7 +2387,8 @@ static int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, struct snd_card_asihpi *asihpi = (struct snd_card_asihpi *)(kcontrol->private_data); struct clk_cache *clkcache = &asihpi->cc; - int change, item; + unsigned int item; + int change; u32 h_control = kcontrol->private_value; change = 1; diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c index b46dc9b24dbd..9fb03b4ea925 100644 --- a/sound/pci/au88x0/au88x0_pcm.c +++ b/sound/pci/au88x0/au88x0_pcm.c @@ -671,7 +671,7 @@ static int snd_vortex_new_pcm(vortex_t *chip, int idx, int nr) return err; break; #endif - }; + } if (VORTEX_PCM_TYPE(pcm) == VORTEX_PCM_SPDIF) { for (i = 0; i < ARRAY_SIZE(snd_vortex_mixer_spdif); i++) { diff --git a/sound/pci/au88x0/au88x0_synth.c b/sound/pci/au88x0/au88x0_synth.c index 8bef47311e45..922a84bba2ef 100644 --- a/sound/pci/au88x0/au88x0_synth.c +++ b/sound/pci/au88x0/au88x0_synth.c @@ -219,7 +219,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_RUN(wt), val); return 0xc; - break; case 1: /* param 0 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -227,7 +226,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 0), val); return 0xc; - break; case 2: /* param 1 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -235,7 +233,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 1), val); return 0xc; - break; case 3: /* param 2 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -243,7 +240,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 2), val); return 0xc; - break; case 4: /* param 3 */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -251,7 +247,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_PARM(wt, 3), val); return 0xc; - break; case 6: /* mute */ /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", @@ -259,20 +254,17 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, */ hwwrite(vortex->mmio, WT_MUTE(wt), val); return 0xc; - break; case 0xb: - { /* delay */ - /* - printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", - WT_DELAY(wt,0), (int)val); - */ - hwwrite(vortex->mmio, WT_DELAY(wt, 3), val); - hwwrite(vortex->mmio, WT_DELAY(wt, 2), val); - hwwrite(vortex->mmio, WT_DELAY(wt, 1), val); - hwwrite(vortex->mmio, WT_DELAY(wt, 0), val); - return 0xc; - } - break; + /* delay */ + /* + printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", + WT_DELAY(wt,0), (int)val); + */ + hwwrite(vortex->mmio, WT_DELAY(wt, 3), val); + hwwrite(vortex->mmio, WT_DELAY(wt, 2), val); + hwwrite(vortex->mmio, WT_DELAY(wt, 1), val); + hwwrite(vortex->mmio, WT_DELAY(wt, 0), val); + return 0xc; /* Global WT block parameters */ case 5: /* sramp */ ecx = WT_SRAMP(wt); @@ -291,7 +283,6 @@ vortex_wt_SetReg(vortex_t * vortex, unsigned char reg, int wt, break; default: return 0; - break; } /* printk(KERN_DEBUG "vortex: WT SetReg(0x%x) = 0x%08x\n", ecx, (int)val); diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index c8e121611593..1aef7128f7ca 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -715,14 +715,14 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97) const struct snd_azf3328 *chip = ac97->private_data; unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); unsigned short reg_val = 0; - bool unsupported = 0; + bool unsupported = false; snd_azf3328_dbgmixer( "snd_azf3328_mixer_ac97_read reg_ac97 %u\n", reg_ac97 ); if (reg_azf & AZF_AC97_REG_UNSUPPORTED) - unsupported = 1; + unsupported = true; else { if (reg_azf & AZF_AC97_REG_REAL_IO_READ) reg_val = snd_azf3328_mixer_inw(chip, @@ -759,7 +759,7 @@ snd_azf3328_mixer_ac97_read(struct snd_ac97 *ac97, unsigned short reg_ac97) reg_val = azf_emulated_ac97_vendor_id & 0xffff; break; default: - unsupported = 1; + unsupported = true; break; } } @@ -776,14 +776,14 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97, { const struct snd_azf3328 *chip = ac97->private_data; unsigned short reg_azf = snd_azf3328_mixer_ac97_map_reg_idx(reg_ac97); - bool unsupported = 0; + bool unsupported = false; snd_azf3328_dbgmixer( "snd_azf3328_mixer_ac97_write reg_ac97 %u val %u\n", reg_ac97, val ); if (reg_azf & AZF_AC97_REG_UNSUPPORTED) - unsupported = 1; + unsupported = true; else { if (reg_azf & AZF_AC97_REG_REAL_IO_WRITE) snd_azf3328_mixer_outw( @@ -808,7 +808,7 @@ snd_azf3328_mixer_ac97_write(struct snd_ac97 *ac97, */ break; default: - unsupported = 1; + unsupported = true; break; } } @@ -1559,7 +1559,7 @@ snd_azf3328_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_azf3328_codec_data *codec = runtime->private_data; int result = 0; u16 flags1; - bool previously_muted = 0; + bool previously_muted = false; bool is_main_mixer_playback_codec = (AZF_CODEC_PLAYBACK == codec->type); snd_azf3328_dbgcalls("snd_azf3328_pcm_trigger cmd %d\n", cmd); diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c index 0c00eb4088ef..84f86bf63b8f 100644 --- a/sound/pci/ctxfi/ctdaio.c +++ b/sound/pci/ctxfi/ctdaio.c @@ -33,7 +33,7 @@ struct daio_rsc_idx { unsigned short right; }; -struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { +static struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { [LINEO1] = {.left = 0x00, .right = 0x01}, [LINEO2] = {.left = 0x18, .right = 0x19}, [LINEO3] = {.left = 0x08, .right = 0x09}, @@ -44,7 +44,7 @@ struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = { [SPDIFI1] = {.left = 0x95, .right = 0x9d}, }; -struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { +static struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = { [LINEO1] = {.left = 0x40, .right = 0x41}, [LINEO2] = {.left = 0x60, .right = 0x61}, [LINEO3] = {.left = 0x50, .right = 0x51}, diff --git a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c index 0275209ca82e..1f9c7c4bbcd8 100644 --- a/sound/pci/emu10k1/emufx.c +++ b/sound/pci/emu10k1/emufx.c @@ -1182,15 +1182,20 @@ static int _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu) u32 *gpr_map; mm_segment_t seg; - if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL || - (icode->gpr_map = (u_int32_t __user *) - kcalloc(512 + 256 + 256 + 2 * 1024, sizeof(u_int32_t), - GFP_KERNEL)) == NULL || - (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, - sizeof(*controls), GFP_KERNEL)) == NULL) { - err = -ENOMEM; - goto __err; - } + err = -ENOMEM; + icode = kzalloc(sizeof(*icode), GFP_KERNEL); + if (!icode) + return err; + + icode->gpr_map = (u_int32_t __user *) kcalloc(512 + 256 + 256 + 2 * 1024, + sizeof(u_int32_t), GFP_KERNEL); + if (!icode->gpr_map) + goto __err_gpr; + controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, + sizeof(*controls), GFP_KERNEL); + if (!controls) + goto __err_ctrls; + gpr_map = (u32 __force *)icode->gpr_map; icode->tram_data_map = icode->gpr_map + 512; @@ -1741,12 +1746,12 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input)) emu->support_tlv = 0; /* clear again */ snd_leave_user(seg); - __err: +__err: kfree(controls); - if (icode != NULL) { - kfree((void __force *)icode->gpr_map); - kfree(icode); - } +__err_ctrls: + kfree((void __force *)icode->gpr_map); +__err_gpr: + kfree(icode); return err; } @@ -1813,18 +1818,26 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) u32 *gpr_map; mm_segment_t seg; - if ((icode = kzalloc(sizeof(*icode), GFP_KERNEL)) == NULL) - return -ENOMEM; - if ((icode->gpr_map = (u_int32_t __user *) - kcalloc(256 + 160 + 160 + 2 * 512, sizeof(u_int32_t), - GFP_KERNEL)) == NULL || - (controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, - sizeof(struct snd_emu10k1_fx8010_control_gpr), - GFP_KERNEL)) == NULL || - (ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL)) == NULL) { - err = -ENOMEM; - goto __err; - } + err = -ENOMEM; + icode = kzalloc(sizeof(*icode), GFP_KERNEL); + if (!icode) + return err; + + icode->gpr_map = (u_int32_t __user *) kcalloc(256 + 160 + 160 + 2 * 512, + sizeof(u_int32_t), GFP_KERNEL); + if (!icode->gpr_map) + goto __err_gpr; + + controls = kcalloc(SND_EMU10K1_GPR_CONTROLS, + sizeof(struct snd_emu10k1_fx8010_control_gpr), + GFP_KERNEL); + if (!controls) + goto __err_ctrls; + + ipcm = kzalloc(sizeof(*ipcm), GFP_KERNEL); + if (!ipcm) + goto __err_ipcm; + gpr_map = (u32 __force *)icode->gpr_map; icode->tram_data_map = icode->gpr_map + 256; @@ -2363,13 +2376,14 @@ static int _snd_emu10k1_init_efx(struct snd_emu10k1 *emu) snd_leave_user(seg); if (err >= 0) err = snd_emu10k1_ipcm_poke(emu, ipcm); - __err: +__err: kfree(ipcm); +__err_ipcm: kfree(controls); - if (icode != NULL) { - kfree((void __force *)icode->gpr_map); - kfree(icode); - } +__err_ctrls: + kfree((void __force *)icode->gpr_map); +__err_gpr: + kfree(icode); return err; } diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 63c99090a4ec..20d065a17226 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -151,10 +151,8 @@ static int snd_hda_do_attach(struct hda_beep *beep) int err; input_dev = input_allocate_device(); - if (!input_dev) { - printk(KERN_INFO "hda_beep: unable to allocate input device\n"); + if (!input_dev) return -ENOMEM; - } /* setup digital beep device */ input_dev->name = "HDA Digital PCBeep"; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5b6c4e3c92ca..a1632f4056d4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -565,7 +565,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; if (val == 0 && null_count++) { /* no second chance */ - snd_printk(KERN_WARNING "hda_codec: " + snd_printdd("hda_codec: " "invalid CONNECT_LIST verb %x[%i]:%x\n", nid, i, parm); return 0; @@ -5395,11 +5395,6 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, mout->hp_out_nid[i], stream_tag, 0, format); - for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) - if (!mout->no_share_stream && mout->extra_out_nid[i]) - snd_hda_codec_setup_stream(codec, - mout->extra_out_nid[i], - stream_tag, 0, format); /* surrounds */ for (i = 1; i < mout->num_dacs; i++) { @@ -5410,6 +5405,20 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format); } + + /* extra surrounds */ + for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) { + int ch = 0; + if (!mout->extra_out_nid[i]) + break; + if (chs >= (i + 1) * 2) + ch = i * 2; + else if (!mout->no_share_stream) + break; + snd_hda_codec_setup_stream(codec, mout->extra_out_nid[i], + stream_tag, ch, format); + } + return 0; } EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare); diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 7aa9870040c1..77db69480c19 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -698,6 +698,7 @@ struct hda_bus { unsigned int in_reset:1; /* during reset operation */ unsigned int power_keep_link_on:1; /* don't power off HDA link */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ + unsigned int avoid_link_reset:1; /* don't reset link at runtime PM */ int primary_dig_out_type; /* primary digital out PCM type */ }; diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c index d0d7ac1e99d2..32d3e3855a6e 100644 --- a/sound/pci/hda/hda_eld.c +++ b/sound/pci/hda/hda_eld.c @@ -2,6 +2,7 @@ * Generic routines and proc interface for ELD(EDID Like Data) information * * Copyright(c) 2008 Intel Corporation. + * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi> * * Authors: * Wu Fengguang <wfg@linux.intel.com> @@ -478,10 +479,9 @@ static void hdmi_print_sad_info(int i, struct cea_sad *a, snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile); } -static void hdmi_print_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +void snd_hdmi_print_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer) { - struct hdmi_eld *eld = entry->private_data; struct parsed_hdmi_eld *e = &eld->info; char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE]; int i; @@ -500,13 +500,10 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, [4 ... 7] = "reserved" }; - mutex_lock(&eld->lock); snd_iprintf(buffer, "monitor_present\t\t%d\n", eld->monitor_present); snd_iprintf(buffer, "eld_valid\t\t%d\n", eld->eld_valid); - if (!eld->eld_valid) { - mutex_unlock(&eld->lock); + if (!eld->eld_valid) return; - } snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name); snd_iprintf(buffer, "connection_type\t\t%s\n", eld_connection_type_names[e->conn_type]); @@ -528,13 +525,11 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry, for (i = 0; i < e->sad_count; i++) hdmi_print_sad_info(i, e->sad + i, buffer); - mutex_unlock(&eld->lock); } -static void hdmi_write_eld_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +void snd_hdmi_write_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer) { - struct hdmi_eld *eld = entry->private_data; struct parsed_hdmi_eld *e = &eld->info; char line[64]; char name[64]; @@ -542,7 +537,6 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, long long val; unsigned int n; - mutex_lock(&eld->lock); while (!snd_info_get_line(buffer, line, sizeof(line))) { if (sscanf(line, "%s %llx", name, &val) != 2) continue; @@ -594,38 +588,7 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry, e->sad_count = n + 1; } } - mutex_unlock(&eld->lock); -} - - -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, - int index) -{ - char name[32]; - struct snd_info_entry *entry; - int err; - - snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->bus->card, name, &entry); - if (err < 0) - return err; - - snd_info_set_text_ops(entry, eld, hdmi_print_eld_info); - entry->c.text.write = hdmi_write_eld_info; - entry->mode |= S_IWUSR; - eld->proc_entry = entry; - - return 0; -} - -void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld) -{ - if (!codec->bus->shutdown && eld->proc_entry) { - snd_device_free(codec->bus->card, eld->proc_entry); - eld->proc_entry = NULL; - } } - #endif /* CONFIG_PROC_FS */ /* update PCM info based on ELD */ @@ -671,3 +634,153 @@ void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, hinfo->maxbps = min(hinfo->maxbps, maxbps); hinfo->channels_max = min(hinfo->channels_max, channels_max); } + + +/* ATI/AMD specific stuff (ELD emulation) */ + +#define ATI_VERB_SET_AUDIO_DESCRIPTOR 0x776 +#define ATI_VERB_SET_SINK_INFO_INDEX 0x780 +#define ATI_VERB_GET_SPEAKER_ALLOCATION 0xf70 +#define ATI_VERB_GET_AUDIO_DESCRIPTOR 0xf76 +#define ATI_VERB_GET_AUDIO_VIDEO_DELAY 0xf7b +#define ATI_VERB_GET_SINK_INFO_INDEX 0xf80 +#define ATI_VERB_GET_SINK_INFO_DATA 0xf81 + +#define ATI_SPKALLOC_SPKALLOC 0x007f +#define ATI_SPKALLOC_TYPE_HDMI 0x0100 +#define ATI_SPKALLOC_TYPE_DISPLAYPORT 0x0200 + +/* first three bytes are just standard SAD */ +#define ATI_AUDIODESC_CHANNELS 0x00000007 +#define ATI_AUDIODESC_RATES 0x0000ff00 +#define ATI_AUDIODESC_LPCM_STEREO_RATES 0xff000000 + +/* in standard HDMI VSDB format */ +#define ATI_DELAY_VIDEO_LATENCY 0x000000ff +#define ATI_DELAY_AUDIO_LATENCY 0x0000ff00 + +enum ati_sink_info_idx { + ATI_INFO_IDX_MANUFACTURER_ID = 0, + ATI_INFO_IDX_PRODUCT_ID = 1, + ATI_INFO_IDX_SINK_DESC_LEN = 2, + ATI_INFO_IDX_PORT_ID_LOW = 3, + ATI_INFO_IDX_PORT_ID_HIGH = 4, + ATI_INFO_IDX_SINK_DESC_FIRST = 5, + ATI_INFO_IDX_SINK_DESC_LAST = 22, /* max len 18 bytes */ +}; + +int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size, bool rev3_or_later) +{ + int spkalloc, ati_sad, aud_synch; + int sink_desc_len = 0; + int pos, i; + + /* ATI/AMD does not have ELD, emulate it */ + + spkalloc = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SPEAKER_ALLOCATION, 0); + + if (!spkalloc) { + snd_printd(KERN_INFO "HDMI ATI/AMD: no speaker allocation for ELD\n"); + return -EINVAL; + } + + memset(buf, 0, ELD_FIXED_BYTES + ELD_MAX_MNL + ELD_MAX_SAD * 3); + + /* version */ + buf[0] = ELD_VER_CEA_861D << 3; + + /* speaker allocation from EDID */ + buf[7] = spkalloc & ATI_SPKALLOC_SPKALLOC; + + /* is DisplayPort? */ + if (spkalloc & ATI_SPKALLOC_TYPE_DISPLAYPORT) + buf[5] |= 0x04; + + pos = ELD_FIXED_BYTES; + + if (rev3_or_later) { + int sink_info; + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_LOW); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 8); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PORT_ID_HIGH); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le32(sink_info, buf + 12); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_MANUFACTURER_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 16); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_PRODUCT_ID); + sink_info = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + put_unaligned_le16(sink_info, buf + 18); + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_LEN); + sink_desc_len = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + + if (sink_desc_len > ELD_MAX_MNL) { + snd_printd(KERN_INFO "HDMI ATI/AMD: Truncating HDMI sink description with length %d\n", + sink_desc_len); + sink_desc_len = ELD_MAX_MNL; + } + + buf[4] |= sink_desc_len; + + for (i = 0; i < sink_desc_len; i++) { + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_SINK_INFO_INDEX, ATI_INFO_IDX_SINK_DESC_FIRST + i); + buf[pos++] = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_SINK_INFO_DATA, 0); + } + } + + for (i = AUDIO_CODING_TYPE_LPCM; i <= AUDIO_CODING_TYPE_WMAPRO; i++) { + if (i == AUDIO_CODING_TYPE_SACD || i == AUDIO_CODING_TYPE_DST) + continue; /* not handled by ATI/AMD */ + + snd_hda_codec_write(codec, nid, 0, ATI_VERB_SET_AUDIO_DESCRIPTOR, i << 3); + ati_sad = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_DESCRIPTOR, 0); + + if (ati_sad & ATI_AUDIODESC_RATES) { + /* format is supported, copy SAD as-is */ + buf[pos++] = (ati_sad & 0x0000ff) >> 0; + buf[pos++] = (ati_sad & 0x00ff00) >> 8; + buf[pos++] = (ati_sad & 0xff0000) >> 16; + } + + if (i == AUDIO_CODING_TYPE_LPCM + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) + && (ati_sad & ATI_AUDIODESC_LPCM_STEREO_RATES) >> 16 != (ati_sad & ATI_AUDIODESC_RATES)) { + /* for PCM there is a separate stereo rate mask */ + buf[pos++] = ((ati_sad & 0x000000ff) & ~ATI_AUDIODESC_CHANNELS) | 0x1; + /* rates from the extra byte */ + buf[pos++] = (ati_sad & 0xff000000) >> 24; + buf[pos++] = (ati_sad & 0x00ff0000) >> 16; + } + } + + if (pos == ELD_FIXED_BYTES + sink_desc_len) { + snd_printd(KERN_INFO "HDMI ATI/AMD: no audio descriptors for ELD\n"); + return -EINVAL; + } + + aud_synch = snd_hda_codec_read(codec, nid, 0, ATI_VERB_GET_AUDIO_VIDEO_DELAY, 0); + if ((aud_synch & ATI_DELAY_VIDEO_LATENCY) && (aud_synch & ATI_DELAY_AUDIO_LATENCY)) { + int video_latency = (aud_synch & ATI_DELAY_VIDEO_LATENCY) - 1; + int audio_latency = ((aud_synch & ATI_DELAY_AUDIO_LATENCY) >> 8) - 1; + + if (video_latency > audio_latency) + buf[6] = min(video_latency - audio_latency, 0xfa); + } + + /* Baseline length */ + buf[2] = pos - 4; + + /* SAD count */ + buf[5] |= ((pos - ELD_FIXED_BYTES - sink_desc_len) / 3) << 4; + + *eld_size = pos; + + return 0; +} diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 6e61a019aa5e..a0a06f79daa3 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2986,7 +2986,8 @@ static int azx_runtime_suspend(struct device *dev) STATESTS_INT_MASK); azx_stop_chip(chip); - azx_enter_link_reset(chip); + if (!chip->bus->avoid_link_reset) + azx_enter_link_reset(chip); azx_clear_irq_pending(chip); if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) hda_display_power(false); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 2e7493ef8ee0..d398b648bb5d 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -428,6 +428,7 @@ enum { HDA_FIXUP_ACT_PROBE, HDA_FIXUP_ACT_INIT, HDA_FIXUP_ACT_BUILD, + HDA_FIXUP_ACT_FREE, }; int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); @@ -751,10 +752,6 @@ struct hdmi_eld { int eld_size; char eld_buffer[ELD_MAX_SIZE]; struct parsed_hdmi_eld info; - struct mutex lock; -#ifdef CONFIG_PROC_FS - struct snd_info_entry *proc_entry; -#endif }; int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid); @@ -766,21 +763,15 @@ void snd_hdmi_show_eld(struct parsed_hdmi_eld *e); void snd_hdmi_eld_update_pcm_info(struct parsed_hdmi_eld *e, struct hda_pcm_stream *hinfo); +int snd_hdmi_get_eld_ati(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size, + bool rev3_or_later); + #ifdef CONFIG_PROC_FS -int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld, - int index); -void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld); -#else -static inline int snd_hda_eld_proc_new(struct hda_codec *codec, - struct hdmi_eld *eld, - int index) -{ - return 0; -} -static inline void snd_hda_eld_proc_free(struct hda_codec *codec, - struct hdmi_eld *eld) -{ -} +void snd_hdmi_print_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer); +void snd_hdmi_write_eld_info(struct hdmi_eld *eld, + struct snd_info_buffer *buffer); #endif #define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80 diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 6e9876f27d95..54d14793725a 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -759,7 +759,7 @@ struct ca0132_spec { /* * CA0132 codec access */ -unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, +static unsigned int codec_send_command(struct hda_codec *codec, hda_nid_t nid, unsigned int verb, unsigned int parm, unsigned int *res) { unsigned int response; diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index ec68eaea0336..993b25c17711 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3208,11 +3208,17 @@ static int cx_auto_init(struct hda_codec *codec) return 0; } +static void cx_auto_free(struct hda_codec *codec) +{ + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_FREE); + snd_hda_gen_free(codec); +} + static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cx_auto_init, - .free = snd_hda_gen_free, + .free = cx_auto_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .check_power_status = snd_hda_gen_check_power_status, @@ -3232,8 +3238,84 @@ enum { CXT_FIXUP_HEADPHONE_MIC_PIN, CXT_FIXUP_HEADPHONE_MIC, CXT_FIXUP_GPIO1, + CXT_FIXUP_THINKPAD_ACPI, }; +#if IS_ENABLED(CONFIG_THINKPAD_ACPI) + +#include <linux/thinkpad_acpi.h> + +static int (*led_set_func)(int, bool); + +static void update_tpacpi_mute_led(void *private_data, int enabled) +{ + struct hda_codec *codec = private_data; + struct conexant_spec *spec = codec->spec; + + if (spec->dynamic_eapd) + cx_auto_vmaster_hook(private_data, enabled); + + if (led_set_func) + led_set_func(TPACPI_LED_MUTE, !enabled); +} + +static void update_tpacpi_micmute_led(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) +{ + if (!ucontrol || !led_set_func) + return; + if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { + /* TODO: How do I verify if it's a mono or stereo here? */ + bool val = ucontrol->value.integer.value[0] || ucontrol->value.integer.value[1]; + led_set_func(TPACPI_LED_MICMUTE, !val); + } +} + +static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + + bool removefunc = false; + + if (action == HDA_FIXUP_ACT_PROBE) { + if (!led_set_func) + led_set_func = symbol_request(tpacpi_led_set); + if (!led_set_func) { + snd_printk(KERN_WARNING "Failed to find thinkpad-acpi symbol tpacpi_led_set\n"); + return; + } + + removefunc = true; + if (led_set_func(TPACPI_LED_MUTE, false) >= 0) { + spec->gen.vmaster_mute.hook = update_tpacpi_mute_led; + removefunc = false; + } + if (led_set_func(TPACPI_LED_MICMUTE, false) >= 0) { + if (spec->gen.num_adc_nids > 1) + snd_printdd("Skipping micmute LED control due to several ADCs"); + else { + spec->gen.cap_sync_hook = update_tpacpi_micmute_led; + removefunc = false; + } + } + } + + if (led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { + symbol_put(tpacpi_led_set); + led_set_func = NULL; + } +} + +#else + +static void cxt_fixup_thinkpad_acpi(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ +} + +#endif + static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -3344,6 +3426,8 @@ static const struct hda_fixup cxt_fixups[] = { [CXT_PINCFG_LENOVO_TP410] = { .type = HDA_FIXUP_PINS, .v.pins = cxt_pincfg_lenovo_tp410, + .chained = true, + .chain_id = CXT_FIXUP_THINKPAD_ACPI, }, [CXT_PINCFG_LEMOTE_A1004] = { .type = HDA_FIXUP_PINS, @@ -3385,6 +3469,10 @@ static const struct hda_fixup cxt_fixups[] = { { } }, }, + [CXT_FIXUP_THINKPAD_ACPI] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt_fixup_thinkpad_acpi, + }, }; static const struct snd_pci_quirk cxt5051_fixups[] = { @@ -3507,7 +3595,7 @@ static int patch_conexant_auto(struct hda_codec *codec) return 0; error: - snd_hda_gen_free(codec); + cx_auto_free(codec); return err; } diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 50173d412ac5..772827b9fb20 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -6,6 +6,7 @@ * Copyright (c) 2006 ATI Technologies Inc. * Copyright (c) 2008 NVIDIA Corp. All rights reserved. * Copyright (c) 2008 Wei Ni <wni@nvidia.com> + * Copyright (c) 2013 Anssi Hannula <anssi.hannula@iki.fi> * * Authors: * Wu Fengguang <wfg@linux.intel.com> @@ -63,9 +64,11 @@ struct hdmi_spec_per_pin { hda_nid_t pin_nid; int num_mux_nids; hda_nid_t mux_nids[HDA_MAX_CONNECTIONS]; + hda_nid_t cvt_nid; struct hda_codec *codec; struct hdmi_eld sink_eld; + struct mutex lock; struct delayed_work work; struct snd_kcontrol *eld_ctl; int repoll_count; @@ -75,6 +78,42 @@ struct hdmi_spec_per_pin { bool chmap_set; /* channel-map override by ALSA API? */ unsigned char chmap[8]; /* ALSA API channel-map */ char pcm_name[8]; /* filled in build_pcm callbacks */ +#ifdef CONFIG_PROC_FS + struct snd_info_entry *proc_entry; +#endif +}; + +struct cea_channel_speaker_allocation; + +/* operations used by generic code that can be overridden by patches */ +struct hdmi_ops { + int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid, + unsigned char *buf, int *eld_size); + + /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */ + int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot); + int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot, int channel); + + void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid, + int ca, int active_channels, int conn_type); + + /* enable/disable HBR (HD passthrough) */ + int (*pin_hbr_setup)(struct hda_codec *codec, hda_nid_t pin_nid, bool hbr); + + int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format); + + /* Helpers for producing the channel map TLVs. These can be overridden + * for devices that have non-standard mapping requirements. */ + int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap, + int channels); + void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels); + + /* check that the user-given chmap is supported */ + int (*chmap_validate)(int ca, int channels, unsigned char *chmap); }; struct hdmi_spec { @@ -88,8 +127,9 @@ struct hdmi_spec { unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; + struct hdmi_ops ops; /* - * Non-generic ATI/NVIDIA specific + * Non-generic VIA/NVIDIA specific */ struct hda_multi_out multiout; struct hda_pcm_stream pcm_playback; @@ -348,17 +388,19 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; int pin_idx; uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; pin_idx = kcontrol->private_value; - eld = &get_pin(spec, pin_idx)->sink_eld; + per_pin = get_pin(spec, pin_idx); + eld = &per_pin->sink_eld; - mutex_lock(&eld->lock); + mutex_lock(&per_pin->lock); uinfo->count = eld->eld_valid ? eld->eld_size : 0; - mutex_unlock(&eld->lock); + mutex_unlock(&per_pin->lock); return 0; } @@ -368,15 +410,17 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_pin *per_pin; struct hdmi_eld *eld; int pin_idx; pin_idx = kcontrol->private_value; - eld = &get_pin(spec, pin_idx)->sink_eld; + per_pin = get_pin(spec, pin_idx); + eld = &per_pin->sink_eld; - mutex_lock(&eld->lock); + mutex_lock(&per_pin->lock); if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) { - mutex_unlock(&eld->lock); + mutex_unlock(&per_pin->lock); snd_BUG(); return -EINVAL; } @@ -386,7 +430,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, if (eld->eld_valid) memcpy(ucontrol->value.bytes.data, eld->eld_buffer, eld->eld_size); - mutex_unlock(&eld->lock); + mutex_unlock(&per_pin->lock); return 0; } @@ -477,6 +521,68 @@ static void hdmi_set_channel_count(struct hda_codec *codec, AC_VERB_SET_CVT_CHAN_COUNT, chs - 1); } +/* + * ELD proc files + */ + +#ifdef CONFIG_PROC_FS +static void print_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_spec_per_pin *per_pin = entry->private_data; + + mutex_lock(&per_pin->lock); + snd_hdmi_print_eld_info(&per_pin->sink_eld, buffer); + mutex_unlock(&per_pin->lock); +} + +static void write_eld_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hdmi_spec_per_pin *per_pin = entry->private_data; + + mutex_lock(&per_pin->lock); + snd_hdmi_write_eld_info(&per_pin->sink_eld, buffer); + mutex_unlock(&per_pin->lock); +} + +static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) +{ + char name[32]; + struct hda_codec *codec = per_pin->codec; + struct snd_info_entry *entry; + int err; + + snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); + err = snd_card_proc_new(codec->bus->card, name, &entry); + if (err < 0) + return err; + + snd_info_set_text_ops(entry, per_pin, print_eld_info); + entry->c.text.write = write_eld_info; + entry->mode |= S_IWUSR; + per_pin->proc_entry = entry; + + return 0; +} + +static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) +{ + if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) { + snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry); + per_pin->proc_entry = NULL; + } +} +#else +static inline int eld_proc_new(struct hdmi_spec_per_pin *per_pin, + int index) +{ + return 0; +} +static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin) +{ +} +#endif /* * Channel mapping routines @@ -577,74 +683,91 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid) { #ifdef CONFIG_SND_DEBUG_VERBOSE + struct hdmi_spec *spec = codec->spec; int i; - int slot; + int channel; for (i = 0; i < 8; i++) { - slot = snd_hda_codec_read(codec, pin_nid, 0, - AC_VERB_GET_HDMI_CHAN_SLOT, i); + channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i); printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n", - slot >> 4, slot & 0xf); + channel, i); } #endif } - static void hdmi_std_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, bool non_pcm, int ca) { + struct hdmi_spec *spec = codec->spec; + struct cea_channel_speaker_allocation *ch_alloc; int i; int err; int order; int non_pcm_mapping[8]; order = get_channel_allocation_order(ca); + ch_alloc = &channel_allocations[order]; if (hdmi_channel_mapping[ca][1] == 0) { - for (i = 0; i < channel_allocations[order].channels; i++) - hdmi_channel_mapping[ca][i] = i | (i << 4); - for (; i < 8; i++) - hdmi_channel_mapping[ca][i] = 0xf | (i << 4); + int hdmi_slot = 0; + /* fill actual channel mappings in ALSA channel (i) order */ + for (i = 0; i < ch_alloc->channels; i++) { + while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8)) + hdmi_slot++; /* skip zero slots */ + + hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++; + } + /* fill the rest of the slots with ALSA channel 0xf */ + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) + if (!ch_alloc->speakers[7 - hdmi_slot]) + hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot; } if (non_pcm) { - for (i = 0; i < channel_allocations[order].channels; i++) - non_pcm_mapping[i] = i | (i << 4); + for (i = 0; i < ch_alloc->channels; i++) + non_pcm_mapping[i] = (i << 4) | i; for (; i < 8; i++) - non_pcm_mapping[i] = 0xf | (i << 4); + non_pcm_mapping[i] = (0xf << 4) | i; } for (i = 0; i < 8; i++) { - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]); + int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i]; + int hdmi_slot = slotsetup & 0x0f; + int channel = (slotsetup & 0xf0) >> 4; + err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel); if (err) { snd_printdd(KERN_NOTICE "HDMI: channel mapping failed\n"); break; } } - - hdmi_debug_channel_mapping(codec, pin_nid); } struct channel_map_table { unsigned char map; /* ALSA API channel map position */ - unsigned char cea_slot; /* CEA slot value */ int spk_mask; /* speaker position bit mask */ }; static struct channel_map_table map_tables[] = { - { SNDRV_CHMAP_FL, 0x00, FL }, - { SNDRV_CHMAP_FR, 0x01, FR }, - { SNDRV_CHMAP_RL, 0x04, RL }, - { SNDRV_CHMAP_RR, 0x05, RR }, - { SNDRV_CHMAP_LFE, 0x02, LFE }, - { SNDRV_CHMAP_FC, 0x03, FC }, - { SNDRV_CHMAP_RLC, 0x06, RLC }, - { SNDRV_CHMAP_RRC, 0x07, RRC }, + { SNDRV_CHMAP_FL, FL }, + { SNDRV_CHMAP_FR, FR }, + { SNDRV_CHMAP_RL, RL }, + { SNDRV_CHMAP_RR, RR }, + { SNDRV_CHMAP_LFE, LFE }, + { SNDRV_CHMAP_FC, FC }, + { SNDRV_CHMAP_RLC, RLC }, + { SNDRV_CHMAP_RRC, RRC }, + { SNDRV_CHMAP_RC, RC }, + { SNDRV_CHMAP_FLC, FLC }, + { SNDRV_CHMAP_FRC, FRC }, + { SNDRV_CHMAP_FLH, FLH }, + { SNDRV_CHMAP_FRH, FRH }, + { SNDRV_CHMAP_FLW, FLW }, + { SNDRV_CHMAP_FRW, FRW }, + { SNDRV_CHMAP_TC, TC }, + { SNDRV_CHMAP_FCH, FCH }, {} /* terminator */ }; @@ -660,25 +783,19 @@ static int to_spk_mask(unsigned char c) } /* from ALSA API channel position to CEA slot */ -static int to_cea_slot(unsigned char c) +static int to_cea_slot(int ordered_ca, unsigned char pos) { - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->map == c) - return t->cea_slot; - } - return 0x0f; -} + int mask = to_spk_mask(pos); + int i; -/* from CEA slot to ALSA API channel position */ -static int from_cea_slot(unsigned char c) -{ - struct channel_map_table *t = map_tables; - for (; t->map; t++) { - if (t->cea_slot == c) - return t->map; + if (mask) { + for (i = 0; i < 8; i++) { + if (channel_allocations[ordered_ca].speakers[7 - i] == mask) + return i; + } } - return 0; + + return -1; } /* from speaker bit mask to ALSA API channel position */ @@ -692,6 +809,14 @@ static int spk_to_chmap(int spk) return 0; } +/* from CEA slot to ALSA API channel position */ +static int from_cea_slot(int ordered_ca, unsigned char slot) +{ + int mask = channel_allocations[ordered_ca].speakers[7 - slot]; + + return spk_to_chmap(mask); +} + /* get the CA index corresponding to the given ALSA API channel map */ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) { @@ -718,18 +843,29 @@ static int hdmi_manual_channel_allocation(int chs, unsigned char *map) /* set up the channel slots for the given ALSA API channel map */ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, hda_nid_t pin_nid, - int chs, unsigned char *map) + int chs, unsigned char *map, + int ca) { - int i; - for (i = 0; i < 8; i++) { - int val, err; - if (i < chs) - val = to_cea_slot(map[i]); - else - val = 0xf; - val |= (i << 4); - err = snd_hda_codec_write(codec, pin_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, val); + struct hdmi_spec *spec = codec->spec; + int ordered_ca = get_channel_allocation_order(ca); + int alsa_pos, hdmi_slot; + int assignments[8] = {[0 ... 7] = 0xf}; + + for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) { + + hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]); + + if (hdmi_slot < 0) + continue; /* unassigned channel */ + + assignments[hdmi_slot] = alsa_pos; + } + + for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) { + int err; + + err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, + assignments[hdmi_slot]); if (err) return -EINVAL; } @@ -740,9 +876,10 @@ static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec, static void hdmi_setup_fake_chmap(unsigned char *map, int ca) { int i; + int ordered_ca = get_channel_allocation_order(ca); for (i = 0; i < 8; i++) { - if (i < channel_allocations[ca].channels) - map[i] = from_cea_slot((hdmi_channel_mapping[ca][i] >> 4) & 0x0f); + if (i < channel_allocations[ordered_ca].channels) + map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f); else map[i] = 0; } @@ -755,11 +892,29 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec, { if (!non_pcm && chmap_set) { hdmi_manual_setup_channel_mapping(codec, pin_nid, - channels, map); + channels, map, ca); } else { hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca); hdmi_setup_fake_chmap(map, ca); } + + hdmi_debug_channel_mapping(codec, pin_nid); +} + +static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot, int channel) +{ + return snd_hda_codec_write(codec, pin_nid, 0, + AC_VERB_SET_HDMI_CHAN_SLOT, + (channel << 4) | asp_slot); +} + +static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot) +{ + return (snd_hda_codec_read(codec, pin_nid, 0, + AC_VERB_GET_HDMI_CHAN_SLOT, + asp_slot) & 0xf0) >> 4; } /* @@ -883,15 +1038,64 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, return true; } +static void hdmi_pin_setup_infoframe(struct hda_codec *codec, + hda_nid_t pin_nid, + int ca, int active_channels, + int conn_type) +{ + union audio_infoframe ai; + + if (conn_type == 0) { /* HDMI */ + struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; + + hdmi_ai->type = 0x84; + hdmi_ai->ver = 0x01; + hdmi_ai->len = 0x0a; + hdmi_ai->CC02_CT47 = active_channels - 1; + hdmi_ai->CA = ca; + hdmi_checksum_audio_infoframe(hdmi_ai); + } else if (conn_type == 1) { /* DisplayPort */ + struct dp_audio_infoframe *dp_ai = &ai.dp; + + dp_ai->type = 0x84; + dp_ai->len = 0x1b; + dp_ai->ver = 0x11 << 2; + dp_ai->CC02_CT47 = active_channels - 1; + dp_ai->CA = ca; + } else { + snd_printd("HDMI: unknown connection type at pin %d\n", + pin_nid); + return; + } + + /* + * sizeof(ai) is used instead of sizeof(*hdmi_ai) or + * sizeof(*dp_ai) to avoid partial match/update problems when + * the user switches between HDMI/DP monitors. + */ + if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, + sizeof(ai))) { + snd_printdd("hdmi_pin_setup_infoframe: " + "pin=%d channels=%d ca=0x%02x\n", + pin_nid, + active_channels, ca); + hdmi_stop_infoframe_trans(codec, pin_nid); + hdmi_fill_audio_infoframe(codec, pin_nid, + ai.bytes, sizeof(ai)); + hdmi_start_infoframe_trans(codec, pin_nid); + } +} + static void hdmi_setup_audio_infoframe(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin, bool non_pcm) { + struct hdmi_spec *spec = codec->spec; hda_nid_t pin_nid = per_pin->pin_nid; int channels = per_pin->channels; + int active_channels; struct hdmi_eld *eld; - int ca; - union audio_infoframe ai; + int ca, ordered_ca; if (!channels) return; @@ -912,29 +1116,10 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, if (ca < 0) ca = 0; - memset(&ai, 0, sizeof(ai)); - if (eld->info.conn_type == 0) { /* HDMI */ - struct hdmi_audio_infoframe *hdmi_ai = &ai.hdmi; + ordered_ca = get_channel_allocation_order(ca); + active_channels = channel_allocations[ordered_ca].channels; - hdmi_ai->type = 0x84; - hdmi_ai->ver = 0x01; - hdmi_ai->len = 0x0a; - hdmi_ai->CC02_CT47 = channels - 1; - hdmi_ai->CA = ca; - hdmi_checksum_audio_infoframe(hdmi_ai); - } else if (eld->info.conn_type == 1) { /* DisplayPort */ - struct dp_audio_infoframe *dp_ai = &ai.dp; - - dp_ai->type = 0x84; - dp_ai->len = 0x1b; - dp_ai->ver = 0x11 << 2; - dp_ai->CC02_CT47 = channels - 1; - dp_ai->CA = ca; - } else { - snd_printd("HDMI: unknown connection type at pin %d\n", - pin_nid); - return; - } + hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels); /* * always configure channel mapping, it may have been changed by the @@ -944,27 +1129,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec, channels, per_pin->chmap, per_pin->chmap_set); - /* - * sizeof(ai) is used instead of sizeof(*hdmi_ai) or - * sizeof(*dp_ai) to avoid partial match/update problems when - * the user switches between HDMI/DP monitors. - */ - if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, - sizeof(ai))) { - snd_printdd("hdmi_setup_audio_infoframe: " - "pin=%d channels=%d\n", - pin_nid, - channels); - hdmi_stop_infoframe_trans(codec, pin_nid); - hdmi_fill_audio_infoframe(codec, pin_nid, - ai.bytes, sizeof(ai)); - hdmi_start_infoframe_trans(codec, pin_nid); - } + spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels, + eld->info.conn_type); per_pin->non_pcm = non_pcm; } - /* * Unsolicited events */ @@ -1067,26 +1237,22 @@ static void haswell_verify_D0(struct hda_codec *codec, #define is_hbr_format(format) \ ((format & AC_FMT_TYPE_NON_PCM) && (format & AC_FMT_CHAN_MASK) == 7) -static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, - hda_nid_t pin_nid, u32 stream_tag, int format) +static int hdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, + bool hbr) { - int pinctl; - int new_pinctl = 0; - - if (is_haswell(codec)) - haswell_verify_D0(codec, cvt_nid, pin_nid); + int pinctl, new_pinctl; if (snd_hda_query_pin_caps(codec, pin_nid) & AC_PINCAP_HBR) { pinctl = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); new_pinctl = pinctl & ~AC_PINCTL_EPT; - if (is_hbr_format(format)) + if (hbr) new_pinctl |= AC_PINCTL_EPT_HBR; else new_pinctl |= AC_PINCTL_EPT_NATIVE; - snd_printdd("hdmi_setup_stream: " + snd_printdd("hdmi_pin_hbr_setup: " "NID=0x%x, %spinctl=0x%x\n", pin_nid, pinctl == new_pinctl ? "" : "new-", @@ -1096,11 +1262,26 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_pinctl); + } else if (hbr) + return -EINVAL; - } - if (is_hbr_format(format) && !new_pinctl) { + return 0; +} + +static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) +{ + struct hdmi_spec *spec = codec->spec; + int err; + + if (is_haswell(codec)) + haswell_verify_D0(codec, cvt_nid, pin_nid); + + err = spec->ops.pin_hbr_setup(codec, pin_nid, is_hbr_format(format)); + + if (err) { snd_printdd("hdmi_setup_stream: HBR is not supported\n"); - return -EINVAL; + return err; } snd_hda_codec_setup_stream(codec, cvt_nid, stream_tag, 0, format); @@ -1217,6 +1398,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo, per_cvt = get_cvt(spec, cvt_idx); /* Claim converter */ per_cvt->assigned = 1; + per_pin->cvt_nid = per_cvt->cvt_nid; hinfo->nid = per_cvt->cvt_nid; snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, @@ -1302,6 +1484,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) bool update_eld = false; bool eld_changed = false; + mutex_lock(&per_pin->lock); pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE); if (pin_eld->monitor_present) eld->eld_valid = !!(present & AC_PINSENSE_ELDV); @@ -1313,7 +1496,7 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) codec->addr, pin_nid, pin_eld->monitor_present, eld->eld_valid); if (eld->eld_valid) { - if (snd_hdmi_get_eld(codec, pin_nid, eld->eld_buffer, + if (spec->ops.pin_get_eld(codec, pin_nid, eld->eld_buffer, &eld->eld_size) < 0) eld->eld_valid = false; else { @@ -1331,11 +1514,10 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) queue_delayed_work(codec->bus->workq, &per_pin->work, msecs_to_jiffies(300)); - return; + goto unlock; } } - mutex_lock(&pin_eld->lock); if (pin_eld->eld_valid && !eld->eld_valid) { update_eld = true; eld_changed = true; @@ -1352,20 +1534,22 @@ static void hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) pin_eld->eld_size = eld->eld_size; pin_eld->info = eld->info; - /* Haswell-specific workaround: re-setup when the transcoder is - * changed during the stream playback + /* + * Re-setup pin and infoframe. This is needed e.g. when + * - sink is first plugged-in (infoframe is not set up if !monitor_present) + * - transcoder can change during stream playback on Haswell */ - if (is_haswell(codec) && - eld->eld_valid && !old_eld_valid && per_pin->setup) + if (eld->eld_valid && !old_eld_valid && per_pin->setup) hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); } - mutex_unlock(&pin_eld->lock); if (eld_changed) snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); + unlock: + mutex_unlock(&per_pin->lock); } static void hdmi_repoll_eld(struct work_struct *work) @@ -1536,14 +1720,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, bool non_pcm; non_pcm = check_non_pcm_per_cvt(codec, cvt_nid); + mutex_lock(&per_pin->lock); per_pin->channels = substream->runtime->channels; per_pin->setup = true; - hdmi_set_channel_count(codec, cvt_nid, substream->runtime->channels); - hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); + mutex_unlock(&per_pin->lock); - return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); + return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); } static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -1579,11 +1763,14 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_pin = get_pin(spec, pin_idx); snd_hda_spdif_ctls_unassign(codec, pin_idx); + + mutex_lock(&per_pin->lock); per_pin->chmap_set = false; memset(per_pin->chmap, 0, sizeof(per_pin->chmap)); per_pin->setup = false; per_pin->channels = 0; + mutex_unlock(&per_pin->lock); } return 0; @@ -1612,14 +1799,40 @@ static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol, return 0; } +static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, + int channels) +{ + /* If the speaker allocation matches the channel count, it is OK.*/ + if (cap->channels != channels) + return -1; + + /* all channels are remappable freely */ + return SNDRV_CTL_TLVT_CHMAP_VAR; +} + +static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int spk = cap->speakers[c]; + if (!spk) + continue; + + chmap[count++] = spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, unsigned int size, unsigned int __user *tlv) { struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = info->private_data; struct hdmi_spec *spec = codec->spec; - const unsigned int valid_mask = - FL | FR | RL | RR | LFE | FC | RLC | RRC; unsigned int __user *dst; int chs, count = 0; @@ -1630,18 +1843,19 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, size -= 8; dst = tlv + 2; for (chs = 2; chs <= spec->channels_max; chs++) { - int i, c; + int i; struct cea_channel_speaker_allocation *cap; cap = channel_allocations; for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) { int chs_bytes = chs * 4; - if (cap->channels != chs) - continue; - if (cap->spk_mask & ~valid_mask) + int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs); + unsigned int tlv_chmap[8]; + + if (type < 0) continue; if (size < 8) return -ENOMEM; - if (put_user(SNDRV_CTL_TLVT_CHMAP_VAR, dst) || + if (put_user(type, dst) || put_user(chs_bytes, dst + 1)) return -EFAULT; dst += 2; @@ -1651,14 +1865,10 @@ static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, return -ENOMEM; size -= chs_bytes; count += chs_bytes; - for (c = 7; c >= 0; c--) { - int spk = cap->speakers[c]; - if (!spk) - continue; - if (put_user(spk_to_chmap(spk), dst)) - return -EFAULT; - dst++; - } + spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs); + if (copy_to_user(dst, tlv_chmap, chs_bytes)) + return -EFAULT; + dst += chs; } } if (put_user(count, tlv + 1)) @@ -1692,7 +1902,7 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, unsigned int ctl_idx; struct snd_pcm_substream *substream; unsigned char chmap[8]; - int i, ca, prepared = 0; + int i, err, ca, prepared = 0; ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); substream = snd_pcm_chmap_substream(info, ctl_idx); @@ -1716,10 +1926,17 @@ static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol, ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap); if (ca < 0) return -EINVAL; + if (spec->ops.chmap_validate) { + err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap); + if (err) + return err; + } + mutex_lock(&per_pin->lock); per_pin->chmap_set = true; memcpy(per_pin->chmap, chmap, sizeof(chmap)); if (prepared) hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + mutex_unlock(&per_pin->lock); return 0; } @@ -1836,12 +2053,11 @@ static int generic_hdmi_init_per_pins(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - struct hdmi_eld *eld = &per_pin->sink_eld; per_pin->codec = codec; - mutex_init(&eld->lock); + mutex_init(&per_pin->lock); INIT_DELAYED_WORK(&per_pin->work, hdmi_repoll_eld); - snd_hda_eld_proc_new(codec, eld, pin_idx); + eld_proc_new(per_pin, pin_idx); } return 0; } @@ -1882,10 +2098,9 @@ static void generic_hdmi_free(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - struct hdmi_eld *eld = &per_pin->sink_eld; cancel_delayed_work(&per_pin->work); - snd_hda_eld_proc_free(codec, eld); + eld_proc_free(per_pin); } flush_workqueue(codec->bus->workq); @@ -1922,6 +2137,17 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = { #endif }; +static const struct hdmi_ops generic_standard_hdmi_ops = { + .pin_get_eld = snd_hdmi_get_eld, + .pin_get_slot_channel = hdmi_pin_get_slot_channel, + .pin_set_slot_channel = hdmi_pin_set_slot_channel, + .pin_setup_infoframe = hdmi_pin_setup_infoframe, + .pin_hbr_setup = hdmi_pin_hbr_setup, + .setup_stream = hdmi_setup_stream, + .chmap_cea_alloc_validate_get_type = hdmi_chmap_cea_alloc_validate_get_type, + .cea_alloc_to_tlv_chmap = hdmi_cea_alloc_to_tlv_chmap, +}; + static void intel_haswell_fixup_connect_list(struct hda_codec *codec, hda_nid_t nid) @@ -2004,6 +2230,7 @@ static int patch_generic_hdmi(struct hda_codec *codec) if (spec == NULL) return -ENOMEM; + spec->ops = generic_standard_hdmi_ops; codec->spec = spec; hdmi_array_init(spec, 4); @@ -2559,49 +2786,358 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec) } /* - * ATI-specific implementations - * - * FIXME: we may omit the whole this and use the generic code once after - * it's confirmed to work. + * ATI/AMD-specific implementations */ -#define ATIHDMI_CVT_NID 0x02 /* audio converter */ -#define ATIHDMI_PIN_NID 0x03 /* HDMI output pin */ +#define is_amdhdmi_rev3_or_later(codec) \ + ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300) +#define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) -static int atihdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +/* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ +#define ATI_VERB_SET_CHANNEL_ALLOCATION 0x771 +#define ATI_VERB_SET_DOWNMIX_INFO 0x772 +#define ATI_VERB_SET_MULTICHANNEL_01 0x777 +#define ATI_VERB_SET_MULTICHANNEL_23 0x778 +#define ATI_VERB_SET_MULTICHANNEL_45 0x779 +#define ATI_VERB_SET_MULTICHANNEL_67 0x77a +#define ATI_VERB_SET_HBR_CONTROL 0x77c +#define ATI_VERB_SET_MULTICHANNEL_1 0x785 +#define ATI_VERB_SET_MULTICHANNEL_3 0x786 +#define ATI_VERB_SET_MULTICHANNEL_5 0x787 +#define ATI_VERB_SET_MULTICHANNEL_7 0x788 +#define ATI_VERB_SET_MULTICHANNEL_MODE 0x789 +#define ATI_VERB_GET_CHANNEL_ALLOCATION 0xf71 +#define ATI_VERB_GET_DOWNMIX_INFO 0xf72 +#define ATI_VERB_GET_MULTICHANNEL_01 0xf77 +#define ATI_VERB_GET_MULTICHANNEL_23 0xf78 +#define ATI_VERB_GET_MULTICHANNEL_45 0xf79 +#define ATI_VERB_GET_MULTICHANNEL_67 0xf7a +#define ATI_VERB_GET_HBR_CONTROL 0xf7c +#define ATI_VERB_GET_MULTICHANNEL_1 0xf85 +#define ATI_VERB_GET_MULTICHANNEL_3 0xf86 +#define ATI_VERB_GET_MULTICHANNEL_5 0xf87 +#define ATI_VERB_GET_MULTICHANNEL_7 0xf88 +#define ATI_VERB_GET_MULTICHANNEL_MODE 0xf89 + +/* AMD specific HDA cvt verbs */ +#define ATI_VERB_SET_RAMP_RATE 0x770 +#define ATI_VERB_GET_RAMP_RATE 0xf70 + +#define ATI_OUT_ENABLE 0x1 + +#define ATI_MULTICHANNEL_MODE_PAIRED 0 +#define ATI_MULTICHANNEL_MODE_SINGLE 1 + +#define ATI_HBR_CAPABLE 0x01 +#define ATI_HBR_ENABLE 0x10 + +static int atihdmi_pin_get_eld(struct hda_codec *codec, hda_nid_t nid, + unsigned char *buf, int *eld_size) +{ + /* call hda_eld.c ATI/AMD-specific function */ + return snd_hdmi_get_eld_ati(codec, nid, buf, eld_size, + is_amdhdmi_rev3_or_later(codec)); +} + +static void atihdmi_pin_setup_infoframe(struct hda_codec *codec, hda_nid_t pin_nid, int ca, + int active_channels, int conn_type) +{ + snd_hda_codec_write(codec, pin_nid, 0, ATI_VERB_SET_CHANNEL_ALLOCATION, ca); +} + +static int atihdmi_paired_swap_fc_lfe(int pos) +{ + /* + * ATI/AMD have automatic FC/LFE swap built-in + * when in pairwise mapping mode. + */ + + switch (pos) { + /* see channel_allocations[].speakers[] */ + case 2: return 3; + case 3: return 2; + default: break; + } + + return pos; +} + +static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map) +{ + struct cea_channel_speaker_allocation *cap; + int i, j; + + /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */ + + cap = &channel_allocations[get_channel_allocation_order(ca)]; + for (i = 0; i < chs; ++i) { + int mask = to_spk_mask(map[i]); + bool ok = false; + bool companion_ok = false; + + if (!mask) + continue; + + for (j = 0 + i % 2; j < 8; j += 2) { + int chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j); + if (cap->speakers[chan_idx] == mask) { + /* channel is in a supported position */ + ok = true; + + if (i % 2 == 0 && i + 1 < chs) { + /* even channel, check the odd companion */ + int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1); + int comp_mask_req = to_spk_mask(map[i+1]); + int comp_mask_act = cap->speakers[comp_chan_idx]; + + if (comp_mask_req == comp_mask_act) + companion_ok = true; + else + return -EINVAL; + } + break; + } + } + + if (!ok) + return -EINVAL; + + if (companion_ok) + i++; /* companion channel already checked */ + } + + return 0; +} + +static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int hdmi_slot, int stream_channel) +{ + int verb; + int ati_channel_setup = 0; + + if (hdmi_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + hdmi_slot = atihdmi_paired_swap_fc_lfe(hdmi_slot); + + /* In case this is an odd slot but without stream channel, do not + * disable the slot since the corresponding even slot could have a + * channel. In case neither have a channel, the slot pair will be + * disabled when this function is called for the even slot. */ + if (hdmi_slot % 2 != 0 && stream_channel == 0xf) + return 0; + + hdmi_slot -= hdmi_slot % 2; + + if (stream_channel != 0xf) + stream_channel -= stream_channel % 2; + } + + verb = ATI_VERB_SET_MULTICHANNEL_01 + hdmi_slot/2 + (hdmi_slot % 2) * 0x00e; + + /* ati_channel_setup format: [7..4] = stream_channel_id, [1] = mute, [0] = enable */ + + if (stream_channel != 0xf) + ati_channel_setup = (stream_channel << 4) | ATI_OUT_ENABLE; + + return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup); +} + +static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid, + int asp_slot) +{ + bool was_odd = false; + int ati_asp_slot = asp_slot; + int verb; + int ati_channel_setup; + + if (asp_slot > 7) + return -EINVAL; + + if (!has_amd_full_remap_support(codec)) { + ati_asp_slot = atihdmi_paired_swap_fc_lfe(asp_slot); + if (ati_asp_slot % 2 != 0) { + ati_asp_slot -= 1; + was_odd = true; + } + } + + verb = ATI_VERB_GET_MULTICHANNEL_01 + ati_asp_slot/2 + (ati_asp_slot % 2) * 0x00e; + + ati_channel_setup = snd_hda_codec_read(codec, pin_nid, 0, verb, 0); + + if (!(ati_channel_setup & ATI_OUT_ENABLE)) + return 0xf; + + return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd; +} + +static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap, + int channels) +{ + int c; + + /* + * Pre-rev3 ATI/AMD codecs operate in a paired channel mode, so + * we need to take that into account (a single channel may take 2 + * channel slots if we need to carry a silent channel next to it). + * On Rev3+ AMD codecs this function is not used. + */ + int chanpairs = 0; + + /* We only produce even-numbered channel count TLVs */ + if ((channels % 2) != 0) + return -1; + + for (c = 0; c < 7; c += 2) { + if (cap->speakers[c] || cap->speakers[c+1]) + chanpairs++; + } + + if (chanpairs * 2 != channels) + return -1; + + return SNDRV_CTL_TLVT_CHMAP_PAIRED; +} + +static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap, + unsigned int *chmap, int channels) +{ + /* produce paired maps for pre-rev3 ATI/AMD codecs */ + int count = 0; + int c; + + for (c = 7; c >= 0; c--) { + int chan = 7 - atihdmi_paired_swap_fc_lfe(7 - c); + int spk = cap->speakers[chan]; + if (!spk) { + /* add N/A channel if the companion channel is occupied */ + if (cap->speakers[chan + (chan % 2 ? -1 : 1)]) + chmap[count++] = SNDRV_CHMAP_NA; + + continue; + } + + chmap[count++] = spk_to_chmap(spk); + } + + WARN_ON(count != channels); +} + +static int atihdmi_pin_hbr_setup(struct hda_codec *codec, hda_nid_t pin_nid, + bool hbr) +{ + int hbr_ctl, hbr_ctl_new; + + hbr_ctl = snd_hda_codec_read(codec, pin_nid, 0, ATI_VERB_GET_HBR_CONTROL, 0); + if (hbr_ctl & ATI_HBR_CAPABLE) { + if (hbr) + hbr_ctl_new = hbr_ctl | ATI_HBR_ENABLE; + else + hbr_ctl_new = hbr_ctl & ~ATI_HBR_ENABLE; + + snd_printdd("atihdmi_pin_hbr_setup: " + "NID=0x%x, %shbr-ctl=0x%x\n", + pin_nid, + hbr_ctl == hbr_ctl_new ? "" : "new-", + hbr_ctl_new); + + if (hbr_ctl != hbr_ctl_new) + snd_hda_codec_write(codec, pin_nid, 0, + ATI_VERB_SET_HBR_CONTROL, + hbr_ctl_new); + + } else if (hbr) + return -EINVAL; + + return 0; +} + +static int atihdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, u32 stream_tag, int format) +{ + + if (is_amdhdmi_rev3_or_later(codec)) { + int ramp_rate = 180; /* default as per AMD spec */ + /* disable ramp-up/down for non-pcm as per AMD spec */ + if (format & AC_FMT_TYPE_NON_PCM) + ramp_rate = 0; + + snd_hda_codec_write(codec, cvt_nid, 0, ATI_VERB_SET_RAMP_RATE, ramp_rate); + } + + return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format); +} + + +static int atihdmi_init(struct hda_codec *codec) { struct hdmi_spec *spec = codec->spec; - struct hdmi_spec_per_cvt *per_cvt = get_cvt(spec, 0); - int chans = substream->runtime->channels; - int i, err; + int pin_idx, err; - err = simple_playback_pcm_prepare(hinfo, codec, stream_tag, format, - substream); - if (err < 0) + err = generic_hdmi_init(codec); + + if (err) return err; - snd_hda_codec_write(codec, per_cvt->cvt_nid, 0, - AC_VERB_SET_CVT_CHAN_COUNT, chans - 1); - /* FIXME: XXX */ - for (i = 0; i < chans; i++) { - snd_hda_codec_write(codec, per_cvt->cvt_nid, 0, - AC_VERB_SET_HDMI_CHAN_SLOT, - (i << 4) | i); + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + /* make sure downmix information in infoframe is zero */ + snd_hda_codec_write(codec, per_pin->pin_nid, 0, ATI_VERB_SET_DOWNMIX_INFO, 0); + + /* enable channel-wise remap mode if supported */ + if (has_amd_full_remap_support(codec)) + snd_hda_codec_write(codec, per_pin->pin_nid, 0, + ATI_VERB_SET_MULTICHANNEL_MODE, + ATI_MULTICHANNEL_MODE_SINGLE); } + return 0; } static int patch_atihdmi(struct hda_codec *codec) { struct hdmi_spec *spec; - int err = patch_simple_hdmi(codec, ATIHDMI_CVT_NID, ATIHDMI_PIN_NID); - if (err < 0) + struct hdmi_spec_per_cvt *per_cvt; + int err, cvt_idx; + + err = patch_generic_hdmi(codec); + + if (err) return err; + + codec->patch_ops.init = atihdmi_init; + spec = codec->spec; - spec->pcm_playback.ops.prepare = atihdmi_playback_pcm_prepare; + + spec->ops.pin_get_eld = atihdmi_pin_get_eld; + spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel; + spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel; + spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe; + spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup; + spec->ops.setup_stream = atihdmi_setup_stream; + + if (!has_amd_full_remap_support(codec)) { + /* override to ATI/AMD-specific versions with pairwise mapping */ + spec->ops.chmap_cea_alloc_validate_get_type = + atihdmi_paired_chmap_cea_alloc_validate_get_type; + spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap; + spec->ops.chmap_validate = atihdmi_paired_chmap_validate; + } + + /* ATI/AMD converters do not advertise all of their capabilities */ + for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->channels_max = max(per_cvt->channels_max, 8u); + per_cvt->rates |= SUPPORTED_RATES; + per_cvt->formats |= SUPPORTED_FORMATS; + per_cvt->maxbps = max(per_cvt->maxbps, 24u); + } + + spec->channels_max = max(spec->channels_max, 8u); + return 0; } @@ -2621,7 +3157,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi }, { .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi }, -{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi }, { .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_generic_hdmi }, { .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_generic_hdmi }, { .id = 0x17e80047, .name = "Chrontel HDMI", .patch = patch_generic_hdmi }, @@ -2669,6 +3205,7 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862807, .name = "Haswell HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi }, +{ .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi }, { .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, {} /* terminator */ }; @@ -2723,6 +3260,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862805"); MODULE_ALIAS("snd-hda-codec-id:80862806"); MODULE_ALIAS("snd-hda-codec-id:80862807"); MODULE_ALIAS("snd-hda-codec-id:80862880"); +MODULE_ALIAS("snd-hda-codec-id:80862882"); MODULE_ALIAS("snd-hda-codec-id:808629fb"); MODULE_LICENSE("GPL"); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bf313bea7085..1f0a040a9818 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2539,7 +2539,9 @@ enum { ALC269_TYPE_ALC282, ALC269_TYPE_ALC283, ALC269_TYPE_ALC284, + ALC269_TYPE_ALC285, ALC269_TYPE_ALC286, + ALC269_TYPE_ALC255, }; /* @@ -2558,6 +2560,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC269VC: case ALC269_TYPE_ALC280: case ALC269_TYPE_ALC284: + case ALC269_TYPE_ALC285: ssids = alc269va_ssids; break; case ALC269_TYPE_ALC269VB: @@ -2565,6 +2568,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC282: case ALC269_TYPE_ALC283: case ALC269_TYPE_ALC286: + case ALC269_TYPE_ALC255: ssids = alc269_ssids; break; default: @@ -2652,7 +2656,7 @@ static void alc283_shutup(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); if (hp_pin_sense) - msleep(85); + msleep(100); snd_hda_codec_write(codec, hp_pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); @@ -2661,7 +2665,7 @@ static void alc283_shutup(struct hda_codec *codec) alc_write_coef_idx(codec, 0x46, val | (3 << 12)); if (hp_pin_sense) - msleep(85); + msleep(100); snd_hda_shutup_pins(codec); alc_write_coef_idx(codec, 0x43, 0x9614); } @@ -4125,9 +4129,16 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0292: spec->codec_variant = ALC269_TYPE_ALC284; break; + case 0x10ec0285: + case 0x10ec0293: + spec->codec_variant = ALC269_TYPE_ALC285; + break; case 0x10ec0286: spec->codec_variant = ALC269_TYPE_ALC286; break; + case 0x10ec0255: + spec->codec_variant = ALC269_TYPE_ALC255; + break; } if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { @@ -4841,6 +4852,7 @@ static int patch_alc680(struct hda_codec *codec) static const struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 }, { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 }, + { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 }, { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 }, @@ -4854,9 +4866,11 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = { { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 }, { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 }, { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 }, + { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 }, { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 }, { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 }, { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 }, + { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 }, { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", .patch = patch_alc861 }, { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fba0cef1c47f..69a549a82345 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -2091,8 +2091,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, { struct sigmatel_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ + codec->bus->avoid_link_reset = 1; + } } static void stac92hd83xxx_fixup_headset_jack(struct hda_codec *codec, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 3cde55b753e2..2907e68150cb 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -3996,7 +3996,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) return 1; } return 0; - break; case AES32: status = hdspm_read(hdspm, HDSPM_statusRegister); if (status & HDSPM_tcoLockAes) { @@ -4006,9 +4005,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) return 1; } return 0; - - break; - case RayDAT: case AIO: status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); @@ -4018,7 +4014,6 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm) if (status & 0x4000000) return 1; /* Lock */ return 0; /* No signal */ - break; default: break; diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index bb53dea85b17..8697cedccd21 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -777,7 +777,7 @@ static int asoc_ssc_init(struct device *dev) if (ret) { dev_err(dev, "Could not register PCM: %d\n", ret); goto err_unregister_dai; - }; + } return 0; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 5f9af1fb76e8..49cc5f6d6dba 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -328,7 +328,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ak4641->playback_fs = rate; ak4641_set_deemph(codec); - }; + } return 0; } diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c index f5472adee674..bae60164c7b7 100644 --- a/sound/soc/codecs/mc13783.c +++ b/sound/soc/codecs/mc13783.c @@ -343,7 +343,7 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai, break; default: return -EINVAL; - }; + } snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val); diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index fe4d29d88564..a895a5e4bdf2 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -432,7 +432,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream, default: dev_err(codec->dev, "Invalid bit width\n"); return -EINVAL; - }; + } ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val); if (ret < 0) diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index 52af7f6fb37f..364bf6a907e1 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -297,7 +297,7 @@ static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg) @@ -310,7 +310,7 @@ static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg) @@ -321,7 +321,7 @@ static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config tegra20_i2s_regmap_config = { diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index 551b3c93ce93..08bc6931c7c7 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -213,7 +213,7 @@ static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) @@ -234,7 +234,7 @@ static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) @@ -247,7 +247,7 @@ static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config tegra20_spdif_regmap_config = { diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index bdd19db4a08b..31154338c1eb 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -360,7 +360,7 @@ static bool tegra30_ahub_apbif_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: break; - }; + } if (REG_IN_ARRAY(reg, CHANNEL_CTRL) || REG_IN_ARRAY(reg, CHANNEL_CLEAR) || @@ -395,7 +395,7 @@ static bool tegra30_ahub_apbif_volatile_reg(struct device *dev, return true; default: break; - }; + } if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) || REG_IN_ARRAY(reg, CHANNEL_STATUS) || diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 5f20b695eba2..231a785b3921 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -376,7 +376,7 @@ static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) @@ -389,7 +389,7 @@ static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg) return true; default: return false; - }; + } } static const struct regmap_config tegra30_i2s_regmap_config = { diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c index ae6b50f9ed56..f65fc0987cfb 100644 --- a/sound/usb/caiaq/control.c +++ b/sound/usb/caiaq/control.c @@ -28,6 +28,7 @@ #include "control.h" #define CNT_INTVAL 0x10000 +#define MASCHINE_BANK_SIZE 32 static int control_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -105,6 +106,10 @@ static int control_put(struct snd_kcontrol *kcontrol, USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1)) cmd = EP1_CMD_DIMM_LEDS; + if (cdev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) + cmd = EP1_CMD_DIMM_LEDS; + if (pos & CNT_INTVAL) { int i = pos & ~CNT_INTVAL; @@ -121,6 +126,20 @@ static int control_put(struct snd_kcontrol *kcontrol, usb_sndbulkpipe(cdev->chip.dev, 8), cdev->ep8_out_buf, sizeof(cdev->ep8_out_buf), &actual_len, 200); + } else if (cdev->chip.usb_id == + USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER)) { + + int bank = 0; + int offset = 0; + + if (i >= MASCHINE_BANK_SIZE) { + bank = 0x1e; + offset = MASCHINE_BANK_SIZE; + } + + snd_usb_caiaq_send_command_bank(cdev, cmd, bank, + cdev->control_state + offset, + MASCHINE_BANK_SIZE); } else { snd_usb_caiaq_send_command(cdev, cmd, cdev->control_state, sizeof(cdev->control_state)); @@ -490,6 +509,74 @@ static struct caiaq_controller kontrols4_controller[] = { { "LED: FX2: Mode", 133 | CNT_INTVAL }, }; +static struct caiaq_controller maschine_controller[] = { + { "LED: Pad 1", 3 | CNT_INTVAL }, + { "LED: Pad 2", 2 | CNT_INTVAL }, + { "LED: Pad 3", 1 | CNT_INTVAL }, + { "LED: Pad 4", 0 | CNT_INTVAL }, + { "LED: Pad 5", 7 | CNT_INTVAL }, + { "LED: Pad 6", 6 | CNT_INTVAL }, + { "LED: Pad 7", 5 | CNT_INTVAL }, + { "LED: Pad 8", 4 | CNT_INTVAL }, + { "LED: Pad 9", 11 | CNT_INTVAL }, + { "LED: Pad 10", 10 | CNT_INTVAL }, + { "LED: Pad 11", 9 | CNT_INTVAL }, + { "LED: Pad 12", 8 | CNT_INTVAL }, + { "LED: Pad 13", 15 | CNT_INTVAL }, + { "LED: Pad 14", 14 | CNT_INTVAL }, + { "LED: Pad 15", 13 | CNT_INTVAL }, + { "LED: Pad 16", 12 | CNT_INTVAL }, + + { "LED: Mute", 16 | CNT_INTVAL }, + { "LED: Solo", 17 | CNT_INTVAL }, + { "LED: Select", 18 | CNT_INTVAL }, + { "LED: Duplicate", 19 | CNT_INTVAL }, + { "LED: Navigate", 20 | CNT_INTVAL }, + { "LED: Pad Mode", 21 | CNT_INTVAL }, + { "LED: Pattern", 22 | CNT_INTVAL }, + { "LED: Scene", 23 | CNT_INTVAL }, + + { "LED: Shift", 24 | CNT_INTVAL }, + { "LED: Erase", 25 | CNT_INTVAL }, + { "LED: Grid", 26 | CNT_INTVAL }, + { "LED: Right Bottom", 27 | CNT_INTVAL }, + { "LED: Rec", 28 | CNT_INTVAL }, + { "LED: Play", 29 | CNT_INTVAL }, + { "LED: Left Bottom", 32 | CNT_INTVAL }, + { "LED: Restart", 33 | CNT_INTVAL }, + + { "LED: Group A", 41 | CNT_INTVAL }, + { "LED: Group B", 40 | CNT_INTVAL }, + { "LED: Group C", 37 | CNT_INTVAL }, + { "LED: Group D", 36 | CNT_INTVAL }, + { "LED: Group E", 39 | CNT_INTVAL }, + { "LED: Group F", 38 | CNT_INTVAL }, + { "LED: Group G", 35 | CNT_INTVAL }, + { "LED: Group H", 34 | CNT_INTVAL }, + + { "LED: Auto Write", 42 | CNT_INTVAL }, + { "LED: Snap", 43 | CNT_INTVAL }, + { "LED: Right Top", 44 | CNT_INTVAL }, + { "LED: Left Top", 45 | CNT_INTVAL }, + { "LED: Sampling", 46 | CNT_INTVAL }, + { "LED: Browse", 47 | CNT_INTVAL }, + { "LED: Step", 48 | CNT_INTVAL }, + { "LED: Control", 49 | CNT_INTVAL }, + + { "LED: Top Button 1", 57 | CNT_INTVAL }, + { "LED: Top Button 2", 56 | CNT_INTVAL }, + { "LED: Top Button 3", 55 | CNT_INTVAL }, + { "LED: Top Button 4", 54 | CNT_INTVAL }, + { "LED: Top Button 5", 53 | CNT_INTVAL }, + { "LED: Top Button 6", 52 | CNT_INTVAL }, + { "LED: Top Button 7", 51 | CNT_INTVAL }, + { "LED: Top Button 8", 50 | CNT_INTVAL }, + + { "LED: Note Repeat", 58 | CNT_INTVAL }, + + { "Backlight Display", 59 | CNT_INTVAL } +}; + static int add_controls(struct caiaq_controller *c, int num, struct snd_usb_caiaqdev *cdev) { @@ -553,6 +640,11 @@ int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *cdev) ret = add_controls(kontrols4_controller, ARRAY_SIZE(kontrols4_controller), cdev); break; + + case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): + ret = add_controls(maschine_controller, + ARRAY_SIZE(maschine_controller), cdev); + break; } return ret; diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 1a61dd12fe38..bc55f708a696 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -235,6 +235,31 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev, cdev->ep1_out_buf, len+1, &actual_len, 200); } +int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev, + unsigned char command, + unsigned char bank, + const unsigned char *buffer, + int len) +{ + int actual_len; + struct usb_device *usb_dev = cdev->chip.dev; + + if (!usb_dev) + return -EIO; + + if (len > EP1_BUFSIZE - 2) + len = EP1_BUFSIZE - 2; + + if (buffer && len > 0) + memcpy(cdev->ep1_out_buf+2, buffer, len); + + cdev->ep1_out_buf[0] = command; + cdev->ep1_out_buf[1] = bank; + + return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1), + cdev->ep1_out_buf, len+2, &actual_len, 200); +} + int snd_usb_caiaq_set_audio_params (struct snd_usb_caiaqdev *cdev, int rate, int depth, int bpp) { diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h index ad102fac6942..ab0f7520a99b 100644 --- a/sound/usb/caiaq/device.h +++ b/sound/usb/caiaq/device.h @@ -128,5 +128,10 @@ int snd_usb_caiaq_send_command(struct snd_usb_caiaqdev *cdev, unsigned char command, const unsigned char *buffer, int len); +int snd_usb_caiaq_send_command_bank(struct snd_usb_caiaqdev *cdev, + unsigned char command, + unsigned char bank, + const unsigned char *buffer, + int len); #endif /* CAIAQ_DEVICE_H */ diff --git a/sound/usb/card.c b/sound/usb/card.c index 64952e2d3ed1..d979050e6a6a 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -79,7 +79,6 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card * /* Vendor/product IDs for this card */ static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; -static int nrpacks = 8; /* max. number of packets per urb */ static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; static bool autoclock = true; @@ -94,8 +93,6 @@ module_param_array(vid, int, NULL, 0444); MODULE_PARM_DESC(vid, "Vendor ID for the USB audio device."); module_param_array(pid, int, NULL, 0444); MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); -module_param(nrpacks, int, 0644); -MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); module_param_array(device_setup, int, NULL, 0444); MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); module_param(ignore_ctl_error, bool, 0444); @@ -349,6 +346,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: break; default: @@ -374,7 +372,6 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, chip->dev = dev; chip->card = card; chip->setup = device_setup[idx]; - chip->nrpacks = nrpacks; chip->autoclock = autoclock; chip->probing = 1; @@ -754,19 +751,4 @@ static struct usb_driver usb_audio_driver = { .supports_autosuspend = 1, }; -static int __init snd_usb_audio_init(void) -{ - if (nrpacks < 1 || nrpacks > MAX_PACKS) { - printk(KERN_WARNING "invalid nrpacks value.\n"); - return -EINVAL; - } - return usb_register(&usb_audio_driver); -} - -static void __exit snd_usb_audio_cleanup(void) -{ - usb_deregister(&usb_audio_driver); -} - -module_init(snd_usb_audio_init); -module_exit(snd_usb_audio_cleanup); +module_usb_driver(usb_audio_driver); diff --git a/sound/usb/card.h b/sound/usb/card.h index 5ecacaa90b53..9867ab866857 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -2,11 +2,11 @@ #define __USBAUDIO_CARD_H #define MAX_NR_RATES 1024 -#define MAX_PACKS 20 +#define MAX_PACKS 6 /* per URB */ #define MAX_PACKS_HS (MAX_PACKS * 8) /* in high speed mode */ -#define MAX_URBS 8 +#define MAX_URBS 12 #define SYNC_URBS 4 /* always four urbs for sync */ -#define MAX_QUEUE 24 /* try not to exceed this queue length, in ms */ +#define MAX_QUEUE 18 /* try not to exceed this queue length, in ms */ struct audioformat { struct list_head list; @@ -87,6 +87,7 @@ struct snd_usb_endpoint { unsigned int phase; /* phase accumulator */ unsigned int maxpacksize; /* max packet size in bytes */ unsigned int maxframesize; /* max packet size in frames */ + unsigned int max_urb_frames; /* max URB size in frames */ unsigned int curpacksize; /* current packet size in bytes (for capture) */ unsigned int curframesize; /* current packet size in frames (for capture) */ unsigned int syncmaxsize; /* sync endpoint packet size */ @@ -95,7 +96,7 @@ struct snd_usb_endpoint { unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned char silence_value; unsigned int stride; - int iface, alt_idx; + int iface, altsetting; int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ @@ -116,6 +117,8 @@ struct snd_usb_substream { unsigned int channels_max; /* max channels in the all audiofmts */ unsigned int cur_rate; /* current rate (for hw_params callback) */ unsigned int period_bytes; /* current period bytes (for hw_params callback) */ + unsigned int period_frames; /* current frames per period */ + unsigned int buffer_periods; /* current periods per buffer */ unsigned int altset_idx; /* USB data format: index of alternate setting */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ unsigned int fmt_type; /* USB audio format type (1-3) */ @@ -125,6 +128,7 @@ struct snd_usb_substream { unsigned int hwptr_done; /* processed byte position in the buffer */ unsigned int transfer_done; /* processed frames since last period update */ + unsigned int frame_limit; /* limits number of packets in URB */ /* data and sync endpoints for this stream */ unsigned int ep_num; /* the endpoint number */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 93e970f2b3c0..b9ba0fcc45df 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -33,7 +33,6 @@ #include "pcm.h" #include "quirks.h" -#define EP_FLAG_ACTIVATED 0 #define EP_FLAG_RUNNING 1 #define EP_FLAG_STOPPING 2 @@ -426,9 +425,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, list_for_each_entry(ep, &chip->ep_list, list) { if (ep->ep_num == ep_num && ep->iface == alts->desc.bInterfaceNumber && - ep->alt_idx == alts->desc.bAlternateSetting) { + ep->altsetting == alts->desc.bAlternateSetting) { snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n", - ep_num, ep->iface, ep->alt_idx, ep); + ep_num, ep->iface, ep->altsetting, ep); goto __exit_unlock; } } @@ -447,7 +446,7 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, ep->type = type; ep->ep_num = ep_num; ep->iface = alts->desc.bInterfaceNumber; - ep->alt_idx = alts->desc.bAlternateSetting; + ep->altsetting = alts->desc.bAlternateSetting; INIT_LIST_HEAD(&ep->ready_playback_urbs); ep_num &= USB_ENDPOINT_NUMBER_MASK; @@ -574,11 +573,14 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, snd_pcm_format_t pcm_format, unsigned int channels, unsigned int period_bytes, + unsigned int frames_per_period, + unsigned int periods_per_buffer, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep) { - unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms; - int is_playback = usb_pipeout(ep->pipe); + unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb; + unsigned int max_packs_per_period, urbs_per_period, urb_packs; + unsigned int max_urbs, i; int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { @@ -611,58 +613,67 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, else ep->curpacksize = maxsize; - if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) + if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) { packs_per_ms = 8 >> ep->datainterval; - else - packs_per_ms = 1; - - if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) { - urb_packs = max(ep->chip->nrpacks, 1); - urb_packs = min(urb_packs, (unsigned int) MAX_PACKS); + max_packs_per_urb = MAX_PACKS_HS; } else { - urb_packs = 1; + packs_per_ms = 1; + max_packs_per_urb = MAX_PACKS; } + if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) + max_packs_per_urb = min(max_packs_per_urb, + 1U << sync_ep->syncinterval); + max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); - urb_packs *= packs_per_ms; + /* + * Capture endpoints need to use small URBs because there's no way + * to tell in advance where the next period will end, and we don't + * want the next URB to complete much after the period ends. + * + * Playback endpoints with implicit sync much use the same parameters + * as their corresponding capture endpoint. + */ + if (usb_pipein(ep->pipe) || + snd_usb_endpoint_implicit_feedback_sink(ep)) { - if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) - urb_packs = min(urb_packs, 1U << sync_ep->syncinterval); + /* make capture URBs <= 1 ms and smaller than a period */ + urb_packs = min(max_packs_per_urb, packs_per_ms); + while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + urb_packs >>= 1; + ep->nurbs = MAX_URBS; - /* decide how many packets to be used */ - if (is_playback && !snd_usb_endpoint_implicit_feedback_sink(ep)) { - unsigned int minsize, maxpacks; + /* + * Playback endpoints without implicit sync are adjusted so that + * a period fits as evenly as possible in the smallest number of + * URBs. The total number of URBs is adjusted to the size of the + * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits. + */ + } else { /* determine how small a packet can be */ - minsize = (ep->freqn >> (16 - ep->datainterval)) - * (frame_bits >> 3); + minsize = (ep->freqn >> (16 - ep->datainterval)) * + (frame_bits >> 3); /* with sync from device, assume it can be 12% lower */ if (sync_ep) minsize -= minsize >> 3; minsize = max(minsize, 1u); - total_packs = (period_bytes + minsize - 1) / minsize; - /* we need at least two URBs for queueing */ - if (total_packs < 2) { - total_packs = 2; - } else { - /* and we don't want too long a queue either */ - maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2); - total_packs = min(total_packs, maxpacks); - } - } else { - while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) - urb_packs >>= 1; - total_packs = MAX_URBS * urb_packs; - } - ep->nurbs = (total_packs + urb_packs - 1) / urb_packs; - if (ep->nurbs > MAX_URBS) { - /* too much... */ - ep->nurbs = MAX_URBS; - total_packs = MAX_URBS * urb_packs; - } else if (ep->nurbs < 2) { - /* too little - we need at least two packets - * to ensure contiguous playback/capture - */ - ep->nurbs = 2; + /* how many packets will contain an entire ALSA period? */ + max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize); + + /* how many URBs will contain a period? */ + urbs_per_period = DIV_ROUND_UP(max_packs_per_period, + max_packs_per_urb); + /* how many packets are needed in each URB? */ + urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period); + + /* limit the number of frames in a single URB */ + ep->max_urb_frames = DIV_ROUND_UP(frames_per_period, + urbs_per_period); + + /* try to use enough URBs to contain an entire ALSA buffer */ + max_urbs = min((unsigned) MAX_URBS, + MAX_QUEUE * packs_per_ms / urb_packs); + ep->nurbs = min(max_urbs, urbs_per_period * periods_per_buffer); } /* allocate and initialize data urbs */ @@ -670,8 +681,7 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, struct snd_urb_ctx *u = &ep->urb[i]; u->index = i; u->ep = ep; - u->packets = (i + 1) * total_packs / ep->nurbs - - i * total_packs / ep->nurbs; + u->packets = urb_packs; u->buffer_size = maxsize * u->packets; if (fmt->fmt_type == UAC_FORMAT_TYPE_II) @@ -703,8 +713,7 @@ out_of_memory: /* * configure a sync endpoint */ -static int sync_ep_set_params(struct snd_usb_endpoint *ep, - struct audioformat *fmt) +static int sync_ep_set_params(struct snd_usb_endpoint *ep) { int i; @@ -748,6 +757,8 @@ out_of_memory: * @pcm_format: the audio fomat. * @channels: the number of audio channels. * @period_bytes: the number of bytes in one alsa period. + * @period_frames: the number of frames in one alsa period. + * @buffer_periods: the number of periods in one alsa buffer. * @rate: the frame rate. * @fmt: the USB audio format information * @sync_ep: the sync endpoint to use, if any @@ -760,6 +771,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, snd_pcm_format_t pcm_format, unsigned int channels, unsigned int period_bytes, + unsigned int period_frames, + unsigned int buffer_periods, unsigned int rate, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep) @@ -793,10 +806,11 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, switch (ep->type) { case SND_USB_ENDPOINT_TYPE_DATA: err = data_ep_set_params(ep, pcm_format, channels, - period_bytes, fmt, sync_ep); + period_bytes, period_frames, + buffer_periods, fmt, sync_ep); break; case SND_USB_ENDPOINT_TYPE_SYNC: - err = sync_ep_set_params(ep, fmt); + err = sync_ep_set_params(ep); break; default: err = -EINVAL; @@ -931,28 +945,21 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) * * @ep: the endpoint to deactivate * - * If the endpoint is not currently in use, this functions will select the - * alternate interface setting 0 for the interface of this endpoint. + * If the endpoint is not currently in use, this functions will + * deactivate its associated URBs. * * In case of any active users, this functions does nothing. - * - * Returns an error if usb_set_interface() failed, 0 in all other - * cases. */ -int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) +void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) { if (!ep) - return -EINVAL; - - deactivate_urbs(ep, true); - wait_clear_urbs(ep); + return; if (ep->use_count != 0) - return 0; - - clear_bit(EP_FLAG_ACTIVATED, &ep->flags); + return; - return 0; + deactivate_urbs(ep, true); + wait_clear_urbs(ep); } /** diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 2287adf5ca59..1c7e8ee48abc 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -12,6 +12,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, snd_pcm_format_t pcm_format, unsigned int channels, unsigned int period_bytes, + unsigned int period_frames, + unsigned int buffer_periods, unsigned int rate, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); @@ -20,7 +22,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); -int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); +void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free(struct list_head *head); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); diff --git a/sound/usb/helper.c b/sound/usb/helper.c index 620902463c6e..51ed1ac825fd 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -118,6 +118,7 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, { switch (snd_usb_get_speed(chip->dev)) { case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: case USB_SPEED_SUPER: if (get_endpoint(alts, 0)->bInterval >= 1 && get_endpoint(alts, 0)->bInterval <= 4) diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 95558ef4a7a0..44b0ba4feab3 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1151,14 +1151,14 @@ static void check_no_speaker_on_headset(struct snd_kcontrol *kctl, const char *names_to_check[] = { "Headset", "headset", "Headphone", "headphone", NULL}; const char **s; - bool found = 0; + bool found = false; if (strcmp("Speaker", kctl->id.name)) return; for (s = names_to_check; *s; s++) if (strstr(card->shortname, *s)) { - found = 1; + found = true; break; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b375d58871e7..ca3256d6fde3 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -241,16 +241,17 @@ static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) struct snd_usb_endpoint *ep = subs->sync_endpoint; if (subs->data_endpoint->iface != subs->sync_endpoint->iface || - subs->data_endpoint->alt_idx != subs->sync_endpoint->alt_idx) { + subs->data_endpoint->altsetting != subs->sync_endpoint->altsetting) { err = usb_set_interface(subs->dev, subs->sync_endpoint->iface, - subs->sync_endpoint->alt_idx); + subs->sync_endpoint->altsetting); if (err < 0) { + clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); snd_printk(KERN_ERR "%d:%d:%d: cannot set interface (%d)\n", subs->dev->devnum, subs->sync_endpoint->iface, - subs->sync_endpoint->alt_idx, err); + subs->sync_endpoint->altsetting, err); return -EIO; } } @@ -282,22 +283,6 @@ static void stop_endpoints(struct snd_usb_substream *subs, bool wait) } } -static int deactivate_endpoints(struct snd_usb_substream *subs) -{ - int reta, retb; - - reta = snd_usb_endpoint_deactivate(subs->sync_endpoint); - retb = snd_usb_endpoint_deactivate(subs->data_endpoint); - - if (reta < 0) - return reta; - - if (retb < 0) - return retb; - - return 0; -} - static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, unsigned int altsetting, struct usb_host_interface **alts, @@ -595,6 +580,7 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs) subs->pcm_format, subs->channels, subs->period_bytes, + 0, 0, subs->cur_rate, subs->cur_audiofmt, NULL); @@ -631,6 +617,7 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs) subs->pcm_format, sync_fp->channels, sync_period_bytes, + 0, 0, subs->cur_rate, sync_fp, NULL); @@ -653,6 +640,8 @@ static int configure_endpoint(struct snd_usb_substream *subs) subs->pcm_format, subs->channels, subs->period_bytes, + subs->period_frames, + subs->buffer_periods, subs->cur_rate, subs->cur_audiofmt, subs->sync_endpoint); @@ -689,6 +678,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, subs->pcm_format = params_format(hw_params); subs->period_bytes = params_period_bytes(hw_params); + subs->period_frames = params_period_size(hw_params); + subs->buffer_periods = params_periods(hw_params); subs->channels = params_channels(hw_params); subs->cur_rate = params_rate(hw_params); @@ -730,7 +721,8 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) down_read(&subs->stream->chip->shutdown_rwsem); if (!subs->stream->chip->shutdown) { stop_endpoints(subs, true); - deactivate_endpoints(subs); + snd_usb_endpoint_deactivate(subs->sync_endpoint); + snd_usb_endpoint_deactivate(subs->data_endpoint); } up_read(&subs->stream->chip->shutdown_rwsem); return snd_pcm_lib_free_vmalloc_buffer(substream); @@ -1363,6 +1355,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, frames = 0; urb->number_of_packets = 0; spin_lock_irqsave(&subs->lock, flags); + subs->frame_limit += ep->max_urb_frames; for (i = 0; i < ctx->packets; i++) { if (ctx->packet_size[i]) counts = ctx->packet_size[i]; @@ -1377,6 +1370,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, subs->transfer_done += counts; if (subs->transfer_done >= runtime->period_size) { subs->transfer_done -= runtime->period_size; + subs->frame_limit = 0; period_elapsed = 1; if (subs->fmt_type == UAC_FORMAT_TYPE_II) { if (subs->transfer_done > 0) { @@ -1399,8 +1393,10 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, break; } } - if (period_elapsed && - !snd_usb_endpoint_implicit_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */ + /* finish at the period boundary or after enough frames */ + if ((period_elapsed || + subs->transfer_done >= subs->frame_limit) && + !snd_usb_endpoint_implicit_feedback_sink(ep)) break; } bytes = frames * ep->stride; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index caabe9b3af49..5d2fe0530745 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -55,7 +55,6 @@ struct snd_usb_audio { struct list_head mixer_list; /* list of mixer interfaces */ int setup; /* from the 'device_setup' module param */ - int nrpacks; /* from the 'nrpacks' module param */ bool autoclock; /* from the 'autoclock' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ |