diff options
Diffstat (limited to 'sound')
76 files changed, 4443 insertions, 400 deletions
diff --git a/sound/Kconfig b/sound/Kconfig index 5a240e0..ee2e69a 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -108,6 +108,8 @@ source "sound/parisc/Kconfig" source "sound/soc/Kconfig" +source "sound/x86/Kconfig" + endif # SND menuconfig SOUND_PRIME diff --git a/sound/Makefile b/sound/Makefile index c41bdf5..6de45d2 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 2096bb0..8da9cb2 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1749,7 +1749,7 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device) * Sets the rawmidi operators for the given stream direction. */ void snd_rawmidi_set_ops(struct snd_rawmidi *rmidi, int stream, - struct snd_rawmidi_ops *ops) + const struct snd_rawmidi_ops *ops) { struct snd_rawmidi_substream *substream; diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index c850345..dfa5156 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -419,7 +419,6 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) { unsigned long flags; struct snd_seq_event_cell *ptr; - int max_count = 5 * HZ; if (snd_BUG_ON(!pool)) return -EINVAL; @@ -432,14 +431,8 @@ int snd_seq_pool_done(struct snd_seq_pool *pool) if (waitqueue_active(&pool->output_sleep)) wake_up(&pool->output_sleep); - while (atomic_read(&pool->counter) > 0) { - if (max_count == 0) { - pr_warn("ALSA: snd_seq_pool_done timeout: %d cells remain\n", atomic_read(&pool->counter)); - break; - } + while (atomic_read(&pool->counter) > 0) schedule_timeout_uninterruptible(1); - max_count--; - } /* release all resources */ spin_lock_irqsave(&pool->lock, flags); diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 0bec02e..450c518 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -181,6 +181,8 @@ void __exit snd_seq_queues_delete(void) } } +static void queue_use(struct snd_seq_queue *queue, int client, int use); + /* allocate a new queue - * return queue index value or negative value for error */ @@ -192,11 +194,11 @@ int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags) if (q == NULL) return -ENOMEM; q->info_flags = info_flags; + queue_use(q, client, 1); if (queue_list_add(q) < 0) { queue_delete(q); return -ENOMEM; } - snd_seq_queue_use(q->queue, client, 1); /* use this queue */ return q->queue; } @@ -502,19 +504,9 @@ int snd_seq_queue_timer_set_tempo(int queueid, int client, return result; } - -/* use or unuse this queue - - * if it is the first client, starts the timer. - * if it is not longer used by any clients, stop the timer. - */ -int snd_seq_queue_use(int queueid, int client, int use) +/* use or unuse this queue */ +static void queue_use(struct snd_seq_queue *queue, int client, int use) { - struct snd_seq_queue *queue; - - queue = queueptr(queueid); - if (queue == NULL) - return -EINVAL; - mutex_lock(&queue->timer_mutex); if (use) { if (!test_and_set_bit(client, queue->clients_bitmap)) queue->clients++; @@ -529,6 +521,21 @@ int snd_seq_queue_use(int queueid, int client, int use) } else { snd_seq_timer_close(queue); } +} + +/* use or unuse this queue - + * if it is the first client, starts the timer. + * if it is not longer used by any clients, stop the timer. + */ +int snd_seq_queue_use(int queueid, int client, int use) +{ + struct snd_seq_queue *queue; + + queue = queueptr(queueid); + if (queue == NULL) + return -EINVAL; + mutex_lock(&queue->timer_mutex); + queue_use(queue, client, use); mutex_unlock(&queue->timer_mutex); queuefree(queue); return 0; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index c82ed3e..52f31f1 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -349,13 +349,13 @@ static int snd_virmidi_unuse(void *private_data, * Register functions */ -static struct snd_rawmidi_ops snd_virmidi_input_ops = { +static const struct snd_rawmidi_ops snd_virmidi_input_ops = { .open = snd_virmidi_input_open, .close = snd_virmidi_input_close, .trigger = snd_virmidi_input_trigger, }; -static struct snd_rawmidi_ops snd_virmidi_output_ops = { +static const struct snd_rawmidi_ops snd_virmidi_output_ops = { .open = snd_virmidi_output_open, .close = snd_virmidi_output_close, .trigger = snd_virmidi_output_trigger, diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index 776596b..3a7c317 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -481,14 +481,14 @@ snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) */ -static struct snd_rawmidi_ops snd_mpu401_uart_output = +static const struct snd_rawmidi_ops snd_mpu401_uart_output = { .open = snd_mpu401_uart_output_open, .close = snd_mpu401_uart_output_close, .trigger = snd_mpu401_uart_output_trigger, }; -static struct snd_rawmidi_ops snd_mpu401_uart_input = +static const struct snd_rawmidi_ops snd_mpu401_uart_input = { .open = snd_mpu401_uart_input_open, .close = snd_mpu401_uart_input_close, diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index 30e8a1d..00b31f9 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -600,13 +600,13 @@ static int snd_mtpav_get_ISA(struct mtpav *mcard) /* */ -static struct snd_rawmidi_ops snd_mtpav_output = { +static const struct snd_rawmidi_ops snd_mtpav_output = { .open = snd_mtpav_output_open, .close = snd_mtpav_output_close, .trigger = snd_mtpav_output_trigger, }; -static struct snd_rawmidi_ops snd_mtpav_input = { +static const struct snd_rawmidi_ops snd_mtpav_input = { .open = snd_mtpav_input_open, .close = snd_mtpav_input_close, .trigger = snd_mtpav_input_trigger, diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c index fd4d18d..f32e813 100644 --- a/sound/drivers/mts64.c +++ b/sound/drivers/mts64.c @@ -749,13 +749,13 @@ static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substr spin_unlock_irqrestore(&mts->lock, flags); } -static struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = { +static const struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = { .open = snd_mts64_rawmidi_open, .close = snd_mts64_rawmidi_close, .trigger = snd_mts64_rawmidi_output_trigger }; -static struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = { +static const struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = { .open = snd_mts64_rawmidi_open, .close = snd_mts64_rawmidi_close, .trigger = snd_mts64_rawmidi_input_trigger diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 189e3e7..ec8a943 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -546,13 +546,13 @@ static void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substr spin_unlock_irqrestore(&pm->reg_lock, flags); } -static struct snd_rawmidi_ops snd_portman_midi_output = { +static const struct snd_rawmidi_ops snd_portman_midi_output = { .open = snd_portman_midi_open, .close = snd_portman_midi_close, .trigger = snd_portman_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_portman_midi_input = { +static const struct snd_rawmidi_ops snd_portman_midi_input = { .open = snd_portman_midi_open, .close = snd_portman_midi_close, .trigger = snd_portman_midi_input_trigger, diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index 1927b89..60d51ac 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -752,14 +752,14 @@ static void snd_uart16550_output_trigger(struct snd_rawmidi_substream *substream snd_uart16550_output_write(substream); } -static struct snd_rawmidi_ops snd_uart16550_output = +static const struct snd_rawmidi_ops snd_uart16550_output = { .open = snd_uart16550_output_open, .close = snd_uart16550_output_close, .trigger = snd_uart16550_output_trigger, }; -static struct snd_rawmidi_ops snd_uart16550_input = +static const struct snd_rawmidi_ops snd_uart16550_input = { .open = snd_uart16550_input_open, .close = snd_uart16550_input_close, diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index 1146727..ea7b377 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -1015,7 +1015,7 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream int size, space, count; struct snd_pcm_runtime *runtime = subs->runtime; - if (! pipe->prepared || (chip->chip_status & VX_STAT_IS_STALE)) + if (!pipe->running || (chip->chip_status & VX_STAT_IS_STALE)) return; size = runtime->buffer_size - snd_pcm_capture_avail(runtime); @@ -1048,8 +1048,10 @@ static void vx_pcm_capture_update(struct vx_core *chip, struct snd_pcm_substream /* ok, let's accelerate! */ int align = pipe->align * 3; space = (count / align) * align; - vx_pseudo_dma_read(chip, runtime, pipe, space); - count -= space; + if (space > 0) { + vx_pseudo_dma_read(chip, runtime, pipe, space); + count -= space; + } } /* read the rest of bytes */ while (count > 0) { diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig index ab894ed..9f00696 100644 --- a/sound/firewire/Kconfig +++ b/sound/firewire/Kconfig @@ -34,6 +34,7 @@ config SND_OXFW * LaCie Firewire Speakers * Behringer F-Control Audio 202 * Mackie(Loud) Onyx-i series (former models) + * Mackie(Loud) Onyx 1640i (former model) * Mackie(Loud) Onyx Satellite * Mackie(Loud) Tapco Link.Firewire * Mackie(Loud) d.2 pro/d.4 pro diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c index ce731f4..2b367c2 100644 --- a/sound/firewire/bebob/bebob_hwdep.c +++ b/sound/firewire/bebob/bebob_hwdep.c @@ -172,16 +172,15 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -190,7 +189,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob) goto end; strcpy(hwdep->name, "BeBoB"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = bebob; hwdep->exclusive = true; end: diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 868eb0d..3befa3e 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -106,18 +106,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&bebob->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_bebob *bebob, struct snd_rawmidi_str *str) { @@ -132,6 +120,16 @@ static void set_midi_substream_names(struct snd_bebob *bebob, int snd_bebob_create_midi_devices(struct snd_bebob *bebob) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; int err; @@ -151,7 +149,7 @@ int snd_bebob_create_midi_devices(struct snd_bebob *bebob) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; @@ -162,7 +160,7 @@ int snd_bebob_create_midi_devices(struct snd_bebob *bebob) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c index 5d7b934..9e27eb8 100644 --- a/sound/firewire/bebob/bebob_pcm.c +++ b/sound/firewire/bebob/bebob_pcm.c @@ -359,32 +359,31 @@ pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&bebob->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -395,8 +394,8 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob) pcm->private_data = bebob; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", bebob->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; } diff --git a/sound/firewire/dice/dice-interface.h b/sound/firewire/dice/dice-interface.h index 27b044f..47f2c0a 100644 --- a/sound/firewire/dice/dice-interface.h +++ b/sound/firewire/dice/dice-interface.h @@ -251,6 +251,7 @@ /* * The speed at which the packets are sent, SCODE_100-_400; read/write. + * SCODE_800 is only available in Dice III. */ #define TX_SPEED 0x014 diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index a040617..8ff6da3 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -78,18 +78,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&dice->lock, flags); } -static struct snd_rawmidi_ops capture_ops = { - .open = midi_open, - .close = midi_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops playback_ops = { - .open = midi_open, - .close = midi_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_dice *dice, struct snd_rawmidi_str *str) { @@ -103,6 +91,16 @@ static void set_midi_substream_names(struct snd_dice *dice, int snd_dice_create_midi(struct snd_dice *dice) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_open, + .close = midi_close, + .trigger = midi_playback_trigger, + }; __be32 reg; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c index ec4db3a..8573289 100644 --- a/sound/firewire/dice/dice-stream.c +++ b/sound/firewire/dice/dice-stream.c @@ -195,6 +195,7 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, unsigned int i, pcm_chs, midi_ports; struct amdtp_stream *streams; struct fw_iso_resources *resources; + struct fw_device *fw_dev = fw_parent_device(dice->unit); int err = 0; if (dir == AMDTP_IN_STREAM) { @@ -237,8 +238,17 @@ static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir, if (err < 0) return err; + if (dir == AMDTP_IN_STREAM) { + reg[0] = cpu_to_be32(fw_dev->max_speed); + err = snd_dice_transaction_write_tx(dice, + params->size * i + TX_SPEED, + reg, sizeof(reg[0])); + if (err < 0) + return err; + } + err = amdtp_stream_start(&streams[i], resources[i].channel, - fw_parent_device(dice->unit)->max_speed); + fw_dev->max_speed); if (err < 0) return err; } diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c index f188e47..463c6b8 100644 --- a/sound/firewire/digi00x/digi00x-hwdep.c +++ b/sound/firewire/digi00x/digi00x-hwdep.c @@ -173,16 +173,15 @@ static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -192,7 +191,7 @@ int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x) strcpy(hwdep->name, "Digi00x"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = dg00x; hwdep->exclusive = true; diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 1a72a38..915d2a2 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -76,18 +76,6 @@ static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&dg00x->lock, flags); } -static struct snd_rawmidi_ops midi_phys_capture_ops = { - .open = midi_phys_open, - .close = midi_phys_close, - .trigger = midi_phys_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_phys_playback_ops = { - .open = midi_phys_open, - .close = midi_phys_close, - .trigger = midi_phys_playback_trigger, -}; - static int midi_ctl_open(struct snd_rawmidi_substream *substream) { /* Do nothing. */ @@ -139,18 +127,6 @@ static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&dg00x->lock, flags); } -static struct snd_rawmidi_ops midi_ctl_capture_ops = { - .open = midi_ctl_open, - .close = midi_ctl_capture_close, - .trigger = midi_ctl_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_ctl_playback_ops = { - .open = midi_ctl_open, - .close = midi_ctl_playback_close, - .trigger = midi_ctl_playback_trigger, -}; - static void set_midi_substream_names(struct snd_dg00x *dg00x, struct snd_rawmidi_str *str, bool is_ctl) @@ -172,6 +148,26 @@ static void set_midi_substream_names(struct snd_dg00x *dg00x, int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) { + static const struct snd_rawmidi_ops phys_capture_ops = { + .open = midi_phys_open, + .close = midi_phys_close, + .trigger = midi_phys_capture_trigger, + }; + static const struct snd_rawmidi_ops phys_playback_ops = { + .open = midi_phys_open, + .close = midi_phys_close, + .trigger = midi_phys_playback_trigger, + }; + static const struct snd_rawmidi_ops ctl_capture_ops = { + .open = midi_ctl_open, + .close = midi_ctl_capture_close, + .trigger = midi_ctl_capture_trigger, + }; + static const struct snd_rawmidi_ops ctl_playback_ops = { + .open = midi_ctl_open, + .close = midi_ctl_playback_close, + .trigger = midi_ctl_playback_trigger, + }; struct snd_rawmidi *rmidi[2]; struct snd_rawmidi_str *str; unsigned int i; @@ -187,9 +183,9 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) "%s MIDI", dg00x->card->shortname); snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT, - &midi_phys_capture_ops); + &phys_capture_ops); snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_phys_playback_ops); + &phys_playback_ops); /* Add a pair of control midi ports. */ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1, @@ -201,9 +197,9 @@ int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x) "%s control", dg00x->card->shortname); snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT, - &midi_ctl_capture_ops); + &ctl_capture_ops); snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_ctl_playback_ops); + &ctl_playback_ops); for (i = 0; i < ARRAY_SIZE(rmidi); i++) { rmidi[i]->private_data = dg00x; diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c index 613f058..68d1c52 100644 --- a/sound/firewire/digi00x/digi00x-pcm.c +++ b/sound/firewire/digi00x/digi00x-pcm.c @@ -329,33 +329,31 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&dg00x->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -366,8 +364,8 @@ int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x) pcm->private_data = dg00x; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", dg00x->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; } diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c index 2e1d9a2..a3a3a16 100644 --- a/sound/firewire/fireworks/fireworks_hwdep.c +++ b/sound/firewire/fireworks/fireworks_hwdep.c @@ -303,17 +303,16 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .write = hwdep_write, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_efw_create_hwdep_device(struct snd_efw *efw) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .write = hwdep_write, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -322,7 +321,7 @@ int snd_efw_create_hwdep_device(struct snd_efw *efw) goto end; strcpy(hwdep->name, "Fireworks"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = efw; hwdep->exclusive = true; end: diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index 3e8c4cf..f5da2cd 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -107,18 +107,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&efw->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_efw *efw, struct snd_rawmidi_str *str) { @@ -132,6 +120,16 @@ static void set_midi_substream_names(struct snd_efw *efw, int snd_efw_create_midi_devices(struct snd_efw *efw) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; int err; @@ -151,7 +149,7 @@ int snd_efw_create_midi_devices(struct snd_efw *efw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; @@ -162,7 +160,7 @@ int snd_efw_create_midi_devices(struct snd_efw *efw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index f4fbf75..9171702 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -383,33 +383,31 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&efw->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_efw_create_pcm_devices(struct snd_efw *efw) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -419,8 +417,8 @@ int snd_efw_create_pcm_devices(struct snd_efw *efw) pcm->private_data = efw; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); end: return err; } diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 8665e10..b7bbd77 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -116,18 +116,6 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&oxfw->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - static void set_midi_substream_names(struct snd_oxfw *oxfw, struct snd_rawmidi_str *str) { @@ -142,6 +130,16 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw, int snd_oxfw_create_midi(struct snd_oxfw *oxfw) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *str; int err; @@ -164,7 +162,7 @@ int snd_oxfw_create_midi(struct snd_oxfw *oxfw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; @@ -175,7 +173,7 @@ int snd_oxfw_create_midi(struct snd_oxfw *oxfw) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c index f897c98..93209eb 100644 --- a/sound/firewire/oxfw/oxfw-scs1x.c +++ b/sound/firewire/oxfw/oxfw-scs1x.c @@ -297,7 +297,7 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *stream, int up) } } -static struct snd_rawmidi_ops midi_capture_ops = { +static const struct snd_rawmidi_ops midi_capture_ops = { .open = midi_capture_open, .close = midi_capture_close, .trigger = midi_capture_trigger, @@ -338,12 +338,6 @@ static void midi_playback_drain(struct snd_rawmidi_substream *stream) wait_event(scs->idle_wait, scs->output_idle); } -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, - .drain = midi_playback_drain, -}; static int register_address(struct snd_oxfw *oxfw) { struct fw_scs1x *scs = oxfw->spec; @@ -369,6 +363,12 @@ void snd_oxfw_scs1x_update(struct snd_oxfw *oxfw) int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) { + static const struct snd_rawmidi_ops midi_playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + .drain = midi_playback_drain, + }; struct snd_rawmidi *rmidi; struct fw_scs1x *scs; int err; diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index e629b88..74d7fb6 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -43,6 +43,7 @@ static bool detect_loud_models(struct fw_unit *unit) const char *const models[] = { "Onyxi", "Onyx-i", + "Onyx 1640i", "d.Pro", "Mackie Onyx Satellite", "Tapco LINK.firewire 4x6", diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 106406c..8c4437d 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -163,16 +163,15 @@ static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, #define hwdep_compat_ioctl NULL #endif -static const struct snd_hwdep_ops hwdep_ops = { - .read = hwdep_read, - .release = hwdep_release, - .poll = hwdep_poll, - .ioctl = hwdep_ioctl, - .ioctl_compat = hwdep_compat_ioctl, -}; - int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) { + static const struct snd_hwdep_ops ops = { + .read = hwdep_read, + .release = hwdep_release, + .poll = hwdep_poll, + .ioctl = hwdep_ioctl, + .ioctl_compat = hwdep_compat_ioctl, + }; struct snd_hwdep *hwdep; int err; @@ -182,7 +181,7 @@ int snd_tscm_create_hwdep_device(struct snd_tscm *tscm) strcpy(hwdep->name, "Tascam"); hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM; - hwdep->ops = hwdep_ops; + hwdep->ops = ops; hwdep->private_data = tscm; hwdep->exclusive = true; diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c index 41f8420..df4f95d 100644 --- a/sound/firewire/tascam/tascam-midi.c +++ b/sound/firewire/tascam/tascam-midi.c @@ -68,20 +68,18 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up) spin_unlock_irqrestore(&tscm->lock, flags); } -static struct snd_rawmidi_ops midi_capture_ops = { - .open = midi_capture_open, - .close = midi_capture_close, - .trigger = midi_capture_trigger, -}; - -static struct snd_rawmidi_ops midi_playback_ops = { - .open = midi_playback_open, - .close = midi_playback_close, - .trigger = midi_playback_trigger, -}; - int snd_tscm_create_midi_devices(struct snd_tscm *tscm) { + static const struct snd_rawmidi_ops capture_ops = { + .open = midi_capture_open, + .close = midi_capture_close, + .trigger = midi_capture_trigger, + }; + static const struct snd_rawmidi_ops playback_ops = { + .open = midi_playback_open, + .close = midi_playback_close, + .trigger = midi_playback_trigger, + }; struct snd_rawmidi *rmidi; struct snd_rawmidi_str *stream; struct snd_rawmidi_substream *subs; @@ -100,7 +98,7 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &midi_capture_ops); + &capture_ops); stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]; /* Set port names for MIDI input. */ @@ -116,7 +114,7 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm) rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &midi_playback_ops); + &playback_ops); stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]; /* Set port names for MIDI ourput. */ diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c index 79db1b6..f5dd6ce 100644 --- a/sound/firewire/tascam/tascam-pcm.c +++ b/sound/firewire/tascam/tascam-pcm.c @@ -268,33 +268,31 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm) return amdtp_stream_pcm_pointer(&tscm->rx_stream); } -static const struct snd_pcm_ops pcm_capture_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_capture_hw_params, - .hw_free = pcm_capture_hw_free, - .prepare = pcm_capture_prepare, - .trigger = pcm_capture_trigger, - .pointer = pcm_capture_pointer, - .page = snd_pcm_lib_get_vmalloc_page, -}; - -static const struct snd_pcm_ops pcm_playback_ops = { - .open = pcm_open, - .close = pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = pcm_playback_hw_params, - .hw_free = pcm_playback_hw_free, - .prepare = pcm_playback_prepare, - .trigger = pcm_playback_trigger, - .pointer = pcm_playback_pointer, - .page = snd_pcm_lib_get_vmalloc_page, - .mmap = snd_pcm_lib_mmap_vmalloc, -}; - int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) { + static const struct snd_pcm_ops capture_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_capture_hw_params, + .hw_free = pcm_capture_hw_free, + .prepare = pcm_capture_prepare, + .trigger = pcm_capture_trigger, + .pointer = pcm_capture_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + }; + static const struct snd_pcm_ops playback_ops = { + .open = pcm_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_playback_hw_params, + .hw_free = pcm_playback_hw_free, + .prepare = pcm_playback_prepare, + .trigger = pcm_playback_trigger, + .pointer = pcm_playback_pointer, + .page = snd_pcm_lib_get_vmalloc_page, + .mmap = snd_pcm_lib_mmap_vmalloc, + }; struct snd_pcm *pcm; int err; @@ -305,8 +303,8 @@ int snd_tscm_create_pcm_devices(struct snd_tscm *tscm) pcm->private_data = tscm; snprintf(pcm->name, sizeof(pcm->name), "%s PCM", tscm->card->shortname); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops); return 0; } diff --git a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c index 3992912..ac5f568 100644 --- a/sound/isa/gus/gus_uart.c +++ b/sound/isa/gus/gus_uart.c @@ -227,14 +227,14 @@ static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&gus->uart_cmd_lock, flags); } -static struct snd_rawmidi_ops snd_gf1_uart_output = +static const struct snd_rawmidi_ops snd_gf1_uart_output = { .open = snd_gf1_uart_output_open, .close = snd_gf1_uart_output_close, .trigger = snd_gf1_uart_output_trigger, }; -static struct snd_rawmidi_ops snd_gf1_uart_input = +static const struct snd_rawmidi_ops snd_gf1_uart_input = { .open = snd_gf1_uart_input_open, .close = snd_gf1_uart_input_close, diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c index ffc67fd..912b5a9 100644 --- a/sound/isa/msnd/msnd_midi.c +++ b/sound/isa/msnd/msnd_midi.c @@ -142,7 +142,7 @@ void snd_msndmidi_input_read(void *mpuv) } EXPORT_SYMBOL(snd_msndmidi_input_read); -static struct snd_rawmidi_ops snd_msndmidi_input = { +static const struct snd_rawmidi_ops snd_msndmidi_input = { .open = snd_msndmidi_input_open, .close = snd_msndmidi_input_close, .trigger = snd_msndmidi_input_trigger, diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c index d551c50..bd672ab 100644 --- a/sound/isa/sb/sb8_midi.c +++ b/sound/isa/sb/sb8_midi.c @@ -247,14 +247,14 @@ static void snd_sb8dsp_midi_output_trigger(struct snd_rawmidi_substream *substre snd_sb8dsp_midi_output_write(substream); } -static struct snd_rawmidi_ops snd_sb8dsp_midi_output = +static const struct snd_rawmidi_ops snd_sb8dsp_midi_output = { .open = snd_sb8dsp_midi_output_open, .close = snd_sb8dsp_midi_output_close, .trigger = snd_sb8dsp_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_sb8dsp_midi_input = +static const struct snd_rawmidi_ops snd_sb8dsp_midi_input = { .open = snd_sb8dsp_midi_input_open, .close = snd_sb8dsp_midi_input_close, diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index 8a80fc6..2aa05f3 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -559,14 +559,14 @@ snd_wavefront_midi_start (snd_wavefront_card_t *card) return 0; } -struct snd_rawmidi_ops snd_wavefront_midi_output = +const struct snd_rawmidi_ops snd_wavefront_midi_output = { .open = snd_wavefront_midi_output_open, .close = snd_wavefront_midi_output_close, .trigger = snd_wavefront_midi_output_trigger, }; -struct snd_rawmidi_ops snd_wavefront_midi_input = +const struct snd_rawmidi_ops snd_wavefront_midi_input = { .open = snd_wavefront_midi_input_open, .close = snd_wavefront_midi_input_close, diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c index ede449f..00fc924 100644 --- a/sound/mips/hal2.c +++ b/sound/mips/hal2.c @@ -219,6 +219,8 @@ static int hal2_gain_get(struct snd_kcontrol *kcontrol, l = (tmp >> H2I_C2_L_GAIN_SHIFT) & 15; r = (tmp >> H2I_C2_R_GAIN_SHIFT) & 15; break; + default: + return -EINVAL; } ucontrol->value.integer.value[0] = l; ucontrol->value.integer.value[1] = r; @@ -256,6 +258,8 @@ static int hal2_gain_put(struct snd_kcontrol *kcontrol, new |= (r << H2I_C2_R_GAIN_SHIFT); hal2_i_write32(hal2, H2I_ADC_C2, new); break; + default: + return -EINVAL; } return old != new; } diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c index 6368e5c..f6156d8 100644 --- a/sound/oss/ad1848.c +++ b/sound/oss/ad1848.c @@ -121,11 +121,6 @@ static bool deskpro_xl; static bool deskpro_m; static bool soundpro; -static volatile signed char irq2dev[17] = { - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; - #ifndef EXCLUDE_TIMERS static int timer_installed = -1; #endif @@ -2060,7 +2055,7 @@ int ad1848_init (char *name, struct resource *ports, int irq, int dma_playback, else devc->irq_ok = 1; /* Couldn't test. assume it's OK */ } else if (irq < 0) - irq2dev[-irq] = devc->dev_no = my_dev; + devc->dev_no = my_dev; #ifndef EXCLUDE_TIMERS if ((capabilities[devc->model].flags & CAP_F_TIMER) && diff --git a/sound/pci/ca0106/ca_midi.c b/sound/pci/ca0106/ca_midi.c index b91c7f6..4d4d385 100644 --- a/sound/pci/ca0106/ca_midi.c +++ b/sound/pci/ca0106/ca_midi.c @@ -255,14 +255,14 @@ static void ca_midi_output_trigger(struct snd_rawmidi_substream *substream, int } } -static struct snd_rawmidi_ops ca_midi_output = +static const struct snd_rawmidi_ops ca_midi_output = { .open = ca_midi_output_open, .close = ca_midi_output_close, .trigger = ca_midi_output_trigger, }; -static struct snd_rawmidi_ops ca_midi_input = +static const struct snd_rawmidi_ops ca_midi_input = { .open = ca_midi_input_open, .close = ca_midi_input_close, diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index 8f0f5f2..fa7c516 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1767,14 +1767,14 @@ static void snd_cs4281_midi_output_trigger(struct snd_rawmidi_substream *substre spin_unlock_irqrestore(&chip->reg_lock, flags); } -static struct snd_rawmidi_ops snd_cs4281_midi_output = +static const struct snd_rawmidi_ops snd_cs4281_midi_output = { .open = snd_cs4281_midi_output_open, .close = snd_cs4281_midi_output_close, .trigger = snd_cs4281_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_cs4281_midi_input = +static const struct snd_rawmidi_ops snd_cs4281_midi_input = { .open = snd_cs4281_midi_input_open, .close = snd_cs4281_midi_input_close, diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index fde3cd4..e4cf318 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -72,18 +72,18 @@ static void amp_voyetra(struct snd_cs46xx *chip, int change); #ifdef CONFIG_SND_CS46XX_NEW_DSP -static struct snd_pcm_ops snd_cs46xx_playback_rear_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops; -static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops; -static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops; #endif -static struct snd_pcm_ops snd_cs46xx_playback_ops; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops; -static struct snd_pcm_ops snd_cs46xx_capture_ops; -static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_ops; +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops; +static const struct snd_pcm_ops snd_cs46xx_capture_ops; +static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops; static unsigned short snd_cs46xx_codec_read(struct snd_cs46xx *chip, unsigned short reg, @@ -1654,7 +1654,7 @@ static int snd_cs46xx_capture_close(struct snd_pcm_substream *substream) } #ifdef CONFIG_SND_CS46XX_NEW_DSP -static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_rear_ops = { .open = snd_cs46xx_playback_open_rear, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1665,7 +1665,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_rear_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = { .open = snd_cs46xx_playback_open_rear, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1677,7 +1677,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_rear_ops = { .ack = snd_cs46xx_playback_transfer, }; -static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = { .open = snd_cs46xx_playback_open_clfe, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1688,7 +1688,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_clfe_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = { .open = snd_cs46xx_playback_open_clfe, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1700,7 +1700,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_clfe_ops = { .ack = snd_cs46xx_playback_transfer, }; -static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = { .open = snd_cs46xx_playback_open_iec958, .close = snd_cs46xx_playback_close_iec958, .ioctl = snd_pcm_lib_ioctl, @@ -1711,7 +1711,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_iec958_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = { .open = snd_cs46xx_playback_open_iec958, .close = snd_cs46xx_playback_close_iec958, .ioctl = snd_pcm_lib_ioctl, @@ -1725,7 +1725,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_iec958_ops = { #endif -static struct snd_pcm_ops snd_cs46xx_playback_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_ops = { .open = snd_cs46xx_playback_open, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1736,7 +1736,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_ops = { .pointer = snd_cs46xx_playback_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = { +static const struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = { .open = snd_cs46xx_playback_open, .close = snd_cs46xx_playback_close, .ioctl = snd_pcm_lib_ioctl, @@ -1748,7 +1748,7 @@ static struct snd_pcm_ops snd_cs46xx_playback_indirect_ops = { .ack = snd_cs46xx_playback_transfer, }; -static struct snd_pcm_ops snd_cs46xx_capture_ops = { +static const struct snd_pcm_ops snd_cs46xx_capture_ops = { .open = snd_cs46xx_capture_open, .close = snd_cs46xx_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -1759,7 +1759,7 @@ static struct snd_pcm_ops snd_cs46xx_capture_ops = { .pointer = snd_cs46xx_capture_direct_pointer, }; -static struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = { +static const struct snd_pcm_ops snd_cs46xx_capture_indirect_ops = { .open = snd_cs46xx_capture_open, .close = snd_cs46xx_capture_close, .ioctl = snd_pcm_lib_ioctl, @@ -2683,14 +2683,14 @@ static void snd_cs46xx_midi_output_trigger(struct snd_rawmidi_substream *substre spin_unlock_irqrestore(&chip->reg_lock, flags); } -static struct snd_rawmidi_ops snd_cs46xx_midi_output = +static const struct snd_rawmidi_ops snd_cs46xx_midi_output = { .open = snd_cs46xx_midi_output_open, .close = snd_cs46xx_midi_output_close, .trigger = snd_cs46xx_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_cs46xx_midi_input = +static const struct snd_rawmidi_ops snd_cs46xx_midi_input = { .open = snd_cs46xx_midi_input_open, .close = snd_cs46xx_midi_input_close, diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c index 06ac5d8..82bd10b 100644 --- a/sound/pci/cs5535audio/cs5535audio_pm.c +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -55,7 +55,7 @@ static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) } -static int snd_cs5535audio_suspend(struct device *dev) +static int __maybe_unused snd_cs5535audio_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; @@ -74,7 +74,7 @@ static int snd_cs5535audio_suspend(struct device *dev) return 0; } -static int snd_cs5535audio_resume(struct device *dev) +static int __maybe_unused snd_cs5535audio_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct cs5535audio *cs5535au = card->private_data; diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c index a8fe583..8c685dd 100644 --- a/sound/pci/echoaudio/midi.c +++ b/sound/pci/echoaudio/midi.c @@ -288,13 +288,13 @@ static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream) -static struct snd_rawmidi_ops snd_echo_midi_input = { +static const struct snd_rawmidi_ops snd_echo_midi_input = { .open = snd_echo_midi_input_open, .close = snd_echo_midi_input_close, .trigger = snd_echo_midi_input_trigger, }; -static struct snd_rawmidi_ops snd_echo_midi_output = { +static const struct snd_rawmidi_ops snd_echo_midi_output = { .open = snd_echo_midi_output_open, .close = snd_echo_midi_output_close, .trigger = snd_echo_midi_output_trigger, diff --git a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c index d2c7ea3..aa2cc27 100644 --- a/sound/pci/emu10k1/emu10k1_callback.c +++ b/sound/pci/emu10k1/emu10k1_callback.c @@ -61,7 +61,7 @@ static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); /* * set up operators */ -static struct snd_emux_operators emu10k1_ops = { +static const struct snd_emux_operators emu10k1_ops = { .owner = THIS_MODULE, .get_voice = get_voice, .prepare = start_voice, diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index 921037e..3284273 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -1486,14 +1486,14 @@ static void snd_emu10k1x_midi_output_trigger(struct snd_rawmidi_substream *subst */ -static struct snd_rawmidi_ops snd_emu10k1x_midi_output = +static const struct snd_rawmidi_ops snd_emu10k1x_midi_output = { .open = snd_emu10k1x_midi_output_open, .close = snd_emu10k1x_midi_output_close, .trigger = snd_emu10k1x_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_emu10k1x_midi_input = +static const struct snd_rawmidi_ops snd_emu10k1x_midi_input = { .open = snd_emu10k1x_midi_input_open, .close = snd_emu10k1x_midi_input_close, diff --git a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c index fdf2b0a..b6650f5 100644 --- a/sound/pci/emu10k1/emumpu401.c +++ b/sound/pci/emu10k1/emumpu401.c @@ -308,14 +308,14 @@ static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substr */ -static struct snd_rawmidi_ops snd_emu10k1_midi_output = +static const struct snd_rawmidi_ops snd_emu10k1_midi_output = { .open = snd_emu10k1_midi_output_open, .close = snd_emu10k1_midi_output_close, .trigger = snd_emu10k1_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_emu10k1_midi_input = +static const struct snd_rawmidi_ops snd_emu10k1_midi_input = { .open = snd_emu10k1_midi_input_open, .close = snd_emu10k1_midi_input_close, diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index 51736c2..164adad 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -2317,14 +2317,14 @@ static void snd_ensoniq_midi_output_trigger(struct snd_rawmidi_substream *substr spin_unlock_irqrestore(&ensoniq->reg_lock, flags); } -static struct snd_rawmidi_ops snd_ensoniq_midi_output = +static const struct snd_rawmidi_ops snd_ensoniq_midi_output = { .open = snd_ensoniq_midi_output_open, .close = snd_ensoniq_midi_output_close, .trigger = snd_ensoniq_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_ensoniq_midi_input = +static const struct snd_rawmidi_ops snd_ensoniq_midi_input = { .open = snd_ensoniq_midi_input_open, .close = snd_ensoniq_midi_input_close, diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 5008785..3715a57 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -861,6 +861,10 @@ static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, return -EIO; } + /* no fallback mechanism? */ + if (!chip->fallback_to_single_cmd) + return -EIO; + /* a fatal communication error; need either to reset or to fallback * to the single_cmd mode */ diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index a50e053..35a9ab2 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -150,6 +150,7 @@ struct azx { int bdl_pos_adj; int poll_count; unsigned int running:1; + unsigned int fallback_to_single_cmd:1; unsigned int single_cmd:1; unsigned int polling_mode:1; unsigned int msi:1; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c64d986..16108f0e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -128,7 +128,7 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; static int probe_only[SNDRV_CARDS]; static int jackpoll_ms[SNDRV_CARDS]; -static bool single_cmd; +static int single_cmd = -1; static int enable_msi = -1; #ifdef CONFIG_SND_HDA_PATCH_LOADER static char *patch[SNDRV_CARDS]; @@ -157,7 +157,7 @@ module_param_array(probe_only, int, NULL, 0444); MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); module_param_array(jackpoll_ms, int, NULL, 0444); MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)"); -module_param(single_cmd, bool, 0444); +module_param(single_cmd, bint, 0444); MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " "(for debugging only)."); module_param(enable_msi, bint, 0444); @@ -1596,7 +1596,11 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, check_probe_mask(chip, dev); - chip->single_cmd = single_cmd; + if (single_cmd < 0) /* allow fallback to single_cmd at errors */ + chip->fallback_to_single_cmd = 1; + else /* explicitly set to single_cmd or not */ + chip->single_cmd = single_cmd; + azx_check_snoop_available(chip); if (bdl_pos_adj[dev] < 0) @@ -1774,6 +1778,14 @@ static int azx_first_init(struct azx *chip) chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; + /* sanity check for the SDxCTL.STRM field overflow */ + if (chip->num_streams > 15 && + (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) == 0) { + dev_warn(chip->card->dev, "number of I/O streams is %d, " + "forcing separate stream tags", chip->num_streams); + chip->driver_caps |= AZX_DCAPS_SEPARATE_STREAM_TAG; + } + /* initialize streams */ err = azx_init_streams(chip); if (err < 0) @@ -2155,7 +2167,20 @@ static void azx_remove(struct pci_dev *pci) /* cancel the pending probing work */ chip = card->private_data; hda = container_of(chip, struct hda_intel, chip); + /* FIXME: below is an ugly workaround. + * Both device_release_driver() and driver_probe_device() + * take *both* the device's and its parent's lock before + * calling the remove() and probe() callbacks. The codec + * probe takes the locks of both the codec itself and its + * parent, i.e. the PCI controller dev. Meanwhile, when + * the PCI controller is unbound, it takes its lock, too + * ==> ouch, a deadlock! + * As a workaround, we unlock temporarily here the controller + * device during cancel_work_sync() call. + */ + device_unlock(&pci->dev); cancel_work_sync(&hda->probe_work); + device_lock(&pci->dev); snd_card_free(card); } @@ -2197,9 +2222,9 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, /* Lewisburg */ { PCI_DEVICE(0x8086, 0xa1f0), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, { PCI_DEVICE(0x8086, 0xa270), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE }, /* Lynx Point-LP */ { PCI_DEVICE(0x8086, 0x9c20), .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 11b9b2f..9ec4dba 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1482,6 +1482,9 @@ static int dspio_scp(struct hda_codec *codec, } else if (ret_size != reply_data_size) { codec_dbg(codec, "RetLen and HdrLen .NE.\n"); return -EINVAL; + } else if (!reply) { + codec_dbg(codec, "NULL reply\n"); + return -EINVAL; } else { *reply_len = ret_size*sizeof(unsigned int); memcpy(reply, scp_reply.data, *reply_len); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index cf9bc042..3fc201c 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -3639,6 +3639,7 @@ HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP", patch_nvhdmi), +HDA_CODEC_ENTRY(0x10de0080, "GPU 80 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP", patch_nvhdmi), HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI", patch_nvhdmi_2ch), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7d660ee..73a0046 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -337,6 +337,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0288: case 0x10ec0295: case 0x10ec0298: + case 0x10ec0299: alc_update_coef_idx(codec, 0x10, 1<<9, 0); break; case 0x10ec0285: @@ -379,6 +380,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) break; case 0x10ec0899: case 0x10ec0900: + case 0x10ec1220: alc_update_coef_idx(codec, 0x7, 1<<1, 0); break; } @@ -912,6 +914,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = { { 0x10ec0256, 0x1028, 0, "ALC3246" }, { 0x10ec0225, 0x1028, 0, "ALC3253" }, { 0x10ec0295, 0x1028, 0, "ALC3254" }, + { 0x10ec0299, 0x1028, 0, "ALC3271" }, { 0x10ec0670, 0x1025, 0, "ALC669X" }, { 0x10ec0676, 0x1025, 0, "ALC679X" }, { 0x10ec0282, 0x1043, 0, "ALC3229" }, @@ -2309,6 +2312,7 @@ static int patch_alc882(struct hda_codec *codec) case 0x10ec0882: case 0x10ec0885: case 0x10ec0900: + case 0x10ec1220: break; default: /* ALC883 and variants */ @@ -3717,6 +3721,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; case 0x10ec0867: @@ -3812,6 +3817,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, case 0x10ec0867: alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14); /* fallthru */ + case 0x10ec0221: case 0x10ec0662: snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); @@ -3824,6 +3830,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); alc_process_coef_fw(codec, coef0225); @@ -3882,6 +3889,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) switch (codec->core.vendor_id) { case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; case 0x10ec0255: @@ -3997,6 +4005,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; case 0x10ec0867: @@ -4090,6 +4099,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); break; } @@ -4174,6 +4184,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: alc_process_coef_fw(codec, coef0225); msleep(800); val = alc_read_coef_idx(codec, 0x46); @@ -4401,7 +4412,7 @@ static void alc_no_shutup(struct hda_codec *codec) static void alc_fixup_no_shutup(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - if (action == HDA_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { struct alc_spec *spec = codec->spec; spec->shutup = alc_no_shutup; } @@ -4857,6 +4868,8 @@ enum { ALC292_FIXUP_TPT460, ALC298_FIXUP_SPK_VOLUME, ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER, + ALC269_FIXUP_ATIV_BOOK_8, + ALC221_FIXUP_HP_MIC_NO_PRESENCE, }; static const struct hda_fixup alc269_fixups[] = { @@ -5529,6 +5542,22 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE }, + [ALC269_FIXUP_ATIV_BOOK_8] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_auto_mute_via_amp, + .chained = true, + .chain_id = ALC269_FIXUP_NO_SHUTUP + }, + [ALC221_FIXUP_HP_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5639,6 +5668,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC), SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), + SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -5665,6 +5696,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN), SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), + SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8), SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), @@ -6065,6 +6097,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE, ALC298_STANDARD_PINS, {0x17, 0x90170150}), + SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_SPK_VOLUME, + {0x12, 0xb7a60140}, + {0x13, 0xb7a60150}, + {0x17, 0x90170110}, + {0x1a, 0x03011020}, + {0x21, 0x03211030}), {} }; @@ -6212,6 +6250,7 @@ static int patch_alc269(struct hda_codec *codec) break; case 0x10ec0225: case 0x10ec0295: + case 0x10ec0299: spec->codec_variant = ALC269_TYPE_ALC225; break; case 0x10ec0234: @@ -7250,6 +7289,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269), HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0299, "ALC299", patch_alc269), HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861), @@ -7281,6 +7321,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), + HDA_CODEC_ENTRY(0x10ec1220, "ALC1220", patch_alc882), {} /* terminator */ }; MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 37b70f8..faa3d38 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -166,6 +166,7 @@ enum { STAC_D965_VERBS, STAC_DELL_3ST, STAC_DELL_BIOS, + STAC_NEMO_DEFAULT, STAC_DELL_BIOS_AMIC, STAC_DELL_BIOS_SPDIF, STAC_927X_DELL_DMIC, @@ -1360,6 +1361,27 @@ static const struct hda_pintbl oqo9200_pin_configs[] = { {} }; +/* + * STAC 92HD700 + * 18881000 Amigaone X1000 + */ +static const struct hda_pintbl nemo_pin_configs[] = { + { 0x0a, 0x02214020 }, /* Front panel HP socket */ + { 0x0b, 0x02a19080 }, /* Front Mic */ + { 0x0c, 0x0181304e }, /* Line in */ + { 0x0d, 0x01014010 }, /* Line out */ + { 0x0e, 0x01a19040 }, /* Rear Mic */ + { 0x0f, 0x01011012 }, /* Rear speakers */ + { 0x10, 0x01016011 }, /* Center speaker */ + { 0x11, 0x01012014 }, /* Side speakers (7.1) */ + { 0x12, 0x103301f0 }, /* Motherboard CD line in connector */ + { 0x13, 0x411111f0 }, /* Unused */ + { 0x14, 0x411111f0 }, /* Unused */ + { 0x21, 0x01442170 }, /* S/PDIF line out */ + { 0x22, 0x411111f0 }, /* Unused */ + { 0x23, 0x411111f0 }, /* Unused */ + {} +}; static void stac9200_fixup_panasonic(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -3883,6 +3905,10 @@ static const struct hda_fixup stac927x_fixups[] = { .type = HDA_FIXUP_PINS, .v.pins = d965_5st_no_fp_pin_configs, }, + [STAC_NEMO_DEFAULT] = { + .type = HDA_FIXUP_PINS, + .v.pins = nemo_pin_configs, + }, [STAC_DELL_3ST] = { .type = HDA_FIXUP_PINS, .v.pins = dell_3st_pin_configs, @@ -3939,6 +3965,7 @@ static const struct hda_model_fixup stac927x_models[] = { { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, { .id = STAC_DELL_3ST, .name = "dell-3stack" }, { .id = STAC_DELL_BIOS, .name = "dell-bios" }, + { .id = STAC_NEMO_DEFAULT, .name = "nemo-default" }, { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" }, { .id = STAC_927X_VOLKNOB, .name = "volknob" }, {} @@ -3977,6 +4004,8 @@ static const struct snd_pci_quirk stac927x_fixup_tbl[] = { "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_INTEL, 0xff00, 0x2500, "Intel D965", STAC_D965_5ST), + /* Nemo */ + SND_PCI_QUIRK(0x1888, 0x1000, "AmigaOne X1000", STAC_NEMO_DEFAULT), /* volume-knob fixes */ SND_PCI_QUIRK_VENDOR(0x10cf, "FSC", STAC_927X_VOLKNOB), {} /* terminator */ @@ -5036,6 +5065,7 @@ static const struct hda_device_id snd_hda_id_sigmatel[] = { HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x), HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x), HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x), + HDA_CODEC_ENTRY(0x83847638, "STAC92HD700", patch_stac927x), HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x), HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x), HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x), diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index e5c52ed..842744e 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -367,7 +367,7 @@ static void vt1724_midi_output_drain(struct snd_rawmidi_substream *s) } while (time_after(timeout, jiffies)); } -static struct snd_rawmidi_ops vt1724_midi_output_ops = { +static const struct snd_rawmidi_ops vt1724_midi_output_ops = { .open = vt1724_midi_output_open, .close = vt1724_midi_output_close, .trigger = vt1724_midi_output_trigger, @@ -402,7 +402,7 @@ static void vt1724_midi_input_trigger(struct snd_rawmidi_substream *s, int up) spin_unlock_irqrestore(&ice->reg_lock, flags); } -static struct snd_rawmidi_ops vt1724_midi_input_ops = { +static const struct snd_rawmidi_ops vt1724_midi_input_ops = { .open = vt1724_midi_input_open, .close = vt1724_midi_input_close, .trigger = vt1724_midi_input_trigger, diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h index 0cc17e0..4267438 100644 --- a/sound/pci/mixart/mixart.h +++ b/sound/pci/mixart/mixart.h @@ -86,7 +86,7 @@ struct mixart_mgr { u32 msg_fifo[MSG_FIFO_SIZE]; int msg_fifo_readptr; int msg_fifo_writeptr; - atomic_t msg_processed; /* number of messages to be processed in takslet */ + atomic_t msg_processed; /* number of messages to be processed in tasklet */ struct mutex lock; /* interrupt lock */ struct mutex msg_lock; /* mailbox lock */ diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index b94fc63..fc0face 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -1510,14 +1510,14 @@ static int snd_hdsp_midi_output_close(struct snd_rawmidi_substream *substream) return 0; } -static struct snd_rawmidi_ops snd_hdsp_midi_output = +static const struct snd_rawmidi_ops snd_hdsp_midi_output = { .open = snd_hdsp_midi_output_open, .close = snd_hdsp_midi_output_close, .trigger = snd_hdsp_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_hdsp_midi_input = +static const struct snd_rawmidi_ops snd_hdsp_midi_input = { .open = snd_hdsp_midi_input_open, .close = snd_hdsp_midi_input_close, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 14bbf55..c48acdb 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2043,14 +2043,14 @@ static int snd_hdspm_midi_output_close(struct snd_rawmidi_substream *substream) return 0; } -static struct snd_rawmidi_ops snd_hdspm_midi_output = +static const struct snd_rawmidi_ops snd_hdspm_midi_output = { .open = snd_hdspm_midi_output_open, .close = snd_hdspm_midi_output_close, .trigger = snd_hdspm_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_hdspm_midi_input = +static const struct snd_rawmidi_ops snd_hdspm_midi_input = { .open = snd_hdspm_midi_input_open, .close = snd_hdspm_midi_input_close, diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c index af83b3b..8e457ea 100644 --- a/sound/pci/vx222/vx222_ops.c +++ b/sound/pci/vx222/vx222_ops.c @@ -269,12 +269,12 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, /* Transfer using pseudo-dma. */ - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) { + for (; length > 0; length--) { outl(cpu_to_le32(*addr), port); addr++; } @@ -284,7 +284,7 @@ static void vx2_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) { + for (; count > 0; count--) { outl(cpu_to_le32(*addr), port); addr++; } @@ -307,12 +307,12 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, vx2_setup_pseudo_dma(chip, 0); /* Transfer using pseudo-dma. */ - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) + for (; length > 0; length--) *addr++ = le32_to_cpu(inl(port)); addr = (u32 *)runtime->dma_area; pipe->hw_ptr = 0; @@ -320,7 +320,7 @@ static void vx2_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 2; /* in 32bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) + for (; count > 0; count--) *addr++ = le32_to_cpu(inl(port)); vx2_release_pseudo_dma(chip); diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 2819729..56aa1ba 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -369,12 +369,12 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, unsigned short *addr = (unsigned short *)(runtime->dma_area + offset); vx_setup_pseudo_dma(chip, 1); - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) { + for (; length > 0; length--) { outw(cpu_to_le16(*addr), port); addr++; } @@ -384,7 +384,7 @@ static void vxp_dma_write(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 0) { + for (; count > 0; count--) { outw(cpu_to_le16(*addr), port); addr++; } @@ -411,12 +411,12 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, if (snd_BUG_ON(count % 2)) return; vx_setup_pseudo_dma(chip, 0); - if (offset + count > pipe->buffer_bytes) { + if (offset + count >= pipe->buffer_bytes) { int length = pipe->buffer_bytes - offset; count -= length; length >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (length-- > 0) + for (; length > 0; length--) *addr++ = le16_to_cpu(inw(port)); addr = (unsigned short *)runtime->dma_area; pipe->hw_ptr = 0; @@ -424,7 +424,7 @@ static void vxp_dma_read(struct vx_core *chip, struct snd_pcm_runtime *runtime, pipe->hw_ptr += count; count >>= 1; /* in 16bit words */ /* Transfer using pseudo-dma. */ - while (count-- > 1) + for (; count > 1; count--) *addr++ = le16_to_cpu(inw(port)); /* Disable DMA */ pchip->regDIALOG &= ~VXP_DLG_DMAREAD_SEL_MASK; diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c index a020920..55579f6 100644 --- a/sound/synth/emux/emux_seq.c +++ b/sound/synth/emux/emux_seq.c @@ -33,13 +33,13 @@ static int snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *inf * MIDI emulation operators */ static struct snd_midi_op emux_ops = { - snd_emux_note_on, - snd_emux_note_off, - snd_emux_key_press, - snd_emux_terminate_note, - snd_emux_control, - snd_emux_nrpn, - snd_emux_sysex, + .note_on = snd_emux_note_on, + .note_off = snd_emux_note_off, + .key_press = snd_emux_key_press, + .note_terminate = snd_emux_terminate_note, + .control = snd_emux_control, + .nrpn = snd_emux_nrpn, + .sysex = snd_emux_sysex, }; diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index 3d41096..aa5adbb 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -139,14 +139,14 @@ static void usb6fire_midi_in_trigger( spin_unlock_irqrestore(&rt->in_lock, flags); } -static struct snd_rawmidi_ops out_ops = { +static const struct snd_rawmidi_ops out_ops = { .open = usb6fire_midi_out_open, .close = usb6fire_midi_out_close, .trigger = usb6fire_midi_out_trigger, .drain = usb6fire_midi_out_drain }; -static struct snd_rawmidi_ops in_ops = { +static const struct snd_rawmidi_ops in_ops = { .open = usb6fire_midi_in_open, .close = usb6fire_midi_in_close, .trigger = usb6fire_midi_in_trigger diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 2d2d122..42cb33b 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -10,6 +10,7 @@ snd-usb-audio-objs := card.o \ mixer.o \ mixer_quirks.o \ mixer_scarlett.o \ + mixer_us16x08.o \ pcm.o \ proc.o \ quirks.o \ diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c index d060ddd..2ff9d57 100644 --- a/sound/usb/bcd2000/bcd2000.c +++ b/sound/usb/bcd2000/bcd2000.c @@ -252,13 +252,13 @@ static void bcd2000_input_complete(struct urb *urb) __func__, ret); } -static struct snd_rawmidi_ops bcd2000_midi_output = { +static const struct snd_rawmidi_ops bcd2000_midi_output = { .open = bcd2000_midi_output_open, .close = bcd2000_midi_output_close, .trigger = bcd2000_midi_output_trigger, }; -static struct snd_rawmidi_ops bcd2000_midi_input = { +static const struct snd_rawmidi_ops bcd2000_midi_input = { .open = bcd2000_midi_input_open, .close = bcd2000_midi_input_close, .trigger = bcd2000_midi_input_trigger, diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c index 2d75884..f8e5b1b 100644 --- a/sound/usb/caiaq/midi.c +++ b/sound/usb/caiaq/midi.c @@ -102,14 +102,14 @@ static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *subs } -static struct snd_rawmidi_ops snd_usb_caiaq_midi_output = +static const struct snd_rawmidi_ops snd_usb_caiaq_midi_output = { .open = snd_usb_caiaq_midi_output_open, .close = snd_usb_caiaq_midi_output_close, .trigger = snd_usb_caiaq_midi_output_trigger, }; -static struct snd_rawmidi_ops snd_usb_caiaq_midi_input = +static const struct snd_rawmidi_ops snd_usb_caiaq_midi_input = { .open = snd_usb_caiaq_midi_input_open, .close = snd_usb_caiaq_midi_input_close, diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index 90009c0..0ff5a7d 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -492,42 +492,46 @@ static void line6_destruct(struct snd_card *card) usb_put_dev(usbdev); } -/* get data from endpoint descriptor (see usb_maxpacket): */ -static void line6_get_interval(struct usb_line6 *line6) +static void line6_get_usb_properties(struct usb_line6 *line6) { struct usb_device *usbdev = line6->usbdev; const struct line6_properties *properties = line6->properties; int pipe; - struct usb_host_endpoint *ep; + struct usb_host_endpoint *ep = NULL; - if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) { - pipe = - usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r); - } else { - pipe = - usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r); + if (properties->capabilities & LINE6_CAP_CONTROL) { + if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) { + pipe = usb_rcvintpipe(line6->usbdev, + line6->properties->ep_ctrl_r); + } else { + pipe = usb_rcvbulkpipe(line6->usbdev, + line6->properties->ep_ctrl_r); + } + ep = usbdev->ep_in[usb_pipeendpoint(pipe)]; } - ep = usbdev->ep_in[usb_pipeendpoint(pipe)]; + /* Control data transfer properties */ if (ep) { line6->interval = ep->desc.bInterval; - if (usbdev->speed == USB_SPEED_LOW) { - line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND; - line6->iso_buffers = USB_LOW_ISO_BUFFERS; - } else { - line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND; - line6->iso_buffers = USB_HIGH_ISO_BUFFERS; - } - line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); } else { - dev_err(line6->ifcdev, - "endpoint not available, using fallback values"); + if (properties->capabilities & LINE6_CAP_CONTROL) { + dev_err(line6->ifcdev, + "endpoint not available, using fallback values"); + } line6->interval = LINE6_FALLBACK_INTERVAL; line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; } -} + /* Isochronous transfer properties */ + if (usbdev->speed == USB_SPEED_LOW) { + line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND; + line6->iso_buffers = USB_LOW_ISO_BUFFERS; + } else { + line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND; + line6->iso_buffers = USB_HIGH_ISO_BUFFERS; + } +} /* Enable buffering of incoming messages, flush the buffer */ static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file) @@ -754,8 +758,9 @@ int line6_probe(struct usb_interface *interface, goto error; } + line6_get_usb_properties(line6); + if (properties->capabilities & LINE6_CAP_CONTROL) { - line6_get_interval(line6); ret = line6_init_cap_control(line6); if (ret < 0) goto error; diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c index d0fb2f2..1d3a23b 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -200,14 +200,14 @@ static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, line6->line6midi->substream_receive = NULL; } -static struct snd_rawmidi_ops line6_midi_output_ops = { +static const struct snd_rawmidi_ops line6_midi_output_ops = { .open = line6_midi_output_open, .close = line6_midi_output_close, .trigger = line6_midi_output_trigger, .drain = line6_midi_output_drain, }; -static struct snd_rawmidi_ops line6_midi_input_ops = { +static const struct snd_rawmidi_ops line6_midi_input_ops = { .open = line6_midi_input_open, .close = line6_midi_input_close, .trigger = line6_midi_input_trigger, diff --git a/sound/usb/midi.c b/sound/usb/midi.c index 7ba9292..6e763bc 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -1234,14 +1234,14 @@ static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, clear_bit(substream->number, &umidi->input_triggered); } -static struct snd_rawmidi_ops snd_usbmidi_output_ops = { +static const struct snd_rawmidi_ops snd_usbmidi_output_ops = { .open = snd_usbmidi_output_open, .close = snd_usbmidi_output_close, .trigger = snd_usbmidi_output_trigger, .drain = snd_usbmidi_output_drain, }; -static struct snd_rawmidi_ops snd_usbmidi_input_ops = { +static const struct snd_rawmidi_ops snd_usbmidi_input_ops = { .open = snd_usbmidi_input_open, .close = snd_usbmidi_input_close, .trigger = snd_usbmidi_input_trigger diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 04991b0..4fa0053 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -43,6 +43,7 @@ #include "mixer.h" #include "mixer_quirks.h" #include "mixer_scarlett.h" +#include "mixer_us16x08.h" #include "helper.h" extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl; @@ -1729,6 +1730,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) return err; switch (mixer->chip->usb_id) { + /* Tascam US-16x08 */ + case USB_ID(0x0644, 0x8047): + err = snd_us16x08_controls_create(mixer); + break; case USB_ID(0x041e, 0x3020): case USB_ID(0x041e, 0x3040): case USB_ID(0x041e, 0x3042): diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c new file mode 100644 index 0000000..301939b --- /dev/null +++ b/sound/usb/mixer_us16x08.c @@ -0,0 +1,1465 @@ +/* + * Tascam US-16x08 ALSA driver + * + * Copyright (c) 2016 by Detlef Urban (onkel@paraair.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/usb/audio-v2.h> + +#include <sound/core.h> +#include <sound/control.h> + +#include "usbaudio.h" +#include "mixer.h" +#include "helper.h" + +#include "mixer_us16x08.h" + +/* USB control message templates */ +static const char route_msg[] = { + 0x61, + 0x02, + 0x03, /* input from master (0x02) or input from computer bus (0x03) */ + 0x62, + 0x02, + 0x01, /* input index (0x01/0x02 eq. left/right) or bus (0x01-0x08) */ + 0x41, + 0x01, + 0x61, + 0x02, + 0x01, + 0x62, + 0x02, + 0x01, /* output index (0x01-0x08) */ + 0x42, + 0x01, + 0x43, + 0x01, + 0x00, + 0x00 +}; + +static const char mix_init_msg1[] = { + 0x71, 0x01, 0x00, 0x00 +}; + +static const char mix_init_msg2[] = { + 0x62, 0x02, 0x00, 0x61, 0x02, 0x04, 0xb1, 0x01, 0x00, 0x00 +}; + +static const char mix_msg_in[] = { + /* default message head, equal to all mixers */ + 0x61, 0x02, 0x04, 0x62, 0x02, 0x01, + 0x81, /* 0x06: Controller ID */ + 0x02, /* 0x07: */ + 0x00, /* 0x08: Value of common mixer */ + 0x00, + 0x00 +}; + +static const char mix_msg_out[] = { + /* default message head, equal to all mixers */ + 0x61, 0x02, 0x02, 0x62, 0x02, 0x01, + 0x81, /* 0x06: Controller ID */ + 0x02, /* 0x07: */ + 0x00, /* 0x08: Value of common mixer */ + 0x00, + 0x00 +}; + +static const char bypass_msg_out[] = { + 0x45, + 0x02, + 0x01, /* on/off flag */ + 0x00, + 0x00 +}; + +static const char bus_msg_out[] = { + 0x44, + 0x02, + 0x01, /* on/off flag */ + 0x00, + 0x00 +}; + +static const char comp_msg[] = { + /* default message head, equal to all mixers */ + 0x61, 0x02, 0x04, 0x62, 0x02, 0x01, + 0x91, + 0x02, + 0xf0, /* 0x08: Threshold db (8) (e0 ... 00) (+-0dB -- -32dB) x-32 */ + 0x92, + 0x02, + 0x0a, /* 0x0b: Ratio (0a,0b,0d,0f,11,14,19,1e,23,28,32,3c,50,a0,ff) */ + 0x93, + 0x02, + 0x02, /* 0x0e: Attack (0x02 ... 0xc0) (2ms ... 200ms) */ + 0x94, + 0x02, + 0x01, /* 0x11: Release (0x01 ... 0x64) (10ms ... 1000ms) x*10 */ + 0x95, + 0x02, + 0x03, /* 0x14: gain (0 ... 20) (0dB .. 20dB) */ + 0x96, + 0x02, + 0x01, + 0x97, + 0x02, + 0x01, /* 0x1a: main Comp switch (0 ... 1) (off ... on)) */ + 0x00, + 0x00 +}; + +static const char eqs_msq[] = { + /* default message head, equal to all mixers */ + 0x61, 0x02, 0x04, 0x62, 0x02, 0x01, + 0x51, /* 0x06: Controller ID */ + 0x02, + 0x04, /* 0x08: EQ set num (0x01..0x04) (LOW, LOWMID, HIGHMID, HIGH)) */ + 0x52, + 0x02, + 0x0c, /* 0x0b: value dB (0 ... 12) (-12db .. +12db) x-6 */ + 0x53, + 0x02, + 0x0f, /* 0x0e: value freq (32-47) (1.7kHz..18kHz) */ + 0x54, + 0x02, + 0x02, /* 0x11: band width (0-6) (Q16-Q0.25) 2^x/4 (EQ xxMID only) */ + 0x55, + 0x02, + 0x01, /* 0x14: main EQ switch (0 ... 1) (off ... on)) */ + 0x00, + 0x00 +}; + +/* compressor ratio map */ +static const char ratio_map[] = { + 0x0a, 0x0b, 0x0d, 0x0f, 0x11, 0x14, 0x19, 0x1e, + 0x23, 0x28, 0x32, 0x3c, 0x50, 0xa0, 0xff +}; + +/* route enumeration names */ +const const char *route_names[] = { + "Master Left", "Master Right", "Output 1", "Output 2", "Output 3", + "Output 4", "Output 5", "Output 6", "Output 7", "Output 8", +}; + +static int snd_us16x08_recv_urb(struct snd_usb_audio *chip, + unsigned char *buf, int size) +{ + + mutex_lock(&chip->mutex); + snd_usb_ctl_msg(chip->dev, + usb_rcvctrlpipe(chip->dev, 0), + SND_US16X08_URB_METER_REQUEST, + SND_US16X08_URB_METER_REQUESTTYPE, 0, 0, buf, size); + mutex_unlock(&chip->mutex); + return 0; +} + +/* wrapper function to send prepared URB buffer to usb device. Return an error + * code if something went wrong + */ +static int snd_us16x08_send_urb(struct snd_usb_audio *chip, char *buf, int size) +{ + int err = 0; + + if (chip) { + err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), + SND_US16X08_URB_REQUEST, SND_US16X08_URB_REQUESTTYPE, + 0, 0, buf, size); + } + + return err; +} + +static int snd_us16x08_route_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_ctl_enum_info(uinfo, 1, 10, route_names); +} + +static int snd_us16x08_route_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + int index = ucontrol->id.index; + + /* route has no bias */ + ucontrol->value.enumerated.item[0] = elem->cache_val[index]; + + return 0; +} + +static int snd_us16x08_route_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + int index = ucontrol->id.index; + char buf[sizeof(route_msg)]; + int val, val_org, err = 0; + + /* prepare the message buffer from template */ + memcpy(buf, route_msg, sizeof(route_msg)); + + /* get the new value (no bias for routes) */ + val = ucontrol->value.enumerated.item[0]; + + /* sanity check */ + if (val < 0 || val > 9) + return -EINVAL; + + if (val < 2) { + /* input comes from a master channel */ + val_org = val; + buf[2] = 0x02; + } else { + /* input comes from a computer channel */ + buf[2] = 0x03; + val_org = val - 2; + } + + /* place new route selection in URB message */ + buf[5] = (unsigned char) (val_org & 0x0f) + 1; + /* place route selector in URB message */ + buf[13] = index + 1; + + err = snd_us16x08_send_urb(chip, buf, sizeof(route_msg)); + + if (err > 0) { + elem->cached |= 1 << index; + elem->cache_val[index] = val; + } else { + usb_audio_dbg(chip, "Failed to set routing, err:%d\n", err); + } + + return err > 0 ? 1 : 0; +} + +static int snd_us16x08_master_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->count = 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.max = SND_US16X08_KCMAX(kcontrol); + uinfo->value.integer.min = SND_US16X08_KCMIN(kcontrol); + uinfo->value.integer.step = SND_US16X08_KCSTEP(kcontrol); + return 0; +} + +static int snd_us16x08_master_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + int index = ucontrol->id.index; + + ucontrol->value.integer.value[0] = elem->cache_val[index]; + + return 0; +} + +static int snd_us16x08_master_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + char buf[sizeof(mix_msg_out)]; + int val, err = 0; + int index = ucontrol->id.index; + + /* prepare the message buffer from template */ + memcpy(buf, mix_msg_out, sizeof(mix_msg_out)); + + /* new control value incl. bias*/ + val = ucontrol->value.integer.value[0]; + + /* sanity check */ + if (val < SND_US16X08_KCMIN(kcontrol) + || val > SND_US16X08_KCMAX(kcontrol)) + return -EINVAL; + + buf[8] = val - SND_US16X08_KCBIAS(kcontrol); + buf[6] = elem->head.id; + + /* place channel selector in URB message */ + buf[5] = index + 1; + err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out)); + + if (err > 0) { + elem->cached |= 1 << index; + elem->cache_val[index] = val; + } else { + usb_audio_dbg(chip, "Failed to set master, err:%d\n", err); + } + + return err > 0 ? 1 : 0; +} + +static int snd_us16x08_bus_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + char buf[sizeof(mix_msg_out)]; + int val, err = 0; + + val = ucontrol->value.integer.value[0]; + + /* prepare the message buffer from template */ + switch (elem->head.id) { + case SND_US16X08_ID_BYPASS: + memcpy(buf, bypass_msg_out, sizeof(bypass_msg_out)); + buf[2] = val; + err = snd_us16x08_send_urb(chip, buf, sizeof(bypass_msg_out)); + break; + case SND_US16X08_ID_BUSS_OUT: + memcpy(buf, bus_msg_out, sizeof(bus_msg_out)); + buf[2] = val; + err = snd_us16x08_send_urb(chip, buf, sizeof(bus_msg_out)); + break; + case SND_US16X08_ID_MUTE: + memcpy(buf, mix_msg_out, sizeof(mix_msg_out)); + buf[8] = val; + buf[6] = elem->head.id; + buf[5] = 1; + err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_out)); + break; + } + + if (err > 0) { + elem->cached |= 1; + elem->cache_val[0] = val; + } else { + usb_audio_dbg(chip, "Failed to set buss param, err:%d\n", err); + } + + return err > 0 ? 1 : 0; +} + +static int snd_us16x08_bus_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + + switch (elem->head.id) { + case SND_US16X08_ID_BUSS_OUT: + ucontrol->value.integer.value[0] = elem->cache_val[0]; + break; + case SND_US16X08_ID_BYPASS: + ucontrol->value.integer.value[0] = elem->cache_val[0]; + break; + case SND_US16X08_ID_MUTE: + ucontrol->value.integer.value[0] = elem->cache_val[0]; + break; + } + + return 0; +} + +/* gets a current mixer value from common store */ +static int snd_us16x08_channel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + int index = ucontrol->id.index; + + ucontrol->value.integer.value[0] = elem->cache_val[index]; + + return 0; +} + +static int snd_us16x08_channel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + char buf[sizeof(mix_msg_in)]; + int val, err; + int index = ucontrol->id.index; + + /* prepare URB message from template */ + memcpy(buf, mix_msg_in, sizeof(mix_msg_in)); + + val = ucontrol->value.integer.value[0]; + + /* sanity check */ + if (val < SND_US16X08_KCMIN(kcontrol) + || val > SND_US16X08_KCMAX(kcontrol)) + return -EINVAL; + + /* add the bias to the new value */ + buf[8] = val - SND_US16X08_KCBIAS(kcontrol); + buf[6] = elem->head.id; + buf[5] = index + 1; + + err = snd_us16x08_send_urb(chip, buf, sizeof(mix_msg_in)); + + if (err > 0) { + elem->cached |= 1 << index; + elem->cache_val[index] = val; + } else { + usb_audio_dbg(chip, "Failed to set channel, err:%d\n", err); + } + + return err > 0 ? 1 : 0; +} + +static int snd_us16x08_mix_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->count = 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.max = SND_US16X08_KCMAX(kcontrol); + uinfo->value.integer.min = SND_US16X08_KCMIN(kcontrol); + uinfo->value.integer.step = SND_US16X08_KCSTEP(kcontrol); + return 0; +} + +static int snd_us16x08_comp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_us16x08_comp_store *store = + ((struct snd_us16x08_comp_store *) elem->private_data); + int index = ucontrol->id.index; + int val_idx = COMP_STORE_IDX(elem->head.id); + + ucontrol->value.integer.value[0] = store->val[val_idx][index]; + + return 0; +} + +static int snd_us16x08_comp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + struct snd_us16x08_comp_store *store = + ((struct snd_us16x08_comp_store *) elem->private_data); + int index = ucontrol->id.index; + char buf[sizeof(comp_msg)]; + int val_idx, val; + int err = 0; + + /* prepare compressor URB message from template */ + memcpy(buf, comp_msg, sizeof(comp_msg)); + + /* new control value incl. bias*/ + val_idx = elem->head.id - SND_US16X08_ID_COMP_BASE; + + val = ucontrol->value.integer.value[0]; + + /* sanity check */ + if (val < SND_US16X08_KCMIN(kcontrol) + || val > SND_US16X08_KCMAX(kcontrol)) + return -EINVAL; + + store->val[val_idx][index] = ucontrol->value.integer.value[0]; + + /* place comp values in message buffer watch bias! */ + buf[8] = store->val[ + COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][index] + - SND_US16X08_COMP_THRESHOLD_BIAS; + buf[11] = ratio_map[store->val[ + COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][index]]; + buf[14] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][index] + + SND_US16X08_COMP_ATTACK_BIAS; + buf[17] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][index] + + SND_US16X08_COMP_RELEASE_BIAS; + buf[20] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][index]; + buf[26] = store->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][index]; + + /* place channel selector in message buffer */ + buf[5] = index + 1; + + err = snd_us16x08_send_urb(chip, buf, sizeof(comp_msg)); + + if (err > 0) { + elem->cached |= 1 << index; + elem->cache_val[index] = val; + } else { + usb_audio_dbg(chip, "Failed to set compressor, err:%d\n", err); + } + + return 1; +} + +static int snd_us16x08_eqswitch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int val = 0; + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_us16x08_eq_store *store = + ((struct snd_us16x08_eq_store *) elem->private_data); + int index = ucontrol->id.index; + + /* get low switch from cache is enough, cause all bands are together */ + val = store->val[EQ_STORE_BAND_IDX(elem->head.id)] + [EQ_STORE_PARAM_IDX(elem->head.id)][index]; + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int snd_us16x08_eqswitch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + struct snd_us16x08_eq_store *store = + ((struct snd_us16x08_eq_store *) elem->private_data); + int index = ucontrol->id.index; + + char buf[sizeof(eqs_msq)]; + int val, err = 0; + int b_idx; + + /* new control value incl. bias*/ + val = ucontrol->value.integer.value[0] + SND_US16X08_KCBIAS(kcontrol); + + /* prepare URB message from EQ template */ + memcpy(buf, eqs_msq, sizeof(eqs_msq)); + + /* place channel index in URB message */ + buf[5] = index + 1; + for (b_idx = 0; b_idx < SND_US16X08_ID_EQ_BAND_COUNT; b_idx++) { + /* all four EQ bands have to be enabled/disabled in once */ + buf[20] = val; + buf[17] = store->val[b_idx][2][index]; + buf[14] = store->val[b_idx][1][index]; + buf[11] = store->val[b_idx][0][index]; + buf[8] = b_idx + 1; + err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq)); + if (err < 0) + break; + store->val[b_idx][3][index] = val; + msleep(15); + } + + if (err > 0) { + elem->cached |= 1 << index; + elem->cache_val[index] = val; + } else { + usb_audio_dbg(chip, "Failed to set eq switch, err:%d\n", err); + } + + return 1; +} + +static int snd_us16x08_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int val = 0; + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_us16x08_eq_store *store = + ((struct snd_us16x08_eq_store *) elem->private_data); + int index = ucontrol->id.index; + int b_idx = EQ_STORE_BAND_IDX(elem->head.id) - 1; + int p_idx = EQ_STORE_PARAM_IDX(elem->head.id); + + val = store->val[b_idx][p_idx][index]; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + struct snd_us16x08_eq_store *store = + ((struct snd_us16x08_eq_store *) elem->private_data); + int index = ucontrol->id.index; + char buf[sizeof(eqs_msq)]; + int val, err = 0; + int b_idx = EQ_STORE_BAND_IDX(elem->head.id) - 1; + int p_idx = EQ_STORE_PARAM_IDX(elem->head.id); + + /* copy URB buffer from EQ template */ + memcpy(buf, eqs_msq, sizeof(eqs_msq)); + + val = ucontrol->value.integer.value[0]; + + /* sanity check */ + if (val < SND_US16X08_KCMIN(kcontrol) + || val > SND_US16X08_KCMAX(kcontrol)) + return -EINVAL; + + store->val[b_idx][p_idx][index] = val; + buf[20] = store->val[b_idx][3][index]; + buf[17] = store->val[b_idx][2][index]; + buf[14] = store->val[b_idx][1][index]; + buf[11] = store->val[b_idx][0][index]; + + /* place channel index in URB buffer */ + buf[5] = index + 1; + + /* place EQ band in URB buffer */ + buf[8] = b_idx + 1; + + err = snd_us16x08_send_urb(chip, buf, sizeof(eqs_msq)); + + if (err > 0) { + /* store new value in EQ band cache */ + elem->cached |= 1 << index; + elem->cache_val[index] = val; + } else { + usb_audio_dbg(chip, "Failed to set eq param, err:%d\n", err); + } + + return 1; +} + +static int snd_us16x08_meter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->count = 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->value.integer.max = 0x7FFF; + uinfo->value.integer.min = 0; + + return 0; +} + +/* calculate compressor index for reduction level request */ +static int snd_get_meter_comp_index(struct snd_us16x08_meter_store *store) +{ + int ret; + + /* any channel active */ + if (store->comp_active_index) { + /* check for stereo link */ + if (store->comp_active_index & 0x20) { + /* reset comp_index to left channel*/ + if (store->comp_index - + store->comp_active_index > 1) + store->comp_index = + store->comp_active_index; + + ret = store->comp_index++ & 0x1F; + } else { + /* no stereo link */ + ret = store->comp_active_index; + } + } else { + /* skip channels with no compressor active */ + while (!store->comp_store->val[ + COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)] + [store->comp_index - 1] + && store->comp_index <= SND_US16X08_MAX_CHANNELS) { + store->comp_index++; + } + ret = store->comp_index++; + if (store->comp_index > SND_US16X08_MAX_CHANNELS) + store->comp_index = 1; + } + return ret; +} + +/* retrieve the meter level values from URB message */ +static void get_meter_levels_from_urb(int s, + struct snd_us16x08_meter_store *store, + u8 *meter_urb) +{ + int val = MUC2(meter_urb, s) + (MUC3(meter_urb, s) << 8); + + if (MUA0(meter_urb, s) == 0x61 && MUA1(meter_urb, s) == 0x02 && + MUA2(meter_urb, s) == 0x04 && MUB0(meter_urb, s) == 0x62) { + if (MUC0(meter_urb, s) == 0x72) + store->meter_level[MUB2(meter_urb, s) - 1] = val; + if (MUC0(meter_urb, s) == 0xb2) + store->comp_level[MUB2(meter_urb, s) - 1] = val; + } + if (MUA0(meter_urb, s) == 0x61 && MUA1(meter_urb, s) == 0x02 && + MUA2(meter_urb, s) == 0x02 && MUB0(meter_urb, s) == 0x62) + store->master_level[MUB2(meter_urb, s) - 1] = val; +} + +/* Function to retrieve current meter values from the device. + * + * The device needs to be polled for meter values with an initial + * requests. It will return with a sequence of different meter value + * packages. The first request (case 0:) initiate this meter response sequence. + * After the third response, an additional request can be placed, + * to retrieve compressor reduction level value for given channel. This round + * trip channel selector will skip all inactive compressors. + * A mixer can interrupt this round-trip by selecting one ore two (stereo-link) + * specific channels. + */ +static int snd_us16x08_meter_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i, set; + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_usb_audio *chip = elem->head.mixer->chip; + struct snd_us16x08_meter_store *store = elem->private_data; + u8 meter_urb[64]; + char tmp[max(sizeof(mix_init_msg1), sizeof(mix_init_msg2))]; + + if (elem) { + store = (struct snd_us16x08_meter_store *) elem->private_data; + chip = elem->head.mixer->chip; + } else + return 0; + + switch (kcontrol->private_value) { + case 0: + memcpy(tmp, mix_init_msg1, sizeof(mix_init_msg1)); + snd_us16x08_send_urb(chip, tmp, 4); + snd_us16x08_recv_urb(chip, meter_urb, + sizeof(meter_urb)); + kcontrol->private_value++; + break; + case 1: + snd_us16x08_recv_urb(chip, meter_urb, + sizeof(meter_urb)); + kcontrol->private_value++; + break; + case 2: + snd_us16x08_recv_urb(chip, meter_urb, + sizeof(meter_urb)); + kcontrol->private_value++; + break; + case 3: + memcpy(tmp, mix_init_msg2, sizeof(mix_init_msg2)); + tmp[2] = snd_get_meter_comp_index(store); + snd_us16x08_send_urb(chip, tmp, 10); + snd_us16x08_recv_urb(chip, meter_urb, + sizeof(meter_urb)); + kcontrol->private_value = 0; + break; + } + + for (set = 0; set < 6; set++) + get_meter_levels_from_urb(set, store, meter_urb); + + for (i = 0; i < SND_US16X08_MAX_CHANNELS; i++) { + ucontrol->value.integer.value[i] = + store ? store->meter_level[i] : 0; + } + + ucontrol->value.integer.value[i++] = store ? store->master_level[0] : 0; + ucontrol->value.integer.value[i++] = store ? store->master_level[1] : 0; + + for (i = 2; i < SND_US16X08_MAX_CHANNELS + 2; i++) + ucontrol->value.integer.value[i + SND_US16X08_MAX_CHANNELS] = + store ? store->comp_level[i - 2] : 0; + + return 1; +} + +static int snd_us16x08_meter_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct usb_mixer_elem_info *elem = kcontrol->private_data; + struct snd_us16x08_meter_store *store = elem->private_data; + int val; + + val = ucontrol->value.integer.value[0]; + + /* sanity check */ + if (val < 0 || val >= SND_US16X08_MAX_CHANNELS) + return -EINVAL; + + store->comp_active_index = val; + store->comp_index = val; + + return 1; +} + +static struct snd_kcontrol_new snd_us16x08_ch_boolean_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_switch_info, + .get = snd_us16x08_channel_get, + .put = snd_us16x08_channel_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) +}; + +static struct snd_kcontrol_new snd_us16x08_ch_int_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_channel_get, + .put = snd_us16x08_channel_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 133) +}; + +static struct snd_kcontrol_new snd_us16x08_pan_int_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_channel_get, + .put = snd_us16x08_channel_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 255) +}; + +static struct snd_kcontrol_new snd_us16x08_master_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 1, + .info = snd_us16x08_master_info, + .get = snd_us16x08_master_get, + .put = snd_us16x08_master_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_FADER_BIAS, 1, 0, 133) +}; + +static struct snd_kcontrol_new snd_us16x08_route_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 8, + .info = snd_us16x08_route_info, + .get = snd_us16x08_route_get, + .put = snd_us16x08_route_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 9) +}; + +static struct snd_kcontrol_new snd_us16x08_bus_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 1, + .info = snd_us16x08_switch_info, + .get = snd_us16x08_bus_get, + .put = snd_us16x08_bus_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) +}; + +static struct snd_kcontrol_new snd_us16x08_compswitch_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_switch_info, + .get = snd_us16x08_comp_get, + .put = snd_us16x08_comp_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) +}; + +static struct snd_kcontrol_new snd_us16x08_comp_threshold_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_comp_get, + .put = snd_us16x08_comp_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_COMP_THRESHOLD_BIAS, 1, + 0, 0x20) +}; + +static struct snd_kcontrol_new snd_us16x08_comp_ratio_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_comp_get, + .put = snd_us16x08_comp_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, + sizeof(ratio_map) - 1), /*max*/ +}; + +static struct snd_kcontrol_new snd_us16x08_comp_gain_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_comp_get, + .put = snd_us16x08_comp_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x14) +}; + +static struct snd_kcontrol_new snd_us16x08_comp_attack_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_comp_get, + .put = snd_us16x08_comp_put, + .private_value = + SND_US16X08_KCSET(SND_US16X08_COMP_ATTACK_BIAS, 1, 0, 0xc6), +}; + +static struct snd_kcontrol_new snd_us16x08_comp_release_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_comp_get, + .put = snd_us16x08_comp_put, + .private_value = + SND_US16X08_KCSET(SND_US16X08_COMP_RELEASE_BIAS, 1, 0, 0x63), +}; + +static struct snd_kcontrol_new snd_us16x08_eq_gain_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_eq_get, + .put = snd_us16x08_eq_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 24), +}; + +static struct snd_kcontrol_new snd_us16x08_eq_low_freq_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_eq_get, + .put = snd_us16x08_eq_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x1F), +}; + +static struct snd_kcontrol_new snd_us16x08_eq_mid_freq_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_eq_get, + .put = snd_us16x08_eq_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x3F) +}; + +static struct snd_kcontrol_new snd_us16x08_eq_mid_width_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_eq_get, + .put = snd_us16x08_eq_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 0x06) +}; + +static struct snd_kcontrol_new snd_us16x08_eq_high_freq_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_mix_info, + .get = snd_us16x08_eq_get, + .put = snd_us16x08_eq_put, + .private_value = + SND_US16X08_KCSET(SND_US16X08_EQ_HIGHFREQ_BIAS, 1, 0, 0x1F) +}; + +static struct snd_kcontrol_new snd_us16x08_eq_switch_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 16, + .info = snd_us16x08_switch_info, + .get = snd_us16x08_eqswitch_get, + .put = snd_us16x08_eqswitch_put, + .private_value = SND_US16X08_KCSET(SND_US16X08_NO_BIAS, 1, 0, 1) +}; + +static struct snd_kcontrol_new snd_us16x08_meter_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .count = 1, + .info = snd_us16x08_meter_info, + .get = snd_us16x08_meter_get, + .put = snd_us16x08_meter_put +}; + +/* control store preparation */ + +/* setup compressor store and assign default value */ +static struct snd_us16x08_comp_store *snd_us16x08_create_comp_store(void) +{ + int i = 0; + struct snd_us16x08_comp_store *tmp = + kmalloc(sizeof(struct snd_us16x08_comp_store), GFP_KERNEL); + + if (tmp == NULL) + return NULL; + + for (i = 0; i < SND_US16X08_MAX_CHANNELS; i++) { + tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_THRESHOLD)][i] + = 0x20; + tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RATIO)][i] = 0x00; + tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_GAIN)][i] = 0x00; + tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_SWITCH)][i] = 0x00; + tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_ATTACK)][i] = 0x00; + tmp->val[COMP_STORE_IDX(SND_US16X08_ID_COMP_RELEASE)][i] = 0x00; + } + return tmp; +} + +/* setup EQ store and assign default values */ +static struct snd_us16x08_eq_store *snd_us16x08_create_eq_store(void) +{ + int i, b_idx; + struct snd_us16x08_eq_store *tmp = + kmalloc(sizeof(struct snd_us16x08_eq_store), GFP_KERNEL); + + if (tmp == NULL) + return NULL; + + for (i = 0; i < SND_US16X08_MAX_CHANNELS; i++) { + for (b_idx = 0; b_idx < SND_US16X08_ID_EQ_BAND_COUNT; b_idx++) { + tmp->val[b_idx][0][i] = 0x0c; + tmp->val[b_idx][3][i] = 0x00; + switch (b_idx) { + case 0: /* EQ Low */ + tmp->val[b_idx][1][i] = 0x05; + tmp->val[b_idx][2][i] = 0xff; + break; + case 1: /* EQ Mid low */ + tmp->val[b_idx][1][i] = 0x0e; + tmp->val[b_idx][2][i] = 0x02; + break; + case 2: /* EQ Mid High */ + tmp->val[b_idx][1][i] = 0x1b; + tmp->val[b_idx][2][i] = 0x02; + break; + case 3: /* EQ High */ + tmp->val[b_idx][1][i] = 0x2f + - SND_US16X08_EQ_HIGHFREQ_BIAS; + tmp->val[b_idx][2][i] = 0xff; + break; + } + } + } + return tmp; +} + +struct snd_us16x08_meter_store *snd_us16x08_create_meter_store(void) +{ + struct snd_us16x08_meter_store *tmp = + kzalloc(sizeof(struct snd_us16x08_meter_store), GFP_KERNEL); + + if (!tmp) + return NULL; + tmp->comp_index = 1; + tmp->comp_active_index = 0; + return tmp; + +} + +static int add_new_ctl(struct usb_mixer_interface *mixer, + const struct snd_kcontrol_new *ncontrol, + int index, int val_type, int channels, + const char *name, const void *opt, + void (*freeer)(struct snd_kcontrol *kctl), + struct usb_mixer_elem_info **elem_ret) +{ + struct snd_kcontrol *kctl; + struct usb_mixer_elem_info *elem; + int err; + + usb_audio_dbg(mixer->chip, "us16x08 add mixer %s\n", name); + + elem = kzalloc(sizeof(*elem), GFP_KERNEL); + if (!elem) + return -ENOMEM; + + elem->head.mixer = mixer; + elem->head.resume = NULL; + elem->control = 0; + elem->idx_off = 0; + elem->head.id = index; + elem->val_type = val_type; + elem->channels = channels; + elem->private_data = (void *) opt; + + kctl = snd_ctl_new1(ncontrol, elem); + if (!kctl) { + kfree(elem); + return -ENOMEM; + } + + kctl->private_free = freeer; + + strlcpy(kctl->id.name, name, sizeof(kctl->id.name)); + + err = snd_usb_mixer_add_control(&elem->head, kctl); + if (err < 0) + return err; + + if (elem_ret) + *elem_ret = elem; + + return 0; +} + +static struct snd_us16x08_control_params control_params; + +/* table of EQ controls */ +static struct snd_us16x08_control_params eq_controls[] = { + { /* EQ switch */ + .kcontrol_new = &snd_us16x08_eq_switch_ctl, + .control_id = SND_US16X08_ID_EQENABLE, + .type = USB_MIXER_BOOLEAN, + .num_channels = 16, + .name = "EQ Switch", + .freeer = snd_usb_mixer_elem_free + }, + { /* EQ low gain */ + .kcontrol_new = &snd_us16x08_eq_gain_ctl, + .control_id = SND_US16X08_ID_EQLOWLEVEL, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ Low Volume", + .freeer = snd_usb_mixer_elem_free + }, + { /* EQ low freq */ + .kcontrol_new = &snd_us16x08_eq_low_freq_ctl, + .control_id = SND_US16X08_ID_EQLOWFREQ, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ Low Frequence", + .freeer = NULL + }, + { /* EQ mid low gain */ + .kcontrol_new = &snd_us16x08_eq_gain_ctl, + .control_id = SND_US16X08_ID_EQLOWMIDLEVEL, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ MidLow Volume", + .freeer = snd_usb_mixer_elem_free + }, + { /* EQ mid low freq */ + .kcontrol_new = &snd_us16x08_eq_mid_freq_ctl, + .control_id = SND_US16X08_ID_EQLOWMIDFREQ, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ MidLow Frequence", + .freeer = NULL + }, + { /* EQ mid low Q */ + .kcontrol_new = &snd_us16x08_eq_mid_width_ctl, + .control_id = SND_US16X08_ID_EQLOWMIDWIDTH, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ MidQLow Q", + .freeer = NULL + }, + { /* EQ mid high gain */ + .kcontrol_new = &snd_us16x08_eq_gain_ctl, + .control_id = SND_US16X08_ID_EQHIGHMIDLEVEL, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ MidHigh Volume", + .freeer = snd_usb_mixer_elem_free + }, + { /* EQ mid high freq */ + .kcontrol_new = &snd_us16x08_eq_mid_freq_ctl, + .control_id = SND_US16X08_ID_EQHIGHMIDFREQ, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ MidHigh Frequence", + .freeer = NULL + }, + { /* EQ mid high Q */ + .kcontrol_new = &snd_us16x08_eq_mid_width_ctl, + .control_id = SND_US16X08_ID_EQHIGHMIDWIDTH, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ MidHigh Q", + .freeer = NULL + }, + { /* EQ high gain */ + .kcontrol_new = &snd_us16x08_eq_gain_ctl, + .control_id = SND_US16X08_ID_EQHIGHLEVEL, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ High Volume", + .freeer = snd_usb_mixer_elem_free + }, + { /* EQ low freq */ + .kcontrol_new = &snd_us16x08_eq_high_freq_ctl, + .control_id = SND_US16X08_ID_EQHIGHFREQ, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "EQ High Frequence", + .freeer = NULL + }, +}; + +/* table of compressor controls */ +static struct snd_us16x08_control_params comp_controls[] = { + { /* Comp enable */ + .kcontrol_new = &snd_us16x08_compswitch_ctl, + .control_id = SND_US16X08_ID_COMP_SWITCH, + .type = USB_MIXER_BOOLEAN, + .num_channels = 16, + .name = "Compressor Switch", + .freeer = snd_usb_mixer_elem_free + }, + { /* Comp threshold */ + .kcontrol_new = &snd_us16x08_comp_threshold_ctl, + .control_id = SND_US16X08_ID_COMP_THRESHOLD, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "Compressor Threshold Volume", + .freeer = NULL + }, + { /* Comp ratio */ + .kcontrol_new = &snd_us16x08_comp_ratio_ctl, + .control_id = SND_US16X08_ID_COMP_RATIO, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "Compressor Ratio", + .freeer = NULL + }, + { /* Comp attack */ + .kcontrol_new = &snd_us16x08_comp_attack_ctl, + .control_id = SND_US16X08_ID_COMP_ATTACK, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "Compressor Attack", + .freeer = NULL + }, + { /* Comp release */ + .kcontrol_new = &snd_us16x08_comp_release_ctl, + .control_id = SND_US16X08_ID_COMP_RELEASE, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "Compressor Release", + .freeer = NULL + }, + { /* Comp gain */ + .kcontrol_new = &snd_us16x08_comp_gain_ctl, + .control_id = SND_US16X08_ID_COMP_GAIN, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "Compressor Volume", + .freeer = NULL + }, +}; + +/* table of channel controls */ +static struct snd_us16x08_control_params channel_controls[] = { + { /* Phase */ + .kcontrol_new = &snd_us16x08_ch_boolean_ctl, + .control_id = SND_US16X08_ID_PHASE, + .type = USB_MIXER_BOOLEAN, + .num_channels = 16, + .name = "Phase Switch", + .freeer = snd_usb_mixer_elem_free, + .default_val = 0 + }, + { /* Fader */ + .kcontrol_new = &snd_us16x08_ch_int_ctl, + .control_id = SND_US16X08_ID_FADER, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "Line Volume", + .freeer = NULL, + .default_val = 127 + }, + { /* Mute */ + .kcontrol_new = &snd_us16x08_ch_boolean_ctl, + .control_id = SND_US16X08_ID_MUTE, + .type = USB_MIXER_BOOLEAN, + .num_channels = 16, + .name = "Mute Switch", + .freeer = NULL, + .default_val = 0 + }, + { /* Pan */ + .kcontrol_new = &snd_us16x08_pan_int_ctl, + .control_id = SND_US16X08_ID_PAN, + .type = USB_MIXER_U16, + .num_channels = 16, + .name = "Pan Left-Right Volume", + .freeer = NULL, + .default_val = 127 + }, +}; + +/* table of master controls */ +static struct snd_us16x08_control_params master_controls[] = { + { /* Master */ + .kcontrol_new = &snd_us16x08_master_ctl, + .control_id = SND_US16X08_ID_FADER, + .type = USB_MIXER_U8, + .num_channels = 16, + .name = "Master Volume", + .freeer = NULL, + .default_val = 127 + }, + { /* Bypass */ + .kcontrol_new = &snd_us16x08_bus_ctl, + .control_id = SND_US16X08_ID_BYPASS, + .type = USB_MIXER_BOOLEAN, + .num_channels = 16, + .name = "DSP Bypass Switch", + .freeer = NULL, + .default_val = 0 + }, + { /* Buss out */ + .kcontrol_new = &snd_us16x08_bus_ctl, + .control_id = SND_US16X08_ID_BUSS_OUT, + .type = USB_MIXER_BOOLEAN, + .num_channels = 16, + .name = "Buss Out Switch", + .freeer = NULL, + .default_val = 0 + }, + { /* Master mute */ + .kcontrol_new = &snd_us16x08_bus_ctl, + .control_id = SND_US16X08_ID_MUTE, + .type = USB_MIXER_BOOLEAN, + .num_channels = 16, + .name = "Master Mute Switch", + .freeer = NULL, + .default_val = 0 + }, + +}; + +int snd_us16x08_controls_create(struct usb_mixer_interface *mixer) +{ + int i, j; + int err; + struct usb_mixer_elem_info *elem; + struct snd_us16x08_comp_store *comp_store; + struct snd_us16x08_meter_store *meter_store; + struct snd_us16x08_eq_store *eq_store; + + /* just check for non-MIDI interface */ + if (mixer->hostif->desc.bInterfaceNumber == 3) { + + /* create compressor mixer elements */ + comp_store = snd_us16x08_create_comp_store(); + if (comp_store == NULL) + return -ENOMEM; + + /* create eq store */ + eq_store = snd_us16x08_create_eq_store(); + if (eq_store == NULL) { + kfree(comp_store); + return -ENOMEM; + } + + /* create meters store */ + meter_store = snd_us16x08_create_meter_store(); + if (meter_store == NULL) { + kfree(comp_store); + kfree(eq_store); + return -ENOMEM; + } + + /* add routing control */ + err = add_new_ctl(mixer, &snd_us16x08_route_ctl, + SND_US16X08_ID_ROUTE, USB_MIXER_U8, 8, "Line Out Route", + NULL, NULL, &elem); + if (err < 0) { + usb_audio_dbg(mixer->chip, + "Failed to create route control, err:%d\n", + err); + return err; + } + for (i = 0; i < 8; i++) + elem->cache_val[i] = i < 2 ? i : i + 2; + elem->cached = 0xff; + + /* add master controls */ + for (i = 0; + i < sizeof(master_controls) + / sizeof(control_params); + i++) { + + err = add_new_ctl(mixer, + master_controls[i].kcontrol_new, + master_controls[i].control_id, + master_controls[i].type, + master_controls[i].num_channels, + master_controls[i].name, + comp_store, + master_controls[i].freeer, &elem); + if (err < 0) + return err; + elem->cache_val[0] = master_controls[i].default_val; + elem->cached = 1; + } + + /* add channel controls */ + for (i = 0; + i < sizeof(channel_controls) + / sizeof(control_params); + i++) { + + err = add_new_ctl(mixer, + channel_controls[i].kcontrol_new, + channel_controls[i].control_id, + channel_controls[i].type, + channel_controls[i].num_channels, + channel_controls[i].name, + comp_store, + channel_controls[i].freeer, &elem); + if (err < 0) + return err; + for (j = 0; j < SND_US16X08_MAX_CHANNELS; j++) { + elem->cache_val[j] = + channel_controls[i].default_val; + } + elem->cached = 0xffff; + } + + /* add EQ controls */ + for (i = 0; i < sizeof(eq_controls) / + sizeof(control_params); i++) { + + err = add_new_ctl(mixer, + eq_controls[i].kcontrol_new, + eq_controls[i].control_id, + eq_controls[i].type, + eq_controls[i].num_channels, + eq_controls[i].name, + eq_store, + eq_controls[i].freeer, NULL); + if (err < 0) + return err; + } + + /* add compressor controls */ + for (i = 0; + i < sizeof(comp_controls) + / sizeof(control_params); + i++) { + + err = add_new_ctl(mixer, + comp_controls[i].kcontrol_new, + comp_controls[i].control_id, + comp_controls[i].type, + comp_controls[i].num_channels, + comp_controls[i].name, + comp_store, + comp_controls[i].freeer, NULL); + if (err < 0) + return err; + } + + /* meter function 'get' must access to compressor store + * so place a reference here + */ + meter_store->comp_store = comp_store; + err = add_new_ctl(mixer, &snd_us16x08_meter_ctl, + SND_US16X08_ID_METER, USB_MIXER_U16, 0, "Level Meter", + (void *) meter_store, snd_usb_mixer_elem_free, NULL); + if (err < 0) + return err; + } + + return 0; +} + diff --git a/sound/usb/mixer_us16x08.h b/sound/usb/mixer_us16x08.h new file mode 100644 index 0000000..64f89b5 --- /dev/null +++ b/sound/usb/mixer_us16x08.h @@ -0,0 +1,122 @@ +#ifndef __USB_MIXER_US16X08_H +#define __USB_MIXER_US16X08_H + +#define SND_US16X08_MAX_CHANNELS 16 + +/* define some bias, cause some alsa-mixers wont work with + * negative ranges or if mixer-min != 0 + */ +#define SND_US16X08_NO_BIAS 0 +#define SND_US16X08_FADER_BIAS 127 +#define SND_US16X08_EQ_HIGHFREQ_BIAS 0x20 +#define SND_US16X08_COMP_THRESHOLD_BIAS 0x20 +#define SND_US16X08_COMP_ATTACK_BIAS 2 +#define SND_US16X08_COMP_RELEASE_BIAS 1 + +/* get macro for components of kcontrol private_value */ +#define SND_US16X08_KCBIAS(x) (((x)->private_value >> 24) & 0xff) +#define SND_US16X08_KCSTEP(x) (((x)->private_value >> 16) & 0xff) +#define SND_US16X08_KCMIN(x) (((x)->private_value >> 8) & 0xff) +#define SND_US16X08_KCMAX(x) (((x)->private_value >> 0) & 0xff) +/* set macro for kcontrol private_value */ +#define SND_US16X08_KCSET(bias, step, min, max) \ + (((bias) << 24) | ((step) << 16) | ((min) << 8) | (max)) + +/* the URB request/type to control Tascam mixers */ +#define SND_US16X08_URB_REQUEST 0x1D +#define SND_US16X08_URB_REQUESTTYPE 0x40 + +/* the URB params to retrieve meter ranges */ +#define SND_US16X08_URB_METER_REQUEST 0x1e +#define SND_US16X08_URB_METER_REQUESTTYPE 0xc0 + +#define MUA0(x, y) ((x)[(y) * 10 + 4]) +#define MUA1(x, y) ((x)[(y) * 10 + 5]) +#define MUA2(x, y) ((x)[(y) * 10 + 6]) +#define MUB0(x, y) ((x)[(y) * 10 + 7]) +#define MUB1(x, y) ((x)[(y) * 10 + 8]) +#define MUB2(x, y) ((x)[(y) * 10 + 9]) +#define MUC0(x, y) ((x)[(y) * 10 + 10]) +#define MUC1(x, y) ((x)[(y) * 10 + 11]) +#define MUC2(x, y) ((x)[(y) * 10 + 12]) +#define MUC3(x, y) ((x)[(y) * 10 + 13]) + +/* Common Channel control IDs */ +#define SND_US16X08_ID_BYPASS 0x45 +#define SND_US16X08_ID_BUSS_OUT 0x44 +#define SND_US16X08_ID_PHASE 0x85 +#define SND_US16X08_ID_MUTE 0x83 +#define SND_US16X08_ID_FADER 0x81 +#define SND_US16X08_ID_PAN 0x82 +#define SND_US16X08_ID_METER 0xB1 + +#define SND_US16X08_ID_EQ_BAND_COUNT 4 +#define SND_US16X08_ID_EQ_PARAM_COUNT 4 + +/* EQ level IDs */ +#define SND_US16X08_ID_EQLOWLEVEL 0x01 +#define SND_US16X08_ID_EQLOWMIDLEVEL 0x02 +#define SND_US16X08_ID_EQHIGHMIDLEVEL 0x03 +#define SND_US16X08_ID_EQHIGHLEVEL 0x04 + +/* EQ frequence IDs */ +#define SND_US16X08_ID_EQLOWFREQ 0x11 +#define SND_US16X08_ID_EQLOWMIDFREQ 0x12 +#define SND_US16X08_ID_EQHIGHMIDFREQ 0x13 +#define SND_US16X08_ID_EQHIGHFREQ 0x14 + +/* EQ width IDs */ +#define SND_US16X08_ID_EQLOWMIDWIDTH 0x22 +#define SND_US16X08_ID_EQHIGHMIDWIDTH 0x23 + +#define SND_US16X08_ID_EQENABLE 0x30 + +#define EQ_STORE_BAND_IDX(x) ((x) & 0xf) +#define EQ_STORE_PARAM_IDX(x) (((x) & 0xf0) >> 4) + +#define SND_US16X08_ID_ROUTE 0x00 + +/* Compressor Ids */ +#define SND_US16X08_ID_COMP_BASE 0x32 +#define SND_US16X08_ID_COMP_THRESHOLD SND_US16X08_ID_COMP_BASE +#define SND_US16X08_ID_COMP_RATIO (SND_US16X08_ID_COMP_BASE + 1) +#define SND_US16X08_ID_COMP_ATTACK (SND_US16X08_ID_COMP_BASE + 2) +#define SND_US16X08_ID_COMP_RELEASE (SND_US16X08_ID_COMP_BASE + 3) +#define SND_US16X08_ID_COMP_GAIN (SND_US16X08_ID_COMP_BASE + 4) +#define SND_US16X08_ID_COMP_SWITCH (SND_US16X08_ID_COMP_BASE + 5) +#define SND_US16X08_ID_COMP_COUNT 6 + +#define COMP_STORE_IDX(x) ((x) - SND_US16X08_ID_COMP_BASE) + +struct snd_us16x08_eq_store { + u8 val[SND_US16X08_ID_EQ_BAND_COUNT][SND_US16X08_ID_EQ_PARAM_COUNT] + [SND_US16X08_MAX_CHANNELS]; +}; + +struct snd_us16x08_comp_store { + u8 val[SND_US16X08_ID_COMP_COUNT][SND_US16X08_MAX_CHANNELS]; +}; + +struct snd_us16x08_meter_store { + int meter_level[SND_US16X08_MAX_CHANNELS]; + int master_level[2]; /* level of meter for master output */ + int comp_index; /* round trip channel selector */ + int comp_active_index; /* channel select from user space mixer */ + int comp_level[16]; /* compressor reduction level */ + struct snd_us16x08_comp_store *comp_store; +}; + +struct snd_us16x08_control_params { + struct snd_kcontrol_new *kcontrol_new; + int control_id; + int type; + int num_channels; + const char *name; + void (*freeer)(struct snd_kcontrol *kctl); + int default_val; +}; + +#define snd_us16x08_switch_info snd_ctl_boolean_mono_info + +int snd_us16x08_controls_create(struct usb_mixer_interface *mixer); +#endif /* __USB_MIXER_US16X08_H */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index eb4b9f7..01eff6c 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1360,6 +1360,21 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, if (fp->altsetting == 3) return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; + + /* Amanero Combo384 USB interface with native DSD support */ + case USB_ID(0x16d0, 0x071a): + if (fp->altsetting == 2) { + switch (chip->dev->descriptor.bcdDevice) { + case 0x199: + return SNDRV_PCM_FMTBIT_DSD_U32_LE; + case 0x19b: + return SNDRV_PCM_FMTBIT_DSD_U32_BE; + default: + break; + } + } + break; + default: break; } diff --git a/sound/x86/Kconfig b/sound/x86/Kconfig new file mode 100644 index 0000000..30d066e --- /dev/null +++ b/sound/x86/Kconfig @@ -0,0 +1,15 @@ +menuconfig SND_X86 + tristate "X86 sound devices" + depends on X86 + ---help--- + X86 sound devices that don't fall under SoC or PCI categories + +if SND_X86 + +config HDMI_LPE_AUDIO + tristate "HDMI audio without HDaudio on Intel Atom platforms" + depends on DRM_I915 + help + Choose this option to support HDMI LPE Audio mode + +endif # SND_X86 diff --git a/sound/x86/Makefile b/sound/x86/Makefile new file mode 100644 index 0000000..7ff9198 --- /dev/null +++ b/sound/x86/Makefile @@ -0,0 +1,4 @@ +snd-hdmi-lpe-audio-objs += \ + intel_hdmi_audio.o + +obj-$(CONFIG_HDMI_LPE_AUDIO) += snd-hdmi-lpe-audio.o diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c new file mode 100644 index 0000000..360cff3 --- /dev/null +++ b/sound/x86/intel_hdmi_audio.c @@ -0,0 +1,1851 @@ +/* + * intel_hdmi_audio.c - Intel HDMI audio driver + * + * Copyright (C) 2016 Intel Corp + * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> + * Ramesh Babu K V <ramesh.babu@intel.com> + * Vaibhav Agarwal <vaibhav.agarwal@intel.com> + * Jerome Anand <jerome.anand@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ALSA driver for Intel HDMI audio + */ + +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <asm/cacheflush.h> +#include <sound/core.h> +#include <sound/asoundef.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <sound/jack.h> +#include <drm/drm_edid.h> +#include <drm/intel_lpe_audio.h> +#include "intel_hdmi_audio.h" + +/*standard module options for ALSA. This module supports only one card*/ +static int hdmi_card_index = SNDRV_DEFAULT_IDX1; +static char *hdmi_card_id = SNDRV_DEFAULT_STR1; + +module_param_named(index, hdmi_card_index, int, 0444); +MODULE_PARM_DESC(index, + "Index value for INTEL Intel HDMI Audio controller."); +module_param_named(id, hdmi_card_id, charp, 0444); +MODULE_PARM_DESC(id, + "ID string for INTEL Intel HDMI Audio controller."); + +/* + * ELD SA bits in the CEA Speaker Allocation data block + */ +static const int eld_speaker_allocation_bits[] = { + [0] = FL | FR, + [1] = LFE, + [2] = FC, + [3] = RL | RR, + [4] = RC, + [5] = FLC | FRC, + [6] = RLC | RRC, + /* the following are not defined in ELD yet */ + [7] = 0, +}; + +/* + * This is an ordered list! + * + * The preceding ones have better chances to be selected by + * hdmi_channel_allocation(). + */ +static struct cea_channel_speaker_allocation channel_allocations[] = { +/* channel: 7 6 5 4 3 2 1 0 */ +{ .ca_index = 0x00, .speakers = { 0, 0, 0, 0, 0, 0, FR, FL } }, + /* 2.1 */ +{ .ca_index = 0x01, .speakers = { 0, 0, 0, 0, 0, LFE, FR, FL } }, + /* Dolby Surround */ +{ .ca_index = 0x02, .speakers = { 0, 0, 0, 0, FC, 0, FR, FL } }, + /* surround40 */ +{ .ca_index = 0x08, .speakers = { 0, 0, RR, RL, 0, 0, FR, FL } }, + /* surround41 */ +{ .ca_index = 0x09, .speakers = { 0, 0, RR, RL, 0, LFE, FR, FL } }, + /* surround50 */ +{ .ca_index = 0x0a, .speakers = { 0, 0, RR, RL, FC, 0, FR, FL } }, + /* surround51 */ +{ .ca_index = 0x0b, .speakers = { 0, 0, RR, RL, FC, LFE, FR, FL } }, + /* 6.1 */ +{ .ca_index = 0x0f, .speakers = { 0, RC, RR, RL, FC, LFE, FR, FL } }, + /* surround71 */ +{ .ca_index = 0x13, .speakers = { RRC, RLC, RR, RL, FC, LFE, FR, FL } }, + +{ .ca_index = 0x03, .speakers = { 0, 0, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x04, .speakers = { 0, 0, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x05, .speakers = { 0, 0, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x06, .speakers = { 0, 0, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x07, .speakers = { 0, 0, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x0c, .speakers = { 0, RC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x0d, .speakers = { 0, RC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x0e, .speakers = { 0, RC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x10, .speakers = { RRC, RLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x11, .speakers = { RRC, RLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x12, .speakers = { RRC, RLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x14, .speakers = { FRC, FLC, 0, 0, 0, 0, FR, FL } }, +{ .ca_index = 0x15, .speakers = { FRC, FLC, 0, 0, 0, LFE, FR, FL } }, +{ .ca_index = 0x16, .speakers = { FRC, FLC, 0, 0, FC, 0, FR, FL } }, +{ .ca_index = 0x17, .speakers = { FRC, FLC, 0, 0, FC, LFE, FR, FL } }, +{ .ca_index = 0x18, .speakers = { FRC, FLC, 0, RC, 0, 0, FR, FL } }, +{ .ca_index = 0x19, .speakers = { FRC, FLC, 0, RC, 0, LFE, FR, FL } }, +{ .ca_index = 0x1a, .speakers = { FRC, FLC, 0, RC, FC, 0, FR, FL } }, +{ .ca_index = 0x1b, .speakers = { FRC, FLC, 0, RC, FC, LFE, FR, FL } }, +{ .ca_index = 0x1c, .speakers = { FRC, FLC, RR, RL, 0, 0, FR, FL } }, +{ .ca_index = 0x1d, .speakers = { FRC, FLC, RR, RL, 0, LFE, FR, FL } }, +{ .ca_index = 0x1e, .speakers = { FRC, FLC, RR, RL, FC, 0, FR, FL } }, +{ .ca_index = 0x1f, .speakers = { FRC, FLC, RR, RL, FC, LFE, FR, FL } }, +}; + +static const 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 }, + {} /* terminator */ +}; + +/* hardware capability structure */ +static const struct snd_pcm_hardware had_pcm_hardware = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE), + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .rate_min = HAD_MIN_RATE, + .rate_max = HAD_MAX_RATE, + .channels_min = HAD_MIN_CHANNEL, + .channels_max = HAD_MAX_CHANNEL, + .buffer_bytes_max = HAD_MAX_BUFFER, + .period_bytes_min = HAD_MIN_PERIOD_BYTES, + .period_bytes_max = HAD_MAX_PERIOD_BYTES, + .periods_min = HAD_MIN_PERIODS, + .periods_max = HAD_MAX_PERIODS, + .fifo_size = HAD_FIFO_SIZE, +}; + +/* Get the active PCM substream; + * Call had_substream_put() for unreferecing. + * Don't call this inside had_spinlock, as it takes by itself + */ +static struct snd_pcm_substream * +had_substream_get(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + unsigned long flags; + + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + substream = intelhaddata->stream_info.substream; + if (substream) + intelhaddata->stream_info.substream_refcount++; + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + return substream; +} + +/* Unref the active PCM substream; + * Don't call this inside had_spinlock, as it takes by itself + */ +static void had_substream_put(struct snd_intelhad *intelhaddata) +{ + unsigned long flags; + + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + intelhaddata->stream_info.substream_refcount--; + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); +} + +/* Register access functions */ +static u32 had_read_register_raw(struct snd_intelhad *ctx, u32 reg) +{ + return ioread32(ctx->mmio_start + ctx->had_config_offset + reg); +} + +static void had_write_register_raw(struct snd_intelhad *ctx, u32 reg, u32 val) +{ + iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg); +} + +static void had_read_register(struct snd_intelhad *ctx, u32 reg, u32 *val) +{ + if (!ctx->connected) + *val = 0; + else + *val = had_read_register_raw(ctx, reg); +} + +static void had_write_register(struct snd_intelhad *ctx, u32 reg, u32 val) +{ + if (ctx->connected) + had_write_register_raw(ctx, reg, val); +} + +/* + * enable / disable audio configuration + * + * The normal read/modify should not directly be used on VLV2 for + * updating AUD_CONFIG register. + * This is because: + * Bit6 of AUD_CONFIG register is writeonly due to a silicon bug on VLV2 + * HDMI IP. As a result a read-modify of AUD_CONFIG regiter will always + * clear bit6. AUD_CONFIG[6:4] represents the "channels" field of the + * register. This field should be 1xy binary for configuration with 6 or + * more channels. Read-modify of AUD_CONFIG (Eg. for enabling audio) + * causes the "channels" field to be updated as 0xy binary resulting in + * bad audio. The fix is to always write the AUD_CONFIG[6:4] with + * appropriate value when doing read-modify of AUD_CONFIG register. + */ +static void had_enable_audio(struct snd_intelhad *intelhaddata, + bool enable) +{ + /* update the cached value */ + intelhaddata->aud_config.regx.aud_en = enable; + had_write_register(intelhaddata, AUD_CONFIG, + intelhaddata->aud_config.regval); +} + +/* forcibly ACKs to both BUFFER_DONE and BUFFER_UNDERRUN interrupts */ +static void had_ack_irqs(struct snd_intelhad *ctx) +{ + u32 status_reg; + + if (!ctx->connected) + return; + had_read_register(ctx, AUD_HDMI_STATUS, &status_reg); + status_reg |= HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; + had_write_register(ctx, AUD_HDMI_STATUS, status_reg); + had_read_register(ctx, AUD_HDMI_STATUS, &status_reg); +} + +/* Reset buffer pointers */ +static void had_reset_audio(struct snd_intelhad *intelhaddata) +{ + had_write_register(intelhaddata, AUD_HDMI_STATUS, + AUD_HDMI_STATUSG_MASK_FUNCRST); + had_write_register(intelhaddata, AUD_HDMI_STATUS, 0); +} + +/* + * initialize audio channel status registers + * This function is called in the prepare callback + */ +static int had_prog_status_reg(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + union aud_cfg cfg_val = {.regval = 0}; + union aud_ch_status_0 ch_stat0 = {.regval = 0}; + union aud_ch_status_1 ch_stat1 = {.regval = 0}; + + ch_stat0.regx.lpcm_id = (intelhaddata->aes_bits & + IEC958_AES0_NONAUDIO) >> 1; + ch_stat0.regx.clk_acc = (intelhaddata->aes_bits & + IEC958_AES3_CON_CLOCK) >> 4; + cfg_val.regx.val_bit = ch_stat0.regx.lpcm_id; + + switch (substream->runtime->rate) { + case AUD_SAMPLE_RATE_32: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_32KHZ; + break; + + case AUD_SAMPLE_RATE_44_1: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_44KHZ; + break; + case AUD_SAMPLE_RATE_48: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_48KHZ; + break; + case AUD_SAMPLE_RATE_88_2: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_88KHZ; + break; + case AUD_SAMPLE_RATE_96: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_96KHZ; + break; + case AUD_SAMPLE_RATE_176_4: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_176KHZ; + break; + case AUD_SAMPLE_RATE_192: + ch_stat0.regx.samp_freq = CH_STATUS_MAP_192KHZ; + break; + + default: + /* control should never come here */ + return -EINVAL; + } + + had_write_register(intelhaddata, + AUD_CH_STATUS_0, ch_stat0.regval); + + switch (substream->runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + ch_stat1.regx.max_wrd_len = MAX_SMPL_WIDTH_20; + ch_stat1.regx.wrd_len = SMPL_WIDTH_16BITS; + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S32_LE: + ch_stat1.regx.max_wrd_len = MAX_SMPL_WIDTH_24; + ch_stat1.regx.wrd_len = SMPL_WIDTH_24BITS; + break; + default: + return -EINVAL; + } + + had_write_register(intelhaddata, + AUD_CH_STATUS_1, ch_stat1.regval); + return 0; +} + +/* + * function to initialize audio + * registers and buffer confgiuration registers + * This function is called in the prepare callback + */ +static int had_init_audio_ctrl(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + union aud_cfg cfg_val = {.regval = 0}; + union aud_buf_config buf_cfg = {.regval = 0}; + u8 channels; + + had_prog_status_reg(substream, intelhaddata); + + buf_cfg.regx.audio_fifo_watermark = FIFO_THRESHOLD; + buf_cfg.regx.dma_fifo_watermark = DMA_FIFO_THRESHOLD; + buf_cfg.regx.aud_delay = 0; + had_write_register(intelhaddata, AUD_BUF_CONFIG, buf_cfg.regval); + + channels = substream->runtime->channels; + cfg_val.regx.num_ch = channels - 2; + if (channels <= 2) + cfg_val.regx.layout = LAYOUT0; + else + cfg_val.regx.layout = LAYOUT1; + + if (substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE) + cfg_val.regx.packet_mode = 1; + + if (substream->runtime->format == SNDRV_PCM_FORMAT_S32_LE) + cfg_val.regx.left_align = 1; + + cfg_val.regx.val_bit = 1; + + /* fix up the DP bits */ + if (intelhaddata->dp_output) { + cfg_val.regx.dp_modei = 1; + cfg_val.regx.set = 1; + } + + had_write_register(intelhaddata, AUD_CONFIG, cfg_val.regval); + intelhaddata->aud_config = cfg_val; + return 0; +} + +/* + * Compute derived values in channel_allocations[]. + */ +static void init_channel_allocations(void) +{ + int i, j; + struct cea_channel_speaker_allocation *p; + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + p = channel_allocations + i; + p->channels = 0; + p->spk_mask = 0; + for (j = 0; j < ARRAY_SIZE(p->speakers); j++) + if (p->speakers[j]) { + p->channels++; + p->spk_mask |= p->speakers[j]; + } + } +} + +/* + * The transformation takes two steps: + * + * eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask + * spk_mask => (channel_allocations[]) => ai->CA + * + * TODO: it could select the wrong CA from multiple candidates. + */ +static int had_channel_allocation(struct snd_intelhad *intelhaddata, + int channels) +{ + int i; + int ca = 0; + int spk_mask = 0; + + /* + * CA defaults to 0 for basic stereo audio + */ + if (channels <= 2) + return 0; + + /* + * expand ELD's speaker allocation mask + * + * ELD tells the speaker mask in a compact(paired) form, + * expand ELD's notions to match the ones used by Audio InfoFrame. + */ + + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (intelhaddata->eld[DRM_ELD_SPEAKER] & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + /* search for the first working match in the CA table */ + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (channels == channel_allocations[i].channels && + (spk_mask & channel_allocations[i].spk_mask) == + channel_allocations[i].spk_mask) { + ca = channel_allocations[i].ca_index; + break; + } + } + + dev_dbg(intelhaddata->dev, "select CA 0x%x for %d\n", ca, channels); + + return ca; +} + +/* from speaker bit mask to ALSA API channel position */ +static int spk_to_chmap(int spk) +{ + const struct channel_map_table *t = map_tables; + + for (; t->map; t++) { + if (t->spk_mask == spk) + return t->map; + } + return 0; +} + +static void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata) +{ + int i, c; + int spk_mask = 0; + struct snd_pcm_chmap_elem *chmap; + u8 eld_high, eld_high_mask = 0xF0; + u8 high_msb; + + kfree(intelhaddata->chmap->chmap); + intelhaddata->chmap->chmap = NULL; + + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); + if (!chmap) + return; + + dev_dbg(intelhaddata->dev, "eld speaker = %x\n", + intelhaddata->eld[DRM_ELD_SPEAKER]); + + /* WA: Fix the max channel supported to 8 */ + + /* + * Sink may support more than 8 channels, if eld_high has more than + * one bit set. SOC supports max 8 channels. + * Refer eld_speaker_allocation_bits, for sink speaker allocation + */ + + /* if 0x2F < eld < 0x4F fall back to 0x2f, else fall back to 0x4F */ + eld_high = intelhaddata->eld[DRM_ELD_SPEAKER] & eld_high_mask; + if ((eld_high & (eld_high-1)) && (eld_high > 0x1F)) { + /* eld_high & (eld_high-1): if more than 1 bit set */ + /* 0x1F: 7 channels */ + for (i = 1; i < 4; i++) { + high_msb = eld_high & (0x80 >> i); + if (high_msb) { + intelhaddata->eld[DRM_ELD_SPEAKER] &= + high_msb | 0xF; + break; + } + } + } + + for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) { + if (intelhaddata->eld[DRM_ELD_SPEAKER] & (1 << i)) + spk_mask |= eld_speaker_allocation_bits[i]; + } + + for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) { + if (spk_mask == channel_allocations[i].spk_mask) { + for (c = 0; c < channel_allocations[i].channels; c++) { + chmap->map[c] = spk_to_chmap( + channel_allocations[i].speakers[ + (MAX_SPEAKERS - 1) - c]); + } + chmap->channels = channel_allocations[i].channels; + intelhaddata->chmap->chmap = chmap; + break; + } + } + if (i >= ARRAY_SIZE(channel_allocations)) + kfree(chmap); +} + +/* + * ALSA API channel-map control callbacks + */ +static int had_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = HAD_MAX_CHANNEL; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +static int had_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct snd_intelhad *intelhaddata = info->private_data; + int i; + const struct snd_pcm_chmap_elem *chmap; + + memset(ucontrol->value.integer.value, 0, + sizeof(long) * HAD_MAX_CHANNEL); + mutex_lock(&intelhaddata->mutex); + if (!intelhaddata->chmap->chmap) { + mutex_unlock(&intelhaddata->mutex); + return 0; + } + + chmap = intelhaddata->chmap->chmap; + for (i = 0; i < chmap->channels; i++) + ucontrol->value.integer.value[i] = chmap->map[i]; + mutex_unlock(&intelhaddata->mutex); + + return 0; +} + +static int had_register_chmap_ctls(struct snd_intelhad *intelhaddata, + struct snd_pcm *pcm) +{ + int err; + + err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, + NULL, 0, (unsigned long)intelhaddata, + &intelhaddata->chmap); + if (err < 0) + return err; + + intelhaddata->chmap->private_data = intelhaddata; + intelhaddata->chmap->kctl->info = had_chmap_ctl_info; + intelhaddata->chmap->kctl->get = had_chmap_ctl_get; + intelhaddata->chmap->chmap = NULL; + return 0; +} + +/* + * Initialize Data Island Packets registers + * This function is called in the prepare callback + */ +static void had_prog_dip(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int i; + union aud_ctrl_st ctrl_state = {.regval = 0}; + union aud_info_frame2 frame2 = {.regval = 0}; + union aud_info_frame3 frame3 = {.regval = 0}; + u8 checksum = 0; + u32 info_frame; + int channels; + int ca; + + channels = substream->runtime->channels; + + had_write_register(intelhaddata, AUD_CNTL_ST, ctrl_state.regval); + + ca = had_channel_allocation(intelhaddata, channels); + if (intelhaddata->dp_output) { + info_frame = DP_INFO_FRAME_WORD1; + frame2.regval = (substream->runtime->channels - 1) | (ca << 24); + } else { + info_frame = HDMI_INFO_FRAME_WORD1; + frame2.regx.chnl_cnt = substream->runtime->channels - 1; + frame3.regx.chnl_alloc = ca; + + /* Calculte the byte wide checksum for all valid DIP words */ + for (i = 0; i < BYTES_PER_WORD; i++) + checksum += (info_frame >> (i * 8)) & 0xff; + for (i = 0; i < BYTES_PER_WORD; i++) + checksum += (frame2.regval >> (i * 8)) & 0xff; + for (i = 0; i < BYTES_PER_WORD; i++) + checksum += (frame3.regval >> (i * 8)) & 0xff; + + frame2.regx.chksum = -(checksum); + } + + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, info_frame); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, frame2.regval); + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, frame3.regval); + + /* program remaining DIP words with zero */ + for (i = 0; i < HAD_MAX_DIP_WORDS-VALID_DIP_WORDS; i++) + had_write_register(intelhaddata, AUD_HDMIW_INFOFR, 0x0); + + ctrl_state.regx.dip_freq = 1; + ctrl_state.regx.dip_en_sta = 1; + had_write_register(intelhaddata, AUD_CNTL_ST, ctrl_state.regval); +} + +static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate) +{ + u32 maud_val; + + /* Select maud according to DP 1.2 spec */ + if (link_rate == DP_2_7_GHZ) { + switch (aud_samp_freq) { + case AUD_SAMPLE_RATE_32: + maud_val = AUD_SAMPLE_RATE_32_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_44_1: + maud_val = AUD_SAMPLE_RATE_44_1_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_48: + maud_val = AUD_SAMPLE_RATE_48_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_88_2: + maud_val = AUD_SAMPLE_RATE_88_2_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_96: + maud_val = AUD_SAMPLE_RATE_96_DP_2_7_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_176_4: + maud_val = AUD_SAMPLE_RATE_176_4_DP_2_7_MAUD_VAL; + break; + + case HAD_MAX_RATE: + maud_val = HAD_MAX_RATE_DP_2_7_MAUD_VAL; + break; + + default: + maud_val = -EINVAL; + break; + } + } else if (link_rate == DP_1_62_GHZ) { + switch (aud_samp_freq) { + case AUD_SAMPLE_RATE_32: + maud_val = AUD_SAMPLE_RATE_32_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_44_1: + maud_val = AUD_SAMPLE_RATE_44_1_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_48: + maud_val = AUD_SAMPLE_RATE_48_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_88_2: + maud_val = AUD_SAMPLE_RATE_88_2_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_96: + maud_val = AUD_SAMPLE_RATE_96_DP_1_62_MAUD_VAL; + break; + + case AUD_SAMPLE_RATE_176_4: + maud_val = AUD_SAMPLE_RATE_176_4_DP_1_62_MAUD_VAL; + break; + + case HAD_MAX_RATE: + maud_val = HAD_MAX_RATE_DP_1_62_MAUD_VAL; + break; + + default: + maud_val = -EINVAL; + break; + } + } else + maud_val = -EINVAL; + + return maud_val; +} + +/* + * Program HDMI audio CTS value + * + * @aud_samp_freq: sampling frequency of audio data + * @tmds: sampling frequency of the display data + * @link_rate: DP link rate + * @n_param: N value, depends on aud_samp_freq + * @intelhaddata: substream private data + * + * Program CTS register based on the audio and display sampling frequency + */ +static void had_prog_cts(u32 aud_samp_freq, u32 tmds, u32 link_rate, + u32 n_param, struct snd_intelhad *intelhaddata) +{ + u32 cts_val; + u64 dividend, divisor; + + if (intelhaddata->dp_output) { + /* Substitute cts_val with Maud according to DP 1.2 spec*/ + cts_val = had_calculate_maud_value(aud_samp_freq, link_rate); + } else { + /* Calculate CTS according to HDMI 1.3a spec*/ + dividend = (u64)tmds * n_param*1000; + divisor = 128 * aud_samp_freq; + cts_val = div64_u64(dividend, divisor); + } + dev_dbg(intelhaddata->dev, "TMDS value=%d, N value=%d, CTS Value=%d\n", + tmds, n_param, cts_val); + had_write_register(intelhaddata, AUD_HDMI_CTS, (BIT(24) | cts_val)); +} + +static int had_calculate_n_value(u32 aud_samp_freq) +{ + int n_val; + + /* Select N according to HDMI 1.3a spec*/ + switch (aud_samp_freq) { + case AUD_SAMPLE_RATE_32: + n_val = 4096; + break; + + case AUD_SAMPLE_RATE_44_1: + n_val = 6272; + break; + + case AUD_SAMPLE_RATE_48: + n_val = 6144; + break; + + case AUD_SAMPLE_RATE_88_2: + n_val = 12544; + break; + + case AUD_SAMPLE_RATE_96: + n_val = 12288; + break; + + case AUD_SAMPLE_RATE_176_4: + n_val = 25088; + break; + + case HAD_MAX_RATE: + n_val = 24576; + break; + + default: + n_val = -EINVAL; + break; + } + return n_val; +} + +/* + * Program HDMI audio N value + * + * @aud_samp_freq: sampling frequency of audio data + * @n_param: N value, depends on aud_samp_freq + * @intelhaddata: substream private data + * + * This function is called in the prepare callback. + * It programs based on the audio and display sampling frequency + */ +static int had_prog_n(u32 aud_samp_freq, u32 *n_param, + struct snd_intelhad *intelhaddata) +{ + int n_val; + + if (intelhaddata->dp_output) { + /* + * According to DP specs, Maud and Naud values hold + * a relationship, which is stated as: + * Maud/Naud = 512 * fs / f_LS_Clk + * where, fs is the sampling frequency of the audio stream + * and Naud is 32768 for Async clock. + */ + + n_val = DP_NAUD_VAL; + } else + n_val = had_calculate_n_value(aud_samp_freq); + + if (n_val < 0) + return n_val; + + had_write_register(intelhaddata, AUD_N_ENABLE, (BIT(24) | n_val)); + *n_param = n_val; + return 0; +} + +/* + * PCM ring buffer handling + * + * The hardware provides a ring buffer with the fixed 4 buffer descriptors + * (BDs). The driver maps these 4 BDs onto the PCM ring buffer. The mapping + * moves at each period elapsed. The below illustrates how it works: + * + * At time=0 + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 0 | 1 | 2 | 3 | + * + * At time=1 (period elapsed) + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 1 | 2 | 3 | 0 | + * + * At time=2 (second period elapsed) + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 2 | 3 | 0 | 1 | + * + * The bd_head field points to the index of the BD to be read. It's also the + * position to be filled at next. The pcm_head and the pcm_filled fields + * point to the indices of the current position and of the next position to + * be filled, respectively. For PCM buffer there are both _head and _filled + * because they may be difference when nperiods > 4. For example, in the + * example above at t=1, bd_head=1 and pcm_head=1 while pcm_filled=5: + * + * pcm_head (=1) --v v-- pcm_filled (=5) + * PCM | 0 | 1 | 2 | 3 | 4 | 5 | .... |n-1| + * BD | 1 | 2 | 3 | 0 | + * bd_head (=1) --^ ^-- next to fill (= bd_head) + * + * For nperiods < 4, the remaining BDs out of 4 are marked as invalid, so that + * the hardware skips those BDs in the loop. + * + * An exceptional setup is the case with nperiods=1. Since we have to update + * BDs after finishing one BD processing, we'd need at least two BDs, where + * both BDs point to the same content, the same address, the same size of the + * whole PCM buffer. + */ + +#define AUD_BUF_ADDR(x) (AUD_BUF_A_ADDR + (x) * HAD_REG_WIDTH) +#define AUD_BUF_LEN(x) (AUD_BUF_A_LENGTH + (x) * HAD_REG_WIDTH) + +/* Set up a buffer descriptor at the "filled" position */ +static void had_prog_bd(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int idx = intelhaddata->bd_head; + int ofs = intelhaddata->pcmbuf_filled * intelhaddata->period_bytes; + u32 addr = substream->runtime->dma_addr + ofs; + + addr |= AUD_BUF_VALID; + if (!substream->runtime->no_period_wakeup) + addr |= AUD_BUF_INTR_EN; + had_write_register(intelhaddata, AUD_BUF_ADDR(idx), addr); + had_write_register(intelhaddata, AUD_BUF_LEN(idx), + intelhaddata->period_bytes); + + /* advance the indices to the next */ + intelhaddata->bd_head++; + intelhaddata->bd_head %= intelhaddata->num_bds; + intelhaddata->pcmbuf_filled++; + intelhaddata->pcmbuf_filled %= substream->runtime->periods; +} + +/* invalidate a buffer descriptor with the given index */ +static void had_invalidate_bd(struct snd_intelhad *intelhaddata, + int idx) +{ + had_write_register(intelhaddata, AUD_BUF_ADDR(idx), 0); + had_write_register(intelhaddata, AUD_BUF_LEN(idx), 0); +} + +/* Initial programming of ring buffer */ +static void had_init_ringbuf(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int i, num_periods; + + num_periods = runtime->periods; + intelhaddata->num_bds = min(num_periods, HAD_NUM_OF_RING_BUFS); + /* set the minimum 2 BDs for num_periods=1 */ + intelhaddata->num_bds = max(intelhaddata->num_bds, 2U); + intelhaddata->period_bytes = + frames_to_bytes(runtime, runtime->period_size); + WARN_ON(intelhaddata->period_bytes & 0x3f); + + intelhaddata->bd_head = 0; + intelhaddata->pcmbuf_head = 0; + intelhaddata->pcmbuf_filled = 0; + + for (i = 0; i < HAD_NUM_OF_RING_BUFS; i++) { + if (i < intelhaddata->num_bds) + had_prog_bd(substream, intelhaddata); + else /* invalidate the rest */ + had_invalidate_bd(intelhaddata, i); + } + + intelhaddata->bd_head = 0; /* reset at head again before starting */ +} + +/* process a bd, advance to the next */ +static void had_advance_ringbuf(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int num_periods = substream->runtime->periods; + + /* reprogram the next buffer */ + had_prog_bd(substream, intelhaddata); + + /* proceed to next */ + intelhaddata->pcmbuf_head++; + intelhaddata->pcmbuf_head %= num_periods; +} + +/* process the current BD(s); + * returns the current PCM buffer byte position, or -EPIPE for underrun. + */ +static int had_process_ringbuf(struct snd_pcm_substream *substream, + struct snd_intelhad *intelhaddata) +{ + int len, processed; + unsigned long flags; + + processed = 0; + spin_lock_irqsave(&intelhaddata->had_spinlock, flags); + for (;;) { + /* get the remaining bytes on the buffer */ + had_read_register(intelhaddata, + AUD_BUF_LEN(intelhaddata->bd_head), + &len); + if (len < 0 || len > intelhaddata->period_bytes) { + dev_dbg(intelhaddata->dev, "Invalid buf length %d\n", + len); + len = -EPIPE; + goto out; + } + + if (len > 0) /* OK, this is the current buffer */ + break; + + /* len=0 => already empty, check the next buffer */ + if (++processed >= intelhaddata->num_bds) { + len = -EPIPE; /* all empty? - report underrun */ + goto out; + } + had_advance_ringbuf(substream, intelhaddata); + } + + len = intelhaddata->period_bytes - len; + len += intelhaddata->period_bytes * intelhaddata->pcmbuf_head; + out: + spin_unlock_irqrestore(&intelhaddata->had_spinlock, flags); + return len; +} + +/* called from irq handler */ +static void had_process_buffer_done(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + substream = had_substream_get(intelhaddata); + if (!substream) + return; /* no stream? - bail out */ + + if (!intelhaddata->connected) { + snd_pcm_stop_xrun(substream); + goto out; /* disconnected? - bail out */ + } + + /* process or stop the stream */ + if (had_process_ringbuf(substream, intelhaddata) < 0) + snd_pcm_stop_xrun(substream); + else + snd_pcm_period_elapsed(substream); + + out: + had_substream_put(intelhaddata); +} + +/* + * The interrupt status 'sticky' bits might not be cleared by + * setting '1' to that bit once... + */ +static void wait_clear_underrun_bit(struct snd_intelhad *intelhaddata) +{ + int i; + u32 val; + + for (i = 0; i < 100; i++) { + /* clear bit30, 31 AUD_HDMI_STATUS */ + had_read_register(intelhaddata, AUD_HDMI_STATUS, &val); + if (!(val & AUD_HDMI_STATUS_MASK_UNDERRUN)) + return; + udelay(100); + cond_resched(); + had_write_register(intelhaddata, AUD_HDMI_STATUS, val); + } + dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n"); +} + +/* Perform some reset procedure but only when need_reset is set; + * this is called from prepare or hw_free callbacks once after trigger STOP + * or underrun has been processed in order to settle down the h/w state. + */ +static void had_do_reset(struct snd_intelhad *intelhaddata) +{ + if (!intelhaddata->need_reset || !intelhaddata->connected) + return; + + /* Reset buffer pointers */ + had_reset_audio(intelhaddata); + wait_clear_underrun_bit(intelhaddata); + intelhaddata->need_reset = false; +} + +/* called from irq handler */ +static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + /* Report UNDERRUN error to above layers */ + substream = had_substream_get(intelhaddata); + if (substream) { + snd_pcm_stop_xrun(substream); + had_substream_put(intelhaddata); + } + intelhaddata->need_reset = true; +} + +/* + * ALSA PCM open callback + */ +static int had_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + struct snd_pcm_runtime *runtime; + int retval; + + intelhaddata = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + pm_runtime_get_sync(intelhaddata->dev); + + /* set the runtime hw parameter with local snd_pcm_hardware struct */ + runtime->hw = had_pcm_hardware; + + retval = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (retval < 0) + goto error; + + /* Make sure, that the period size is always aligned + * 64byte boundary + */ + retval = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64); + if (retval < 0) + goto error; + + retval = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); + if (retval < 0) + goto error; + + /* expose PCM substream */ + spin_lock_irq(&intelhaddata->had_spinlock); + intelhaddata->stream_info.substream = substream; + intelhaddata->stream_info.substream_refcount++; + spin_unlock_irq(&intelhaddata->had_spinlock); + + return retval; + error: + pm_runtime_put(intelhaddata->dev); + return retval; +} + +/* + * ALSA PCM close callback + */ +static int had_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + + intelhaddata = snd_pcm_substream_chip(substream); + + /* unreference and sync with the pending PCM accesses */ + spin_lock_irq(&intelhaddata->had_spinlock); + intelhaddata->stream_info.substream = NULL; + intelhaddata->stream_info.substream_refcount--; + while (intelhaddata->stream_info.substream_refcount > 0) { + spin_unlock_irq(&intelhaddata->had_spinlock); + cpu_relax(); + spin_lock_irq(&intelhaddata->had_spinlock); + } + spin_unlock_irq(&intelhaddata->had_spinlock); + + pm_runtime_put(intelhaddata->dev); + return 0; +} + +/* + * ALSA PCM hw_params callback + */ +static int had_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_intelhad *intelhaddata; + unsigned long addr; + int pages, buf_size, retval; + + intelhaddata = snd_pcm_substream_chip(substream); + buf_size = params_buffer_bytes(hw_params); + retval = snd_pcm_lib_malloc_pages(substream, buf_size); + if (retval < 0) + return retval; + dev_dbg(intelhaddata->dev, "%s:allocated memory = %d\n", + __func__, buf_size); + /* mark the pages as uncached region */ + addr = (unsigned long) substream->runtime->dma_area; + pages = (substream->runtime->dma_bytes + PAGE_SIZE - 1) / PAGE_SIZE; + retval = set_memory_uc(addr, pages); + if (retval) { + dev_err(intelhaddata->dev, "set_memory_uc failed.Error:%d\n", + retval); + return retval; + } + memset(substream->runtime->dma_area, 0, buf_size); + + return retval; +} + +/* + * ALSA PCM hw_free callback + */ +static int had_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + unsigned long addr; + u32 pages; + + intelhaddata = snd_pcm_substream_chip(substream); + had_do_reset(intelhaddata); + + /* mark back the pages as cached/writeback region before the free */ + if (substream->runtime->dma_area != NULL) { + addr = (unsigned long) substream->runtime->dma_area; + pages = (substream->runtime->dma_bytes + PAGE_SIZE - 1) / + PAGE_SIZE; + set_memory_wb(addr, pages); + return snd_pcm_lib_free_pages(substream); + } + return 0; +} + +/* + * ALSA PCM trigger callback + */ +static int had_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int retval = 0; + struct snd_intelhad *intelhaddata; + + intelhaddata = snd_pcm_substream_chip(substream); + + spin_lock(&intelhaddata->had_spinlock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + /* Enable Audio */ + had_ack_irqs(intelhaddata); /* FIXME: do we need this? */ + had_enable_audio(intelhaddata, true); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* Disable Audio */ + had_enable_audio(intelhaddata, false); + intelhaddata->need_reset = true; + break; + + default: + retval = -EINVAL; + } + spin_unlock(&intelhaddata->had_spinlock); + return retval; +} + +/* + * ALSA PCM prepare callback + */ +static int had_pcm_prepare(struct snd_pcm_substream *substream) +{ + int retval; + u32 disp_samp_freq, n_param; + u32 link_rate = 0; + struct snd_intelhad *intelhaddata; + struct snd_pcm_runtime *runtime; + + intelhaddata = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + dev_dbg(intelhaddata->dev, "period_size=%d\n", + (int)frames_to_bytes(runtime, runtime->period_size)); + dev_dbg(intelhaddata->dev, "periods=%d\n", runtime->periods); + dev_dbg(intelhaddata->dev, "buffer_size=%d\n", + (int)snd_pcm_lib_buffer_bytes(substream)); + dev_dbg(intelhaddata->dev, "rate=%d\n", runtime->rate); + dev_dbg(intelhaddata->dev, "channels=%d\n", runtime->channels); + + had_do_reset(intelhaddata); + + /* Get N value in KHz */ + disp_samp_freq = intelhaddata->tmds_clock_speed; + + retval = had_prog_n(substream->runtime->rate, &n_param, intelhaddata); + if (retval) { + dev_err(intelhaddata->dev, + "programming N value failed %#x\n", retval); + goto prep_end; + } + + if (intelhaddata->dp_output) + link_rate = intelhaddata->link_rate; + + had_prog_cts(substream->runtime->rate, disp_samp_freq, link_rate, + n_param, intelhaddata); + + had_prog_dip(substream, intelhaddata); + + retval = had_init_audio_ctrl(substream, intelhaddata); + + /* Prog buffer address */ + had_init_ringbuf(substream, intelhaddata); + + /* + * Program channel mapping in following order: + * FL, FR, C, LFE, RL, RR + */ + + had_write_register(intelhaddata, AUD_BUF_CH_SWAP, SWAP_LFE_CENTER); + +prep_end: + return retval; +} + +/* + * ALSA PCM pointer callback + */ +static snd_pcm_uframes_t had_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_intelhad *intelhaddata; + int len; + + intelhaddata = snd_pcm_substream_chip(substream); + + if (!intelhaddata->connected) + return SNDRV_PCM_POS_XRUN; + + len = had_process_ringbuf(substream, intelhaddata); + if (len < 0) + return SNDRV_PCM_POS_XRUN; + len = bytes_to_frames(substream->runtime, len); + /* wrapping may happen when periods=1 */ + len %= substream->runtime->buffer_size; + return len; +} + +/* + * ALSA PCM mmap callback + */ +static int had_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + return remap_pfn_range(vma, vma->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +/* + * ALSA PCM ops + */ +static const struct snd_pcm_ops had_pcm_ops = { + .open = had_pcm_open, + .close = had_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = had_pcm_hw_params, + .hw_free = had_pcm_hw_free, + .prepare = had_pcm_prepare, + .trigger = had_pcm_trigger, + .pointer = had_pcm_pointer, + .mmap = had_pcm_mmap, +}; + +/* process mode change of the running stream; called in mutex */ +static int had_process_mode_change(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + int retval = 0; + u32 disp_samp_freq, n_param; + u32 link_rate = 0; + + substream = had_substream_get(intelhaddata); + if (!substream) + return 0; + + /* Disable Audio */ + had_enable_audio(intelhaddata, false); + + /* Update CTS value */ + disp_samp_freq = intelhaddata->tmds_clock_speed; + + retval = had_prog_n(substream->runtime->rate, &n_param, intelhaddata); + if (retval) { + dev_err(intelhaddata->dev, + "programming N value failed %#x\n", retval); + goto out; + } + + if (intelhaddata->dp_output) + link_rate = intelhaddata->link_rate; + + had_prog_cts(substream->runtime->rate, disp_samp_freq, link_rate, + n_param, intelhaddata); + + /* Enable Audio */ + had_enable_audio(intelhaddata, true); + +out: + had_substream_put(intelhaddata); + return retval; +} + +/* process hot plug, called from wq with mutex locked */ +static void had_process_hot_plug(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + spin_lock_irq(&intelhaddata->had_spinlock); + if (intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "Device already connected\n"); + spin_unlock_irq(&intelhaddata->had_spinlock); + return; + } + + intelhaddata->connected = true; + dev_dbg(intelhaddata->dev, + "%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED\n", + __func__, __LINE__); + spin_unlock_irq(&intelhaddata->had_spinlock); + + had_build_channel_allocation_map(intelhaddata); + + /* Report to above ALSA layer */ + substream = had_substream_get(intelhaddata); + if (substream) { + snd_pcm_stop_xrun(substream); + had_substream_put(intelhaddata); + } + + snd_jack_report(intelhaddata->jack, SND_JACK_AVOUT); +} + +/* process hot unplug, called from wq with mutex locked */ +static void had_process_hot_unplug(struct snd_intelhad *intelhaddata) +{ + struct snd_pcm_substream *substream; + + spin_lock_irq(&intelhaddata->had_spinlock); + if (!intelhaddata->connected) { + dev_dbg(intelhaddata->dev, "Device already disconnected\n"); + spin_unlock_irq(&intelhaddata->had_spinlock); + return; + + } + + /* Disable Audio */ + had_enable_audio(intelhaddata, false); + + intelhaddata->connected = false; + dev_dbg(intelhaddata->dev, + "%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED\n", + __func__, __LINE__); + spin_unlock_irq(&intelhaddata->had_spinlock); + + kfree(intelhaddata->chmap->chmap); + intelhaddata->chmap->chmap = NULL; + + /* Report to above ALSA layer */ + substream = had_substream_get(intelhaddata); + if (substream) { + snd_pcm_stop_xrun(substream); + had_substream_put(intelhaddata); + } + + snd_jack_report(intelhaddata->jack, 0); +} + +/* + * ALSA iec958 and ELD controls + */ + +static int had_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int had_iec958_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); + + mutex_lock(&intelhaddata->mutex); + ucontrol->value.iec958.status[0] = (intelhaddata->aes_bits >> 0) & 0xff; + ucontrol->value.iec958.status[1] = (intelhaddata->aes_bits >> 8) & 0xff; + ucontrol->value.iec958.status[2] = + (intelhaddata->aes_bits >> 16) & 0xff; + ucontrol->value.iec958.status[3] = + (intelhaddata->aes_bits >> 24) & 0xff; + mutex_unlock(&intelhaddata->mutex); + return 0; +} + +static int had_iec958_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.iec958.status[0] = 0xff; + ucontrol->value.iec958.status[1] = 0xff; + ucontrol->value.iec958.status[2] = 0xff; + ucontrol->value.iec958.status[3] = 0xff; + return 0; +} + +static int had_iec958_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned int val; + struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); + int changed = 0; + + val = (ucontrol->value.iec958.status[0] << 0) | + (ucontrol->value.iec958.status[1] << 8) | + (ucontrol->value.iec958.status[2] << 16) | + (ucontrol->value.iec958.status[3] << 24); + mutex_lock(&intelhaddata->mutex); + if (intelhaddata->aes_bits != val) { + intelhaddata->aes_bits = val; + changed = 1; + } + mutex_unlock(&intelhaddata->mutex); + return changed; +} + +static int had_ctl_eld_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = HDMI_MAX_ELD_BYTES; + return 0; +} + +static int had_ctl_eld_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_intelhad *intelhaddata = snd_kcontrol_chip(kcontrol); + + mutex_lock(&intelhaddata->mutex); + memcpy(ucontrol->value.bytes.data, intelhaddata->eld, + HDMI_MAX_ELD_BYTES); + mutex_unlock(&intelhaddata->mutex); + return 0; +} + +static const struct snd_kcontrol_new had_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .info = had_iec958_info, /* shared */ + .get = had_iec958_mask_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .info = had_iec958_info, + .get = had_iec958_get, + .put = had_iec958_put, + }, + { + .access = (SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = had_ctl_eld_info, + .get = had_ctl_eld_get, + }, +}; + +/* + * audio interrupt handler + */ +static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) +{ + struct snd_intelhad *ctx = dev_id; + u32 audio_stat; + + /* use raw register access to ack IRQs even while disconnected */ + audio_stat = had_read_register_raw(ctx, AUD_HDMI_STATUS); + + if (audio_stat & HDMI_AUDIO_UNDERRUN) { + had_write_register_raw(ctx, AUD_HDMI_STATUS, + HDMI_AUDIO_UNDERRUN); + had_process_buffer_underrun(ctx); + } + + if (audio_stat & HDMI_AUDIO_BUFFER_DONE) { + had_write_register_raw(ctx, AUD_HDMI_STATUS, + HDMI_AUDIO_BUFFER_DONE); + had_process_buffer_done(ctx); + } + + return IRQ_HANDLED; +} + +/* + * monitor plug/unplug notification from i915; just kick off the work + */ +static void notify_audio_lpe(struct platform_device *pdev) +{ + struct snd_intelhad *ctx = platform_get_drvdata(pdev); + + schedule_work(&ctx->hdmi_audio_wq); +} + +/* the work to handle monitor hot plug/unplug */ +static void had_audio_wq(struct work_struct *work) +{ + struct snd_intelhad *ctx = + container_of(work, struct snd_intelhad, hdmi_audio_wq); + struct intel_hdmi_lpe_audio_pdata *pdata = ctx->dev->platform_data; + + pm_runtime_get_sync(ctx->dev); + mutex_lock(&ctx->mutex); + if (!pdata->hdmi_connected) { + dev_dbg(ctx->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG\n", + __func__); + memset(ctx->eld, 0, sizeof(ctx->eld)); /* clear the old ELD */ + had_process_hot_unplug(ctx); + } else { + struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld; + + dev_dbg(ctx->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n", + __func__, eld->port_id, pdata->tmds_clock_speed); + + switch (eld->pipe_id) { + case 0: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; + break; + case 1: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_B; + break; + case 2: + ctx->had_config_offset = AUDIO_HDMI_CONFIG_C; + break; + default: + dev_dbg(ctx->dev, "Invalid pipe %d\n", + eld->pipe_id); + break; + } + + memcpy(ctx->eld, eld->eld_data, sizeof(ctx->eld)); + + ctx->dp_output = pdata->dp_output; + ctx->tmds_clock_speed = pdata->tmds_clock_speed; + ctx->link_rate = pdata->link_rate; + + had_process_hot_plug(ctx); + + /* Process mode change if stream is active */ + had_process_mode_change(ctx); + } + mutex_unlock(&ctx->mutex); + pm_runtime_put(ctx->dev); +} + +/* + * Jack interface + */ +static int had_create_jack(struct snd_intelhad *ctx) +{ + int err; + + err = snd_jack_new(ctx->card, "HDMI/DP", SND_JACK_AVOUT, &ctx->jack, + true, false); + if (err < 0) + return err; + ctx->jack->private_data = ctx; + return 0; +} + +/* + * PM callbacks + */ + +static int hdmi_lpe_audio_runtime_suspend(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + struct snd_pcm_substream *substream; + + substream = had_substream_get(ctx); + if (substream) { + snd_pcm_suspend(substream); + had_substream_put(ctx); + } + + return 0; +} + +static int __maybe_unused hdmi_lpe_audio_suspend(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + int err; + + err = hdmi_lpe_audio_runtime_suspend(dev); + if (!err) + snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D3hot); + return err; +} + +static int __maybe_unused hdmi_lpe_audio_resume(struct device *dev) +{ + struct snd_intelhad *ctx = dev_get_drvdata(dev); + + snd_power_change_state(ctx->card, SNDRV_CTL_POWER_D0); + return 0; +} + +/* release resources */ +static void hdmi_lpe_audio_free(struct snd_card *card) +{ + struct snd_intelhad *ctx = card->private_data; + + cancel_work_sync(&ctx->hdmi_audio_wq); + + if (ctx->mmio_start) + iounmap(ctx->mmio_start); + if (ctx->irq >= 0) + free_irq(ctx->irq, ctx); +} + +/* + * hdmi_lpe_audio_probe - start bridge with i915 + * + * This function is called when the i915 driver creates the + * hdmi-lpe-audio platform device. + */ +static int hdmi_lpe_audio_probe(struct platform_device *pdev) +{ + struct snd_card *card; + struct snd_intelhad *ctx; + struct snd_pcm *pcm; + struct intel_hdmi_lpe_audio_pdata *pdata; + int irq; + struct resource *res_mmio; + int i, ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "%s: quit: pdata not allocated by i915!!\n", __func__); + return -EINVAL; + } + + /* get resources */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Could not get irq resource\n"); + return -ENODEV; + } + + res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mmio) { + dev_err(&pdev->dev, "Could not get IO_MEM resources\n"); + return -ENXIO; + } + + /* create a card instance with ALSA framework */ + ret = snd_card_new(&pdev->dev, hdmi_card_index, hdmi_card_id, + THIS_MODULE, sizeof(*ctx), &card); + if (ret) + return ret; + + ctx = card->private_data; + spin_lock_init(&ctx->had_spinlock); + mutex_init(&ctx->mutex); + ctx->connected = false; + ctx->dev = &pdev->dev; + ctx->card = card; + ctx->aes_bits = SNDRV_PCM_DEFAULT_CON_SPDIF; + strcpy(card->driver, INTEL_HAD); + strcpy(card->shortname, "Intel HDMI/DP LPE Audio"); + strcpy(card->longname, "Intel HDMI/DP LPE Audio"); + + ctx->irq = -1; + ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; + INIT_WORK(&ctx->hdmi_audio_wq, had_audio_wq); + + card->private_free = hdmi_lpe_audio_free; + + /* assume pipe A as default */ + ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; + + platform_set_drvdata(pdev, ctx); + + dev_dbg(&pdev->dev, "%s: mmio_start = 0x%x, mmio_end = 0x%x\n", + __func__, (unsigned int)res_mmio->start, + (unsigned int)res_mmio->end); + + ctx->mmio_start = ioremap_nocache(res_mmio->start, + (size_t)(resource_size(res_mmio))); + if (!ctx->mmio_start) { + dev_err(&pdev->dev, "Could not get ioremap\n"); + ret = -EACCES; + goto err; + } + + /* setup interrupt handler */ + ret = request_irq(irq, display_pipe_interrupt_handler, 0, + pdev->name, ctx); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto err; + } + + ctx->irq = irq; + + ret = snd_pcm_new(card, INTEL_HAD, PCM_INDEX, MAX_PB_STREAMS, + MAX_CAP_STREAMS, &pcm); + if (ret) + goto err; + + /* setup private data which can be retrieved when required */ + pcm->private_data = ctx; + pcm->info_flags = 0; + strncpy(pcm->name, card->shortname, strlen(card->shortname)); + /* setup the ops for playabck */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &had_pcm_ops); + + /* only 32bit addressable */ + dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + + /* allocate dma pages; + * try to allocate 600k buffer as default which is large enough + */ + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_DEV, NULL, + HAD_DEFAULT_BUFFER, HAD_MAX_BUFFER); + + /* create controls */ + for (i = 0; i < ARRAY_SIZE(had_controls); i++) { + ret = snd_ctl_add(card, snd_ctl_new1(&had_controls[i], ctx)); + if (ret < 0) + goto err; + } + + init_channel_allocations(); + + /* Register channel map controls */ + ret = had_register_chmap_ctls(ctx, pcm); + if (ret < 0) + goto err; + + ret = had_create_jack(ctx); + if (ret < 0) + goto err; + + ret = snd_card_register(card); + if (ret) + goto err; + + spin_lock_irq(&pdata->lpe_audio_slock); + pdata->notify_audio_lpe = notify_audio_lpe; + pdata->notify_pending = false; + spin_unlock_irq(&pdata->lpe_audio_slock); + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); + schedule_work(&ctx->hdmi_audio_wq); + + return 0; + +err: + snd_card_free(card); + return ret; +} + +/* + * hdmi_lpe_audio_remove - stop bridge with i915 + * + * This function is called when the platform device is destroyed. + */ +static int hdmi_lpe_audio_remove(struct platform_device *pdev) +{ + struct snd_intelhad *ctx = platform_get_drvdata(pdev); + + snd_card_free(ctx->card); + return 0; +} + +static const struct dev_pm_ops hdmi_lpe_audio_pm = { + SET_SYSTEM_SLEEP_PM_OPS(hdmi_lpe_audio_suspend, hdmi_lpe_audio_resume) + SET_RUNTIME_PM_OPS(hdmi_lpe_audio_runtime_suspend, NULL, NULL) +}; + +static struct platform_driver hdmi_lpe_audio_driver = { + .driver = { + .name = "hdmi-lpe-audio", + .pm = &hdmi_lpe_audio_pm, + }, + .probe = hdmi_lpe_audio_probe, + .remove = hdmi_lpe_audio_remove, +}; + +module_platform_driver(hdmi_lpe_audio_driver); +MODULE_ALIAS("platform:hdmi_lpe_audio"); + +MODULE_AUTHOR("Sailaja Bandarupalli <sailaja.bandarupalli@intel.com>"); +MODULE_AUTHOR("Ramesh Babu K V <ramesh.babu@intel.com>"); +MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@intel.com>"); +MODULE_AUTHOR("Jerome Anand <jerome.anand@intel.com>"); +MODULE_DESCRIPTION("Intel HDMI Audio driver"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{Intel,Intel_HAD}"); diff --git a/sound/x86/intel_hdmi_audio.h b/sound/x86/intel_hdmi_audio.h new file mode 100644 index 0000000..2d3e389 --- /dev/null +++ b/sound/x86/intel_hdmi_audio.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 Intel Corporation + * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> + * Ramesh Babu K V <ramesh.babu@intel.com> + * Vaibhav Agarwal <vaibhav.agarwal@intel.com> + * Jerome Anand <jerome.anand@intel.com> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _INTEL_HDMI_AUDIO_H_ +#define _INTEL_HDMI_AUDIO_H_ + +#include "intel_hdmi_lpe_audio.h" + +#define PCM_INDEX 0 +#define MAX_PB_STREAMS 1 +#define MAX_CAP_STREAMS 0 +#define BYTES_PER_WORD 0x4 +#define INTEL_HAD "HdmiLpeAudio" + +/* + * CEA speaker placement: + * + * FL FLC FC FRC FR + * + * LFE + * + * RL RLC RC RRC RR + * + * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M + * corresponds to CEA RL/RR; The SMPTE channel _assignment_ C/LFE is + * swapped to CEA LFE/FC. + */ +enum cea_speaker_placement { + FL = (1 << 0), /* Front Left */ + FC = (1 << 1), /* Front Center */ + FR = (1 << 2), /* Front Right */ + FLC = (1 << 3), /* Front Left Center */ + FRC = (1 << 4), /* Front Right Center */ + RL = (1 << 5), /* Rear Left */ + RC = (1 << 6), /* Rear Center */ + RR = (1 << 7), /* Rear Right */ + RLC = (1 << 8), /* Rear Left Center */ + RRC = (1 << 9), /* Rear Right Center */ + LFE = (1 << 10), /* Low Frequency Effect */ +}; + +struct cea_channel_speaker_allocation { + int ca_index; + int speakers[8]; + + /* derived values, just for convenience */ + int channels; + int spk_mask; +}; + +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 */ +}; + +struct pcm_stream_info { + struct snd_pcm_substream *substream; + int substream_refcount; +}; + +/* + * struct snd_intelhad - intelhad driver structure + * + * @card: ptr to hold card details + * @connected: the monitor connection status + * @stream_info: stream information + * @eld: holds ELD info + * @curr_buf: pointer to hold current active ring buf + * @valid_buf_cnt: ring buffer count for stream + * @had_spinlock: driver lock + * @aes_bits: IEC958 status bits + * @buff_done: id of current buffer done intr + * @dev: platoform device handle + * @chmap: holds channel map info + */ +struct snd_intelhad { + struct snd_card *card; + bool connected; + struct pcm_stream_info stream_info; + unsigned char eld[HDMI_MAX_ELD_BYTES]; + bool dp_output; + unsigned int aes_bits; + spinlock_t had_spinlock; + struct device *dev; + struct snd_pcm_chmap *chmap; + int tmds_clock_speed; + int link_rate; + + /* ring buffer (BD) position index */ + unsigned int bd_head; + /* PCM buffer position indices */ + unsigned int pcmbuf_head; /* being processed */ + unsigned int pcmbuf_filled; /* to be filled */ + + unsigned int num_bds; /* number of BDs */ + unsigned int period_bytes; /* PCM period size in bytes */ + + /* internal stuff */ + int irq; + void __iomem *mmio_start; + unsigned int had_config_offset; + union aud_cfg aud_config; /* AUD_CONFIG reg value cache */ + struct work_struct hdmi_audio_wq; + struct mutex mutex; /* for protecting chmap and eld */ + bool need_reset; + struct snd_jack *jack; +}; + +#endif /* _INTEL_HDMI_AUDIO_ */ diff --git a/sound/x86/intel_hdmi_lpe_audio.h b/sound/x86/intel_hdmi_lpe_audio.h new file mode 100644 index 0000000..477e515 --- /dev/null +++ b/sound/x86/intel_hdmi_lpe_audio.h @@ -0,0 +1,328 @@ +/* + * intel_hdmi_lpe_audio.h - Intel HDMI LPE audio driver + * + * Copyright (C) 2016 Intel Corp + * Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com> + * Ramesh Babu K V <ramesh.babu@intel.com> + * Vaibhav Agarwal <vaibhav.agarwal@intel.com> + * Jerome Anand <jerome.anand@intel.com> + * Aravind Siddappaji <aravindx.siddappaji@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#ifndef __INTEL_HDMI_LPE_AUDIO_H +#define __INTEL_HDMI_LPE_AUDIO_H + +#define HAD_MIN_CHANNEL 2 +#define HAD_MAX_CHANNEL 8 +#define HAD_NUM_OF_RING_BUFS 4 + +/* max 20bit address, aligned to 64 */ +#define HAD_MAX_BUFFER ((1024 * 1024 - 1) & ~0x3f) +#define HAD_DEFAULT_BUFFER (600 * 1024) /* default prealloc size */ +#define HAD_MAX_PERIODS 256 /* arbitrary, but should suffice */ +#define HAD_MIN_PERIODS 1 +#define HAD_MAX_PERIOD_BYTES ((HAD_MAX_BUFFER / HAD_MIN_PERIODS) & ~0x3f) +#define HAD_MIN_PERIOD_BYTES 1024 /* might be smaller */ +#define HAD_FIFO_SIZE 0 /* fifo not being used */ +#define MAX_SPEAKERS 8 + +#define AUD_SAMPLE_RATE_32 32000 +#define AUD_SAMPLE_RATE_44_1 44100 +#define AUD_SAMPLE_RATE_48 48000 +#define AUD_SAMPLE_RATE_88_2 88200 +#define AUD_SAMPLE_RATE_96 96000 +#define AUD_SAMPLE_RATE_176_4 176400 +#define AUD_SAMPLE_RATE_192 192000 + +#define HAD_MIN_RATE AUD_SAMPLE_RATE_32 +#define HAD_MAX_RATE AUD_SAMPLE_RATE_192 + +#define DIS_SAMPLE_RATE_25_2 25200 +#define DIS_SAMPLE_RATE_27 27000 +#define DIS_SAMPLE_RATE_54 54000 +#define DIS_SAMPLE_RATE_74_25 74250 +#define DIS_SAMPLE_RATE_148_5 148500 +#define HAD_REG_WIDTH 0x08 +#define HAD_MAX_DIP_WORDS 16 + +/* DP Link Rates */ +#define DP_2_7_GHZ 270000 +#define DP_1_62_GHZ 162000 + +/* Maud Values */ +#define AUD_SAMPLE_RATE_32_DP_2_7_MAUD_VAL 1988 +#define AUD_SAMPLE_RATE_44_1_DP_2_7_MAUD_VAL 2740 +#define AUD_SAMPLE_RATE_48_DP_2_7_MAUD_VAL 2982 +#define AUD_SAMPLE_RATE_88_2_DP_2_7_MAUD_VAL 5480 +#define AUD_SAMPLE_RATE_96_DP_2_7_MAUD_VAL 5965 +#define AUD_SAMPLE_RATE_176_4_DP_2_7_MAUD_VAL 10961 +#define HAD_MAX_RATE_DP_2_7_MAUD_VAL 11930 +#define AUD_SAMPLE_RATE_32_DP_1_62_MAUD_VAL 3314 +#define AUD_SAMPLE_RATE_44_1_DP_1_62_MAUD_VAL 4567 +#define AUD_SAMPLE_RATE_48_DP_1_62_MAUD_VAL 4971 +#define AUD_SAMPLE_RATE_88_2_DP_1_62_MAUD_VAL 9134 +#define AUD_SAMPLE_RATE_96_DP_1_62_MAUD_VAL 9942 +#define AUD_SAMPLE_RATE_176_4_DP_1_62_MAUD_VAL 18268 +#define HAD_MAX_RATE_DP_1_62_MAUD_VAL 19884 + +/* Naud Value */ +#define DP_NAUD_VAL 32768 + +/* HDMI Controller register offsets - audio domain common */ +/* Base address for below regs = 0x65000 */ +enum hdmi_ctrl_reg_offset_common { + AUDIO_HDMI_CONFIG_A = 0x000, + AUDIO_HDMI_CONFIG_B = 0x800, + AUDIO_HDMI_CONFIG_C = 0x900, +}; +/* HDMI controller register offsets */ +enum hdmi_ctrl_reg_offset { + AUD_CONFIG = 0x0, + AUD_CH_STATUS_0 = 0x08, + AUD_CH_STATUS_1 = 0x0C, + AUD_HDMI_CTS = 0x10, + AUD_N_ENABLE = 0x14, + AUD_SAMPLE_RATE = 0x18, + AUD_BUF_CONFIG = 0x20, + AUD_BUF_CH_SWAP = 0x24, + AUD_BUF_A_ADDR = 0x40, + AUD_BUF_A_LENGTH = 0x44, + AUD_BUF_B_ADDR = 0x48, + AUD_BUF_B_LENGTH = 0x4c, + AUD_BUF_C_ADDR = 0x50, + AUD_BUF_C_LENGTH = 0x54, + AUD_BUF_D_ADDR = 0x58, + AUD_BUF_D_LENGTH = 0x5c, + AUD_CNTL_ST = 0x60, + AUD_HDMI_STATUS = 0x64, /* v2 */ + AUD_HDMIW_INFOFR = 0x68, /* v2 */ +}; + +/* Audio configuration */ +union aud_cfg { + struct { + u32 aud_en:1; + u32 layout:1; /* LAYOUT[01], see below */ + u32 fmt:2; + u32 num_ch:3; + u32 set:1; + u32 flat:1; + u32 val_bit:1; + u32 user_bit:1; + u32 underrun:1; /* 0: send null packets, + * 1: send silence stream + */ + u32 packet_mode:1; /* 0: 32bit container, 1: 16bit */ + u32 left_align:1; /* 0: MSB bits 0-23, 1: bits 8-31 */ + u32 bogus_sample:1; /* bogus sample for odd channels */ + u32 dp_modei:1; /* 0: HDMI, 1: DP */ + u32 rsvd:16; + } regx; + u32 regval; +}; + +#define AUD_CONFIG_VALID_BIT (1 << 9) +#define AUD_CONFIG_DP_MODE (1 << 15) +#define AUD_CONFIG_CH_MASK 0x70 +#define LAYOUT0 0 /* interleaved stereo */ +#define LAYOUT1 1 /* for channels > 2 */ + +/* Audio Channel Status 0 Attributes */ +union aud_ch_status_0 { + struct { + u32 ch_status:1; + u32 lpcm_id:1; + u32 cp_info:1; + u32 format:3; + u32 mode:2; + u32 ctg_code:8; + u32 src_num:4; + u32 ch_num:4; + u32 samp_freq:4; /* CH_STATUS_MAP_XXX */ + u32 clk_acc:2; + u32 rsvd:2; + } regx; + u32 regval; +}; + +/* samp_freq values - Sampling rate as per IEC60958 Ver 3 */ +#define CH_STATUS_MAP_32KHZ 0x3 +#define CH_STATUS_MAP_44KHZ 0x0 +#define CH_STATUS_MAP_48KHZ 0x2 +#define CH_STATUS_MAP_88KHZ 0x8 +#define CH_STATUS_MAP_96KHZ 0xA +#define CH_STATUS_MAP_176KHZ 0xC +#define CH_STATUS_MAP_192KHZ 0xE + +/* Audio Channel Status 1 Attributes */ +union aud_ch_status_1 { + struct { + u32 max_wrd_len:1; + u32 wrd_len:3; + u32 rsvd:28; + } regx; + u32 regval; +}; + +#define MAX_SMPL_WIDTH_20 0x0 +#define MAX_SMPL_WIDTH_24 0x1 +#define SMPL_WIDTH_16BITS 0x1 +#define SMPL_WIDTH_24BITS 0x5 + +/* CTS register */ +union aud_hdmi_cts { + struct { + u32 cts_val:24; + u32 en_cts_prog:1; + u32 rsvd:7; + } regx; + u32 regval; +}; + +/* N register */ +union aud_hdmi_n_enable { + struct { + u32 n_val:24; + u32 en_n_prog:1; + u32 rsvd:7; + } regx; + u32 regval; +}; + +/* Audio Buffer configurations */ +union aud_buf_config { + struct { + u32 audio_fifo_watermark:8; + u32 dma_fifo_watermark:3; + u32 rsvd0:5; + u32 aud_delay:8; + u32 rsvd1:8; + } regx; + u32 regval; +}; + +#define FIFO_THRESHOLD 0xFE +#define DMA_FIFO_THRESHOLD 0x7 + +/* Audio Sample Swapping offset */ +union aud_buf_ch_swap { + struct { + u32 first_0:3; + u32 second_0:3; + u32 first_1:3; + u32 second_1:3; + u32 first_2:3; + u32 second_2:3; + u32 first_3:3; + u32 second_3:3; + u32 rsvd:8; + } regx; + u32 regval; +}; + +#define SWAP_LFE_CENTER 0x00fac4c8 /* octal 76543210 */ + +/* Address for Audio Buffer */ +union aud_buf_addr { + struct { + u32 valid:1; + u32 intr_en:1; + u32 rsvd:4; + u32 addr:26; + } regx; + u32 regval; +}; + +#define AUD_BUF_VALID (1U << 0) +#define AUD_BUF_INTR_EN (1U << 1) + +/* Length of Audio Buffer */ +union aud_buf_len { + struct { + u32 buf_len:20; + u32 rsvd:12; + } regx; + u32 regval; +}; + +/* Audio Control State Register offset */ +union aud_ctrl_st { + struct { + u32 ram_addr:4; + u32 eld_ack:1; + u32 eld_addr:4; + u32 eld_buf_size:5; + u32 eld_valid:1; + u32 cp_ready:1; + u32 dip_freq:2; + u32 dip_idx:3; + u32 dip_en_sta:4; + u32 rsvd:7; + } regx; + u32 regval; +}; + +/* Audio HDMI Widget Data Island Packet offset */ +union aud_info_frame1 { + struct { + u32 pkt_type:8; + u32 ver_num:8; + u32 len:5; + u32 rsvd:11; + } regx; + u32 regval; +}; + +#define HDMI_INFO_FRAME_WORD1 0x000a0184 +#define DP_INFO_FRAME_WORD1 0x00441b84 + +/* DIP frame 2 */ +union aud_info_frame2 { + struct { + u32 chksum:8; + u32 chnl_cnt:3; + u32 rsvd0:1; + u32 coding_type:4; + u32 smpl_size:2; + u32 smpl_freq:3; + u32 rsvd1:3; + u32 format:8; + } regx; + u32 regval; +}; + +/* DIP frame 3 */ +union aud_info_frame3 { + struct { + u32 chnl_alloc:8; + u32 rsvd0:3; + u32 lsv:4; + u32 dm_inh:1; + u32 rsvd1:16; + } regx; + u32 regval; +}; + +#define VALID_DIP_WORDS 3 + +/* AUD_HDMI_STATUS bits */ +#define HDMI_AUDIO_UNDERRUN (1U << 31) +#define HDMI_AUDIO_BUFFER_DONE (1U << 29) + +/* AUD_HDMI_STATUS register mask */ +#define AUD_HDMI_STATUS_MASK_UNDERRUN 0xC0000000 +#define AUD_HDMI_STATUS_MASK_SRDBG 0x00000002 +#define AUD_HDMI_STATUSG_MASK_FUNCRST 0x00000001 + +#endif |