diff options
author | Mark Brown <broonie@kernel.org> | 2020-12-28 17:16:53 +0300 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2020-12-28 17:16:53 +0300 |
commit | f81325a05e9317f09a2e4ec57a52e4e49eb42b54 (patch) | |
tree | a5a91589c9ef8e212f2899f1462cfb5c3f0130ef /sound | |
parent | 671ee4db952449acde126965bf76817a3159040d (diff) | |
parent | 5c8fe583cce542aa0b84adc939ce85293de36e5e (diff) | |
download | linux-f81325a05e9317f09a2e4ec57a52e4e49eb42b54.tar.xz |
Merge tag 'v5.11-rc1' into asoc-5.11
Linux 5.11-rc1
Diffstat (limited to 'sound')
78 files changed, 2817 insertions, 3425 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index c1fec932c49d..debc30fcf5b3 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -709,11 +709,22 @@ static int snd_compr_pause(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_PAUSED; + break; + case SNDRV_PCM_STATE_DRAINING: + if (!stream->device->use_pause_in_draining) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) + stream->pause_in_draining = true; + break; + default: return -EPERM; - retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); - if (!retval) - stream->runtime->state = SNDRV_PCM_STATE_PAUSED; + } return retval; } @@ -721,11 +732,22 @@ static int snd_compr_resume(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_PAUSED: + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + break; + case SNDRV_PCM_STATE_DRAINING: + if (!stream->pause_in_draining) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (!retval) + stream->pause_in_draining = false; + break; + default: return -EPERM; - retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); - if (!retval) - stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + } return retval; } @@ -768,6 +790,7 @@ static int snd_compr_stop(struct snd_compr_stream *stream) /* clear flags and stop any drain wait */ stream->partial_drain = false; stream->metadata_set = false; + stream->pause_in_draining = false; snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; diff --git a/sound/core/control.c b/sound/core/control.c index 4373de42a5a0..3b44378b9dec 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1539,7 +1539,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, unlock: up_write(&card->controls_rwsem); - return 0; + return err; } static int snd_ctl_elem_add_user(struct snd_ctl_file *file, diff --git a/sound/core/init.c b/sound/core/init.c index 764dbe673d48..75aec71c48a8 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -149,8 +149,6 @@ static void release_card_device(struct device *dev) * @extra_size: allocate this extra size after the main soundcard structure * @card_ret: the pointer to store the created card instance * - * Creates and initializes a soundcard structure. - * * The function allocates snd_card instance via kzalloc with the given * space for the driver to use freely. The allocated struct is stored * in the given card_ret pointer. diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 0aeeb6244ff6..966bef5acc75 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -77,7 +77,8 @@ static void snd_malloc_dev_iram(struct snd_dma_buffer *dmab, size_t size) /* Assign the pool into private_data field */ dmab->private_data = pool; - dmab->area = gen_pool_dma_alloc(pool, size, &dmab->addr); + dmab->area = gen_pool_dma_alloc_align(pool, size, &dmab->addr, + PAGE_SIZE); } /** @@ -132,6 +133,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, if (WARN_ON(!dmab)) return -ENXIO; + size = PAGE_ALIGN(size); dmab->dev.type = type; dmab->dev.dev = device; dmab->bytes = 0; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 327ec42a36b0..142fc751a847 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -693,6 +693,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, oss_buffer_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size; + if (!oss_buffer_size) + return -EINVAL; oss_buffer_size = rounddown_pow_of_two(oss_buffer_size); if (atomic_read(&substream->mmap_count)) { if (oss_buffer_size > runtime->oss.mmap_bytes) @@ -728,17 +730,21 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, min_period_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_min(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL)); - min_period_size *= oss_frame_size; - min_period_size = roundup_pow_of_two(min_period_size); - if (oss_period_size < min_period_size) - oss_period_size = min_period_size; + if (min_period_size) { + min_period_size *= oss_frame_size; + min_period_size = roundup_pow_of_two(min_period_size); + if (oss_period_size < min_period_size) + oss_period_size = min_period_size; + } max_period_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, NULL)); - max_period_size *= oss_frame_size; - max_period_size = rounddown_pow_of_two(max_period_size); - if (oss_period_size > max_period_size) - oss_period_size = max_period_size; + if (max_period_size) { + max_period_size *= oss_frame_size; + max_period_size = rounddown_pow_of_two(max_period_size); + if (oss_period_size > max_period_size) + oss_period_size = max_period_size; + } oss_periods = oss_buffer_size / oss_period_size; @@ -1935,11 +1941,15 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsigned int val) { struct snd_pcm_runtime *runtime; + int fragshift; runtime = substream->runtime; if (runtime->oss.subdivision || runtime->oss.fragshift) return -EINVAL; - runtime->oss.fragshift = val & 0xffff; + fragshift = val & 0xffff; + if (fragshift >= 31) + return -EINVAL; + runtime->oss.fragshift = fragshift; runtime->oss.maxfrags = (val >> 16) & 0xffff; if (runtime->oss.fragshift < 4) /* < 16 */ runtime->oss.fragshift = 4; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index bda3514c7b2d..b7e3d8f44511 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1129,8 +1129,8 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, if (constrs->rules_num >= constrs->rules_all) { struct snd_pcm_hw_rule *new; unsigned int new_rules = constrs->rules_all + 16; - new = krealloc(constrs->rules, new_rules * sizeof(*c), - GFP_KERNEL); + new = krealloc_array(constrs->rules, new_rules, + sizeof(*c), GFP_KERNEL); if (!new) { va_end(args); return -ENOMEM; diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 4f03ba8ed0ae..ee6e9c5eec45 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -89,14 +89,6 @@ static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t siz return 0; } -/* - * release the preallocated buffer if not yet done. - */ -static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream) -{ - do_free_pages(substream->pcm->card, &substream->dma_buffer); -} - /** * snd_pcm_lib_preallocate_free - release the preallocated buffer of the specified substream. * @substream: the pcm substream instance @@ -105,7 +97,7 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream */ void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream) { - snd_pcm_lib_preallocate_dma_free(substream); + do_free_pages(substream->pcm->card, &substream->dma_buffer); } /** diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 47b155a49226..9f3f8e953ff0 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -755,8 +755,13 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, runtime->boundary *= 2; /* clear the buffer for avoiding possible kernel info leaks */ - if (runtime->dma_area && !substream->ops->copy_user) - memset(runtime->dma_area, 0, runtime->dma_bytes); + if (runtime->dma_area && !substream->ops->copy_user) { + size_t size = runtime->dma_bytes; + + if (runtime->info & SNDRV_PCM_INFO_MMAP) + size = PAGE_ALIGN(size); + memset(runtime->dma_area, 0, size); + } snd_pcm_timer_resolution_change(substream); snd_pcm_set_state(substream, SNDRV_PCM_STATE_SETUP); diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index c78720a3299c..257ad5206240 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -95,11 +95,21 @@ static inline unsigned short snd_rawmidi_file_flags(struct file *file) } } -static inline int snd_rawmidi_ready(struct snd_rawmidi_substream *substream) +static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime) +{ + return runtime->avail >= runtime->avail_min; +} + +static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream) { struct snd_rawmidi_runtime *runtime = substream->runtime; + unsigned long flags; + bool ready; - return runtime->avail >= runtime->avail_min; + spin_lock_irqsave(&runtime->lock, flags); + ready = __snd_rawmidi_ready(runtime); + spin_unlock_irqrestore(&runtime->lock, flags); + return ready; } static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream, @@ -1019,7 +1029,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, if (result > 0) { if (runtime->event) schedule_work(&runtime->event_work); - else if (snd_rawmidi_ready(substream)) + else if (__snd_rawmidi_ready(runtime)) wake_up(&runtime->sleep); } spin_unlock_irqrestore(&runtime->lock, flags); @@ -1098,7 +1108,7 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun result = 0; while (count > 0) { spin_lock_irq(&runtime->lock); - while (!snd_rawmidi_ready(substream)) { + while (!__snd_rawmidi_ready(runtime)) { wait_queue_entry_t wait; if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { @@ -1115,9 +1125,11 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; - if (!runtime->avail) - return result > 0 ? result : -EIO; spin_lock_irq(&runtime->lock); + if (!runtime->avail) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EIO; + } } spin_unlock_irq(&runtime->lock); count1 = snd_rawmidi_kernel_read1(substream, @@ -1255,7 +1267,7 @@ int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int coun runtime->avail += count; substream->bytes += count; if (count > 0) { - if (runtime->drain || snd_rawmidi_ready(substream)) + if (runtime->drain || __snd_rawmidi_ready(runtime)) wake_up(&runtime->sleep); } return count; @@ -1444,9 +1456,11 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; - if (!runtime->avail && !timeout) - return result > 0 ? result : -EIO; spin_lock_irq(&runtime->lock); + if (!runtime->avail && !timeout) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EIO; + } } spin_unlock_irq(&runtime->lock); count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count); @@ -1526,6 +1540,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *substream; struct snd_rawmidi_runtime *runtime; + unsigned long buffer_size, avail, xruns; rmidi = entry->private_data; snd_iprintf(buffer, "%s\n\n", rmidi->name); @@ -1544,13 +1559,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; + spin_lock_irq(&runtime->lock); + buffer_size = runtime->buffer_size; + avail = runtime->avail; + spin_unlock_irq(&runtime->lock); snd_iprintf(buffer, " Mode : %s\n" " Buffer size : %lu\n" " Avail : %lu\n", runtime->oss ? "OSS compatible" : "native", - (unsigned long) runtime->buffer_size, - (unsigned long) runtime->avail); + buffer_size, avail); } } } @@ -1568,13 +1586,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; + spin_lock_irq(&runtime->lock); + buffer_size = runtime->buffer_size; + avail = runtime->avail; + xruns = runtime->xruns; + spin_unlock_irq(&runtime->lock); snd_iprintf(buffer, " Buffer size : %lu\n" " Avail : %lu\n" " Overruns : %lu\n", - (unsigned long) runtime->buffer_size, - (unsigned long) runtime->avail, - (unsigned long) runtime->xruns); + buffer_size, avail, xruns); } } } diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index cc93157fa950..f9f2fea58b32 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -279,7 +279,6 @@ static int seq_free_client1(struct snd_seq_client *client) snd_seq_delete_all_ports(client); snd_seq_queue_client_leave(client->number); snd_use_lock_sync(&client->use_lock); - snd_seq_queue_client_termination(client->number); if (client->pool) snd_seq_pool_delete(&client->pool); spin_lock_irq(&clients_lock); diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 71a6ea62c3be..13cfc2d47fa7 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -537,33 +537,6 @@ int snd_seq_queue_is_used(int queueid, int client) /*----------------------------------------------------------------*/ -/* notification that client has left the system - - * stop the timer on all queues owned by this client - */ -void snd_seq_queue_client_termination(int client) -{ - unsigned long flags; - int i; - struct snd_seq_queue *q; - bool matched; - - for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { - if ((q = queueptr(i)) == NULL) - continue; - spin_lock_irqsave(&q->owner_lock, flags); - matched = (q->owner == client); - if (matched) - q->klocked = 1; - spin_unlock_irqrestore(&q->owner_lock, flags); - if (matched) { - if (q->timer->running) - snd_seq_timer_stop(q->timer); - snd_seq_timer_reset(q->timer); - } - queuefree(q); - } -} - /* final stage notification - * remove cells for no longer exist client (for non-owned queue) * or delete this queue (for owned queue) diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h index 9254c8dbe5e3..c69105dc1a10 100644 --- a/sound/core/seq/seq_queue.h +++ b/sound/core/seq/seq_queue.h @@ -26,10 +26,10 @@ struct snd_seq_queue { struct snd_seq_timer *timer; /* time keeper for this queue */ int owner; /* client that 'owns' the timer */ - unsigned int locked:1, /* timer is only accesibble by owner if set */ - klocked:1, /* kernel lock (after START) */ - check_again:1, - check_blocked:1; + bool locked; /* timer is only accesibble by owner if set */ + bool klocked; /* kernel lock (after START) */ + bool check_again; /* concurrent access happened during check */ + bool check_blocked; /* queue being checked */ unsigned int flags; /* status flags */ unsigned int info_flags; /* info for sync */ @@ -59,9 +59,6 @@ struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int f /* delete queue (destructor) */ int snd_seq_queue_delete(int client, int queueid); -/* notification that client has left the system */ -void snd_seq_queue_client_termination(int client); - /* final stage */ void snd_seq_queue_client_leave(int client); diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index c91356326699..702f91b9c60f 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -105,7 +105,7 @@ struct loopback_cable { unsigned int running; unsigned int pause; /* timer specific */ - struct loopback_ops *ops; + const struct loopback_ops *ops; /* If sound timer is used */ struct { int stream; @@ -1021,7 +1021,7 @@ static int loopback_jiffies_timer_open(struct loopback_pcm *dpcm) return 0; } -static struct loopback_ops loopback_jiffies_timer_ops = { +static const struct loopback_ops loopback_jiffies_timer_ops = { .open = loopback_jiffies_timer_open, .start = loopback_jiffies_timer_start, .stop = loopback_jiffies_timer_stop, @@ -1172,7 +1172,7 @@ exit: /* stop_sync() is not required for sound timer because it does not need to be * restarted in loopback_prepare() on Xrun recovery */ -static struct loopback_ops loopback_snd_timer_ops = { +static const struct loopback_ops loopback_snd_timer_ops = { .open = loopback_snd_timer_open, .start = loopback_snd_timer_start, .stop = loopback_snd_timer_stop, diff --git a/sound/drivers/pcsp/pcsp_input.c b/sound/drivers/pcsp/pcsp_input.c index 52b475b310c3..e79603fe743d 100644 --- a/sound/drivers/pcsp/pcsp_input.c +++ b/sound/drivers/pcsp/pcsp_input.c @@ -54,6 +54,7 @@ static int pcspkr_input_event(struct input_dev *dev, unsigned int type, case SND_BELL: if (value) value = 1000; + break; case SND_TONE: break; default: diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 2ceb57d1d58e..a3daa1f2c1c4 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -270,7 +270,7 @@ static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s, unsigned int timeout) { return wait_event_timeout(s->callback_wait, - s->callbacked == true, + s->callbacked, msecs_to_jiffies(timeout)) > 0; } diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c index 0f533f5bd960..9f8c53b39f95 100644 --- a/sound/firewire/fireworks/fireworks_transaction.c +++ b/sound/firewire/fireworks/fireworks_transaction.c @@ -123,7 +123,7 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode) t = (struct snd_efw_transaction *)data; length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length); - spin_lock_irq(&efw->lock); + spin_lock(&efw->lock); if (efw->push_ptr < efw->pull_ptr) capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr); @@ -190,7 +190,7 @@ handle_resp_for_user(struct fw_card *card, int generation, int source, copy_resp_to_buf(efw, data, length, rcode); end: - spin_unlock_irq(&instances_lock); + spin_unlock(&instances_lock); } static void diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c index 86d0d2fdf48a..8d01692c4f2a 100644 --- a/sound/isa/sb/sb8_main.c +++ b/sound/isa/sb/sb8_main.c @@ -506,6 +506,7 @@ static int snd_sb8_open(struct snd_pcm_substream *substream) } else { runtime->hw.rate_max = 15000; } + break; default: break; } diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index def8161cde4c..785ec0cf3933 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -894,8 +894,8 @@ static int snd_emu10k1x_create(struct snd_card *card, if ((err = pci_enable_device(pci)) < 0) return err; - if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 || - pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) { + + if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(28)) < 0) { dev_err(card->dev, "error to set 28bit mask DMA\n"); pci_disable_device(pci); return -ENXIO; diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 4bb58e8b08a8..687216e74526 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1803,7 +1803,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) return -EBUSY; /* OK, let it free */ - snd_hdac_device_unregister(&codec->core); + device_release_driver(hda_codec_dev(codec)); /* allow device access again */ snd_hda_unlock_devices(bus); diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index bbb17481159e..8060cc86dfea 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1364,16 +1364,20 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, struct nid_path *path; hda_nid_t pin = pins[i]; - path = snd_hda_get_path_from_idx(codec, path_idx[i]); - if (path) { - badness += assign_out_path_ctls(codec, path); - continue; + if (!spec->obey_preferred_dacs) { + path = snd_hda_get_path_from_idx(codec, path_idx[i]); + if (path) { + badness += assign_out_path_ctls(codec, path); + continue; + } } dacs[i] = get_preferred_dac(codec, pin); if (dacs[i]) { if (is_dac_already_used(codec, dacs[i])) badness += bad->shared_primary; + } else if (spec->obey_preferred_dacs) { + badness += BAD_NO_PRIMARY_DAC; } if (!dacs[i]) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a43f0bb77dae..0886bc81f40b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -237,6 +237,7 @@ struct hda_gen_spec { unsigned int power_down_unused:1; /* power down unused widgets */ unsigned int dac_min_mute:1; /* minimal = mute for DACs */ unsigned int suppress_vmaster:1; /* don't create vmaster kctls */ + unsigned int obey_preferred_dacs:1; /* obey preferred_dacs assignment */ /* other internal flags */ unsigned int no_analog:1; /* digital I/O only */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d539f52009a1..6852668f1bcb 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2506,6 +2506,9 @@ static const struct pci_device_id azx_ids[] = { /* DG1 */ { PCI_DEVICE(0x8086, 0x490d), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Alderlake-S */ + { PCI_DEVICE(0x8086, 0x7ad0), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Elkhart Lake */ { PCI_DEVICE(0x8086, 0x4b55), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 0631f31ef87f..00c2eeb2c472 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -679,6 +679,38 @@ static void print_gpio(struct snd_info_buffer *buffer, print_nid_array(buffer, codec, nid, &codec->nids); } +static void print_dpmst_connections(struct snd_info_buffer *buffer, struct hda_codec *codec, + hda_nid_t nid, int dev_num) +{ + int c, conn_len, curr, dev_id_saved; + hda_nid_t *conn; + + conn_len = snd_hda_get_num_raw_conns(codec, nid); + if (conn_len <= 0) + return; + + conn = kmalloc_array(conn_len, sizeof(hda_nid_t), GFP_KERNEL); + if (!conn) + return; + + dev_id_saved = snd_hda_get_dev_select(codec, nid); + + snd_hda_set_dev_select(codec, nid, dev_num); + curr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0); + if (snd_hda_get_raw_connections(codec, nid, conn, conn_len) < 0) + goto out; + + for (c = 0; c < conn_len; c++) { + snd_iprintf(buffer, " 0x%02x", conn[c]); + if (c == curr) + snd_iprintf(buffer, "*"); + } + +out: + kfree(conn); + snd_hda_set_dev_select(codec, nid, dev_id_saved); +} + static void print_device_list(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { @@ -702,10 +734,14 @@ static void print_device_list(struct snd_info_buffer *buffer, snd_iprintf(buffer, " "); snd_iprintf(buffer, - "Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i, + "Dev %02d: PD = %d, ELDV = %d, IA = %d, Connections [", i, !!(dev_list[i] & AC_DE_PD), !!(dev_list[i] & AC_DE_ELDV), !!(dev_list[i] & AC_DE_IA)); + + print_dpmst_connections(buffer, codec, nid, i); + + snd_iprintf(buffer, " ]\n"); } } diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index eb8ec109d7ad..d5ffcba794e5 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -139,7 +139,7 @@ static int reconfig_codec(struct hda_codec *codec) "The codec is being used, can't reconfigure.\n"); goto error; } - err = snd_hda_codec_configure(codec); + err = device_reprobe(hda_codec_dev(codec)); if (err < 0) goto error; err = snd_card_register(codec->card); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index e0c38f2735c6..7e62aed172a9 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -95,7 +95,7 @@ enum { }; /* Strings for Input Source Enum Control */ -static const char *const in_src_str[3] = {"Rear Mic", "Line", "Front Mic" }; +static const char *const in_src_str[3] = { "Microphone", "Line In", "Front Microphone" }; #define IN_SRC_NUM_OF_INPUTS 3 enum { REAR_MIC, @@ -788,6 +788,40 @@ static const struct ae5_filter_set ae5_filter_presets[] = { } }; +/* + * Data structures for storing audio router remapping data. These are used to + * remap a currently active streams ports. + */ +struct chipio_stream_remap_data { + unsigned int stream_id; + unsigned int count; + + unsigned int offset[16]; + unsigned int value[16]; +}; + +static const struct chipio_stream_remap_data stream_remap_data[] = { + { .stream_id = 0x14, + .count = 0x04, + .offset = { 0x00, 0x04, 0x08, 0x0c }, + .value = { 0x0001f8c0, 0x0001f9c1, 0x0001fac6, 0x0001fbc7 }, + }, + { .stream_id = 0x0c, + .count = 0x0c, + .offset = { 0x00, 0x04, 0x08, 0x0c, 0x10, 0x14, 0x18, 0x1c, + 0x20, 0x24, 0x28, 0x2c }, + .value = { 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3, + 0x0001e2c4, 0x0001e3c5, 0x0001e8c6, 0x0001e9c7, + 0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb }, + }, + { .stream_id = 0x0c, + .count = 0x08, + .offset = { 0x08, 0x0c, 0x10, 0x14, 0x20, 0x24, 0x28, 0x2c }, + .value = { 0x000140c2, 0x000141c3, 0x000150c4, 0x000151c5, + 0x000142c8, 0x000143c9, 0x000152ca, 0x000153cb }, + } +}; + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -1223,7 +1257,7 @@ static const struct hda_pintbl ae5_pincfgs[] = { { 0x0e, 0x01c510f0 }, /* SPDIF In */ { 0x0f, 0x01017114 }, /* Port A -- Rear L/R. */ { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */ - { 0x11, 0x01a170ff }, /* Port B -- LineMicIn2 / Rear Headphone */ + { 0x11, 0x012170ff }, /* Port B -- LineMicIn2 / Rear Headphone */ { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */ { 0x13, 0x908700f0 }, /* What U Hear In*/ { 0x18, 0x50d000f0 }, /* N/A */ @@ -1829,6 +1863,18 @@ static void chipio_set_stream_control(struct hda_codec *codec, CONTROL_PARAM_STREAM_CONTROL, enable); } +/* + * Get ChipIO audio stream's status. + */ +static void chipio_get_stream_control(struct hda_codec *codec, + int streamid, unsigned int *enable) +{ + chipio_set_control_param_no_mutex(codec, + CONTROL_PARAM_STREAM_ID, streamid); + *enable = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_GET, + CONTROL_PARAM_STREAM_CONTROL); +} /* * Set sampling rate of the connection point. NO MUTEX. @@ -1868,25 +1914,110 @@ static void chipio_8051_write_direct(struct hda_codec *codec, } /* - * Enable clocks. + * Writes to the 8051's exram, which has 16-bits of address space. + * Data at addresses 0x2000-0x7fff is mirrored to 0x8000-0xdfff. + * Data at 0x8000-0xdfff can also be used as program memory for the 8051 by + * setting the pmem bank selection SFR. + * 0xe000-0xffff is always mapped as program memory, with only 0xf000-0xffff + * being writable. */ -static void chipio_enable_clocks(struct hda_codec *codec) +static void chipio_8051_set_address(struct hda_codec *codec, unsigned int addr) { - struct ca0132_spec *spec = codec->spec; + unsigned int tmp; - mutex_lock(&spec->chipio_mutex); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0); + /* Lower 8-bits. */ + tmp = addr & 0xff; snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 5); + VENDOR_CHIPIO_8051_ADDRESS_LOW, tmp); + + /* Upper 8-bits. */ + tmp = (addr >> 8) & 0xff; snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0x0b); + VENDOR_CHIPIO_8051_ADDRESS_HIGH, tmp); +} + +static void chipio_8051_set_data(struct hda_codec *codec, unsigned int data) +{ + /* 8-bits of data. */ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 6); + VENDOR_CHIPIO_8051_DATA_WRITE, data & 0xff); +} + +static unsigned int chipio_8051_get_data(struct hda_codec *codec) +{ + return snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_READ, 0); +} + +/* PLL_PMU writes share the lower address register of the 8051 exram writes. */ +static void chipio_8051_set_data_pll(struct hda_codec *codec, unsigned int data) +{ + /* 8-bits of data. */ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xff); + VENDOR_CHIPIO_PLL_PMU_WRITE, data & 0xff); +} + +static void chipio_8051_write_exram(struct hda_codec *codec, + unsigned int addr, unsigned int data) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + chipio_8051_set_address(codec, addr); + chipio_8051_set_data(codec, data); + + mutex_unlock(&spec->chipio_mutex); +} + +static void chipio_8051_write_exram_no_mutex(struct hda_codec *codec, + unsigned int addr, unsigned int data) +{ + chipio_8051_set_address(codec, addr); + chipio_8051_set_data(codec, data); +} + +/* Readback data from the 8051's exram. No mutex. */ +static void chipio_8051_read_exram(struct hda_codec *codec, + unsigned int addr, unsigned int *data) +{ + chipio_8051_set_address(codec, addr); + *data = chipio_8051_get_data(codec); +} + +static void chipio_8051_write_pll_pmu(struct hda_codec *codec, + unsigned int addr, unsigned int data) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + chipio_8051_set_address(codec, addr & 0xff); + chipio_8051_set_data_pll(codec, data); + + mutex_unlock(&spec->chipio_mutex); +} + +static void chipio_8051_write_pll_pmu_no_mutex(struct hda_codec *codec, + unsigned int addr, unsigned int data) +{ + chipio_8051_set_address(codec, addr & 0xff); + chipio_8051_set_data_pll(codec, data); +} + +/* + * Enable clocks. + */ +static void chipio_enable_clocks(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + chipio_8051_write_pll_pmu_no_mutex(codec, 0x00, 0xff); + chipio_8051_write_pll_pmu_no_mutex(codec, 0x05, 0x0b); + chipio_8051_write_pll_pmu_no_mutex(codec, 0x06, 0xff); + mutex_unlock(&spec->chipio_mutex); } @@ -2316,13 +2447,6 @@ static int dspio_set_uint_param(struct hda_codec *codec, int mod_id, sizeof(unsigned int)); } -static int dspio_set_uint_param_no_source(struct hda_codec *codec, int mod_id, - int req, const unsigned int data) -{ - return dspio_set_param(codec, mod_id, 0x00, req, &data, - sizeof(unsigned int)); -} - /* * Allocate a DSP DMA channel via an SCP message */ @@ -7388,18 +7512,10 @@ static void ca0132_init_analog_mic2(struct hda_codec *codec) struct ca0132_spec *spec = codec->spec; mutex_lock(&spec->chipio_mutex); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x2D); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); + + chipio_8051_write_exram_no_mutex(codec, 0x1920, 0x00); + chipio_8051_write_exram_no_mutex(codec, 0x192d, 0x00); + mutex_unlock(&spec->chipio_mutex); } @@ -7423,6 +7539,199 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) } } + +/* If there is an active channel for some reason, find it and free it. */ +static void ca0132_alt_free_active_dma_channels(struct hda_codec *codec) +{ + unsigned int i, tmp; + int status; + + /* Read active DSPDMAC channel register. */ + status = chipio_read(codec, DSPDMAC_CHNLSTART_MODULE_OFFSET, &tmp); + if (status >= 0) { + /* AND against 0xfff to get the active channel bits. */ + tmp = tmp & 0xfff; + + /* If there are no active channels, nothing to free. */ + if (!tmp) + return; + } else { + codec_dbg(codec, "%s: Failed to read active DSP DMA channel register.\n", + __func__); + return; + } + + /* + * Check each DSP DMA channel for activity, and if the channel is + * active, free it. + */ + for (i = 0; i < DSPDMAC_DMA_CFG_CHANNEL_COUNT; i++) { + if (dsp_is_dma_active(codec, i)) { + status = dspio_free_dma_chan(codec, i); + if (status < 0) + codec_dbg(codec, "%s: Failed to free active DSP DMA channel %d.\n", + __func__, i); + } + } +} + +/* + * In the case of CT_EXTENSIONS_ENABLE being set to 1, and the DSP being in + * use, audio is no longer routed directly to the DAC/ADC from the HDA stream. + * Instead, audio is now routed through the DSP's DMA controllers, which + * the DSP is tasked with setting up itself. Through debugging, it seems the + * cause of most of the no-audio on startup issues were due to improperly + * configured DSP DMA channels. + * + * Normally, the DSP configures these the first time an HDA audio stream is + * started post DSP firmware download. That is why creating a 'dummy' stream + * worked in fixing the audio in some cases. This works most of the time, but + * sometimes if a stream is started/stopped before the DSP can setup the DMA + * configuration registers, it ends up in a broken state. Issues can also + * arise if streams are started in an unusual order, i.e the audio output dma + * channel being sandwiched between the mic1 and mic2 dma channels. + * + * The solution to this is to make sure that the DSP has no DMA channels + * in use post DSP firmware download, and then to manually start each default + * DSP stream that uses the DMA channels. These are 0x0c, the audio output + * stream, 0x03, analog mic 1, and 0x04, analog mic 2. + */ +static void ca0132_alt_start_dsp_audio_streams(struct hda_codec *codec) +{ + const unsigned int dsp_dma_stream_ids[] = { 0x0c, 0x03, 0x04 }; + struct ca0132_spec *spec = codec->spec; + unsigned int i, tmp; + + /* + * Check if any of the default streams are active, and if they are, + * stop them. + */ + mutex_lock(&spec->chipio_mutex); + + for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) { + chipio_get_stream_control(codec, dsp_dma_stream_ids[i], &tmp); + + if (tmp) { + chipio_set_stream_control(codec, + dsp_dma_stream_ids[i], 0); + } + } + + mutex_unlock(&spec->chipio_mutex); + + /* + * If all DSP streams are inactive, there should be no active DSP DMA + * channels. Check and make sure this is the case, and if it isn't, + * free any active channels. + */ + ca0132_alt_free_active_dma_channels(codec); + + mutex_lock(&spec->chipio_mutex); + + /* Make sure stream 0x0c is six channels. */ + chipio_set_stream_channels(codec, 0x0c, 6); + + for (i = 0; i < ARRAY_SIZE(dsp_dma_stream_ids); i++) { + chipio_set_stream_control(codec, + dsp_dma_stream_ids[i], 1); + + /* Give the DSP some time to setup the DMA channel. */ + msleep(75); + } + + mutex_unlock(&spec->chipio_mutex); +} + +/* + * The region of ChipIO memory from 0x190000-0x1903fc is a sort of 'audio + * router', where each entry represents a 48khz audio channel, with a format + * of an 8-bit destination, an 8-bit source, and an unknown 2-bit number + * value. The 2-bit number value is seemingly 0 if inactive, 1 if active, + * and 3 if it's using Sample Rate Converter ports. + * An example is: + * 0x0001f8c0 + * In this case, f8 is the destination, and c0 is the source. The number value + * is 1. + * This region of memory is normally managed internally by the 8051, where + * the region of exram memory from 0x1477-0x1575 has each byte represent an + * entry within the 0x190000 range, and when a range of entries is in use, the + * ending value is overwritten with 0xff. + * 0x1578 in exram is a table of 0x25 entries, corresponding to the ChipIO + * streamID's, where each entry is a starting 0x190000 port offset. + * 0x159d in exram is the same as 0x1578, except it contains the ending port + * offset for the corresponding streamID. + * + * On certain cards, such as the SBZ/ZxR/AE7, these are originally setup by + * the 8051, then manually overwritten to remap the ports to work with the + * new DACs. + * + * Currently known portID's: + * 0x00-0x1f: HDA audio stream input/output ports. + * 0x80-0xbf: Sample rate converter input/outputs. Only valid ports seem to + * have the lower-nibble set to 0x1, 0x2, and 0x9. + * 0xc0-0xdf: DSP DMA input/output ports. Dynamically assigned. + * 0xe0-0xff: DAC/ADC audio input/output ports. + * + * Currently known streamID's: + * 0x03: Mic1 ADC to DSP. + * 0x04: Mic2 ADC to DSP. + * 0x05: HDA node 0x02 audio stream to DSP. + * 0x0f: DSP Mic exit to HDA node 0x07. + * 0x0c: DSP processed audio to DACs. + * 0x14: DAC0, front L/R. + * + * It is possible to route the HDA audio streams directly to the DAC and + * bypass the DSP entirely, with the only downside being that since the DSP + * does volume control, the only volume control you'll get is through PCM on + * the PC side, in the same way volume is handled for optical out. This may be + * useful for debugging. + */ +static void chipio_remap_stream(struct hda_codec *codec, + const struct chipio_stream_remap_data *remap_data) +{ + unsigned int i, stream_offset; + + /* Get the starting port for the stream to be remapped. */ + chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id, + &stream_offset); + + /* + * Check if the stream's port value is 0xff, because the 8051 may not + * have gotten around to setting up the stream yet. Wait until it's + * setup to remap it's ports. + */ + if (stream_offset == 0xff) { + for (i = 0; i < 5; i++) { + msleep(25); + + chipio_8051_read_exram(codec, 0x1578 + remap_data->stream_id, + &stream_offset); + + if (stream_offset != 0xff) + break; + } + } + + if (stream_offset == 0xff) { + codec_info(codec, "%s: Stream 0x%02x ports aren't allocated, remap failed!\n", + __func__, remap_data->stream_id); + return; + } + + /* Offset isn't in bytes, its in 32-bit words, so multiply it by 4. */ + stream_offset *= 0x04; + stream_offset += 0x190000; + + for (i = 0; i < remap_data->count; i++) { + chipio_write_no_mutex(codec, + stream_offset + remap_data->offset[i], + remap_data->value[i]); + } + + /* Update stream map configuration. */ + chipio_write_no_mutex(codec, 0x19042c, 0x00000001); +} + /* * Default speaker tuning values setup for alternative codecs. */ @@ -7486,24 +7795,6 @@ static void ca0132_alt_init_speaker_tuning(struct hda_codec *codec) } /* - * Creates a dummy stream to bind the output to. This seems to have to be done - * after changing the main outputs source and destination streams. - */ -static void ca0132_alt_create_dummy_stream(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int stream_format; - - stream_format = snd_hdac_calc_stream_format(48000, 2, - SNDRV_PCM_FORMAT_S32_LE, 32, 0); - - snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, - 0, stream_format); - - snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); -} - -/* * Initialize mic for non-chromebook ca0132 implementations. */ static void ca0132_alt_init_analog_mics(struct hda_codec *codec) @@ -7544,9 +7835,6 @@ static void sbz_connect_streams(struct hda_codec *codec) codec_dbg(codec, "Connect Streams entered, mutex locked and loaded.\n"); - chipio_set_stream_channels(codec, 0x0C, 6); - chipio_set_stream_control(codec, 0x0C, 1); - /* This value is 0x43 for 96khz, and 0x83 for 192khz. */ chipio_write_no_mutex(codec, 0x18a020, 0x00000043); @@ -7570,102 +7858,37 @@ static void sbz_connect_streams(struct hda_codec *codec) */ static void sbz_chipio_startup_data(struct hda_codec *codec) { + const struct chipio_stream_remap_data *dsp_out_remap_data; struct ca0132_spec *spec = codec->spec; mutex_lock(&spec->chipio_mutex); codec_dbg(codec, "Startup Data entered, mutex locked and loaded.\n"); - /* These control audio output */ - chipio_write_no_mutex(codec, 0x190060, 0x0001f8c0); - chipio_write_no_mutex(codec, 0x190064, 0x0001f9c1); - chipio_write_no_mutex(codec, 0x190068, 0x0001fac6); - chipio_write_no_mutex(codec, 0x19006c, 0x0001fbc7); - /* Signal to update I think */ - chipio_write_no_mutex(codec, 0x19042c, 0x00000001); + /* Remap DAC0's output ports. */ + chipio_remap_stream(codec, &stream_remap_data[0]); - chipio_set_stream_channels(codec, 0x0C, 6); - chipio_set_stream_control(codec, 0x0C, 1); - /* No clue what these control */ - if (ca0132_quirk(spec) == QUIRK_SBZ) { - chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0); - chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1); - chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2); - chipio_write_no_mutex(codec, 0x19003c, 0x0001e5c3); - chipio_write_no_mutex(codec, 0x190040, 0x0001e2c4); - chipio_write_no_mutex(codec, 0x190044, 0x0001e3c5); - chipio_write_no_mutex(codec, 0x190048, 0x0001e8c6); - chipio_write_no_mutex(codec, 0x19004c, 0x0001e9c7); - chipio_write_no_mutex(codec, 0x190050, 0x0001ecc8); - chipio_write_no_mutex(codec, 0x190054, 0x0001edc9); - chipio_write_no_mutex(codec, 0x190058, 0x0001eaca); - chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb); - } else if (ca0132_quirk(spec) == QUIRK_ZXR) { - chipio_write_no_mutex(codec, 0x190038, 0x000140c2); - chipio_write_no_mutex(codec, 0x19003c, 0x000141c3); - chipio_write_no_mutex(codec, 0x190040, 0x000150c4); - chipio_write_no_mutex(codec, 0x190044, 0x000151c5); - chipio_write_no_mutex(codec, 0x190050, 0x000142c8); - chipio_write_no_mutex(codec, 0x190054, 0x000143c9); - chipio_write_no_mutex(codec, 0x190058, 0x000152ca); - chipio_write_no_mutex(codec, 0x19005c, 0x000153cb); + /* Remap DSP audio output stream ports. */ + switch (ca0132_quirk(spec)) { + case QUIRK_SBZ: + dsp_out_remap_data = &stream_remap_data[1]; + break; + + case QUIRK_ZXR: + dsp_out_remap_data = &stream_remap_data[2]; + break; + + default: + dsp_out_remap_data = NULL; + break; } - chipio_write_no_mutex(codec, 0x19042c, 0x00000001); + + if (dsp_out_remap_data) + chipio_remap_stream(codec, dsp_out_remap_data); codec_dbg(codec, "Startup Data exited, mutex released.\n"); mutex_unlock(&spec->chipio_mutex); } -/* - * Custom DSP SCP commands where the src value is 0x00 instead of 0x20. This is - * done after the DSP is loaded. - */ -static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) -{ - struct ca0132_spec *spec = codec->spec; - unsigned int tmp, i; - - /* - * Gotta run these twice, or else mic works inconsistently. Not clear - * why this is, but multiple tests have confirmed it. - */ - for (i = 0; i < 2; i++) { - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - case QUIRK_AE5: - case QUIRK_AE7: - tmp = 0x00000003; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); - tmp = 0x00000001; - dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); - tmp = 0x00000004; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - tmp = 0x00000005; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - break; - case QUIRK_R3D: - case QUIRK_R3DI: - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); - tmp = 0x00000001; - dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); - tmp = 0x00000004; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - tmp = 0x00000005; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - break; - default: - break; - } - msleep(100); - } -} - static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -7702,10 +7925,7 @@ static void ae5_post_dsp_register_set(struct hda_codec *codec) struct ca0132_spec *spec = codec->spec; chipio_8051_write_direct(codec, 0x93, 0x10); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + chipio_8051_write_pll_pmu(codec, 0x44, 0xc2); writeb(0xff, spec->mem_base + 0x304); writeb(0xff, spec->mem_base + 0x304); @@ -7742,40 +7962,16 @@ static void ae5_post_dsp_param_setup(struct hda_codec *codec) snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83); chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x92); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0xfa); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x22); + chipio_8051_write_exram(codec, 0xfa92, 0x22); } static void ae5_post_dsp_pll_setup(struct hda_codec *codec) { - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x41); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc8); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x45); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xcc); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x40); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xcb); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x51); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0x8d); + chipio_8051_write_pll_pmu(codec, 0x41, 0xc8); + chipio_8051_write_pll_pmu(codec, 0x45, 0xcc); + chipio_8051_write_pll_pmu(codec, 0x40, 0xcb); + chipio_8051_write_pll_pmu(codec, 0x43, 0xc7); + chipio_8051_write_pll_pmu(codec, 0x51, 0x8d); } static void ae5_post_dsp_stream_setup(struct hda_codec *codec) @@ -7788,9 +7984,6 @@ static void ae5_post_dsp_stream_setup(struct hda_codec *codec) chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000); - chipio_set_stream_channels(codec, 0x0C, 6); - chipio_set_stream_control(codec, 0x0C, 1); - chipio_set_stream_source_dest(codec, 0x5, 0x43, 0x0); chipio_set_stream_source_dest(codec, 0x18, 0x9, 0xd0); @@ -7800,10 +7993,7 @@ static void ae5_post_dsp_stream_setup(struct hda_codec *codec) chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7); ca0113_mmio_command_set(codec, 0x48, 0x01, 0x80); @@ -7842,34 +8032,14 @@ static void ae5_post_dsp_startup_data(struct hda_codec *codec) mutex_unlock(&spec->chipio_mutex); } -static const unsigned int ae7_port_set_data[] = { - 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3, 0x0001e2c4, 0x0001e3c5, - 0x0001e8c6, 0x0001e9c7, 0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb -}; - static void ae7_post_dsp_setup_ports(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - unsigned int i, count, addr; mutex_lock(&spec->chipio_mutex); - chipio_set_stream_channels(codec, 0x0c, 6); - chipio_set_stream_control(codec, 0x0c, 1); - - count = ARRAY_SIZE(ae7_port_set_data); - addr = 0x190030; - for (i = 0; i < count; i++) { - chipio_write_no_mutex(codec, addr, ae7_port_set_data[i]); - - /* Addresses are incremented by 4-bytes. */ - addr += 0x04; - } - - /* - * Port setting always ends with a write of 0x1 to address 0x19042c. - */ - chipio_write_no_mutex(codec, 0x19042c, 0x00000001); + /* Seems to share the same port remapping as the SBZ. */ + chipio_remap_stream(codec, &stream_remap_data[1]); ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); ca0113_mmio_command_set(codec, 0x48, 0x0d, 0x40); @@ -7893,8 +8063,6 @@ static void ae7_post_dsp_asi_stream_setup(struct hda_codec *codec) ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000); - chipio_set_stream_channels(codec, 0x0c, 6); - chipio_set_stream_control(codec, 0x0c, 1); chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00); chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0); @@ -7918,12 +8086,8 @@ static void ae7_post_dsp_pll_setup(struct hda_codec *codec) }; unsigned int i; - for (i = 0; i < ARRAY_SIZE(addr); i++) { - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, addr[i]); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, data[i]); - } + for (i = 0; i < ARRAY_SIZE(addr); i++) + chipio_8051_write_pll_pmu_no_mutex(codec, addr[i], data[i]); } static void ae7_post_dsp_asi_setup_ports(struct hda_codec *codec) @@ -7939,10 +8103,7 @@ static void ae7_post_dsp_asi_setup_ports(struct hda_codec *codec) mutex_lock(&spec->chipio_mutex); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + chipio_8051_write_pll_pmu_no_mutex(codec, 0x43, 0xc7); chipio_write_no_mutex(codec, 0x189000, 0x0001f101); chipio_write_no_mutex(codec, 0x189004, 0x0001f101); @@ -8015,10 +8176,7 @@ static void ae7_post_dsp_asi_setup(struct hda_codec *codec) { chipio_8051_write_direct(codec, 0x93, 0x10); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + chipio_8051_write_pll_pmu(codec, 0x44, 0xc2); ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); @@ -8030,20 +8188,12 @@ static void ae7_post_dsp_asi_setup(struct hda_codec *codec) chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); snd_hda_codec_write(codec, 0x17, 0, 0x794, 0x00); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x92); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0xfa); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x22); + chipio_8051_write_exram(codec, 0xfa92, 0x22); ae7_post_dsp_pll_setup(codec); ae7_post_dsp_asi_stream_setup(codec); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + chipio_8051_write_pll_pmu(codec, 0x43, 0xc7); ae7_post_dsp_asi_setup_ports(codec); } @@ -8106,8 +8256,8 @@ static void r3d_setup_defaults(struct hda_codec *codec) if (spec->dsp_state != DSP_DOWNLOADED) return; - ca0132_alt_dsp_scp_startup(codec); ca0132_alt_init_analog_mics(codec); + ca0132_alt_start_dsp_audio_streams(codec); /*remove DSP headroom*/ tmp = FLOAT_ZERO; @@ -8156,14 +8306,11 @@ static void sbz_setup_defaults(struct hda_codec *codec) if (spec->dsp_state != DSP_DOWNLOADED) return; - ca0132_alt_dsp_scp_startup(codec); ca0132_alt_init_analog_mics(codec); + ca0132_alt_start_dsp_audio_streams(codec); sbz_connect_streams(codec); sbz_chipio_startup_data(codec); - chipio_set_stream_control(codec, 0x03, 1); - chipio_set_stream_control(codec, 0x04, 1); - /* * Sets internal input loopback to off, used to have a switch to * enable input loopback, but turned out to be way too buggy. @@ -8198,8 +8345,6 @@ static void sbz_setup_defaults(struct hda_codec *codec) } ca0132_alt_init_speaker_tuning(codec); - - ca0132_alt_create_dummy_stream(codec); } /* @@ -8215,10 +8360,8 @@ static void ae5_setup_defaults(struct hda_codec *codec) if (spec->dsp_state != DSP_DOWNLOADED) return; - ca0132_alt_dsp_scp_startup(codec); ca0132_alt_init_analog_mics(codec); - chipio_set_stream_control(codec, 0x03, 1); - chipio_set_stream_control(codec, 0x04, 1); + ca0132_alt_start_dsp_audio_streams(codec); /* New, unknown SCP req's */ tmp = FLOAT_ZERO; @@ -8267,8 +8410,6 @@ static void ae5_setup_defaults(struct hda_codec *codec) } ca0132_alt_init_speaker_tuning(codec); - - ca0132_alt_create_dummy_stream(codec); } /* @@ -8284,8 +8425,8 @@ static void ae7_setup_defaults(struct hda_codec *codec) if (spec->dsp_state != DSP_DOWNLOADED) return; - ca0132_alt_dsp_scp_startup(codec); ca0132_alt_init_analog_mics(codec); + ca0132_alt_start_dsp_audio_streams(codec); ae7_post_dsp_setup_ports(codec); tmp = FLOAT_ZERO; @@ -8352,8 +8493,6 @@ static void ae7_setup_defaults(struct hda_codec *codec) } ca0132_alt_init_speaker_tuning(codec); - - ca0132_alt_create_dummy_stream(codec); } /* @@ -8544,7 +8683,7 @@ static void amic_callback(struct hda_codec *codec, struct hda_jack_callback *cb) ca0132_select_mic(codec); } -static void ca0132_init_unsol(struct hda_codec *codec) +static void ca0132_setup_unsol(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; snd_hda_jack_detect_enable_callback(codec, spec->unsol_tag_hp, hp_callback); @@ -8642,6 +8781,22 @@ static void ca0132_init_chip(struct hda_codec *codec) mutex_init(&spec->chipio_mutex); + /* + * The Windows driver always does this upon startup, which seems to + * clear out any previous configuration. This should help issues where + * a boot into Windows prior to a boot into Linux breaks things. Also, + * Windows always sends the reset twice. + */ + if (ca0132_use_alt_functions(spec)) { + chipio_set_control_flag(codec, CONTROL_FLAG_IDLE_ENABLE, 0); + chipio_write_no_mutex(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_CODEC_RESET, 0); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_CODEC_RESET, 0); + } + spec->cur_out_type = SPEAKER_OUT; if (!ca0132_use_alt_functions(spec)) spec->cur_mic_type = DIGITAL_MIC; @@ -9013,12 +9168,7 @@ static void r3d_pre_dsp_setup(struct hda_codec *codec) { chipio_write(codec, 0x18b0a4, 0x000000c2); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x1E); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x1C); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x5B); + chipio_8051_write_exram(codec, 0x1c1e, 0x5b); snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44); @@ -9028,27 +9178,53 @@ static void r3di_pre_dsp_setup(struct hda_codec *codec) { chipio_write(codec, 0x18b0a4, 0x000000c2); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x1E); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x1C); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x5B); - - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_DATA_WRITE, 0x40); + chipio_8051_write_exram(codec, 0x1c1e, 0x5b); + chipio_8051_write_exram(codec, 0x1920, 0x00); + chipio_8051_write_exram(codec, 0x1921, 0x40); snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04); } /* + * The ZxR seems to use alternative DAC's for the surround channels, which + * require PLL PMU setup for the clock rate, I'm guessing. Without setting + * this up, we get no audio out of the surround jacks. + */ +static void zxr_pre_dsp_setup(struct hda_codec *codec) +{ + static const unsigned int addr[] = { 0x43, 0x40, 0x41, 0x42, 0x45 }; + static const unsigned int data[] = { 0x08, 0x0c, 0x0b, 0x07, 0x0d }; + unsigned int i; + + chipio_write(codec, 0x189000, 0x0001f100); + msleep(50); + chipio_write(codec, 0x18900c, 0x0001f100); + msleep(50); + + /* + * This writes a RET instruction at the entry point of the function at + * 0xfa92 in exram. This function seems to have something to do with + * ASI. Might be some way to prevent the card from reconfiguring the + * ASI stuff itself. + */ + chipio_8051_write_exram(codec, 0xfa92, 0x22); + + chipio_8051_write_pll_pmu(codec, 0x51, 0x98); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x82); + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 3); + + chipio_write(codec, 0x18902c, 0x00000000); + msleep(50); + chipio_write(codec, 0x18902c, 0x00000003); + msleep(50); + + for (i = 0; i < ARRAY_SIZE(addr); i++) + chipio_8051_write_pll_pmu(codec, addr[i], data[i]); +} + +/* * These are sent before the DSP is downloaded. Not sure * what they do, or if they're necessary. Could possibly * be removed. Figure they're better to leave in. @@ -9183,6 +9359,8 @@ static void ca0132_mmio_init(struct hda_codec *codec) case QUIRK_AE5: ca0132_mmio_init_ae5(codec); break; + default: + break; } } @@ -9210,18 +9388,11 @@ static void ae5_register_set(struct hda_codec *codec) unsigned int i, cur_addr; unsigned char tmp[3]; - if (ca0132_quirk(spec) == QUIRK_AE7) { - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x41); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc8); - } + if (ca0132_quirk(spec) == QUIRK_AE7) + chipio_8051_write_pll_pmu(codec, 0x41, 0xc8); chipio_8051_write_direct(codec, 0x93, 0x10); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + chipio_8051_write_pll_pmu(codec, 0x44, 0xc2); if (ca0132_quirk(spec) == QUIRK_AE7) { tmp[0] = 0x03; @@ -9260,11 +9431,6 @@ static void ae5_register_set(struct hda_codec *codec) if (ca0132_quirk(spec) == QUIRK_AE5) ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); - - chipio_write(codec, 0x18b0a4, 0x000000c2); - - snd_hda_codec_write(codec, 0x01, 0, 0x7ff, 0x00); - snd_hda_codec_write(codec, 0x01, 0, 0x7ff, 0x00); } /* @@ -9302,10 +9468,7 @@ static void ca0132_alt_init(struct hda_codec *codec) break; case QUIRK_AE5: ca0132_gpio_init(codec); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x49); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0x88); + chipio_8051_write_pll_pmu(codec, 0x49, 0x88); chipio_write(codec, 0x18b030, 0x00000020); snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); @@ -9313,10 +9476,7 @@ static void ca0132_alt_init(struct hda_codec *codec) break; case QUIRK_AE7: ca0132_gpio_init(codec); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x49); - snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, - VENDOR_CHIPIO_PLL_PMU_WRITE, 0x88); + chipio_8051_write_pll_pmu(codec, 0x49, 0x88); snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); chipio_write(codec, 0x18b008, 0x000000f8); @@ -9325,8 +9485,10 @@ static void ca0132_alt_init(struct hda_codec *codec) ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); break; case QUIRK_ZXR: + chipio_8051_write_pll_pmu(codec, 0x49, 0x88); snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); + zxr_pre_dsp_setup(codec); break; default: break; @@ -9374,7 +9536,6 @@ static int ca0132_init(struct hda_codec *codec) if (ca0132_quirk(spec) == QUIRK_AE5 || ca0132_quirk(spec) == QUIRK_AE7) ae5_register_set(codec); - ca0132_init_unsol(codec); ca0132_init_params(codec); ca0132_init_flags(codec); @@ -9939,6 +10100,8 @@ static int patch_ca0132(struct hda_codec *codec) if (err < 0) goto error; + ca0132_setup_unsol(codec); + return 0; error: diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index ccd1df059654..1e4a4b83fbf6 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -78,6 +78,7 @@ struct hdmi_spec_per_pin { int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */ int repoll_count; bool setup; /* the stream has been set up by prepare callback */ + bool silent_stream; int channels; /* current number of channels */ bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ @@ -252,7 +253,7 @@ static int pin_id_to_pin_index(struct hda_codec *codec, return pin_idx; } - codec_warn(codec, "HDMI: pin nid %d not registered\n", pin_nid); + codec_warn(codec, "HDMI: pin NID 0x%x not registered\n", pin_nid); return -EINVAL; } @@ -312,7 +313,7 @@ static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid) if (get_cvt(spec, cvt_idx)->cvt_nid == cvt_nid) return cvt_idx; - codec_warn(codec, "HDMI: cvt nid %d not registered\n", cvt_nid); + codec_warn(codec, "HDMI: cvt NID 0x%x not registered\n", cvt_nid); return -EINVAL; } @@ -637,11 +638,11 @@ static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid, u8 val; int i; + hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0) != AC_DIPXMIT_BEST) return false; - hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0); for (i = 0; i < size; i++) { val = snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_DATA, 0); @@ -686,8 +687,7 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, dp_ai->CC02_CT47 = active_channels - 1; dp_ai->CA = ca; } else { - codec_dbg(codec, "HDMI: unknown connection type at pin %d\n", - pin_nid); + codec_dbg(codec, "HDMI: unknown connection type at pin NID 0x%x\n", pin_nid); return; } @@ -700,10 +700,8 @@ static void hdmi_pin_setup_infoframe(struct hda_codec *codec, */ if (!hdmi_infoframe_uptodate(codec, pin_nid, ai.bytes, sizeof(ai))) { - codec_dbg(codec, - "hdmi_pin_setup_infoframe: pin=%d channels=%d ca=0x%02x\n", - pin_nid, - active_channels, ca); + codec_dbg(codec, "%s: pin NID=0x%x channels=%d ca=0x%02x\n", + __func__, pin_nid, active_channels, ca); hdmi_stop_infoframe_trans(codec, pin_nid); hdmi_fill_audio_infoframe(codec, pin_nid, ai.bytes, sizeof(ai)); @@ -795,7 +793,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res, jack->jack_dirty = 1; codec_dbg(codec, - "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", + "HDMI hot plug event: Codec=%d NID=0x%x Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n", codec->addr, jack->nid, jack->dev_id, !!(res & AC_UNSOL_RES_IA), !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); @@ -873,7 +871,7 @@ static void haswell_verify_D0(struct hda_codec *codec, msleep(40); pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); pwr = (pwr & AC_PWRST_ACTUAL) >> AC_PWRST_ACTUAL_SHIFT; - codec_dbg(codec, "Haswell HDMI audio: Power for pin 0x%x is now D%d\n", nid, pwr); + codec_dbg(codec, "Haswell HDMI audio: Power for NID 0x%x is now D%d\n", nid, pwr); } } @@ -979,6 +977,13 @@ static int hdmi_choose_cvt(struct hda_codec *codec, else per_pin = get_pin(spec, pin_idx); + if (per_pin && per_pin->silent_stream) { + cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); + if (cvt_id) + *cvt_id = cvt_idx; + return 0; + } + /* Dynamically assign converter to stream */ for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) { per_cvt = get_cvt(spec, cvt_idx); @@ -1113,8 +1118,8 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, per_cvt = get_cvt(spec, cvt_idx); if (!per_cvt->assigned) { codec_dbg(codec, - "choose cvt %d for pin nid %d\n", - cvt_idx, nid); + "choose cvt %d for pin NID 0x%x\n", + cvt_idx, nid); snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, cvt_idx); @@ -1312,7 +1317,7 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx) if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) { codec_warn(codec, - "HDMI: pin %d wcaps %#x does not support connection list\n", + "HDMI: pin NID 0x%x wcaps %#x does not support connection list\n", pin_nid, get_wcaps(codec, pin_nid)); return -EINVAL; } @@ -1627,7 +1632,7 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, eld->eld_valid = false; codec_dbg(codec, - "HDMI status: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", + "HDMI status: Codec=%d NID=0x%x Presence_Detect=%d ELD_Valid=%d\n", codec->addr, pin_nid, eld->monitor_present, eld->eld_valid); if (eld->eld_valid) { @@ -1642,30 +1647,95 @@ static void hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin, snd_hda_power_down_pm(codec); } +#define I915_SILENT_RATE 48000 +#define I915_SILENT_CHANNELS 2 +#define I915_SILENT_FORMAT SNDRV_PCM_FORMAT_S16_LE +#define I915_SILENT_FORMAT_BITS 16 +#define I915_SILENT_FMT_MASK 0xf + static void silent_stream_enable(struct hda_codec *codec, - struct hdmi_spec_per_pin *per_pin) + struct hdmi_spec_per_pin *per_pin) { - unsigned int newval, oldval; - - codec_dbg(codec, "hdmi: enabling silent stream for NID %d\n", - per_pin->pin_nid); + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int cvt_idx, pin_idx, err; + unsigned int format; mutex_lock(&per_pin->lock); - if (!per_pin->channels) - per_pin->channels = 2; + if (per_pin->setup) { + codec_dbg(codec, "hdmi: PCM already open, no silent stream\n"); + goto unlock_out; + } - oldval = snd_hda_codec_read(codec, per_pin->pin_nid, 0, - AC_VERB_GET_CONV, 0); - newval = (oldval & 0xF0) | 0xF; - snd_hda_codec_write(codec, per_pin->pin_nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, newval); + pin_idx = pin_id_to_pin_index(codec, per_pin->pin_nid, per_pin->dev_id); + err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx); + if (err) { + codec_err(codec, "hdmi: no free converter to enable silent mode\n"); + goto unlock_out; + } + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = 1; + per_pin->cvt_nid = per_cvt->cvt_nid; + per_pin->silent_stream = true; + + codec_dbg(codec, "hdmi: enabling silent stream pin-NID=0x%x cvt-NID=0x%x\n", + per_pin->pin_nid, per_cvt->cvt_nid); + + snd_hda_set_dev_select(codec, per_pin->pin_nid, per_pin->dev_id); + snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0, + AC_VERB_SET_CONNECT_SEL, + per_pin->mux_idx); + + /* configure unused pins to choose other converters */ + pin_cvt_fixup(codec, per_pin, 0); + + snd_hdac_sync_audio_rate(&codec->core, per_pin->pin_nid, + per_pin->dev_id, I915_SILENT_RATE); + + /* trigger silent stream generation in hw */ + format = snd_hdac_calc_stream_format(I915_SILENT_RATE, I915_SILENT_CHANNELS, + I915_SILENT_FORMAT, I915_SILENT_FORMAT_BITS, 0); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, + I915_SILENT_FMT_MASK, I915_SILENT_FMT_MASK, format); + usleep_range(100, 200); + snd_hda_codec_setup_stream(codec, per_pin->cvt_nid, I915_SILENT_FMT_MASK, 0, format); + + per_pin->channels = I915_SILENT_CHANNELS; hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm); + unlock_out: mutex_unlock(&per_pin->lock); } +static void silent_stream_disable(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin) +{ + struct hdmi_spec *spec = codec->spec; + struct hdmi_spec_per_cvt *per_cvt; + int cvt_idx; + + mutex_lock(&per_pin->lock); + if (!per_pin->silent_stream) + goto unlock_out; + + codec_dbg(codec, "HDMI: disable silent stream on pin-NID=0x%x cvt-NID=0x%x\n", + per_pin->pin_nid, per_pin->cvt_nid); + + cvt_idx = cvt_nid_to_cvt_index(codec, per_pin->cvt_nid); + if (cvt_idx >= 0 && cvt_idx < spec->num_cvts) { + per_cvt = get_cvt(spec, cvt_idx); + per_cvt->assigned = 0; + } + + per_pin->cvt_nid = 0; + per_pin->silent_stream = false; + + unlock_out: + mutex_unlock(&spec->pcm_lock); +} + /* update ELD and jack state via audio component */ static void sync_eld_via_acomp(struct hda_codec *codec, struct hdmi_spec_per_pin *per_pin) @@ -1701,6 +1771,7 @@ static void sync_eld_via_acomp(struct hda_codec *codec, pm_ret); silent_stream_enable(codec, per_pin); } else if (monitor_prev && !monitor_next) { + silent_stream_disable(codec, per_pin); pm_ret = snd_hda_power_down_pm(codec); if (pm_ret < 0) codec_err(codec, @@ -2721,7 +2792,7 @@ static int intel_pin2port(void *audio_ptr, int pin_nid) return i; } - codec_info(codec, "Can't find the HDMI/DP port for pin %d\n", pin_nid); + codec_info(codec, "Can't find the HDMI/DP port for pin NID 0x%x\n", pin_nid); return -1; } @@ -4274,6 +4345,7 @@ HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6899089d132e..dde5ba209541 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -119,6 +119,7 @@ struct alc_spec { unsigned int no_shutup_pins:1; unsigned int ultra_low_power:1; unsigned int has_hs_key:1; + unsigned int no_internal_mic_pin:1; /* for PLL fix */ hda_nid_t pll_nid; @@ -445,6 +446,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) alc_update_coef_idx(codec, 0x7, 1<<5, 0); break; case 0x10ec0892: + case 0x10ec0897: alc_update_coef_idx(codec, 0x7, 1<<5, 0); break; case 0x10ec0899: @@ -2514,6 +2516,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1458, 0xa0ce, "Gigabyte X570 Aorus Xtreme", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x11f7, "MSI-GE63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1228, "MSI-GP63", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1462, 0x1229, "MSI-GP73", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1275, "MSI-GL63", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1276, "MSI-GL73", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1462, 0x1293, "MSI-GP65", ALC1220_FIXUP_CLEVO_P950), @@ -2522,13 +2525,23 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), SND_PCI_QUIRK(0x1558, 0x9501, "Clevo P950HR", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x9506, "Clevo P955HQ", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x950A, "Clevo P955H[PR]", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x95e1, "Clevo P95xER", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x95e2, "Clevo P950ER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e3, "Clevo P955[ER]T", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e4, "Clevo P955ER", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e5, "Clevo P955EE6", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x95e6, "Clevo P950R[CDF]", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x96e1, "Clevo P960[ER][CDFN]-K", ALC1220_FIXUP_CLEVO_P950), SND_PCI_QUIRK(0x1558, 0x97e1, "Clevo P970[ER][CDFN]", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x97e2, "Clevo P970RC-M", ALC1220_FIXUP_CLEVO_P950), + SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x65d1, "Clevo PB51[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65d2, "Clevo PB51R[CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x65e1, "Clevo PB51[ED][DF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x67d1, "Clevo PB71[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), - SND_PCI_QUIRK(0x1558, 0x50d3, "Clevo PC50[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), + SND_PCI_QUIRK(0x1558, 0x67e1, "Clevo PB71[DE][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x70d1, "Clevo PC70[ER][CDF]", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK(0x1558, 0x7714, "Clevo X170", ALC1220_FIXUP_CLEVO_PB51ED_PINS), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), @@ -3092,6 +3105,7 @@ static void alc_disable_headset_jack_key(struct hda_codec *codec) case 0x10ec0215: case 0x10ec0225: case 0x10ec0285: + case 0x10ec0287: case 0x10ec0295: case 0x10ec0289: case 0x10ec0299: @@ -3118,6 +3132,7 @@ static void alc_enable_headset_jack_key(struct hda_codec *codec) case 0x10ec0215: case 0x10ec0225: case 0x10ec0285: + case 0x10ec0287: case 0x10ec0295: case 0x10ec0289: case 0x10ec0299: @@ -4216,6 +4231,12 @@ static void alc286_fixup_hp_gpio_led(struct hda_codec *codec, alc_fixup_hp_gpio_led(codec, action, 0x02, 0x20); } +static void alc287_fixup_hp_gpio_led(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_hp_gpio_led(codec, action, 0x10, 0); +} + /* turn on/off mic-mute LED per capture hook via VREF change */ static int vref_micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) @@ -4507,6 +4528,7 @@ static const struct coef_fw alc225_pre_hsmode[] = { static void alc_headset_mode_unplugged(struct hda_codec *codec) { + struct alc_spec *spec = codec->spec; static const struct coef_fw coef0255[] = { WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */ WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */ @@ -4581,6 +4603,11 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) {} }; + if (spec->no_internal_mic_pin) { + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + return; + } + switch (codec->core.vendor_id) { case 0x10ec0255: alc_process_coef_fw(codec, coef0255); @@ -5147,6 +5174,11 @@ static void alc_determine_headset_type(struct hda_codec *codec) {} }; + if (spec->no_internal_mic_pin) { + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + return; + } + switch (codec->core.vendor_id) { case 0x10ec0255: alc_process_coef_fw(codec, coef0255); @@ -5998,6 +6030,21 @@ static void alc274_fixup_bind_dacs(struct hda_codec *codec, codec->power_save_node = 0; } +/* avoid DAC 0x06 for bass speaker 0x17; it has no volume control */ +static void alc289_fixup_asus_ga401(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + static const hda_nid_t preferred_pairs[] = { + 0x14, 0x02, 0x17, 0x02, 0x21, 0x03, 0 + }; + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->gen.preferred_dacs = preferred_pairs; + spec->gen.obey_preferred_dacs = 1; + } +} + /* The DAC of NID 0x3 will introduce click/pop noise on headphones, so invalidate it */ static void alc285_fixup_invalidate_dacs(struct hda_codec *codec, const struct hda_fixup *fix, int action) @@ -6105,6 +6152,23 @@ static void alc274_fixup_hp_headset_mic(struct hda_codec *codec, } } +static void alc_fixup_no_int_mic(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + switch (action) { + case HDA_FIXUP_ACT_PRE_PROBE: + /* Mic RING SLEEVE swap for combo jack */ + alc_update_coef_idx(codec, 0x45, 0xf<<12 | 1<<10, 5<<12); + spec->no_internal_mic_pin = true; + break; + case HDA_FIXUP_ACT_INIT: + alc_combo_jack_hp_jd_restart(codec); + break; + } +} + /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" @@ -6301,6 +6365,11 @@ enum { ALC274_FIXUP_HP_MIC, ALC274_FIXUP_HP_HEADSET_MIC, ALC256_FIXUP_ASUS_HPE, + ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, + ALC287_FIXUP_HP_GPIO_LED, + ALC256_FIXUP_HP_HEADSET_MIC, + ALC236_FIXUP_DELL_AIO_HEADSET_MIC, + ALC282_FIXUP_ACER_DISABLE_LINEOUT, }; static const struct hda_fixup alc269_fixups[] = { @@ -7550,11 +7619,10 @@ static const struct hda_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_HEADSET_MIC }, [ALC289_FIXUP_ASUS_GA401] = { - .type = HDA_FIXUP_PINS, - .v.pins = (const struct hda_pintbl[]) { - { 0x19, 0x03a11020 }, /* headset mic with jack detect */ - { } - }, + .type = HDA_FIXUP_FUNC, + .v.func = alc289_fixup_asus_ga401, + .chained = true, + .chain_id = ALC289_FIXUP_ASUS_GA502, }, [ALC289_FIXUP_ASUS_GA502] = { .type = HDA_FIXUP_PINS, @@ -7678,7 +7746,7 @@ static const struct hda_fixup alc269_fixups[] = { { } }, .chained = true, - .chain_id = ALC289_FIXUP_ASUS_GA401 + .chain_id = ALC289_FIXUP_ASUS_GA502 }, [ALC274_FIXUP_HP_MIC] = { .type = HDA_FIXUP_VERBS, @@ -7705,6 +7773,36 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC }, + [ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, + [ALC287_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_hp_gpio_led, + }, + [ALC256_FIXUP_HP_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_hp_headset_mic, + }, + [ALC236_FIXUP_DELL_AIO_HEADSET_MIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_no_int_mic, + .chained = true, + .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE + }, + [ALC282_FIXUP_ACER_DISABLE_LINEOUT] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1b, 0x411111f0 }, + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { }, + }, + .chained = true, + .chain_id = ALC269_FIXUP_HEADSET_MODE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7719,11 +7817,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572), SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS), + SND_PCI_QUIRK(0x1025, 0x101c, "Acer Veriton N2510G", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x1025, 0x1167, "Acer Veriton N6640G", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x1025, 0x1246, "Acer Predator Helios 500", ALC299_FIXUP_PREDATOR_SPK), SND_PCI_QUIRK(0x1025, 0x1247, "Acer vCopperbox", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1025, 0x1248, "Acer Veriton N4660G", ALC269VC_FIXUP_ACER_MIC_NO_PRESENCE), @@ -7782,6 +7883,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x097d, "Dell Precision", ALC289_FIXUP_DUAL_SPK), SND_PCI_QUIRK(0x1028, 0x098d, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC), + SND_PCI_QUIRK(0x1028, 0x0a58, "Dell Precision 3650 Tower", ALC255_FIXUP_DELL_HEADSET_MIC), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), @@ -7848,6 +7952,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x820d, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC), SND_PCI_QUIRK(0x103c, 0x827e, "HP x360", ALC295_FIXUP_HP_X360), + SND_PCI_QUIRK(0x103c, 0x827f, "HP x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), SND_PCI_QUIRK(0x103c, 0x82bf, "HP G3 mini", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x82c0, "HP G3 mini premium", ALC221_FIXUP_HP_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x83b9, "HP Spectre x360", ALC269_FIXUP_HP_MUTE_LED_MIC3), @@ -7859,6 +7964,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), @@ -7867,6 +7974,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC), @@ -7887,6 +7995,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), @@ -7924,11 +8033,50 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { 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(0x1462, 0xb171, "Cubi N 8GL (MS-B171)", ALC283_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x152d, 0x1082, "Quanta NL3", ALC269_FIXUP_LIFEBOOK), + SND_PCI_QUIRK(0x1558, 0x1323, "Clevo N130ZU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x1325, "System76 Darter Pro (darp5)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1401, "Clevo L140[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1403, "Clevo N140CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x1404, "Clevo N150CU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x14a1, "Clevo L141MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x4018, "Clevo NV40M[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x4019, "Clevo NV40MZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x4020, "Clevo NV40MB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x40a1, "Clevo NL40GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x40c1, "Clevo NL40[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x40d1, "Clevo NL41DU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50a3, "Clevo NJ51GU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50b3, "Clevo NK50S[BEZ]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50b6, "Clevo NK50S5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50b8, "Clevo NK50SZ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50d5, "Clevo NP50D5", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50f0, "Clevo NH50A[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x50f3, "Clevo NH58DPQ", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5101, "Clevo S510WU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x5157, "Clevo W517GU1", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x51a1, "Clevo NS50MU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8228, "Clevo NR40BU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8520, "Clevo NH50D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8521, "Clevo NH77D[CD]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8535, "Clevo NH50D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8536, "Clevo NH79D[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8550, "System76 Gazelle (gaze14)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8551, "System76 Gazelle (gaze14)", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x8560, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1558, 0x8561, "System76 Gazelle (gaze14)", ALC269_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1558, 0x8668, "Clevo NP50B[BE]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8680, "Clevo NJ50LU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8686, "Clevo NH50[CZ]U", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8a20, "Clevo NH55DCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8a51, "Clevo NH70RCQ-Y", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x8d50, "Clevo NH55RCQ-M", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x951d, "Clevo N950T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x961d, "Clevo N960S[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x971d, "Clevo N970T[CDF]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0xa500, "Clevo NL53RU", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS), SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), @@ -7966,6 +8114,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x225d, "Thinkpad T480", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x2292, "Thinkpad X1 Carbon 7th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), + SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), @@ -8278,6 +8428,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x19, 0x02a11020}, {0x1a, 0x02a11030}, {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC, + {0x21, 0x02211010}), + SND_HDA_PIN_QUIRK(0x10ec0236, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, + {0x14, 0x90170110}, + {0x19, 0x02a11020}, + {0x21, 0x02211030}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, {0x14, 0x90170110}, {0x21, 0x02211020}), @@ -8380,6 +8536,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1a, 0x90a70130}, {0x1b, 0x90170110}, {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, + {0x14, 0x90170110}, + {0x19, 0x02a11020}, + {0x21, 0x0221101f}), SND_HDA_PIN_QUIRK(0x10ec0274, 0x103c, "HP", ALC274_FIXUP_HP_HEADSET_MIC, {0x17, 0x90170110}, {0x19, 0x03a11030}, @@ -8421,6 +8581,22 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60140}, {0x19, 0x04a11030}, {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, + ALC282_STANDARD_PINS, + {0x12, 0x90a609c0}, + {0x18, 0x03a11830}, + {0x19, 0x04a19831}, + {0x1a, 0x0481303f}, + {0x1b, 0x04211020}, + {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0282, 0x1025, "Acer", ALC282_FIXUP_ACER_DISABLE_LINEOUT, + ALC282_STANDARD_PINS, + {0x12, 0x90a60940}, + {0x18, 0x03a11830}, + {0x19, 0x04a19831}, + {0x1a, 0x0481303f}, + {0x1b, 0x04211020}, + {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0283, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC282_STANDARD_PINS, {0x12, 0x90a60130}, @@ -8434,11 +8610,20 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x12, 0x90a60130}, {0x19, 0x03a11020}, {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, + {0x14, 0x90170110}, + {0x19, 0x04a11040}, + {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0285, 0x17aa, "Lenovo", ALC285_FIXUP_LENOVO_PC_BEEP_IN_NOISE, {0x12, 0x90a60130}, {0x14, 0x90170110}, {0x19, 0x04a11040}, {0x21, 0x04211020}), + SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC285_FIXUP_THINKPAD_HEADSET_JACK, + {0x14, 0x90170110}, + {0x17, 0x90170111}, + {0x19, 0x03a11030}, + {0x21, 0x03211020}), SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, {0x12, 0x90a60130}, {0x17, 0x90170110}, @@ -8502,6 +8687,9 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, ALC292_STANDARD_PINS, {0x13, 0x90a60140}), + SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_HPE, + {0x17, 0x90170110}, + {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_MIC, {0x14, 0x90170110}, {0x1b, 0x90a70130}, @@ -10088,6 +10276,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882), HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882), HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0897, "ALC897", patch_alc662), HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882), HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882), HDA_CODEC_ENTRY(0x10ec0b00, "ALCS1200A", patch_alc882), diff --git a/sound/pci/mixart/mixart_core.c b/sound/pci/mixart/mixart_core.c index 0bdd33b0af65..fb8895af0363 100644 --- a/sound/pci/mixart/mixart_core.c +++ b/sound/pci/mixart/mixart_core.c @@ -70,7 +70,6 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp, unsigned int i; #endif - mutex_lock(&mgr->msg_lock); err = 0; /* copy message descriptor from miXart to driver */ @@ -119,8 +118,6 @@ static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp, writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD)); _clean_exit: - mutex_unlock(&mgr->msg_lock); - return err; } @@ -258,7 +255,9 @@ int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int resp.data = resp_data; resp.size = max_resp_size; + mutex_lock(&mgr->msg_lock); err = get_msg(mgr, &resp, msg_frame); + mutex_unlock(&mgr->msg_lock); if( request->message_id != resp.message_id ) dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n"); diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 869af8a32c98..4eabece4dcba 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -468,7 +468,6 @@ static int snd_rme32_capture_getrate(struct rme32 * rme32, int *is_adat) return 32000; default: return -1; - break; } else switch (n) { /* supporting the CS8412 */ diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 4a1f576dd9cf..04e878a0f773 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2286,7 +2286,6 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm) case AIO: status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); return (status >> 16) & 0xF; - break; case AES32: status = hdspm_read(hdspm, HDSPM_statusRegister); return (status >> HDSPM_AES32_wcFreq_bit) & 0xF; @@ -2312,7 +2311,6 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm) case AIO: status = hdspm_read(hdspm, HDSPM_RD_STATUS_1); return (status >> 20) & 0xF; - break; case AES32: status = hdspm_read(hdspm, HDSPM_statusRegister); return (status >> 1) & 0xF; @@ -2338,7 +2336,6 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm) case AIO: status = hdspm_read(hdspm, HDSPM_RD_STATUS_2); return (status >> 12) & 0xF; - break; default: break; } @@ -2358,7 +2355,6 @@ static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index) case AES32: timecode = hdspm_read(hdspm, HDSPM_timecodeRegister); return (timecode >> (4*index)) & 0xF; - break; default: break; } @@ -3845,7 +3841,6 @@ static int hdspm_wc_sync_check(struct hdspm *hdspm) return 1; } return 0; - break; case MADI: status2 = hdspm_read(hdspm, HDSPM_statusRegister2); @@ -3856,7 +3851,6 @@ static int hdspm_wc_sync_check(struct hdspm *hdspm) return 1; } return 0; - break; case RayDAT: case AIO: @@ -3868,8 +3862,6 @@ static int hdspm_wc_sync_check(struct hdspm *hdspm) return 1; return 0; - break; - case MADIface: break; } @@ -6321,6 +6313,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, (statusregister & HDSPM_RX_64ch) ? 1 : 0; /* TODO: Mac driver sets it when f_s>48kHz */ status.card_specific.madi.frame_format = 0; + break; default: break; diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 7ab10028d9fa..012fbec5e6a7 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -732,34 +732,27 @@ static inline int rme9652_spdif_sample_rate(struct snd_rme9652 *s) switch (rme9652_decode_spdif_rate(rate_bits)) { case 0x7: return 32000; - break; case 0x6: return 44100; - break; case 0x5: return 48000; - break; case 0x4: return 88200; - break; case 0x3: return 96000; - break; case 0x0: return 64000; - break; default: dev_err(s->card->dev, "%s: unknown S/PDIF input rate (bits = 0x%x)\n", s->card_name, rate_bits); return 0; - break; } } diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c index 58bb49fff184..631a61ce52f4 100644 --- a/sound/ppc/snd_ps3.c +++ b/sound/ppc/snd_ps3.c @@ -896,11 +896,6 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) u64 lpar_addr, lpar_size; static u64 dummy_mask; - if (WARN_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1))) - return -ENODEV; - if (WARN_ON(dev->match_id != PS3_MATCH_ID_SOUND)) - return -ENODEV; - the_card.ps3_dev = dev; ret = ps3_open_hv_device(dev); @@ -1049,12 +1044,10 @@ clean_open: }; /* snd_ps3_probe */ /* called when module removal */ -static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev) +static void snd_ps3_driver_remove(struct ps3_system_bus_device *dev) { int ret; pr_info("%s:start id=%d\n", __func__, dev->match_id); - if (dev->match_id != PS3_MATCH_ID_SOUND) - return -ENXIO; /* * ctl and preallocate buffer will be freed in @@ -1077,7 +1070,6 @@ static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev) lv1_gpu_device_unmap(2); ps3_close_hv_device(dev); pr_info("%s:end id=%d\n", __func__, dev->match_id); - return 0; } /* snd_ps3_remove */ static struct ps3_system_bus_driver snd_ps3_bus_driver_info = { diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c index bd0b1554bd4e..8c138e490f0c 100644 --- a/sound/soc/amd/raven/pci-acp3x.c +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -118,6 +118,10 @@ static int snd_acp3x_probe(struct pci_dev *pci, int ret, i; u32 addr, val; + /* Raven device detection */ + if (pci->revision != 0x00) + return -ENODEV; + if (pci_enable_device(pci)) { dev_err(&pci->dev, "pci_enable_device failed\n"); return -ENODEV; diff --git a/sound/soc/amd/renoir/rn-pci-acp3x.c b/sound/soc/amd/renoir/rn-pci-acp3x.c index 877350f38a68..fa169bf09886 100644 --- a/sound/soc/amd/renoir/rn-pci-acp3x.c +++ b/sound/soc/amd/renoir/rn-pci-acp3x.c @@ -6,6 +6,7 @@ #include <linux/pci.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> @@ -20,14 +21,13 @@ module_param(acp_power_gating, int, 0644); MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating"); /** - * dmic_acpi_check = -1 - Checks ACPI method to know DMIC hardware status runtime - * = 0 - Skips the DMIC device creation and returns probe failure - * = 1 - Assumes that platform has DMIC support and skips ACPI - * method check + * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime + * = 0 - Skip the DMIC device creation and return probe failure + * = 1 - Force DMIC support */ static int dmic_acpi_check = ACP_DMIC_AUTO; module_param(dmic_acpi_check, bint, 0644); -MODULE_PARM_DESC(dmic_acpi_check, "checks Dmic hardware runtime"); +MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)"); struct acp_dev_data { void __iomem *acp_base; @@ -163,6 +163,17 @@ static int rn_acp_deinit(void __iomem *acp_base) return 0; } +static const struct dmi_system_id rn_acp_quirk_table[] = { + { + /* Lenovo IdeaPad Flex 5 14ARE05, IdeaPad 5 15ARE05 */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "LNVNB161216"), + } + }, + {} +}; + static int snd_rn_acp_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { @@ -172,10 +183,15 @@ static int snd_rn_acp_probe(struct pci_dev *pci, acpi_handle handle; acpi_integer dmic_status; #endif + const struct dmi_system_id *dmi_id; unsigned int irqflags; int ret, index; u32 addr; + /* Renoir device check */ + if (pci->revision != 0x01) + return -ENODEV; + if (pci_enable_device(pci)) { dev_err(&pci->dev, "pci_enable_device failed\n"); return -ENODEV; @@ -232,6 +248,12 @@ static int snd_rn_acp_probe(struct pci_dev *pci, goto de_init; } #endif + dmi_id = dmi_first_match(rn_acp_quirk_table); + if (dmi_id && !dmi_id->driver_data) { + dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)"); + ret = -ENODEV; + goto de_init; + } } adata->res = devm_kzalloc(&pci->dev, diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ac63e7c176c1..9bf6bfdaf11e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -180,7 +180,6 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT700_SDW imply SND_SOC_RT711_SDW imply SND_SOC_RT715_SDW - imply SND_SOC_RT715_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_SGTL5000 imply SND_SOC_SI476X @@ -1237,12 +1236,6 @@ config SND_SOC_RT715_SDW select SND_SOC_RT715 select REGMAP_SOUNDWIRE -config SND_SOC_RT715_SDCA_SDW - tristate "Realtek RT715 SDCA Codec - SDW" - depends on SOUNDWIRE - select REGMAP_SOUNDWIRE - select REGMAP_SOUNDWIRE_MBQ - #Freescale sgtl5000 codec config SND_SOC_SGTL5000 tristate "Freescale SGTL5000 CODEC" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f255ec74333c..d277f0366e09 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -194,7 +194,6 @@ snd-soc-rt5682-i2c-objs := rt5682-i2c.o snd-soc-rt700-objs := rt700.o rt700-sdw.o snd-soc-rt711-objs := rt711.o rt711-sdw.o snd-soc-rt715-objs := rt715.o rt715-sdw.o -snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o @@ -511,7 +510,6 @@ obj-$(CONFIG_SND_SOC_RT5682_SDW) += snd-soc-rt5682-sdw.o obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o -obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c index 5c3b7e5e55d2..f33a2a9654e7 100644 --- a/sound/soc/codecs/cros_ec_codec.c +++ b/sound/soc/codecs/cros_ec_codec.c @@ -8,7 +8,7 @@ * EC for audio function. */ -#include <crypto/sha.h> +#include <crypto/sha2.h> #include <linux/acpi.h> #include <linux/delay.h> #include <linux/device.h> diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index ff5cc9bbec29..bb736c44e68a 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -784,6 +784,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) if (fw->size < MAX98390_DSM_PARAM_MIN_SIZE) { dev_err(component->dev, "param fw is invalid.\n"); + ret = -EINVAL; goto err_alloc; } dsm_param = (char *)fw->data; @@ -794,6 +795,7 @@ static int max98390_dsm_init(struct snd_soc_component *component) fw->size < param_size + MAX98390_DSM_PAYLOAD_OFFSET) { dev_err(component->dev, "param fw is invalid.\n"); + ret = -EINVAL; goto err_alloc; } regmap_write(max98390->regmap, MAX98390_R203A_AMP_EN, 0x80); diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c deleted file mode 100644 index 889b6b3b0009..000000000000 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ /dev/null @@ -1,278 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver -// -// Copyright(c) 2020 Realtek Semiconductor Corp. -// -// - -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/mod_devicetable.h> -#include <linux/soundwire/sdw.h> -#include <linux/soundwire/sdw_type.h> -#include <linux/soundwire/sdw_registers.h> -#include <linux/module.h> -#include <linux/regmap.h> -#include <sound/soc.h> -#include "rt715-sdca.h" -#include "rt715-sdca-sdw.h" - -static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x201a ... 0x2027: - case 0x2029 ... 0x202a: - case 0x202d ... 0x2034: - case 0x2200 ... 0x2204: - case 0x2206 ... 0x2212: - case 0x2230 ... 0x2239: - case 0x2f5b: - case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): - return true; - default: - return false; - } -} - -static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x201b: - case 0x201c: - case 0x201d: - case 0x201f: - case 0x2021: - case 0x2023: - case 0x2230: - case 0x202d ... 0x202f: /* BRA */ - case 0x2200 ... 0x2212: /* i2c debug */ - case 0x2f07: - case 0x2f1b ... 0x2f1e: - case 0x2f30 ... 0x2f34: - case 0x2f50 ... 0x2f51: - case 0x2f53 ... 0x2f59: - case 0x2f5c ... 0x2f5f: - case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */ - return true; - default: - return false; - } -} - -static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x2000000: - case 0x200002b: - case 0x2000036: - case 0x2000037: - case 0x2000039: - case 0x6100000: - return true; - default: - return false; - } -} - -static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case 0x2000000: - return true; - default: - return false; - } -} - -static const struct regmap_config rt715_sdca_regmap = { - .reg_bits = 32, - .val_bits = 8, - .readable_reg = rt715_sdca_readable_register, - .volatile_reg = rt715_sdca_volatile_register, - .max_register = 0x43ffffff, - .reg_defaults = rt715_reg_defaults_sdca, - .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, - .use_single_read = true, - .use_single_write = true, -}; - -static const struct regmap_config rt715_sdca_mbq_regmap = { - .name = "sdw-mbq", - .reg_bits = 32, - .val_bits = 16, - .readable_reg = rt715_sdca_mbq_readable_register, - .volatile_reg = rt715_sdca_mbq_volatile_register, - .max_register = 0x43ffffff, - .reg_defaults = rt715_mbq_reg_defaults_sdca, - .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, - .use_single_read = true, - .use_single_write = true, -}; - -static int rt715_update_status(struct sdw_slave *slave, - enum sdw_slave_status status) -{ - struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); - - /* Update the status */ - rt715->status = status; - - /* - * Perform initialization only if slave status is present and - * hw_init flag is false - */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) - return 0; - - /* perform I/O transfers required for Slave initialization */ - return rt715_io_init(&slave->dev, slave); -} - -static int rt715_read_prop(struct sdw_slave *slave) -{ - struct sdw_slave_prop *prop = &slave->prop; - int nval, i; - u32 bit; - unsigned long addr; - struct sdw_dpn_prop *dpn; - - prop->paging_support = true; - - /* first we need to allocate memory for set bits in port lists */ - prop->source_ports = 0x50;/* BITMAP: 01010000 */ - prop->sink_ports = 0x0; /* BITMAP: 00000000 */ - - nval = hweight32(prop->source_ports); - prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, - sizeof(*prop->src_dpn_prop), - GFP_KERNEL); - if (!prop->src_dpn_prop) - return -ENOMEM; - - dpn = prop->src_dpn_prop; - i = 0; - addr = prop->source_ports; - for_each_set_bit(bit, &addr, 32) { - dpn[i].num = bit; - dpn[i].simple_ch_prep_sm = true; - dpn[i].ch_prep_timeout = 10; - i++; - } - - /* set the timeout values */ - prop->clk_stop_timeout = 20; - - return 0; -} - -static struct sdw_slave_ops rt715_sdca_slave_ops = { - .read_prop = rt715_read_prop, - .update_status = rt715_update_status, -}; - -static int rt715_sdca_sdw_probe(struct sdw_slave *slave, - const struct sdw_device_id *id) -{ - struct regmap *mbq_regmap, *regmap; - - slave->ops = &rt715_sdca_slave_ops; - - /* Regmap Initialization */ - mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap); - if (!mbq_regmap) - return -EINVAL; - - regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap); - if (!regmap) - return -EINVAL; - - return rt715_init(&slave->dev, mbq_regmap, regmap, slave); -} - -static const struct sdw_device_id rt715_sdca_id[] = { - SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0), - SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0), - {}, -}; -MODULE_DEVICE_TABLE(sdw, rt715_sdca_id); - -static int __maybe_unused rt715_dev_suspend(struct device *dev) -{ - struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); - - if (!rt715->hw_init) - return 0; - - regcache_cache_only(rt715->regmap, true); - regcache_mark_dirty(rt715->regmap); - regcache_cache_only(rt715->mbq_regmap, true); - regcache_mark_dirty(rt715->mbq_regmap); - - return 0; -} - -#define RT715_PROBE_TIMEOUT 2000 - -static int __maybe_unused rt715_dev_resume(struct device *dev) -{ - struct sdw_slave *slave = dev_to_sdw_dev(dev); - struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); - unsigned long time; - - if (!rt715->hw_init) - return 0; - - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->enumeration_complete, - msecs_to_jiffies(RT715_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Enumeration not complete, timed out\n"); - return -ETIMEDOUT; - } - -regmap_sync: - slave->unattach_request = 0; - regcache_cache_only(rt715->regmap, false); - regcache_sync_region(rt715->regmap, - SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, - CH_00), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); - regcache_cache_only(rt715->mbq_regmap, false); - regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff); - regcache_sync_region(rt715->mbq_regmap, - SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, - CH_00), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00)); - - return 0; -} - -static const struct dev_pm_ops rt715_pm = { - SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) - SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) -}; - -static struct sdw_driver rt715_sdw_driver = { - .driver = { - .name = "rt715-sdca", - .owner = THIS_MODULE, - .pm = &rt715_pm, - }, - .probe = rt715_sdca_sdw_probe, - .ops = &rt715_sdca_slave_ops, - .id_table = rt715_sdca_id, -}; -module_sdw_driver(rt715_sdw_driver); - -MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA"); -MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h deleted file mode 100644 index cd365bb60747..000000000000 --- a/sound/soc/codecs/rt715-sdca-sdw.h +++ /dev/null @@ -1,170 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header - * - * Copyright(c) 2020 Realtek Semiconductor Corp. - */ - -#ifndef __RT715_SDW_SDCA_H__ -#define __RT715_SDW_SDCA_H__ - -#include <linux/soundwire/sdw_registers.h> - -static const struct reg_default rt715_reg_defaults_sdca[] = { - { 0x201a, 0x00 }, - { 0x201e, 0x00 }, - { 0x2020, 0x00 }, - { 0x2021, 0x00 }, - { 0x2022, 0x00 }, - { 0x2023, 0x00 }, - { 0x2024, 0x00 }, - { 0x2025, 0x01 }, - { 0x2026, 0x00 }, - { 0x2027, 0x00 }, - { 0x2029, 0x00 }, - { 0x202a, 0x00 }, - { 0x202d, 0x00 }, - { 0x202e, 0x00 }, - { 0x202f, 0x00 }, - { 0x2030, 0x00 }, - { 0x2031, 0x00 }, - { 0x2032, 0x00 }, - { 0x2033, 0x00 }, - { 0x2034, 0x00 }, - { 0x2230, 0x00 }, - { 0x2231, 0x2f }, - { 0x2232, 0x80 }, - { 0x2233, 0x00 }, - { 0x2234, 0x00 }, - { 0x2235, 0x00 }, - { 0x2236, 0x00 }, - { 0x2237, 0x00 }, - { 0x2238, 0x00 }, - { 0x2239, 0x00 }, - { 0x2f01, 0x00 }, - { 0x2f02, 0x09 }, - { 0x2f03, 0x0b }, - { 0x2f04, 0x00 }, - { 0x2f05, 0x0e }, - { 0x2f06, 0x01 }, - { 0x2f08, 0x00 }, - { 0x2f09, 0x00 }, - { 0x2f0a, 0x00 }, - { 0x2f0b, 0x00 }, - { 0x2f0c, 0x00 }, - { 0x2f0d, 0x00 }, - { 0x2f0e, 0x12 }, - { 0x2f0f, 0x00 }, - { 0x2f10, 0x00 }, - { 0x2f11, 0x00 }, - { 0x2f12, 0x00 }, - { 0x2f13, 0x00 }, - { 0x2f14, 0x00 }, - { 0x2f15, 0x00 }, - { 0x2f16, 0x00 }, - { 0x2f17, 0x00 }, - { 0x2f18, 0x00 }, - { 0x2f19, 0x03 }, - { 0x2f1a, 0x00 }, - { 0x2f1f, 0x10 }, - { 0x2f20, 0x00 }, - { 0x2f21, 0x00 }, - { 0x2f22, 0x00 }, - { 0x2f23, 0x00 }, - { 0x2f24, 0x00 }, - { 0x2f25, 0x00 }, - { 0x2f52, 0x01 }, - { 0x2f5a, 0x02 }, - { 0x2f5b, 0x05 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, - RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 }, -}; - -static const struct reg_default rt715_mbq_reg_defaults_sdca[] = { - { 0x200002b, 0x0420 }, - { 0x2000036, 0x0000 }, - { 0x2000037, 0x0000 }, - { 0x2000039, 0xaa81 }, - { 0x6100000, 0x0100 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 }, - { SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 }, -}; -#endif /* __RT715_SDW_SDCA_H__ */ diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c deleted file mode 100644 index b843e47eb25b..000000000000 --- a/sound/soc/codecs/rt715-sdca.c +++ /dev/null @@ -1,936 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// -// rt715-sdca.c -- rt715 ALSA SoC audio driver -// -// Copyright(c) 2020 Realtek Semiconductor Corp. -// -// -// - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/version.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/pm_runtime.h> -#include <linux/pm.h> -#include <linux/soundwire/sdw.h> -#include <linux/regmap.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> -#include <sound/initval.h> -#include <sound/tlv.h> -#include <linux/soundwire/sdw_registers.h> - -#include "rt715-sdca.h" - -static int rt715_index_write(struct rt715_sdca_priv *rt715, unsigned int nid, - unsigned int reg, unsigned int value) -{ - struct regmap *regmap = rt715->mbq_regmap; - unsigned int addr; - int ret; - - addr = (nid << 20) | reg; - - ret = regmap_write(regmap, addr, value); - if (ret < 0) - dev_err(&rt715->slave->dev, - "Failed to set private value: %08x <= %04x %d\n", ret, addr, - value); - - return ret; -} - -static int rt715_index_read(struct rt715_sdca_priv *rt715, - unsigned int nid, unsigned int reg, unsigned int *value) -{ - struct regmap *regmap = rt715->mbq_regmap; - unsigned int addr; - int ret; - - addr = (nid << 20) | reg; - - ret = regmap_read(regmap, addr, value); - if (ret < 0) - dev_err(&rt715->slave->dev, - "Failed to get private value: %06x => %04x ret=%d\n", - addr, *value, ret); - - return ret; -} - -static int rt715_index_update_bits(struct rt715_sdca_priv *rt715, - unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) -{ - unsigned int tmp; - int ret; - - ret = rt715_index_read(rt715, nid, reg, &tmp); - if (ret < 0) - return ret; - - set_mask_bits(&tmp, mask, val); - - return rt715_index_write(rt715, nid, reg, tmp); -} - -/* SDCA Volume/Boost control */ -static int rt715_set_amp_gain_put_sdca(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - unsigned int val_l, val_r, gain_l_val, gain_r_val; - int ret; - - /* control value to 2s complement */ - /* L channel */ - gain_l_val = ucontrol->value.integer.value[0]; - if (gain_l_val > mc->max) - gain_l_val = mc->max; - val_l = gain_l_val; - - if (mc->shift == 8) { - gain_l_val = (gain_l_val * 10) << mc->shift; - } else { - gain_l_val = - ((abs(gain_l_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000; - if (val_l <= mc->shift) { - gain_l_val = ~gain_l_val; - gain_l_val += 1; - } - gain_l_val &= 0xffff; - } - - /* R channel */ - gain_r_val = ucontrol->value.integer.value[1]; - if (gain_r_val > mc->max) - gain_r_val = mc->max; - val_r = gain_r_val; - - if (mc->shift == 8) { - gain_r_val = (gain_r_val * 10) << mc->shift; - } else { - gain_r_val = - ((abs(gain_r_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000; - if (val_r <= mc->shift) { - gain_r_val = ~gain_r_val; - gain_r_val += 1; - } - gain_r_val &= 0xffff; - } - - /* Lch*/ - ret = regmap_write(rt715->mbq_regmap, mc->reg, gain_l_val); - if (ret != 0) { - dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->reg, - gain_l_val); - return ret; - } - /* Rch */ - ret = regmap_write(rt715->mbq_regmap, mc->rreg, gain_r_val); - if (ret != 0) { - dev_err(component->dev, "Failed to write 0x%x=0x%x\n", mc->rreg, - gain_r_val); - return ret; - } - - return 0; -} - -static int rt715_set_amp_gain_get_sdca(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - unsigned int val_l, val_r, ctl_l, ctl_r, neg_flag = 0; - int ret; - - ret = regmap_read(rt715->mbq_regmap, mc->reg, &val_l); - if (ret < 0) - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->reg, ret); - ret = regmap_read(rt715->mbq_regmap, mc->rreg, &val_r); - if (ret < 0) - dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->rreg, - ret); - - /* L channel */ - if (mc->shift == 8) { - ctl_l = (val_l >> mc->shift) / 10; - } else { - ctl_l = val_l; - if (ctl_l & BIT(15)) { - ctl_l = ~(val_l - 1) & 0xffff; - neg_flag = 1; - } - ctl_l *= 1000; - ctl_l >>= 8; - if (neg_flag) - ctl_l = mc->shift - ctl_l / RT715_SDCA_DB_STEP; - else - ctl_l = mc->shift + ctl_l / RT715_SDCA_DB_STEP; - } - - neg_flag = 0; - /* R channel */ - if (mc->shift == 8) { - ctl_r = (val_r >> mc->shift) / 10; - } else { - ctl_r = val_r; - if (ctl_r & BIT(15)) { - ctl_r = ~(val_r - 1) & 0xffff; - neg_flag = 1; - } - ctl_r *= 1000; - ctl_r >>= 8; - if (neg_flag) - ctl_r = mc->shift - ctl_r / RT715_SDCA_DB_STEP; - else - ctl_r = mc->shift + ctl_r / RT715_SDCA_DB_STEP; - } - - ucontrol->value.integer.value[0] = ctl_l; - ucontrol->value.integer.value[1] = ctl_r; - - return 0; -} - -static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0); -static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0); - -#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .info = snd_soc_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \ - xmax, xinvert) } - -static const struct snd_kcontrol_new rt715_snd_controls_sdca[] = { - /* Capture switch */ - SOC_DOUBLE_R("FU0A Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), - 0, 1, 1), - SOC_DOUBLE_R("FU02 1_2 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), - 0, 1, 1), - SOC_DOUBLE_R("FU02 3_4 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), - 0, 1, 1), - SOC_DOUBLE_R("FU06 1_2 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_02), - 0, 1, 1), - SOC_DOUBLE_R("FU06 3_4 Capture Switch", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_MUTE_CTRL, CH_04), - 0, 1, 1), - /* Volume Control */ - SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), - 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU02 1_2 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, CH_02), - 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU02 3_4 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_04), 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU06 1_2 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_02), 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU06 3_4 Capture Volume", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL, - RT715_SDCA_FU_VOL_CTRL, - CH_04), 0x2f, 0x7f, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - in_vol_tlv), - /* MIC Boost Control */ - SOC_DOUBLE_R_EXT_TLV("FU0E 1_2 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_02), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0E 3_4 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_04), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0E 5_6 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_05), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_06), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0E 7_8 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_07), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_08), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 1_2 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_01), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_02), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 3_4 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_03), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_04), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 5_6 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_05), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_06), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), - SOC_DOUBLE_R_EXT_TLV("FU0C 7_8 Boost", - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_07), - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN, - RT715_SDCA_FU_DMIC_GAIN_CTRL, - CH_08), 8, 3, 0, - rt715_set_amp_gain_get_sdca, rt715_set_amp_gain_put_sdca, - mic_vol_tlv), -}; - -static int rt715_mux_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - unsigned int val, mask_sft; - - if (strstr(ucontrol->id.name, "ADC 22 Mux")) - mask_sft = 12; - else if (strstr(ucontrol->id.name, "ADC 23 Mux")) - mask_sft = 8; - else if (strstr(ucontrol->id.name, "ADC 24 Mux")) - mask_sft = 4; - else if (strstr(ucontrol->id.name, "ADC 25 Mux")) - mask_sft = 0; - else - return -EINVAL; - - rt715_index_read(rt715, RT715_VENDOR_HDA_CTL, - RT715_HDA_LEGACY_MUX_CTL1, &val); - val = (val >> mask_sft) & 0xf; - - /* - * The first two indices of ADC Mux 24/25 are routed to the same - * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2. - * To have a unique set of inputs, we skip the index1 of the muxes. - */ - if ((strstr(ucontrol->id.name, "ADC 24 Mux") || - strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0) - val -= 1; - ucontrol->value.enumerated.item[0] = val; - - return 0; -} - -static int rt715_mux_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = - snd_soc_dapm_kcontrol_component(kcontrol); - struct snd_soc_dapm_context *dapm = - snd_soc_dapm_kcontrol_dapm(kcontrol); - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; - unsigned int *item = ucontrol->value.enumerated.item; - unsigned int val, val2 = 0, change, mask_sft; - - if (item[0] >= e->items) - return -EINVAL; - - if (strstr(ucontrol->id.name, "ADC 22 Mux")) - mask_sft = 12; - else if (strstr(ucontrol->id.name, "ADC 23 Mux")) - mask_sft = 8; - else if (strstr(ucontrol->id.name, "ADC 24 Mux")) - mask_sft = 4; - else if (strstr(ucontrol->id.name, "ADC 25 Mux")) - mask_sft = 0; - else - return -EINVAL; - - /* Verb ID = 0x701h, nid = e->reg */ - val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; - - rt715_index_read(rt715, RT715_VENDOR_HDA_CTL, - RT715_HDA_LEGACY_MUX_CTL1, &val2); - val2 = (val2 >> mask_sft) & 0xf; - - change = val != val2; - - if (change) - rt715_index_update_bits(rt715, RT715_VENDOR_HDA_CTL, - RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft); - - snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL); - - return change; -} - -static const char * const adc_22_23_mux_text[] = { - "MIC1", - "MIC2", - "LINE1", - "LINE2", - "DMIC1", - "DMIC2", - "DMIC3", - "DMIC4", -}; - -/* - * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and - * 1 will be connected to the same dmic source, therefore we skip index 1 to - * avoid misunderstanding on usage of dapm routing. - */ -static int rt715_adc_24_25_values[] = { - 0, - 2, - 3, - 4, - 5, -}; - -static const char * const adc_24_mux_text[] = { - "MIC2", - "DMIC1", - "DMIC2", - "DMIC3", - "DMIC4", -}; - -static const char * const adc_25_mux_text[] = { - "MIC1", - "DMIC1", - "DMIC2", - "DMIC3", - "DMIC4", -}; - -static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0, - adc_22_23_mux_text); - -static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0, - adc_22_23_mux_text); - -static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum, - SND_SOC_NOPM, 0, 0xf, - adc_24_mux_text, rt715_adc_24_25_values); -static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum, - SND_SOC_NOPM, 0, 0xf, - adc_25_mux_text, rt715_adc_24_25_values); - -static const struct snd_kcontrol_new rt715_adc22_mux = - SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum, - rt715_mux_get, rt715_mux_put); - -static const struct snd_kcontrol_new rt715_adc23_mux = - SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum, - rt715_mux_get, rt715_mux_put); - -static const struct snd_kcontrol_new rt715_adc24_mux = - SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum, - rt715_mux_get, rt715_mux_put); - -static const struct snd_kcontrol_new rt715_adc25_mux = - SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum, - rt715_mux_get, rt715_mux_put); - -static int rt715_pde23_24_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_component *component = - snd_soc_dapm_to_component(w->dapm); - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - - switch (event) { - case SND_SOC_DAPM_POST_PMU: - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, - RT715_SDCA_REQ_POW_CTRL, - CH_00), 0x00); - break; - case SND_SOC_DAPM_PRE_PMD: - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN, - RT715_SDCA_REQ_POW_CTRL, - CH_00), 0x03); - break; - } - return 0; -} - -static const struct snd_soc_dapm_widget rt715_dapm_widgets[] = { - SND_SOC_DAPM_INPUT("DMIC1"), - SND_SOC_DAPM_INPUT("DMIC2"), - SND_SOC_DAPM_INPUT("DMIC3"), - SND_SOC_DAPM_INPUT("DMIC4"), - SND_SOC_DAPM_INPUT("MIC1"), - SND_SOC_DAPM_INPUT("MIC2"), - SND_SOC_DAPM_INPUT("LINE1"), - SND_SOC_DAPM_INPUT("LINE2"), - - SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0, - rt715_pde23_24_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - - SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0), - SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc22_mux), - SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc23_mux), - SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc24_mux), - SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, - &rt715_adc25_mux), - SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0), -}; - -static const struct snd_soc_dapm_route rt715_audio_map[] = { - {"DP6TX", NULL, "ADC 09"}, - {"DP6TX", NULL, "ADC 08"}, - {"DP4TX", NULL, "ADC 07"}, - {"DP4TX", NULL, "ADC 27"}, - {"DP4TX", NULL, "ADC 09"}, - {"DP4TX", NULL, "ADC 08"}, - - {"LINE1", NULL, "PDE23_24"}, - {"LINE2", NULL, "PDE23_24"}, - {"MIC1", NULL, "PDE23_24"}, - {"MIC2", NULL, "PDE23_24"}, - {"DMIC1", NULL, "PDE23_24"}, - {"DMIC2", NULL, "PDE23_24"}, - {"DMIC3", NULL, "PDE23_24"}, - {"DMIC4", NULL, "PDE23_24"}, - - {"ADC 09", NULL, "ADC 22 Mux"}, - {"ADC 08", NULL, "ADC 23 Mux"}, - {"ADC 07", NULL, "ADC 24 Mux"}, - {"ADC 27", NULL, "ADC 25 Mux"}, - {"ADC 22 Mux", "MIC1", "MIC1"}, - {"ADC 22 Mux", "MIC2", "MIC2"}, - {"ADC 22 Mux", "LINE1", "LINE1"}, - {"ADC 22 Mux", "LINE2", "LINE2"}, - {"ADC 22 Mux", "DMIC1", "DMIC1"}, - {"ADC 22 Mux", "DMIC2", "DMIC2"}, - {"ADC 22 Mux", "DMIC3", "DMIC3"}, - {"ADC 22 Mux", "DMIC4", "DMIC4"}, - {"ADC 23 Mux", "MIC1", "MIC1"}, - {"ADC 23 Mux", "MIC2", "MIC2"}, - {"ADC 23 Mux", "LINE1", "LINE1"}, - {"ADC 23 Mux", "LINE2", "LINE2"}, - {"ADC 23 Mux", "DMIC1", "DMIC1"}, - {"ADC 23 Mux", "DMIC2", "DMIC2"}, - {"ADC 23 Mux", "DMIC3", "DMIC3"}, - {"ADC 23 Mux", "DMIC4", "DMIC4"}, - {"ADC 24 Mux", "MIC2", "MIC2"}, - {"ADC 24 Mux", "DMIC1", "DMIC1"}, - {"ADC 24 Mux", "DMIC2", "DMIC2"}, - {"ADC 24 Mux", "DMIC3", "DMIC3"}, - {"ADC 24 Mux", "DMIC4", "DMIC4"}, - {"ADC 25 Mux", "MIC1", "MIC1"}, - {"ADC 25 Mux", "DMIC1", "DMIC1"}, - {"ADC 25 Mux", "DMIC2", "DMIC2"}, - {"ADC 25 Mux", "DMIC3", "DMIC3"}, - {"ADC 25 Mux", "DMIC4", "DMIC4"}, -}; - -static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = { - .controls = rt715_snd_controls_sdca, - .num_controls = ARRAY_SIZE(rt715_snd_controls_sdca), - .dapm_widgets = rt715_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(rt715_dapm_widgets), - .dapm_routes = rt715_audio_map, - .num_dapm_routes = ARRAY_SIZE(rt715_audio_map), -}; - -static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, - int direction) -{ - struct rt715_sdw_stream_data *stream; - - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (!stream) - return -ENOMEM; - - stream->sdw_stream = sdw_stream; - - /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; - - return 0; -} - -static void rt715_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) - -{ - struct rt715_sdw_stream_data *stream; - - stream = snd_soc_dai_get_dma_data(dai, substream); - if (!stream) - return; - - snd_soc_dai_set_dma_data(dai, substream, NULL); - kfree(stream); -} - -static int rt715_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - struct sdw_stream_config stream_config; - struct sdw_port_config port_config; - enum sdw_data_direction direction; - struct rt715_sdw_stream_data *stream; - int retval, port, num_channels; - unsigned int val; - - stream = snd_soc_dai_get_dma_data(dai, substream); - - if (!stream) - return -EINVAL; - - if (!rt715->slave) - return -EINVAL; - - switch (dai->id) { - case RT715_AIF1: - direction = SDW_DATA_DIR_TX; - port = 6; - rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, - 0xa500); - break; - case RT715_AIF2: - direction = SDW_DATA_DIR_TX; - port = 4; - rt715_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL, - 0xaf00); - break; - default: - dev_err(component->dev, "Invalid DAI id %d\n", dai->id); - return -EINVAL; - } - - stream_config.frame_rate = params_rate(params); - stream_config.ch_count = params_channels(params); - stream_config.bps = snd_pcm_format_width(params_format(params)); - stream_config.direction = direction; - - num_channels = params_channels(params); - port_config.ch_mask = GENMASK(num_channels - 1, 0); - port_config.num = port; - - retval = sdw_stream_add_slave(rt715->slave, &stream_config, - &port_config, 1, stream->sdw_stream); - if (retval) { - dev_err(component->dev, "Unable to configure port, retval:%d\n", - retval); - return retval; - } - - switch (params_rate(params)) { - case 8000: - val = 0x1; - break; - case 11025: - val = 0x2; - break; - case 12000: - val = 0x3; - break; - case 16000: - val = 0x4; - break; - case 22050: - val = 0x5; - break; - case 24000: - val = 0x6; - break; - case 32000: - val = 0x7; - break; - case 44100: - val = 0x8; - break; - case 48000: - val = 0x9; - break; - case 88200: - val = 0xa; - break; - case 96000: - val = 0xb; - break; - case 176400: - val = 0xc; - break; - case 192000: - val = 0xd; - break; - case 384000: - val = 0xe; - break; - case 768000: - val = 0xf; - break; - default: - dev_err(component->dev, "Unsupported sample rate %d\n", - params_rate(params)); - return -EINVAL; - } - - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN, - RT715_SDCA_FREQ_IND_CTRL, CH_00), val); - - return 0; -} - -static int rt715_pcm_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component); - struct rt715_sdw_stream_data *stream = - snd_soc_dai_get_dma_data(dai, substream); - - if (!rt715->slave) - return -EINVAL; - - sdw_stream_remove_slave(rt715->slave, stream->sdw_stream); - return 0; -} - -#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) -#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8) - -static struct snd_soc_dai_ops rt715_ops = { - .hw_params = rt715_pcm_hw_params, - .hw_free = rt715_pcm_hw_free, - .set_sdw_stream = rt715_set_sdw_stream, - .shutdown = rt715_shutdown, -}; - -static struct snd_soc_dai_driver rt715_dai[] = { - { - .name = "rt715-aif1", - .id = RT715_AIF1, - .capture = { - .stream_name = "DP6 Capture", - .channels_min = 1, - .channels_max = 2, - .rates = RT715_STEREO_RATES, - .formats = RT715_FORMATS, - }, - .ops = &rt715_ops, - }, - { - .name = "rt715-aif2", - .id = RT715_AIF2, - .capture = { - .stream_name = "DP4 Capture", - .channels_min = 1, - .channels_max = 2, - .rates = RT715_STEREO_RATES, - .formats = RT715_FORMATS, - }, - .ops = &rt715_ops, - }, -}; - -/* Bus clock frequency */ -#define RT715_CLK_FREQ_9600000HZ 9600000 -#define RT715_CLK_FREQ_12000000HZ 12000000 -#define RT715_CLK_FREQ_6000000HZ 6000000 -#define RT715_CLK_FREQ_4800000HZ 4800000 -#define RT715_CLK_FREQ_2400000HZ 2400000 -#define RT715_CLK_FREQ_12288000HZ 12288000 - -int rt715_init(struct device *dev, struct regmap *mbq_regmap, - struct regmap *regmap, struct sdw_slave *slave) -{ - struct rt715_sdca_priv *rt715; - int ret; - - rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL); - if (!rt715) - return -ENOMEM; - - dev_set_drvdata(dev, rt715); - rt715->slave = slave; - rt715->regmap = regmap; - rt715->mbq_regmap = mbq_regmap; - rt715->hw_sdw_ver = slave->id.sdw_version; - /* - * Mark hw_init to false - * HW init will be performed when device reports present - */ - rt715->hw_init = false; - rt715->first_init = false; - - ret = devm_snd_soc_register_component(dev, - &soc_codec_dev_rt715_sdca, - rt715_dai, - ARRAY_SIZE(rt715_dai)); - - return ret; -} - -int rt715_io_init(struct device *dev, struct sdw_slave *slave) -{ - struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); - unsigned int hw_ver; - - if (rt715->hw_init) - return 0; - - /* - * PM runtime is only enabled when a Slave reports as Attached - */ - if (!rt715->first_init) { - /* set autosuspend parameters */ - pm_runtime_set_autosuspend_delay(&slave->dev, 3000); - pm_runtime_use_autosuspend(&slave->dev); - - /* update count of parent 'active' children */ - pm_runtime_set_active(&slave->dev); - - /* make sure the device does not suspend immediately */ - pm_runtime_mark_last_busy(&slave->dev); - - pm_runtime_enable(&slave->dev); - - rt715->first_init = true; - } - - pm_runtime_get_noresume(&slave->dev); - - rt715_index_read(rt715, RT715_VENDOR_REG, - RT715_PRODUCT_NUM, &hw_ver); - hw_ver = hw_ver & 0x000f; - - /* set clock selector = external */ - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN, - RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1); - /* set GPIO_4/5/6 to be 3rd/4th DMIC usage */ - if (hw_ver == 0x0) - rt715_index_update_bits(rt715, RT715_VENDOR_REG, - RT715_AD_FUNC_EN, 0x54, 0x54); - else if (hw_ver == 0x1) { - rt715_index_update_bits(rt715, RT715_VENDOR_REG, - RT715_AD_FUNC_EN, 0x55, 0x55); - rt715_index_update_bits(rt715, RT715_VENDOR_REG, - RT715_REV_1, 0x40, 0x40); - } - /* trigger mode = VAD enable */ - regmap_write(rt715->regmap, - SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN, - RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2); - /* SMPU-1 interrupt enable mask */ - regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1); - - /* Mark Slave initialization complete */ - rt715->hw_init = true; - - pm_runtime_mark_last_busy(&slave->dev); - pm_runtime_put_autosuspend(&slave->dev); - - return 0; -} - -MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA"); -MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h deleted file mode 100644 index 6326cd8c374e..000000000000 --- a/sound/soc/codecs/rt715-sdca.h +++ /dev/null @@ -1,124 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * rt715-sdca.h -- RT715 ALSA SoC audio driver header - * - * Copyright(c) 2020 Realtek Semiconductor Corp. - */ - -#ifndef __RT715_SDCA_H__ -#define __RT715_SDCA_H__ - -#include <linux/regmap.h> -#include <linux/soundwire/sdw.h> -#include <linux/soundwire/sdw_type.h> -#include <sound/soc.h> -#include <linux/workqueue.h> -#include <linux/device.h> - -struct rt715_sdca_priv { - struct regmap *regmap; - struct regmap *mbq_regmap; - struct snd_soc_codec *codec; - struct sdw_slave *slave; - struct delayed_work adc_mute_work; - int dbg_nid; - int dbg_vid; - int dbg_payload; - enum sdw_slave_status status; - struct sdw_bus_params params; - bool hw_init; - bool first_init; - int l_is_unmute; - int r_is_unmute; - int hw_sdw_ver; -}; - -struct rt715_sdw_stream_data { - struct sdw_stream_runtime *sdw_stream; -}; - -/* MIPI Register */ -#define RT715_INT_CTRL 0x005a -#define RT715_INT_MASK 0x005e - -/* NID */ -#define RT715_AUDIO_FUNCTION_GROUP 0x01 -#define RT715_MIC_ADC 0x07 -#define RT715_LINE_ADC 0x08 -#define RT715_MIX_ADC 0x09 -#define RT715_DMIC1 0x12 -#define RT715_DMIC2 0x13 -#define RT715_MIC1 0x18 -#define RT715_MIC2 0x19 -#define RT715_LINE1 0x1a -#define RT715_LINE2 0x1b -#define RT715_DMIC3 0x1d -#define RT715_DMIC4 0x29 -#define RT715_VENDOR_REG 0x20 -#define RT715_MUX_IN1 0x22 -#define RT715_MUX_IN2 0x23 -#define RT715_MUX_IN3 0x24 -#define RT715_MUX_IN4 0x25 -#define RT715_MIX_ADC2 0x27 -#define RT715_INLINE_CMD 0x55 -#define RT715_VENDOR_HDA_CTL 0x61 - -/* Index (NID:20h) */ -#define RT715_PRODUCT_NUM 0x0 -#define RT715_IRQ_CTRL 0x2b -#define RT715_AD_FUNC_EN 0x36 -#define RT715_REV_1 0x37 -#define RT715_SDW_INPUT_SEL 0x39 -#define RT715_EXT_DMIC_CLK_CTRL2 0x54 - -/* Index (NID:61h) */ -#define RT715_HDA_LEGACY_MUX_CTL1 0x00 - -/* SDCA (Function) */ -#define FUN_JACK_CODEC 0x01 -#define FUN_MIC_ARRAY 0x02 -#define FUN_HID 0x03 -/* SDCA (Entity) */ -#define RT715_SDCA_ST_EN 0x00 -#define RT715_SDCA_CS_FREQ_IND_EN 0x01 -#define RT715_SDCA_FU_ADC8_9_VOL 0x02 -#define RT715_SDCA_SMPU_TRIG_ST_EN 0x05 -#define RT715_SDCA_FU_ADC10_11_VOL 0x06 -#define RT715_SDCA_FU_ADC7_27_VOL 0x0a -#define RT715_SDCA_FU_AMIC_GAIN_EN 0x0c -#define RT715_SDCA_FU_DMIC_GAIN_EN 0x0e -#define RT715_SDCA_CX_CLK_SEL_EN 0x10 -#define RT715_SDCA_CREQ_POW_EN 0x18 -/* SDCA (Control) */ -#define RT715_SDCA_ST_CTRL 0x00 -#define RT715_SDCA_CX_CLK_SEL_CTRL 0x01 -#define RT715_SDCA_REQ_POW_CTRL 0x01 -#define RT715_SDCA_FU_MUTE_CTRL 0x01 -#define RT715_SDCA_FU_VOL_CTRL 0x02 -#define RT715_SDCA_FU_DMIC_GAIN_CTRL 0x0b -#define RT715_SDCA_FREQ_IND_CTRL 0x10 -#define RT715_SDCA_SMPU_TRIG_EN_CTRL 0x10 -#define RT715_SDCA_SMPU_TRIG_ST_CTRL 0x11 -/* SDCA (Channel) */ -#define CH_00 0x00 -#define CH_01 0x01 -#define CH_02 0x02 -#define CH_03 0x03 -#define CH_04 0x04 -#define CH_05 0x05 -#define CH_06 0x06 -#define CH_07 0x07 -#define CH_08 0x08 - -#define RT715_SDCA_DB_STEP 375 - -enum { - RT715_AIF1, - RT715_AIF2, -}; - -int rt715_io_init(struct device *dev, struct sdw_slave *slave); -int rt715_init(struct device *dev, struct regmap *mbq_regmap, - struct regmap *regmap, struct sdw_slave *slave); - -#endif /* __RT715_SDCA_H__ */ diff --git a/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c index 1be82113c59a..817d8259758c 100644 --- a/sound/soc/codecs/wcd-clsh-v2.c +++ b/sound/soc/codecs/wcd-clsh-v2.c @@ -480,7 +480,6 @@ static int _wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, int req_state, case WCD_CLSH_STATE_HPHR: wcd_clsh_state_hph_r(ctrl, req_state, is_enable, mode); break; - break; case WCD_CLSH_STATE_LO: wcd_clsh_state_lo(ctrl, req_state, is_enable, mode); break; diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c index c56b9329240f..d8ced4559bf2 100644 --- a/sound/soc/codecs/wl1273.c +++ b/sound/soc/codecs/wl1273.c @@ -311,7 +311,6 @@ static int wl1273_startup(struct snd_pcm_substream *substream, break; default: return -EINVAL; - break; } return 0; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index e61d00486c65..dec8716aa8ef 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -1519,7 +1519,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL); if (!ctl_work) { ret = -ENOMEM; - goto err_ctl_cache; + goto err_list_del; } ctl_work->dsp = dsp; @@ -1529,7 +1529,8 @@ static int wm_adsp_create_control(struct wm_adsp *dsp, return 0; -err_ctl_cache: +err_list_del: + list_del(&ctl->list); kfree(ctl->cache); err_ctl_subname: kfree(ctl->subname); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 6d59dafe1d8f..5520d7c80019 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -423,6 +423,18 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { }, { .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ARCHOS 140 CESIUM"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, + { + .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ME176C"), }, diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index b6e63ea13d64..c2a9757181fe 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -49,11 +49,11 @@ static int max98373_hw_params(struct snd_pcm_substream *substream, for_each_rtd_codec_dais(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, MAX_98373_DEV0_NAME)) { /* DEV0 tdm slot configuration */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 24); + snd_soc_dai_set_tdm_slot(codec_dai, 0x03, 3, 8, 32); } if (!strcmp(codec_dai->component->name, MAX_98373_DEV1_NAME)) { /* DEV1 tdm slot configuration */ - snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 24); + snd_soc_dai_set_tdm_slot(codec_dai, 0x0C, 3, 8, 32); } } return 0; diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 0f53a0d43254..a64a0a77dcb7 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -22,17 +22,6 @@ void catpt_sram_free(struct resource *sram); struct resource * catpt_request_region(struct resource *root, resource_size_t size); -static inline bool catpt_resource_overlapping(struct resource *r1, - struct resource *r2, - struct resource *ret) -{ - if (!resource_overlaps(r1, r2)) - return false; - ret->start = max(r1->start, r2->start); - ret->end = min(r1->end, r2->end); - return true; -} - struct catpt_ipc_msg { union { u32 header; diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 40c22e4bb263..ff7b8f0d34ac 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -267,7 +267,7 @@ static int catpt_restore_fwimage(struct catpt_dev *cdev, r2.start = off; r2.end = r2.start + info->size - 1; - if (!catpt_resource_overlapping(&r2, &r1, &common)) + if (!resource_intersection(&r2, &r1, &common)) continue; /* calculate start offset of common data area */ off = common.start - r1.start; diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index bbe8d782e0af..b1ca64d2f7ea 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -502,7 +502,6 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, if (ret < 0) return ret; return skl_run_pipe(skl, mconfig->pipe); - break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index e8bc7ca5ee5e..0a68f4c3d15a 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -309,10 +309,14 @@ static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, switch (clk_id) { case JZ4740_I2S_CLKSRC_EXT: parent = clk_get(NULL, "ext"); + if (IS_ERR(parent)) + return PTR_ERR(parent); clk_set_parent(i2s->clk_i2s, parent); break; case JZ4740_I2S_CLKSRC_PLL: parent = clk_get(NULL, "pll half"); + if (IS_ERR(parent)) + return PTR_ERR(parent); clk_set_parent(i2s->clk_i2s, parent); ret = clk_set_rate(i2s->clk_i2s, freq); break; diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c index 87e4633afe2c..f0362f061652 100644 --- a/sound/soc/qcom/qdsp6/q6afe-clocks.c +++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c @@ -16,6 +16,7 @@ .afe_clk_id = Q6AFE_##id, \ .name = #id, \ .attributes = LPASS_CLK_ATTRIBUTE_COUPLE_NO, \ + .rate = 19200000, \ .hw.init = &(struct clk_init_data) { \ .ops = &clk_q6afe_ops, \ .name = #id, \ diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index c4031988f981..47fae8dd20b4 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -293,6 +293,16 @@ static int stm32_adfsdm_pcm_new(struct snd_soc_component *component, return 0; } +static int stm32_adfsdm_dummy_cb(const void *data, void *private) +{ + /* + * This dummmy callback is requested by iio_channel_get_all_cb() API, + * but the stm32_dfsdm_get_buff_cb() API is used instead, to optimize + * DMA transfers. + */ + return 0; +} + static struct snd_soc_component_driver stm32_adfsdm_soc_platform = { .open = stm32_adfsdm_pcm_open, .close = stm32_adfsdm_pcm_close, @@ -335,7 +345,7 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) if (IS_ERR(priv->iio_ch)) return PTR_ERR(priv->iio_ch); - priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, NULL, NULL); + priv->iio_cb = iio_channel_get_all_cb(&pdev->dev, &stm32_adfsdm_dummy_cb, NULL); if (IS_ERR(priv->iio_cb)) return PTR_ERR(priv->iio_cb); diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 7eab8351177a..6247ec3d3a09 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2319,7 +2319,6 @@ static int davinci_mcasp_probe(struct platform_device *pdev) dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); case -EPROBE_DEFER: goto err; - break; } if (ret) { diff --git a/sound/usb/Makefile b/sound/usb/Makefile index 56031026b113..9ccb21a4ff8a 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -8,6 +8,7 @@ snd-usb-audio-objs := card.o \ endpoint.o \ format.o \ helper.o \ + implicit.o \ mixer.o \ mixer_quirks.o \ mixer_scarlett.o \ diff --git a/sound/usb/card.c b/sound/usb/card.c index fa764b61fe9c..d731ca62d599 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -49,7 +49,6 @@ #include "quirks.h" #include "endpoint.h" #include "helper.h" -#include "debug.h" #include "pcm.h" #include "format.h" #include "power.h" @@ -73,6 +72,7 @@ static bool ignore_ctl_error; static bool autoclock = true; static char *quirk_alias[SNDRV_CARDS]; static char *delayed_register[SNDRV_CARDS]; +static bool implicit_fb[SNDRV_CARDS]; bool snd_usb_use_vmalloc = true; bool snd_usb_skip_validation; @@ -98,6 +98,8 @@ module_param_array(quirk_alias, charp, NULL, 0444); MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); module_param_array(delayed_register, charp, NULL, 0444); MODULE_PARM_DESC(delayed_register, "Quirk for delayed registration, given by id:iface, e.g. 0123abcd:4."); +module_param_array(implicit_fb, bool, NULL, 0444); +MODULE_PARM_DESC(implicit_fb, "Apply generic implicit feedback sync mode."); module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444); MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes)."); module_param_named(skip_validation, snd_usb_skip_validation, bool, 0444); @@ -125,7 +127,6 @@ static void snd_usb_stream_disconnect(struct snd_usb_stream *as) subs = &as->substream[idx]; if (!subs->num_formats) continue; - subs->interface = -1; subs->data_endpoint = NULL; subs->sync_endpoint = NULL; } @@ -379,6 +380,13 @@ static const struct usb_audio_device_name usb_audio_names[] = { DEVICE_NAME(0x046d, 0x0990, "Logitech, Inc.", "QuickCam Pro 9000"), + /* ASUS ROG Strix */ + PROFILE_NAME(0x0b05, 0x1917, + "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), + /* ASUS PRIME TRX40 PRO-S */ + PROFILE_NAME(0x0b05, 0x1918, + "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), + /* Dell WD15 Dock */ PROFILE_NAME(0x0bda, 0x4014, "Dell", "WD15 Dock", "Dell-WD15-Dock"), /* Dell WD19 Dock */ @@ -594,6 +602,7 @@ static int snd_usb_audio_create(struct usb_interface *intf, chip->dev = dev; chip->card = card; chip->setup = device_setup[idx]; + chip->generic_implicit_fb = implicit_fb[idx]; chip->autoclock = autoclock; atomic_set(&chip->active, 1); /* avoid autopm during probing */ atomic_set(&chip->usage_count, 0); @@ -977,6 +986,7 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) { struct snd_usb_audio *chip = usb_get_intfdata(intf); struct snd_usb_stream *as; + struct snd_usb_endpoint *ep; struct usb_mixer_interface *mixer; struct list_head *p; @@ -984,11 +994,10 @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message) return 0; if (!chip->num_suspended_intf++) { - list_for_each_entry(as, &chip->pcm_list, list) { + list_for_each_entry(as, &chip->pcm_list, list) snd_usb_pcm_suspend(as); - as->substream[0].need_setup_ep = - as->substream[1].need_setup_ep = true; - } + list_for_each_entry(ep, &chip->ep_list, list) + snd_usb_endpoint_suspend(ep); list_for_each(p, &chip->midi_list) snd_usbmidi_suspend(p); list_for_each_entry(mixer, &chip->mixer_list, list) diff --git a/sound/usb/card.h b/sound/usb/card.h index 5351d7183b1b..6a027c349194 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -16,12 +16,17 @@ struct audioformat { unsigned int fmt_type; /* USB audio format type (1-3) */ unsigned int fmt_bits; /* number of significant bits */ unsigned int frame_size; /* samples per frame for non-audio */ - int iface; /* interface number */ + unsigned char iface; /* interface number */ unsigned char altsetting; /* corresponding alternate setting */ unsigned char altset_idx; /* array index of altenate setting */ unsigned char attributes; /* corresponding attributes of cs endpoint */ unsigned char endpoint; /* endpoint */ unsigned char ep_attr; /* endpoint attributes */ + bool implicit_fb; /* implicit feedback endpoint */ + unsigned char sync_ep; /* sync endpoint number */ + unsigned char sync_iface; /* sync EP interface */ + unsigned char sync_altsetting; /* sync EP alternate setting */ + unsigned char sync_ep_idx; /* sync EP array index */ unsigned char datainterval; /* log_2 of data packet interval */ unsigned char protocol; /* UAC_VERSION_1/2/3 */ unsigned int maxpacksize; /* max. packet size */ @@ -54,10 +59,16 @@ struct snd_urb_ctx { struct snd_usb_endpoint { struct snd_usb_audio *chip; - int use_count; + int opened; /* open refcount; protect with chip->mutex */ + atomic_t running; /* running status */ int ep_num; /* the referenced endpoint number */ int type; /* SND_USB_ENDPOINT_TYPE_* */ - unsigned long flags; + + unsigned char iface; /* interface number */ + unsigned char altsetting; /* corresponding alternate setting */ + unsigned char ep_idx; /* endpoint array index */ + + unsigned long flags; /* running bit flags */ void (*prepare_data_urb) (struct snd_usb_substream *subs, struct urb *urb); @@ -65,8 +76,8 @@ struct snd_usb_endpoint { struct urb *urb); struct snd_usb_substream *data_subs; - struct snd_usb_endpoint *sync_master; - struct snd_usb_endpoint *sync_slave; + struct snd_usb_endpoint *sync_source; + struct snd_usb_endpoint *sync_sink; struct snd_urb_ctx urb[MAX_URBS]; @@ -74,8 +85,9 @@ struct snd_usb_endpoint { uint32_t packet_size[MAX_PACKS_HS]; int packets; } next_packet[MAX_URBS]; - int next_packet_read_pos, next_packet_write_pos; - struct list_head ready_playback_urbs; + unsigned int next_packet_head; /* ring buffer offset to read */ + unsigned int next_packet_queued; /* queued items in the ring buffer */ + struct list_head ready_playback_urbs; /* playback URB FIFO for implicit fb */ unsigned int nurbs; /* # urbs */ unsigned long active_mask; /* bitmask of active urbs */ @@ -105,10 +117,20 @@ struct snd_usb_endpoint { unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned char silence_value; unsigned int stride; - int iface, altsetting; int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ - bool is_implicit_feedback; /* This endpoint is used as implicit feedback */ + bool implicit_fb_sync; /* syncs with implicit feedback */ + bool need_setup; /* (re-)need for configure? */ + + /* for hw constraints */ + const struct audioformat *cur_audiofmt; + unsigned int cur_rate; + snd_pcm_format_t cur_format; + unsigned int cur_channels; + unsigned int cur_frame_bytes; + unsigned int cur_period_frames; + unsigned int cur_period_bytes; + unsigned int cur_buffer_periods; spinlock_t lock; struct list_head list; @@ -121,18 +143,10 @@ struct snd_usb_substream { struct usb_device *dev; struct snd_pcm_substream *pcm_substream; int direction; /* playback or capture */ - int interface; /* current interface */ int endpoint; /* assigned endpoint */ - struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */ + const struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */ struct snd_usb_power_domain *str_pd; /* UAC3 Power Domain for streaming path */ - snd_pcm_format_t pcm_format; /* current audio format (for hw_params callback) */ - unsigned int channels; /* current number of channels (for hw_params callback) */ unsigned int channels_max; /* max channels in the all audiofmts */ - unsigned int cur_rate; /* current rate (for hw_params callback) */ - unsigned int period_bytes; /* current period bytes (for hw_params callback) */ - unsigned int period_frames; /* current frames per period */ - unsigned int buffer_periods; /* current periods per buffer */ - unsigned int altset_idx; /* USB data format: index of alternate setting */ unsigned int txfr_quirk:1; /* allow sub-frame alignment */ unsigned int tx_length_quirk:1; /* add length specifier to transfers */ unsigned int fmt_type; /* USB audio format type (1-3) */ @@ -150,14 +164,11 @@ struct snd_usb_substream { struct snd_usb_endpoint *data_endpoint; struct snd_usb_endpoint *sync_endpoint; unsigned long flags; - bool need_setup_ep; /* (re)configure EP at prepare? */ - bool need_setup_fmt; /* (re)configure fmt after resume? */ unsigned int speed; /* USB_SPEED_XXX */ u64 formats; /* format bitmasks (all or'ed) */ unsigned int num_formats; /* number of supported audio formats (list) */ struct list_head fmt_list; /* format list */ - struct snd_pcm_hw_constraint_list rate_list; /* limited rates */ spinlock_t lock; int last_frame_number; /* stored frame number */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index f3ca59005d91..31051f2be46d 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -152,7 +152,7 @@ static int uac_clock_selector_set_val(struct snd_usb_audio *chip, int selector_i } static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, - struct audioformat *fmt, + const struct audioformat *fmt, int source_id) { bool ret = false; @@ -215,7 +215,7 @@ static bool uac_clock_source_is_valid_quirk(struct snd_usb_audio *chip, } static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, - struct audioformat *fmt, + const struct audioformat *fmt, int source_id) { int err; @@ -264,7 +264,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, } static int __uac_clock_find_source(struct snd_usb_audio *chip, - struct audioformat *fmt, int entity_id, + const struct audioformat *fmt, int entity_id, unsigned long *visited, bool validate) { struct uac_clock_source_descriptor *source; @@ -358,7 +358,7 @@ static int __uac_clock_find_source(struct snd_usb_audio *chip, } static int __uac3_clock_find_source(struct snd_usb_audio *chip, - struct audioformat *fmt, int entity_id, + const struct audioformat *fmt, int entity_id, unsigned long *visited, bool validate) { struct uac3_clock_source_descriptor *source; @@ -464,7 +464,7 @@ static int __uac3_clock_find_source(struct snd_usb_audio *chip, * Returns the clock source UnitID (>=0) on success, or an error. */ int snd_usb_clock_find_source(struct snd_usb_audio *chip, - struct audioformat *fmt, bool validate) + const struct audioformat *fmt, bool validate) { DECLARE_BITMAP(visited, 256); memset(visited, 0, sizeof(visited)); @@ -481,15 +481,18 @@ int snd_usb_clock_find_source(struct snd_usb_audio *chip, } } -static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt, int rate) +static int set_sample_rate_v1(struct snd_usb_audio *chip, + const struct audioformat *fmt, int rate) { struct usb_device *dev = chip->dev; + struct usb_host_interface *alts; unsigned int ep; unsigned char data[3]; int err, crate; + alts = snd_usb_get_host_interface(chip, fmt->iface, fmt->altsetting); + if (!alts) + return -EINVAL; if (get_iface_desc(alts)->bNumEndpoints < 1) return -EINVAL; ep = get_endpoint(alts, 0)->bEndpointAddress; @@ -507,7 +510,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, data, sizeof(data)); if (err < 0) { dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n", - iface, fmt->altsetting, rate, ep); + fmt->iface, fmt->altsetting, rate, ep); return err; } @@ -525,12 +528,18 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface, data, sizeof(data)); if (err < 0) { dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n", - iface, fmt->altsetting, ep); + fmt->iface, fmt->altsetting, ep); chip->sample_rate_read_error++; return 0; /* some devices don't support reading */ } crate = data[0] | (data[1] << 8) | (data[2] << 16); + if (!crate) { + dev_info(&dev->dev, "failed to read current rate; disabling the check\n"); + chip->sample_rate_read_error = 3; /* three strikes, see above */ + return 0; + } + if (crate != rate) { dev_warn(&dev->dev, "current rate %d is different from the runtime rate %d\n", crate, rate); // runtime->rate = crate; @@ -560,16 +569,58 @@ static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, return le32_to_cpu(data); } -static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt, int rate) +/* + * Try to set the given sample rate: + * + * Return 0 if the clock source is read-only, the actual rate on success, + * or a negative error code. + * + * This function gets called from format.c to validate each sample rate, too. + * Hence no message is shown upon error + */ +int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, + const struct audioformat *fmt, + int clock, int rate) { - struct usb_device *dev = chip->dev; - __le32 data; - int err, cur_rate, prev_rate; - int clock; bool writeable; u32 bmControls; + __le32 data; + int err; + + if (fmt->protocol == UAC_VERSION_3) { + struct uac3_clock_source_descriptor *cs_desc; + + cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock); + bmControls = le32_to_cpu(cs_desc->bmControls); + } else { + struct uac_clock_source_descriptor *cs_desc; + + cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); + bmControls = cs_desc->bmControls; + } + + writeable = uac_v2v3_control_is_writeable(bmControls, + UAC2_CS_CONTROL_SAM_FREQ); + if (!writeable) + return 0; + + data = cpu_to_le32(rate); + err = snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + UAC2_CS_CONTROL_SAM_FREQ << 8, + snd_usb_ctrl_intf(chip) | (clock << 8), + &data, sizeof(data)); + if (err < 0) + return err; + + return get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock); +} + +static int set_sample_rate_v2v3(struct snd_usb_audio *chip, + const struct audioformat *fmt, int rate) +{ + int cur_rate, prev_rate; + int clock; /* First, try to find a valid clock. This may trigger * automatic clock selection if the current clock is not @@ -588,63 +639,26 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, return clock; } - prev_rate = get_sample_rate_v2v3(chip, iface, fmt->altsetting, clock); + prev_rate = get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock); if (prev_rate == rate) goto validation; - if (fmt->protocol == UAC_VERSION_3) { - struct uac3_clock_source_descriptor *cs_desc; - - cs_desc = snd_usb_find_clock_source_v3(chip->ctrl_intf, clock); - bmControls = le32_to_cpu(cs_desc->bmControls); - } else { - struct uac_clock_source_descriptor *cs_desc; - - cs_desc = snd_usb_find_clock_source(chip->ctrl_intf, clock); - bmControls = cs_desc->bmControls; + cur_rate = snd_usb_set_sample_rate_v2v3(chip, fmt, clock, rate); + if (cur_rate < 0) { + usb_audio_err(chip, + "%d:%d: cannot set freq %d (v2/v3): err %d\n", + fmt->iface, fmt->altsetting, rate, cur_rate); + return cur_rate; } - writeable = uac_v2v3_control_is_writeable(bmControls, - UAC2_CS_CONTROL_SAM_FREQ); - if (writeable) { - data = cpu_to_le32(rate); - err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - UAC2_CS_CONTROL_SAM_FREQ << 8, - snd_usb_ctrl_intf(chip) | (clock << 8), - &data, sizeof(data)); - if (err < 0) { - usb_audio_err(chip, - "%d:%d: cannot set freq %d (v2/v3): err %d\n", - iface, fmt->altsetting, rate, err); - return err; - } - - cur_rate = get_sample_rate_v2v3(chip, iface, - fmt->altsetting, clock); - } else { + if (!cur_rate) cur_rate = prev_rate; - } if (cur_rate != rate) { - if (!writeable) { - usb_audio_warn(chip, - "%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n", - iface, fmt->altsetting, rate, cur_rate); - return -ENXIO; - } - usb_audio_dbg(chip, - "current rate %d is different from the runtime rate %d\n", - cur_rate, rate); - } - - /* Some devices doesn't respond to sample rate changes while the - * interface is active. */ - if (rate != prev_rate) { - usb_set_interface(dev, iface, 0); - snd_usb_set_interface_quirk(dev); - usb_set_interface(dev, iface, fmt->altsetting); - snd_usb_set_interface_quirk(dev); + usb_audio_warn(chip, + "%d:%d: freq mismatch (RO clock): req %d, clock runs @%d\n", + fmt->iface, fmt->altsetting, rate, cur_rate); + return -ENXIO; } validation: @@ -654,14 +668,16 @@ validation: return 0; } -int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt, int rate) +int snd_usb_init_sample_rate(struct snd_usb_audio *chip, + const struct audioformat *fmt, int rate) { + usb_audio_dbg(chip, "%d:%d Set sample rate %d, clock %d\n", + fmt->iface, fmt->altsetting, rate, fmt->clock); + switch (fmt->protocol) { case UAC_VERSION_1: default: - return set_sample_rate_v1(chip, iface, alts, fmt, rate); + return set_sample_rate_v1(chip, fmt, rate); case UAC_VERSION_3: if (chip->badd_profile >= UAC3_FUNCTION_SUBCLASS_GENERIC_IO) { @@ -672,7 +688,7 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, } fallthrough; case UAC_VERSION_2: - return set_sample_rate_v2v3(chip, iface, alts, fmt, rate); + return set_sample_rate_v2v3(chip, fmt, rate); } } diff --git a/sound/usb/clock.h b/sound/usb/clock.h index 68df0fbe09d0..ed9fc2dc0510 100644 --- a/sound/usb/clock.h +++ b/sound/usb/clock.h @@ -2,11 +2,14 @@ #ifndef __USBAUDIO_CLOCK_H #define __USBAUDIO_CLOCK_H -int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt, int rate); +int snd_usb_init_sample_rate(struct snd_usb_audio *chip, + const struct audioformat *fmt, int rate); int snd_usb_clock_find_source(struct snd_usb_audio *chip, - struct audioformat *fmt, bool validate); + const struct audioformat *fmt, bool validate); + +int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, + const struct audioformat *fmt, + int clock, int rate); #endif /* __USBAUDIO_CLOCK_H */ diff --git a/sound/usb/debug.h b/sound/usb/debug.h deleted file mode 100644 index 7dd983c35001..000000000000 --- a/sound/usb/debug.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __USBAUDIO_DEBUG_H -#define __USBAUDIO_DEBUG_H - -/* - * h/w constraints - */ - -#ifdef HW_CONST_DEBUG -#define hwc_debug(fmt, args...) printk(KERN_DEBUG fmt, ##args) -#else -#define hwc_debug(fmt, args...) do { } while(0) -#endif - -#endif /* __USBAUDIO_DEBUG_H */ - diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index e2f9ce2f5b8b..162da7a50046 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -18,6 +18,7 @@ #include "card.h" #include "endpoint.h" #include "pcm.h" +#include "clock.h" #include "quirks.h" #define EP_FLAG_RUNNING 1 @@ -116,20 +117,17 @@ static const char *usb_error_string(int err) */ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep) { - return ep->sync_master && - ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA && - ep->type == SND_USB_ENDPOINT_TYPE_DATA && - usb_pipeout(ep->pipe); + return ep->implicit_fb_sync && usb_pipeout(ep->pipe); } /* - * For streaming based on information derived from sync endpoints, - * prepare_outbound_urb_sizes() will call slave_next_packet_size() to - * determine the number of samples to be sent in the next packet. + * Return the number of samples to be sent in the next packet + * for streaming based on information derived from sync endpoints * - * For implicit feedback, slave_next_packet_size() is unused. + * This won't be used for implicit feedback which takes the packet size + * returned from the sync source */ -int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep) +static int slave_next_packet_size(struct snd_usb_endpoint *ep) { unsigned long flags; int ret; @@ -147,11 +145,10 @@ int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep) } /* - * For adaptive and synchronous endpoints, prepare_outbound_urb_sizes() - * will call next_packet_size() to determine the number of samples to be - * sent in the next packet. + * Return the number of samples to be sent in the next packet + * for adaptive and synchronous endpoints */ -int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) +static int next_packet_size(struct snd_usb_endpoint *ep) { int ret; @@ -169,28 +166,57 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) return ret; } +/* + * snd_usb_endpoint_next_packet_size: Return the number of samples to be sent + * in the next packet + */ +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx, int idx) +{ + if (ctx->packet_size[idx]) + return ctx->packet_size[idx]; + else if (ep->sync_source) + return slave_next_packet_size(ep); + else + return next_packet_size(ep); +} + +static void call_retire_callback(struct snd_usb_endpoint *ep, + struct urb *urb) +{ + struct snd_usb_substream *data_subs; + + data_subs = READ_ONCE(ep->data_subs); + if (data_subs && ep->retire_data_urb) + ep->retire_data_urb(data_subs, urb); +} + static void retire_outbound_urb(struct snd_usb_endpoint *ep, struct snd_urb_ctx *urb_ctx) { - if (ep->retire_data_urb) - ep->retire_data_urb(ep->data_subs, urb_ctx->urb); + call_retire_callback(ep, urb_ctx->urb); } +static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, + struct snd_usb_endpoint *sender, + const struct urb *urb); + static void retire_inbound_urb(struct snd_usb_endpoint *ep, struct snd_urb_ctx *urb_ctx) { struct urb *urb = urb_ctx->urb; + struct snd_usb_endpoint *sync_sink; if (unlikely(ep->skip_packets > 0)) { ep->skip_packets--; return; } - if (ep->sync_slave) - snd_usb_handle_sync_urb(ep->sync_slave, ep, urb); + sync_sink = READ_ONCE(ep->sync_sink); + if (sync_sink) + snd_usb_handle_sync_urb(sync_sink, ep, urb); - if (ep->retire_data_urb) - ep->retire_data_urb(ep->data_subs, urb); + call_retire_callback(ep, urb); } static void prepare_silent_urb(struct snd_usb_endpoint *ep, @@ -211,13 +237,7 @@ static void prepare_silent_urb(struct snd_usb_endpoint *ep, unsigned int length; int counts; - if (ctx->packet_size[i]) - counts = ctx->packet_size[i]; - else if (ep->sync_master) - counts = snd_usb_endpoint_slave_next_packet_size(ep); - else - counts = snd_usb_endpoint_next_packet_size(ep); - + counts = snd_usb_endpoint_next_packet_size(ep, ctx, i); length = counts * ep->stride; /* number of silent bytes */ offset = offs * ep->stride + extra * i; urb->iso_frame_desc[i].offset = offset; @@ -244,17 +264,17 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep, { struct urb *urb = ctx->urb; unsigned char *cp = urb->transfer_buffer; + struct snd_usb_substream *data_subs; urb->dev = ep->chip->dev; /* we need to set this at each time */ switch (ep->type) { case SND_USB_ENDPOINT_TYPE_DATA: - if (ep->prepare_data_urb) { - ep->prepare_data_urb(ep->data_subs, urb); - } else { - /* no data provider, so send silence */ + data_subs = READ_ONCE(ep->data_subs); + if (data_subs && ep->prepare_data_urb) + ep->prepare_data_urb(data_subs, urb); + else /* no data provider, so send silence */ prepare_silent_urb(ep, ctx); - } break; case SND_USB_ENDPOINT_TYPE_SYNC: @@ -316,6 +336,39 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, } } +/* notify an error as XRUN to the assigned PCM data substream */ +static void notify_xrun(struct snd_usb_endpoint *ep) +{ + struct snd_usb_substream *data_subs; + + data_subs = READ_ONCE(ep->data_subs); + if (data_subs && data_subs->pcm_substream) + snd_pcm_stop_xrun(data_subs->pcm_substream); +} + +static struct snd_usb_packet_info * +next_packet_fifo_enqueue(struct snd_usb_endpoint *ep) +{ + struct snd_usb_packet_info *p; + + p = ep->next_packet + (ep->next_packet_head + ep->next_packet_queued) % + ARRAY_SIZE(ep->next_packet); + ep->next_packet_queued++; + return p; +} + +static struct snd_usb_packet_info * +next_packet_fifo_dequeue(struct snd_usb_endpoint *ep) +{ + struct snd_usb_packet_info *p; + + p = ep->next_packet + ep->next_packet_head; + ep->next_packet_head++; + ep->next_packet_head %= ARRAY_SIZE(ep->next_packet); + ep->next_packet_queued--; + return p; +} + /* * Send output urbs that have been prepared previously. URBs are dequeued * from ep->ready_playback_urbs and in case there aren't any available @@ -340,17 +393,14 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) int err, i; spin_lock_irqsave(&ep->lock, flags); - if (ep->next_packet_read_pos != ep->next_packet_write_pos) { - packet = ep->next_packet + ep->next_packet_read_pos; - ep->next_packet_read_pos++; - ep->next_packet_read_pos %= MAX_URBS; - + if (ep->next_packet_queued > 0 && + !list_empty(&ep->ready_playback_urbs)) { /* take URB out of FIFO */ - if (!list_empty(&ep->ready_playback_urbs)) { - ctx = list_first_entry(&ep->ready_playback_urbs, + ctx = list_first_entry(&ep->ready_playback_urbs, struct snd_urb_ctx, ready_list); - list_del_init(&ctx->ready_list); - } + list_del_init(&ctx->ready_list); + + packet = next_packet_fifo_dequeue(ep); } spin_unlock_irqrestore(&ep->lock, flags); @@ -365,12 +415,15 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) prepare_outbound_urb(ep, ctx); err = usb_submit_urb(ctx->urb, GFP_ATOMIC); - if (err < 0) + if (err < 0) { usb_audio_err(ep->chip, - "Unable to submit urb #%d: %d (urb %p)\n", - ctx->index, err, ctx->urb); - else - set_bit(ctx->index, &ep->active_mask); + "Unable to submit urb #%d: %d at %s\n", + ctx->index, err, __func__); + notify_xrun(ep); + return; + } + + set_bit(ctx->index, &ep->active_mask); } } @@ -381,7 +434,6 @@ static void snd_complete_urb(struct urb *urb) { struct snd_urb_ctx *ctx = urb->context; struct snd_usb_endpoint *ep = ctx->ep; - struct snd_pcm_substream *substream; unsigned long flags; int err; @@ -406,10 +458,10 @@ static void snd_complete_urb(struct urb *urb) if (snd_usb_endpoint_implicit_feedback_sink(ep)) { spin_lock_irqsave(&ep->lock, flags); list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); + clear_bit(ctx->index, &ep->active_mask); spin_unlock_irqrestore(&ep->lock, flags); queue_pending_output_urbs(ep); - - goto exit_clear; + return; } prepare_outbound_urb(ep, ctx); @@ -430,27 +482,43 @@ static void snd_complete_urb(struct urb *urb) return; usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); - if (ep->data_subs && ep->data_subs->pcm_substream) { - substream = ep->data_subs->pcm_substream; - snd_pcm_stop_xrun(substream); - } + notify_xrun(ep); exit_clear: clear_bit(ctx->index, &ep->active_mask); } +/* + * Get the existing endpoint object corresponding EP + * Returns NULL if not present. + */ +struct snd_usb_endpoint * +snd_usb_get_endpoint(struct snd_usb_audio *chip, int ep_num) +{ + struct snd_usb_endpoint *ep; + + list_for_each_entry(ep, &chip->ep_list, list) { + if (ep->ep_num == ep_num) + return ep; + } + + return NULL; +} + +#define ep_type_name(type) \ + (type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync") + /** * snd_usb_add_endpoint: Add an endpoint to an USB audio chip * * @chip: The chip - * @alts: The USB host interface * @ep_num: The number of the endpoint to use - * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE * @type: SND_USB_ENDPOINT_TYPE_DATA or SND_USB_ENDPOINT_TYPE_SYNC * * If the requested endpoint has not been added to the given chip before, - * a new instance is created. Otherwise, a pointer to the previoulsy - * created instance is returned. In case of any error, NULL is returned. + * a new instance is created. + * + * Returns zero on success or a negative error code. * * New endpoints will be added to chip->ep_list and must be freed by * calling snd_usb_endpoint_free(). @@ -458,79 +526,258 @@ exit_clear: * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that * bNumEndpoints > 1 beforehand. */ -struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, - struct usb_host_interface *alts, - int ep_num, int direction, int type) +int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type) { struct snd_usb_endpoint *ep; - int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK; + bool is_playback; - if (WARN_ON(!alts)) - return NULL; - - mutex_lock(&chip->mutex); - - list_for_each_entry(ep, &chip->ep_list, list) { - if (ep->ep_num == ep_num && - ep->iface == alts->desc.bInterfaceNumber && - ep->altsetting == alts->desc.bAlternateSetting) { - usb_audio_dbg(ep->chip, - "Re-using EP %x in iface %d,%d @%p\n", - ep_num, ep->iface, ep->altsetting, ep); - goto __exit_unlock; - } - } - - usb_audio_dbg(chip, "Creating new %s %s endpoint #%x\n", - is_playback ? "playback" : "capture", - type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync", - ep_num); + ep = snd_usb_get_endpoint(chip, ep_num); + if (ep) + return 0; + usb_audio_dbg(chip, "Creating new %s endpoint #%x\n", + ep_type_name(type), + ep_num); ep = kzalloc(sizeof(*ep), GFP_KERNEL); if (!ep) - goto __exit_unlock; + return -ENOMEM; ep->chip = chip; spin_lock_init(&ep->lock); ep->type = type; ep->ep_num = ep_num; - ep->iface = alts->desc.bInterfaceNumber; - ep->altsetting = alts->desc.bAlternateSetting; INIT_LIST_HEAD(&ep->ready_playback_urbs); - ep_num &= USB_ENDPOINT_NUMBER_MASK; + is_playback = ((ep_num & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); + ep_num &= USB_ENDPOINT_NUMBER_MASK; if (is_playback) ep->pipe = usb_sndisocpipe(chip->dev, ep_num); else ep->pipe = usb_rcvisocpipe(chip->dev, ep_num); - if (type == SND_USB_ENDPOINT_TYPE_SYNC) { - if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && - get_endpoint(alts, 1)->bRefresh >= 1 && - get_endpoint(alts, 1)->bRefresh <= 9) - ep->syncinterval = get_endpoint(alts, 1)->bRefresh; - else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL) - ep->syncinterval = 1; - else if (get_endpoint(alts, 1)->bInterval >= 1 && - get_endpoint(alts, 1)->bInterval <= 16) - ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1; - else - ep->syncinterval = 3; - - ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); - } - list_add_tail(&ep->list, &chip->ep_list); + return 0; +} + +/* Set up syncinterval and maxsyncsize for a sync EP */ +static void endpoint_set_syncinterval(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep) +{ + struct usb_host_interface *alts; + struct usb_endpoint_descriptor *desc; + + alts = snd_usb_get_host_interface(chip, ep->iface, ep->altsetting); + if (!alts) + return; + + desc = get_endpoint(alts, ep->ep_idx); + if (desc->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && + desc->bRefresh >= 1 && desc->bRefresh <= 9) + ep->syncinterval = desc->bRefresh; + else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL) + ep->syncinterval = 1; + else if (desc->bInterval >= 1 && desc->bInterval <= 16) + ep->syncinterval = desc->bInterval - 1; + else + ep->syncinterval = 3; + + ep->syncmaxsize = le16_to_cpu(desc->wMaxPacketSize); +} - ep->is_implicit_feedback = 0; +static bool endpoint_compatible(struct snd_usb_endpoint *ep, + const struct audioformat *fp, + const struct snd_pcm_hw_params *params) +{ + if (!ep->opened) + return false; + if (ep->cur_audiofmt != fp) + return false; + if (ep->cur_rate != params_rate(params) || + ep->cur_format != params_format(params) || + ep->cur_period_frames != params_period_size(params) || + ep->cur_buffer_periods != params_periods(params)) + return false; + return true; +} + +/* + * Check whether the given fp and hw params are compatbile with the current + * setup of the target EP for implicit feedback sync + */ +bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep, + const struct audioformat *fp, + const struct snd_pcm_hw_params *params) +{ + bool ret; -__exit_unlock: + mutex_lock(&chip->mutex); + ret = endpoint_compatible(ep, fp, params); mutex_unlock(&chip->mutex); + return ret; +} +/* + * snd_usb_endpoint_open: Open the endpoint + * + * Called from hw_params to assign the endpoint to the substream. + * It's reference-counted, and only the first opener is allowed to set up + * arbitrary parameters. The later opener must be compatible with the + * former opened parameters. + * The endpoint needs to be closed via snd_usb_endpoint_close() later. + * + * Note that this function doesn't configure the endpoint. The substream + * needs to set it up later via snd_usb_endpoint_configure(). + */ +struct snd_usb_endpoint * +snd_usb_endpoint_open(struct snd_usb_audio *chip, + const struct audioformat *fp, + const struct snd_pcm_hw_params *params, + bool is_sync_ep) +{ + struct snd_usb_endpoint *ep; + int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint; + + mutex_lock(&chip->mutex); + ep = snd_usb_get_endpoint(chip, ep_num); + if (!ep) { + usb_audio_err(chip, "Cannot find EP 0x%x to open\n", ep_num); + goto unlock; + } + + if (!ep->opened) { + if (is_sync_ep) { + ep->iface = fp->sync_iface; + ep->altsetting = fp->sync_altsetting; + ep->ep_idx = fp->sync_ep_idx; + } else { + ep->iface = fp->iface; + ep->altsetting = fp->altsetting; + ep->ep_idx = 0; + } + usb_audio_dbg(chip, "Open EP 0x%x, iface=%d:%d, idx=%d\n", + ep_num, ep->iface, ep->altsetting, ep->ep_idx); + + ep->cur_audiofmt = fp; + ep->cur_channels = fp->channels; + ep->cur_rate = params_rate(params); + ep->cur_format = params_format(params); + ep->cur_frame_bytes = snd_pcm_format_physical_width(ep->cur_format) * + ep->cur_channels / 8; + ep->cur_period_frames = params_period_size(params); + ep->cur_period_bytes = ep->cur_period_frames * ep->cur_frame_bytes; + ep->cur_buffer_periods = params_periods(params); + + if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC) + endpoint_set_syncinterval(chip, ep); + + ep->implicit_fb_sync = fp->implicit_fb; + ep->need_setup = true; + + usb_audio_dbg(chip, " channels=%d, rate=%d, format=%s, period_bytes=%d, periods=%d, implicit_fb=%d\n", + ep->cur_channels, ep->cur_rate, + snd_pcm_format_name(ep->cur_format), + ep->cur_period_bytes, ep->cur_buffer_periods, + ep->implicit_fb_sync); + + } else { + if (!endpoint_compatible(ep, fp, params)) { + usb_audio_err(chip, "Incompatible EP setup for 0x%x\n", + ep_num); + ep = NULL; + goto unlock; + } + + usb_audio_dbg(chip, "Reopened EP 0x%x (count %d)\n", + ep_num, ep->opened); + } + + ep->opened++; + + unlock: + mutex_unlock(&chip->mutex); return ep; } /* + * snd_usb_endpoint_set_sync: Link data and sync endpoints + * + * Pass NULL to sync_ep to unlink again + */ +void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip, + struct snd_usb_endpoint *data_ep, + struct snd_usb_endpoint *sync_ep) +{ + data_ep->sync_source = sync_ep; +} + +/* + * Set data endpoint callbacks and the assigned data stream + * + * Called at PCM trigger and cleanups. + * Pass NULL to deactivate each callback. + */ +void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, + void (*prepare)(struct snd_usb_substream *subs, + struct urb *urb), + void (*retire)(struct snd_usb_substream *subs, + struct urb *urb), + struct snd_usb_substream *data_subs) +{ + ep->prepare_data_urb = prepare; + ep->retire_data_urb = retire; + WRITE_ONCE(ep->data_subs, data_subs); +} + +static int endpoint_set_interface(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep, + bool set) +{ + int altset = set ? ep->altsetting : 0; + int err; + + usb_audio_dbg(chip, "Setting usb interface %d:%d for EP 0x%x\n", + ep->iface, altset, ep->ep_num); + err = usb_set_interface(chip->dev, ep->iface, altset); + if (err < 0) { + usb_audio_err(chip, "%d:%d: usb_set_interface failed (%d)\n", + ep->iface, altset, err); + return err; + } + + snd_usb_set_interface_quirk(chip); + return 0; +} + +/* + * snd_usb_endpoint_close: Close the endpoint + * + * Unreference the already opened endpoint via snd_usb_endpoint_open(). + */ +void snd_usb_endpoint_close(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep) +{ + mutex_lock(&chip->mutex); + usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n", + ep->ep_num, ep->opened); + if (!--ep->opened) { + endpoint_set_interface(chip, ep, false); + ep->iface = 0; + ep->altsetting = 0; + ep->cur_audiofmt = NULL; + ep->cur_rate = 0; + usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num); + } + mutex_unlock(&chip->mutex); +} + +/* Prepare for suspening EP, called from the main suspend handler */ +void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep) +{ + ep->need_setup = true; +} + +/* * wait until all urbs are processed. */ static int wait_clear_urbs(struct snd_usb_endpoint *ep) @@ -538,6 +785,9 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) unsigned long end_time = jiffies + msecs_to_jiffies(1000); int alive; + if (!test_bit(EP_FLAG_STOPPING, &ep->flags)) + return 0; + do { alive = bitmap_weight(&ep->active_mask, ep->nurbs); if (!alive) @@ -552,10 +802,8 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) alive, ep->ep_num); clear_bit(EP_FLAG_STOPPING, &ep->flags); - ep->data_subs = NULL; - ep->sync_slave = NULL; - ep->retire_data_urb = NULL; - ep->prepare_data_urb = NULL; + ep->sync_sink = NULL; + snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); return 0; } @@ -565,25 +813,34 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) */ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep) { - if (ep && test_bit(EP_FLAG_STOPPING, &ep->flags)) + if (ep) wait_clear_urbs(ep); } /* - * unlink active urbs. + * Stop and unlink active urbs. + * + * This function checks and clears EP_FLAG_RUNNING state. + * When @wait_sync is set, it waits until all pending URBs are killed. */ -static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force) +static int stop_and_unlink_urbs(struct snd_usb_endpoint *ep, bool force, + bool wait_sync) { unsigned int i; if (!force && atomic_read(&ep->chip->shutdown)) /* to be sure... */ return -EBADFD; - clear_bit(EP_FLAG_RUNNING, &ep->flags); + if (atomic_read(&ep->running)) + return -EBUSY; + if (!test_and_clear_bit(EP_FLAG_RUNNING, &ep->flags)) + goto out; + + set_bit(EP_FLAG_STOPPING, &ep->flags); INIT_LIST_HEAD(&ep->ready_playback_urbs); - ep->next_packet_read_pos = 0; - ep->next_packet_write_pos = 0; + ep->next_packet_head = 0; + ep->next_packet_queued = 0; for (i = 0; i < ep->nurbs; i++) { if (test_bit(i, &ep->active_mask)) { @@ -594,6 +851,9 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force) } } + out: + if (wait_sync) + return wait_clear_urbs(ep); return 0; } @@ -605,12 +865,10 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) int i; /* route incoming urbs to nirvana */ - ep->retire_data_urb = NULL; - ep->prepare_data_urb = NULL; + snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); /* stop urbs */ - deactivate_urbs(ep, force); - wait_clear_urbs(ep); + stop_and_unlink_urbs(ep, force, true); for (i = 0; i < ep->nurbs; i++) release_urb_ctx(&ep->urb[i]); @@ -623,209 +881,35 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) } /* - * Check data endpoint for format differences - */ -static bool check_ep_params(struct snd_usb_endpoint *ep, - snd_pcm_format_t pcm_format, - unsigned int channels, - unsigned int period_bytes, - unsigned int frames_per_period, - unsigned int periods_per_buffer, - struct audioformat *fmt, - struct snd_usb_endpoint *sync_ep) -{ - unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb; - unsigned int max_packs_per_period, urbs_per_period, urb_packs; - unsigned int max_urbs; - int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; - int tx_length_quirk = (ep->chip->tx_length_quirk && - usb_pipeout(ep->pipe)); - bool ret = 1; - - if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { - /* - * When operating in DSD DOP mode, the size of a sample frame - * in hardware differs from the actual physical format width - * because we need to make room for the DOP markers. - */ - frame_bits += channels << 3; - } - - ret = ret && (ep->datainterval == fmt->datainterval); - ret = ret && (ep->stride == frame_bits >> 3); - - switch (pcm_format) { - case SNDRV_PCM_FORMAT_U8: - ret = ret && (ep->silence_value == 0x80); - break; - case SNDRV_PCM_FORMAT_DSD_U8: - case SNDRV_PCM_FORMAT_DSD_U16_LE: - case SNDRV_PCM_FORMAT_DSD_U32_LE: - case SNDRV_PCM_FORMAT_DSD_U16_BE: - case SNDRV_PCM_FORMAT_DSD_U32_BE: - ret = ret && (ep->silence_value == 0x69); - break; - default: - ret = ret && (ep->silence_value == 0); - } - - /* assume max. frequency is 50% higher than nominal */ - ret = ret && (ep->freqmax == ep->freqn + (ep->freqn >> 1)); - /* Round up freqmax to nearest integer in order to calculate maximum - * packet size, which must represent a whole number of frames. - * This is accomplished by adding 0x0.ffff before converting the - * Q16.16 format into integer. - * In order to accurately calculate the maximum packet size when - * the data interval is more than 1 (i.e. ep->datainterval > 0), - * multiply by the data interval prior to rounding. For instance, - * a freqmax of 41 kHz will result in a max packet size of 6 (5.125) - * frames with a data interval of 1, but 11 (10.25) frames with a - * data interval of 2. - * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the - * maximum datainterval value of 3, at USB full speed, higher for - * USB high speed, noting that ep->freqmax is in units of - * frames per packet in Q16.16 format.) - */ - maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) * - (frame_bits >> 3); - if (tx_length_quirk) - maxsize += sizeof(__le32); /* Space for length descriptor */ - /* but wMaxPacketSize might reduce this */ - if (ep->maxpacksize && ep->maxpacksize < maxsize) { - /* whatever fits into a max. size packet */ - unsigned int data_maxsize = maxsize = ep->maxpacksize; - - if (tx_length_quirk) - /* Need to remove the length descriptor to calc freq */ - data_maxsize -= sizeof(__le32); - ret = ret && (ep->freqmax == (data_maxsize / (frame_bits >> 3)) - << (16 - ep->datainterval)); - } - - if (ep->fill_max) - ret = ret && (ep->curpacksize == ep->maxpacksize); - else - ret = ret && (ep->curpacksize == maxsize); - - if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) { - packs_per_ms = 8 >> ep->datainterval; - max_packs_per_urb = MAX_PACKS_HS; - } else { - packs_per_ms = 1; - max_packs_per_urb = MAX_PACKS; - } - if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) - max_packs_per_urb = min(max_packs_per_urb, - 1U << sync_ep->syncinterval); - max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); - - /* - * Capture endpoints need to use small URBs because there's no way - * to tell in advance where the next period will end, and we don't - * want the next URB to complete much after the period ends. - * - * Playback endpoints with implicit sync much use the same parameters - * as their corresponding capture endpoint. - */ - if (usb_pipein(ep->pipe) || - snd_usb_endpoint_implicit_feedback_sink(ep)) { - - urb_packs = packs_per_ms; - /* - * Wireless devices can poll at a max rate of once per 4ms. - * For dataintervals less than 5, increase the packet count to - * allow the host controller to use bursting to fill in the - * gaps. - */ - if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_WIRELESS) { - int interval = ep->datainterval; - - while (interval < 5) { - urb_packs <<= 1; - ++interval; - } - } - /* make capture URBs <= 1 ms and smaller than a period */ - urb_packs = min(max_packs_per_urb, urb_packs); - while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) - urb_packs >>= 1; - ret = ret && (ep->nurbs == MAX_URBS); - - /* - * Playback endpoints without implicit sync are adjusted so that - * a period fits as evenly as possible in the smallest number of - * URBs. The total number of URBs is adjusted to the size of the - * ALSA buffer, subject to the MAX_URBS and MAX_QUEUE limits. - */ - } else { - /* determine how small a packet can be */ - minsize = (ep->freqn >> (16 - ep->datainterval)) * - (frame_bits >> 3); - /* with sync from device, assume it can be 12% lower */ - if (sync_ep) - minsize -= minsize >> 3; - minsize = max(minsize, 1u); - - /* how many packets will contain an entire ALSA period? */ - max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize); - - /* how many URBs will contain a period? */ - urbs_per_period = DIV_ROUND_UP(max_packs_per_period, - max_packs_per_urb); - /* how many packets are needed in each URB? */ - urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period); - - /* limit the number of frames in a single URB */ - ret = ret && (ep->max_urb_frames == - DIV_ROUND_UP(frames_per_period, urbs_per_period)); - - /* try to use enough URBs to contain an entire ALSA buffer */ - max_urbs = min((unsigned) MAX_URBS, - MAX_QUEUE * packs_per_ms / urb_packs); - ret = ret && (ep->nurbs == min(max_urbs, - urbs_per_period * periods_per_buffer)); - } - - ret = ret && (ep->datainterval == fmt->datainterval); - ret = ret && (ep->maxpacksize == fmt->maxpacksize); - ret = ret && - (ep->fill_max == !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)); - - return ret; -} - -/* * configure a data endpoint */ -static int data_ep_set_params(struct snd_usb_endpoint *ep, - snd_pcm_format_t pcm_format, - unsigned int channels, - unsigned int period_bytes, - unsigned int frames_per_period, - unsigned int periods_per_buffer, - struct audioformat *fmt, - struct snd_usb_endpoint *sync_ep) +static int data_ep_set_params(struct snd_usb_endpoint *ep) { + struct snd_usb_audio *chip = ep->chip; unsigned int maxsize, minsize, packs_per_ms, max_packs_per_urb; unsigned int max_packs_per_period, urbs_per_period, urb_packs; unsigned int max_urbs, i; - int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels; - int tx_length_quirk = (ep->chip->tx_length_quirk && + const struct audioformat *fmt = ep->cur_audiofmt; + int frame_bits = ep->cur_frame_bytes * 8; + int tx_length_quirk = (chip->tx_length_quirk && usb_pipeout(ep->pipe)); - if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { + usb_audio_dbg(chip, "Setting params for data EP 0x%x, pipe 0x%x\n", + ep->ep_num, ep->pipe); + + if (ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { /* * When operating in DSD DOP mode, the size of a sample frame * in hardware differs from the actual physical format width * because we need to make room for the DOP markers. */ - frame_bits += channels << 3; + frame_bits += ep->cur_channels << 3; } ep->datainterval = fmt->datainterval; ep->stride = frame_bits >> 3; - switch (pcm_format) { + switch (ep->cur_format) { case SNDRV_PCM_FORMAT_U8: ep->silence_value = 0x80; break; @@ -878,16 +962,16 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, else ep->curpacksize = maxsize; - if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL) { + if (snd_usb_get_speed(chip->dev) != USB_SPEED_FULL) { packs_per_ms = 8 >> ep->datainterval; max_packs_per_urb = MAX_PACKS_HS; } else { packs_per_ms = 1; max_packs_per_urb = MAX_PACKS; } - if (sync_ep && !snd_usb_endpoint_implicit_feedback_sink(ep)) + if (ep->sync_source && !ep->implicit_fb_sync) max_packs_per_urb = min(max_packs_per_urb, - 1U << sync_ep->syncinterval); + 1U << ep->sync_source->syncinterval); max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); /* @@ -898,8 +982,7 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, * Playback endpoints with implicit sync much use the same parameters * as their corresponding capture endpoint. */ - if (usb_pipein(ep->pipe) || - snd_usb_endpoint_implicit_feedback_sink(ep)) { + if (usb_pipein(ep->pipe) || ep->implicit_fb_sync) { urb_packs = packs_per_ms; /* @@ -908,7 +991,7 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, * allow the host controller to use bursting to fill in the * gaps. */ - if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_WIRELESS) { + if (snd_usb_get_speed(chip->dev) == USB_SPEED_WIRELESS) { int interval = ep->datainterval; while (interval < 5) { urb_packs <<= 1; @@ -917,7 +1000,7 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, } /* make capture URBs <= 1 ms and smaller than a period */ urb_packs = min(max_packs_per_urb, urb_packs); - while (urb_packs > 1 && urb_packs * maxsize >= period_bytes) + while (urb_packs > 1 && urb_packs * maxsize >= ep->cur_period_bytes) urb_packs >>= 1; ep->nurbs = MAX_URBS; @@ -932,12 +1015,12 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, minsize = (ep->freqn >> (16 - ep->datainterval)) * (frame_bits >> 3); /* with sync from device, assume it can be 12% lower */ - if (sync_ep) + if (ep->sync_source) minsize -= minsize >> 3; minsize = max(minsize, 1u); /* how many packets will contain an entire ALSA period? */ - max_packs_per_period = DIV_ROUND_UP(period_bytes, minsize); + max_packs_per_period = DIV_ROUND_UP(ep->cur_period_bytes, minsize); /* how many URBs will contain a period? */ urbs_per_period = DIV_ROUND_UP(max_packs_per_period, @@ -946,13 +1029,13 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, urb_packs = DIV_ROUND_UP(max_packs_per_period, urbs_per_period); /* limit the number of frames in a single URB */ - ep->max_urb_frames = DIV_ROUND_UP(frames_per_period, - urbs_per_period); + ep->max_urb_frames = DIV_ROUND_UP(ep->cur_period_frames, + urbs_per_period); /* try to use enough URBs to contain an entire ALSA buffer */ max_urbs = min((unsigned) MAX_URBS, MAX_QUEUE * packs_per_ms / urb_packs); - ep->nurbs = min(max_urbs, urbs_per_period * periods_per_buffer); + ep->nurbs = min(max_urbs, urbs_per_period * ep->cur_buffer_periods); } /* allocate and initialize data urbs */ @@ -970,7 +1053,7 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, goto out_of_memory; u->urb->transfer_buffer = - usb_alloc_coherent(ep->chip->dev, u->buffer_size, + usb_alloc_coherent(chip->dev, u->buffer_size, GFP_KERNEL, &u->urb->transfer_dma); if (!u->urb->transfer_buffer) goto out_of_memory; @@ -994,9 +1077,13 @@ out_of_memory: */ static int sync_ep_set_params(struct snd_usb_endpoint *ep) { + struct snd_usb_audio *chip = ep->chip; int i; - ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4, + usb_audio_dbg(chip, "Setting params for sync EP 0x%x, pipe 0x%x\n", + ep->ep_num, ep->pipe); + + ep->syncbuf = usb_alloc_coherent(chip->dev, SYNC_URBS * 4, GFP_KERNEL, &ep->sync_dma); if (!ep->syncbuf) return -ENOMEM; @@ -1029,55 +1116,19 @@ out_of_memory: return -ENOMEM; } -/** +/* * snd_usb_endpoint_set_params: configure an snd_usb_endpoint * - * @ep: the snd_usb_endpoint to configure - * @pcm_format: the audio fomat. - * @channels: the number of audio channels. - * @period_bytes: the number of bytes in one alsa period. - * @period_frames: the number of frames in one alsa period. - * @buffer_periods: the number of periods in one alsa buffer. - * @rate: the frame rate. - * @fmt: the USB audio format information - * @sync_ep: the sync endpoint to use, if any - * * Determine the number of URBs to be used on this endpoint. * An endpoint must be configured before it can be started. * An endpoint that is already running can not be reconfigured. */ -int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, - snd_pcm_format_t pcm_format, - unsigned int channels, - unsigned int period_bytes, - unsigned int period_frames, - unsigned int buffer_periods, - unsigned int rate, - struct audioformat *fmt, - struct snd_usb_endpoint *sync_ep) +static int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep) { + const struct audioformat *fmt = ep->cur_audiofmt; int err; - if (ep->use_count != 0) { - bool check = ep->is_implicit_feedback && - check_ep_params(ep, pcm_format, - channels, period_bytes, - period_frames, buffer_periods, - fmt, sync_ep); - - if (!check) { - usb_audio_warn(ep->chip, - "Unable to change format on ep #%x: already in use\n", - ep->ep_num); - return -EBUSY; - } - - usb_audio_dbg(ep->chip, - "Ep #%x already in use as implicit feedback but format not changed\n", - ep->ep_num); - return 0; - } - /* release old buffers, if any */ release_urbs(ep, 0); @@ -1085,17 +1136,17 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, ep->maxpacksize = fmt->maxpacksize; ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX); - if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL) { - ep->freqn = get_usb_full_speed_rate(rate); + if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL) { + ep->freqn = get_usb_full_speed_rate(ep->cur_rate); ep->pps = 1000 >> ep->datainterval; } else { - ep->freqn = get_usb_high_speed_rate(rate); + ep->freqn = get_usb_high_speed_rate(ep->cur_rate); ep->pps = 8000 >> ep->datainterval; } - ep->sample_rem = rate % ep->pps; - ep->packsize[0] = rate / ep->pps; - ep->packsize[1] = (rate + (ep->pps - 1)) / ep->pps; + ep->sample_rem = ep->cur_rate % ep->pps; + ep->packsize[0] = ep->cur_rate / ep->pps; + ep->packsize[1] = (ep->cur_rate + (ep->pps - 1)) / ep->pps; /* calculate the frequency in 16.16 format */ ep->freqm = ep->freqn; @@ -1105,9 +1156,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, switch (ep->type) { case SND_USB_ENDPOINT_TYPE_DATA: - err = data_ep_set_params(ep, pcm_format, channels, - period_bytes, period_frames, - buffer_periods, fmt, sync_ep); + err = data_ep_set_params(ep); break; case SND_USB_ENDPOINT_TYPE_SYNC: err = sync_ep_set_params(ep); @@ -1116,10 +1165,89 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, err = -EINVAL; } - usb_audio_dbg(ep->chip, - "Setting params for ep #%x (type %d, %d urbs), ret=%d\n", - ep->ep_num, ep->type, ep->nurbs, err); + usb_audio_dbg(chip, "Set up %d URBS, ret=%d\n", ep->nurbs, err); + + if (err < 0) + return err; + + /* some unit conversions in runtime */ + ep->maxframesize = ep->maxpacksize / ep->cur_frame_bytes; + ep->curframesize = ep->curpacksize / ep->cur_frame_bytes; + + return 0; +} + +/* + * snd_usb_endpoint_configure: Configure the endpoint + * + * This function sets up the EP to be fully usable state. + * It's called either from hw_params or prepare callback. + * The function checks need_setup flag, and perfoms nothing unless needed, + * so it's safe to call this multiple times. + * + * This returns zero if unchanged, 1 if the configuration has changed, + * or a negative error code. + */ +int snd_usb_endpoint_configure(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep) +{ + bool iface_first; + int err = 0; + + mutex_lock(&chip->mutex); + if (!ep->need_setup) + goto unlock; + + /* No need to (re-)configure the sync EP belonging to the same altset */ + if (ep->ep_idx) { + err = snd_usb_endpoint_set_params(chip, ep); + if (err < 0) + goto unlock; + goto done; + } + + /* Need to deselect altsetting at first */ + endpoint_set_interface(chip, ep, false); + + /* Some UAC1 devices (e.g. Yamaha THR10) need the host interface + * to be set up before parameter setups + */ + iface_first = ep->cur_audiofmt->protocol == UAC_VERSION_1; + if (iface_first) { + err = endpoint_set_interface(chip, ep, true); + if (err < 0) + goto unlock; + } + + err = snd_usb_init_pitch(chip, ep->cur_audiofmt); + if (err < 0) + goto unlock; + err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, ep->cur_rate); + if (err < 0) + goto unlock; + + err = snd_usb_endpoint_set_params(chip, ep); + if (err < 0) + goto unlock; + + err = snd_usb_select_mode_quirk(chip, ep->cur_audiofmt); + if (err < 0) + goto unlock; + + /* for UAC2/3, enable the interface altset here at last */ + if (!iface_first) { + err = endpoint_set_interface(chip, ep, true); + if (err < 0) + goto unlock; + } + + done: + ep->need_setup = false; + err = 1; + +unlock: + mutex_unlock(&chip->mutex); return err; } @@ -1128,7 +1256,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * * @ep: the endpoint to start * - * A call to this function will increment the use count of the endpoint. + * A call to this function will increment the running count of the endpoint. * In case it is not already running, the URBs for this endpoint will be * submitted. Otherwise, this function does nothing. * @@ -1144,13 +1272,17 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (atomic_read(&ep->chip->shutdown)) return -EBADFD; + if (ep->sync_source) + WRITE_ONCE(ep->sync_source->sync_sink, ep); + + usb_audio_dbg(ep->chip, "Starting %s EP 0x%x (running %d)\n", + ep_type_name(ep->type), ep->ep_num, + atomic_read(&ep->running)); + /* already running? */ - if (++ep->use_count != 1) + if (atomic_inc_return(&ep->running) != 1) return 0; - /* just to be sure */ - deactivate_urbs(ep, false); - ep->active_mask = 0; ep->unlink_mask = 0; ep->phase = 0; @@ -1173,6 +1305,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs); } + usb_audio_dbg(ep->chip, "No URB submission due to implicit fb sync\n"); return 0; } @@ -1198,12 +1331,12 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) set_bit(i, &ep->active_mask); } + usb_audio_dbg(ep->chip, "%d URBs submitted for EP 0x%x\n", + ep->nurbs, ep->ep_num); return 0; __error: - clear_bit(EP_FLAG_RUNNING, &ep->flags); - ep->use_count--; - deactivate_urbs(ep, false); + snd_usb_endpoint_stop(ep); return -EPIPE; } @@ -1212,7 +1345,7 @@ __error: * * @ep: the endpoint to stop (may be NULL) * - * A call to this function will decrement the use count of the endpoint. + * A call to this function will decrement the running count of the endpoint. * In case the last user has requested the endpoint stop, the URBs will * actually be deactivated. * @@ -1226,35 +1359,18 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (!ep) return; - if (snd_BUG_ON(ep->use_count == 0)) - return; - - if (--ep->use_count == 0) { - deactivate_urbs(ep, false); - set_bit(EP_FLAG_STOPPING, &ep->flags); - } -} + usb_audio_dbg(ep->chip, "Stopping %s EP 0x%x (running %d)\n", + ep_type_name(ep->type), ep->ep_num, + atomic_read(&ep->running)); -/** - * snd_usb_endpoint_deactivate: deactivate an snd_usb_endpoint - * - * @ep: the endpoint to deactivate - * - * If the endpoint is not currently in use, this functions will - * deactivate its associated URBs. - * - * In case of any active users, this functions does nothing. - */ -void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) -{ - if (!ep) + if (snd_BUG_ON(!atomic_read(&ep->running))) return; - if (ep->use_count != 0) - return; + if (ep->sync_source) + WRITE_ONCE(ep->sync_source->sync_sink, NULL); - deactivate_urbs(ep, true); - wait_clear_urbs(ep); + if (!atomic_dec_return(&ep->running)) + stop_and_unlink_urbs(ep, false, false); } /** @@ -1262,7 +1378,7 @@ void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) * * @ep: the endpoint to release * - * This function does not care for the endpoint's use count but will tear + * This function does not care for the endpoint's running count but will tear * down all the streaming URBs immediately. */ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep) @@ -1282,7 +1398,7 @@ void snd_usb_endpoint_free(struct snd_usb_endpoint *ep) kfree(ep); } -/** +/* * snd_usb_handle_sync_urb: parse an USB sync packet * * @ep: the endpoint to handle the packet @@ -1292,9 +1408,9 @@ void snd_usb_endpoint_free(struct snd_usb_endpoint *ep) * This function is called from the context of an endpoint that received * the packet and is used to let another endpoint object handle the payload. */ -void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, - struct snd_usb_endpoint *sender, - const struct urb *urb) +static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, + struct snd_usb_endpoint *sender, + const struct urb *urb) { int shift; unsigned int f; @@ -1309,7 +1425,7 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, * will take care of them later. */ if (snd_usb_endpoint_implicit_feedback_sink(ep) && - ep->use_count != 0) { + atomic_read(&ep->running)) { /* implicit feedback case */ int i, bytes = 0; @@ -1331,7 +1447,16 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, return; spin_lock_irqsave(&ep->lock, flags); - out_packet = ep->next_packet + ep->next_packet_write_pos; + if (ep->next_packet_queued >= ARRAY_SIZE(ep->next_packet)) { + spin_unlock_irqrestore(&ep->lock, flags); + usb_audio_err(ep->chip, + "next package FIFO overflow EP 0x%x\n", + ep->ep_num); + notify_xrun(ep); + return; + } + + out_packet = next_packet_fifo_enqueue(ep); /* * Iterate through the inbound packet and prepare the lengths @@ -1352,8 +1477,6 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, out_packet->packet_size[i] = 0; } - ep->next_packet_write_pos++; - ep->next_packet_write_pos %= MAX_URBS; spin_unlock_irqrestore(&ep->lock, flags); queue_pending_output_urbs(ep); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index d23fa0a8c11b..11e3bb839fd7 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -5,34 +5,47 @@ #define SND_USB_ENDPOINT_TYPE_DATA 0 #define SND_USB_ENDPOINT_TYPE_SYNC 1 -struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, - struct usb_host_interface *alts, - int ep_num, int direction, int type); - -int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, - snd_pcm_format_t pcm_format, - unsigned int channels, - unsigned int period_bytes, - unsigned int period_frames, - unsigned int buffer_periods, - unsigned int rate, - struct audioformat *fmt, - struct snd_usb_endpoint *sync_ep); - -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); +struct snd_usb_endpoint *snd_usb_get_endpoint(struct snd_usb_audio *chip, + int ep_num); + +int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type); + +struct snd_usb_endpoint * +snd_usb_endpoint_open(struct snd_usb_audio *chip, + const struct audioformat *fp, + const struct snd_pcm_hw_params *params, + bool is_sync_ep); +void snd_usb_endpoint_close(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep); +int snd_usb_endpoint_configure(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep); +void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep); + +bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep, + const struct audioformat *fp, + const struct snd_pcm_hw_params *params); + +void snd_usb_endpoint_set_sync(struct snd_usb_audio *chip, + struct snd_usb_endpoint *data_ep, + struct snd_usb_endpoint *sync_ep); +void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, + void (*prepare)(struct snd_usb_substream *subs, + struct urb *urb), + void (*retire)(struct snd_usb_substream *subs, + struct urb *urb), + struct snd_usb_substream *data_subs); + +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); +void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); -void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); void snd_usb_endpoint_release(struct snd_usb_endpoint *ep); void snd_usb_endpoint_free(struct snd_usb_endpoint *ep); int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep); -int snd_usb_endpoint_slave_next_packet_size(struct snd_usb_endpoint *ep); -int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep); - -void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, - struct snd_usb_endpoint *sender, - const struct urb *urb); +int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, + struct snd_urb_ctx *ctx, int idx); #endif /* __USBAUDIO_ENDPOINT_H */ diff --git a/sound/usb/format.c b/sound/usb/format.c index 3bfead393aa3..9ebc5d202c87 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -16,7 +16,6 @@ #include "card.h" #include "quirks.h" #include "helper.h" -#include "debug.h" #include "clock.h" #include "format.h" @@ -40,6 +39,8 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, case UAC_VERSION_1: default: { struct uac_format_type_i_discrete_descriptor *fmt = _fmt; + if (format >= 64) + return 0; /* invalid format */ sample_width = fmt->bBitResolution; sample_bytes = fmt->bSubframeSize; format = 1ULL << format; @@ -165,6 +166,23 @@ static int set_fixed_rate(struct audioformat *fp, int rate, int rate_bits) return 0; } +/* set up rate_min, rate_max and rates from the rate table */ +static void set_rate_table_min_max(struct audioformat *fp) +{ + unsigned int rate; + int i; + + fp->rate_min = INT_MAX; + fp->rate_max = 0; + fp->rates = 0; + for (i = 0; i < fp->nr_rates; i++) { + rate = fp->rate_table[i]; + fp->rate_min = min(fp->rate_min, rate); + fp->rate_max = max(fp->rate_max, rate); + fp->rates |= snd_pcm_rate_to_rate_bit(rate); + } +} + /* * parse the format descriptor and stores the possible sample rates * on the audioformat table (audio class v1). @@ -199,7 +217,6 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof return -ENOMEM; fp->nr_rates = 0; - fp->rate_min = fp->rate_max = 0; for (r = 0, idx = offset + 1; r < nr_rates; r++, idx += 3) { unsigned int rate = combine_triple(&fmt[idx]); if (!rate) @@ -218,18 +235,15 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof chip->usb_id == USB_ID(0x041e, 0x4068))) rate = 8000; - fp->rate_table[fp->nr_rates] = rate; - if (!fp->rate_min || rate < fp->rate_min) - fp->rate_min = rate; - if (!fp->rate_max || rate > fp->rate_max) - fp->rate_max = rate; - fp->rates |= snd_pcm_rate_to_rate_bit(rate); - fp->nr_rates++; + fp->rate_table[fp->nr_rates++] = rate; } if (!fp->nr_rates) { - hwc_debug("All rates were zero. Skipping format!\n"); + usb_audio_info(chip, + "%u:%d: All rates were zero\n", + fp->iface, fp->altsetting); return -EINVAL; } + set_rate_table_min_max(fp); } else { /* continuous rates */ fp->rates = SNDRV_PCM_RATE_CONTINUOUS; @@ -335,8 +349,6 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, { int i, nr_rates = 0; - fp->rates = fp->rate_min = fp->rate_max = 0; - for (i = 0; i < nr_triplets; i++) { int min = combine_quad(&data[2 + 12 * i]); int max = combine_quad(&data[6 + 12 * i]); @@ -372,12 +384,6 @@ static int parse_uac2_sample_rate_range(struct snd_usb_audio *chip, if (fp->rate_table) fp->rate_table[nr_rates] = rate; - if (!fp->rate_min || rate < fp->rate_min) - fp->rate_min = rate; - if (!fp->rate_max || rate > fp->rate_max) - fp->rate_max = rate; - fp->rates |= snd_pcm_rate_to_rate_bit(rate); - nr_rates++; if (nr_rates >= MAX_NR_RATES) { usb_audio_err(chip, "invalid uac2 rates\n"); @@ -417,6 +423,85 @@ static int line6_parse_audio_format_rates_quirk(struct snd_usb_audio *chip, return -ENODEV; } +/* check whether the given altsetting is supported for the already set rate */ +static bool check_valid_altsetting_v2v3(struct snd_usb_audio *chip, int iface, + int altsetting) +{ + struct usb_device *dev = chip->dev; + __le64 raw_data = 0; + u64 data; + int err; + + /* we assume 64bit is enough for any altsettings */ + if (snd_BUG_ON(altsetting >= 64 - 8)) + return false; + + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + UAC2_AS_VAL_ALT_SETTINGS << 8, + iface, &raw_data, sizeof(raw_data)); + if (err < 0) + return false; + + data = le64_to_cpu(raw_data); + /* first byte contains the bitmap size */ + if ((data & 0xff) * 8 < altsetting) + return false; + if (data & (1ULL << (altsetting + 8))) + return true; + + return false; +} + +/* + * Validate each sample rate with the altsetting + * Rebuild the rate table if only partial values are valid + */ +static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip, + struct audioformat *fp, + int clock) +{ + struct usb_device *dev = chip->dev; + unsigned int *table; + unsigned int nr_rates; + int i, err; + + table = kcalloc(fp->nr_rates, sizeof(*table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + /* clear the interface altsetting at first */ + usb_set_interface(dev, fp->iface, 0); + + nr_rates = 0; + for (i = 0; i < fp->nr_rates; i++) { + err = snd_usb_set_sample_rate_v2v3(chip, fp, clock, + fp->rate_table[i]); + if (err < 0) + continue; + + if (check_valid_altsetting_v2v3(chip, fp->iface, fp->altsetting)) + table[nr_rates++] = fp->rate_table[i]; + } + + if (!nr_rates) { + usb_audio_dbg(chip, + "No valid sample rate available for %d:%d, assuming a firmware bug\n", + fp->iface, fp->altsetting); + nr_rates = fp->nr_rates; /* continue as is */ + } + + if (fp->nr_rates == nr_rates) { + kfree(table); + return 0; + } + + kfree(fp->rate_table); + fp->rate_table = table; + fp->nr_rates = nr_rates; + return 0; +} + /* * parse the format descriptor and stores the possible sample rates * on the audioformat table (audio class v2 and v3). @@ -509,6 +594,12 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, * allocated, so the rates will be stored */ parse_uac2_sample_rate_range(chip, fp, nr_triplets, data); + ret = validate_sample_rate_table_v2v3(chip, fp, clock); + if (ret < 0) + goto err_free; + + set_rate_table_min_max(fp); + err_free: kfree(data); err: diff --git a/sound/usb/helper.c b/sound/usb/helper.c index cf92d7110773..a4410267bf70 100644 --- a/sound/usb/helper.c +++ b/sound/usb/helper.c @@ -121,3 +121,13 @@ unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, return 0; } +struct usb_host_interface * +snd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting) +{ + struct usb_interface *iface; + + iface = usb_ifnum_to_if(chip->dev, ifnum); + if (!iface) + return NULL; + return usb_altnum_to_altsetting(iface, altsetting); +} diff --git a/sound/usb/helper.h b/sound/usb/helper.h index f5b4c6647e4d..e2b51ec96ec6 100644 --- a/sound/usb/helper.h +++ b/sound/usb/helper.h @@ -14,6 +14,9 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip, struct usb_host_interface *alts); +struct usb_host_interface * +snd_usb_get_host_interface(struct snd_usb_audio *chip, int ifnum, int altsetting); + /* * retrieve usb_interface descriptor from the host interface * (conditional for compatibility with the older API) diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c new file mode 100644 index 000000000000..eb3a4c433c3e --- /dev/null +++ b/sound/usb/implicit.c @@ -0,0 +1,405 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Special handling for implicit feedback mode +// + +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/usb/audio.h> +#include <linux/usb/audio-v2.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> + +#include "usbaudio.h" +#include "card.h" +#include "helper.h" +#include "implicit.h" + +enum { + IMPLICIT_FB_NONE, + IMPLICIT_FB_GENERIC, + IMPLICIT_FB_FIXED, +}; + +struct snd_usb_implicit_fb_match { + unsigned int id; + unsigned int iface_class; + unsigned int ep_num; + unsigned int iface; + int type; +}; + +#define IMPLICIT_FB_GENERIC_DEV(vend, prod) \ + { .id = USB_ID(vend, prod), .type = IMPLICIT_FB_GENERIC } +#define IMPLICIT_FB_FIXED_DEV(vend, prod, ep, ifnum) \ + { .id = USB_ID(vend, prod), .type = IMPLICIT_FB_FIXED, .ep_num = (ep),\ + .iface = (ifnum) } +#define IMPLICIT_FB_SKIP_DEV(vend, prod) \ + { .id = USB_ID(vend, prod), .type = IMPLICIT_FB_NONE } + +/* Implicit feedback quirk table for playback */ +static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { + /* Generic matching */ + IMPLICIT_FB_GENERIC_DEV(0x0499, 0x1509), /* Steinberg UR22 */ + IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2080), /* M-Audio FastTrack Ultra */ + IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2081), /* M-Audio FastTrack Ultra */ + IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2030), /* M-Audio Fast Track C400 */ + IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2031), /* M-Audio Fast Track C600 */ + + /* Fixed EP */ + /* FIXME: check the availability of generic matching */ + IMPLICIT_FB_FIXED_DEV(0x1397, 0x0001, 0x81, 1), /* Behringer UFX1604 */ + IMPLICIT_FB_FIXED_DEV(0x1397, 0x0002, 0x81, 1), /* Behringer UFX1204 */ + IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */ + IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0001, 0x81, 2), /* Solid State Logic SSL2 */ + IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0002, 0x81, 2), /* Solid State Logic SSL2+ */ + IMPLICIT_FB_FIXED_DEV(0x0499, 0x172f, 0x81, 2), /* Steinberg UR22C */ + IMPLICIT_FB_FIXED_DEV(0x0d9a, 0x00df, 0x81, 2), /* RTX6001 */ + IMPLICIT_FB_FIXED_DEV(0x22f0, 0x0006, 0x81, 3), /* Allen&Heath Qu-16 */ + IMPLICIT_FB_FIXED_DEV(0x2b73, 0x000a, 0x82, 0), /* Pioneer DJ DJM-900NXS2 */ + IMPLICIT_FB_FIXED_DEV(0x2b73, 0x0017, 0x82, 0), /* Pioneer DJ DJM-250MK2 */ + IMPLICIT_FB_FIXED_DEV(0x1686, 0xf029, 0x82, 2), /* Zoom UAC-2 */ + IMPLICIT_FB_FIXED_DEV(0x2466, 0x8003, 0x86, 2), /* Fractal Audio Axe-Fx II */ + IMPLICIT_FB_FIXED_DEV(0x0499, 0x172a, 0x86, 2), /* Yamaha MODX */ + + /* Special matching */ + { .id = USB_ID(0x07fd, 0x0004), .iface_class = USB_CLASS_AUDIO, + .type = IMPLICIT_FB_NONE }, /* MicroBook IIc */ + /* ep = 0x84, ifnum = 0 */ + { .id = USB_ID(0x07fd, 0x0004), .iface_class = USB_CLASS_VENDOR_SPEC, + .type = IMPLICIT_FB_FIXED, + .ep_num = 0x84, .iface = 0 }, /* MOTU MicroBook II */ + + /* No quirk for playback but with capture quirk (see below) */ + IMPLICIT_FB_SKIP_DEV(0x0582, 0x0130), /* BOSS BR-80 */ + IMPLICIT_FB_SKIP_DEV(0x0582, 0x0189), /* BOSS GT-100v2 */ + IMPLICIT_FB_SKIP_DEV(0x0582, 0x01d6), /* BOSS GT-1 */ + IMPLICIT_FB_SKIP_DEV(0x0582, 0x01d8), /* BOSS Katana */ + IMPLICIT_FB_SKIP_DEV(0x0582, 0x01e5), /* BOSS GT-001 */ + + {} /* terminator */ +}; + +/* Implicit feedback quirk table for capture: only FIXED type */ +static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { + IMPLICIT_FB_FIXED_DEV(0x0582, 0x0130, 0x0d, 0x01), /* BOSS BR-80 */ + IMPLICIT_FB_FIXED_DEV(0x0582, 0x0189, 0x0d, 0x01), /* BOSS GT-100v2 */ + IMPLICIT_FB_FIXED_DEV(0x0582, 0x01d6, 0x0d, 0x01), /* BOSS GT-1 */ + IMPLICIT_FB_FIXED_DEV(0x0582, 0x01d8, 0x0d, 0x01), /* BOSS Katana */ + IMPLICIT_FB_FIXED_DEV(0x0582, 0x01e5, 0x0d, 0x01), /* BOSS GT-001 */ + + {} /* terminator */ +}; + +/* set up sync EP information on the audioformat */ +static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip, + struct audioformat *fmt, + int ep, int ifnum, + const struct usb_host_interface *alts) +{ + struct usb_interface *iface; + + if (!alts) { + iface = usb_ifnum_to_if(chip->dev, ifnum); + if (!iface || iface->num_altsetting < 2) + return 0; + alts = &iface->altsetting[1]; + } + + fmt->sync_ep = ep; + fmt->sync_iface = ifnum; + fmt->sync_altsetting = alts->desc.bAlternateSetting; + fmt->sync_ep_idx = 0; + fmt->implicit_fb = 1; + usb_audio_dbg(chip, + "%d:%d: added %s implicit_fb sync_ep %x, iface %d:%d\n", + fmt->iface, fmt->altsetting, + (ep & USB_DIR_IN) ? "playback" : "capture", + fmt->sync_ep, fmt->sync_iface, fmt->sync_altsetting); + return 1; +} + +/* Check whether the given UAC2 iface:altset points to an implicit fb source */ +static int add_generic_uac2_implicit_fb(struct snd_usb_audio *chip, + struct audioformat *fmt, + unsigned int ifnum, + unsigned int altsetting) +{ + struct usb_host_interface *alts; + struct usb_endpoint_descriptor *epd; + + alts = snd_usb_get_host_interface(chip, ifnum, altsetting); + if (!alts) + return 0; + if (alts->desc.bInterfaceClass != USB_CLASS_AUDIO || + alts->desc.bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING || + alts->desc.bInterfaceProtocol != UAC_VERSION_2 || + alts->desc.bNumEndpoints < 1) + return 0; + epd = get_endpoint(alts, 0); + if (!usb_endpoint_is_isoc_in(epd) || + (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != + USB_ENDPOINT_USAGE_IMPLICIT_FB) + return 0; + return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, + ifnum, alts); +} + +/* Like the function above, but specific to Roland with vendor class and hack */ +static int add_roland_implicit_fb(struct snd_usb_audio *chip, + struct audioformat *fmt, + unsigned int ifnum, + unsigned int altsetting) +{ + struct usb_host_interface *alts; + struct usb_endpoint_descriptor *epd; + + alts = snd_usb_get_host_interface(chip, ifnum, altsetting); + if (!alts) + return 0; + if (alts->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC || + (alts->desc.bInterfaceSubClass != 2 && + alts->desc.bInterfaceProtocol != 2) || + alts->desc.bNumEndpoints < 1) + return 0; + epd = get_endpoint(alts, 0); + if (!usb_endpoint_is_isoc_in(epd) || + (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != + USB_ENDPOINT_USAGE_IMPLICIT_FB) + return 0; + return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, + ifnum, alts); +} + + +static int __add_generic_implicit_fb(struct snd_usb_audio *chip, + struct audioformat *fmt, + int iface, int altset) +{ + struct usb_host_interface *alts; + struct usb_endpoint_descriptor *epd; + + alts = snd_usb_get_host_interface(chip, iface, altset); + if (!alts) + return 0; + + if ((alts->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC && + alts->desc.bInterfaceClass != USB_CLASS_AUDIO) || + alts->desc.bNumEndpoints < 1) + return 0; + epd = get_endpoint(alts, 0); + if (!usb_endpoint_is_isoc_in(epd) || + (epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC) + return 0; + return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, + iface, alts); +} + +/* More generic quirk: look for the sync EP next to the data EP */ +static int add_generic_implicit_fb(struct snd_usb_audio *chip, + struct audioformat *fmt, + struct usb_host_interface *alts) +{ + if ((fmt->ep_attr & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC) + return 0; + + if (__add_generic_implicit_fb(chip, fmt, + alts->desc.bInterfaceNumber + 1, + alts->desc.bAlternateSetting)) + return 1; + return __add_generic_implicit_fb(chip, fmt, + alts->desc.bInterfaceNumber - 1, + alts->desc.bAlternateSetting); +} + +static const struct snd_usb_implicit_fb_match * +find_implicit_fb_entry(struct snd_usb_audio *chip, + const struct snd_usb_implicit_fb_match *match, + const struct usb_host_interface *alts) +{ + for (; match->id; match++) + if (match->id == chip->usb_id && + (!match->iface_class || + (alts->desc.bInterfaceClass == match->iface_class))) + return match; + + return NULL; +} + +/* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk + * applies. Returns 1 if a quirk was found. + */ +static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + struct usb_host_interface *alts) +{ + const struct snd_usb_implicit_fb_match *p; + unsigned int attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; + + p = find_implicit_fb_entry(chip, playback_implicit_fb_quirks, alts); + if (p) { + switch (p->type) { + case IMPLICIT_FB_GENERIC: + return add_generic_implicit_fb(chip, fmt, alts); + case IMPLICIT_FB_NONE: + return 0; /* No quirk */ + case IMPLICIT_FB_FIXED: + return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, + p->iface, NULL); + } + } + + /* Generic UAC2 implicit feedback */ + if (attr == USB_ENDPOINT_SYNC_ASYNC && + alts->desc.bInterfaceClass == USB_CLASS_AUDIO && + alts->desc.bInterfaceProtocol == UAC_VERSION_2 && + alts->desc.bNumEndpoints == 1) { + if (add_generic_uac2_implicit_fb(chip, fmt, + alts->desc.bInterfaceNumber + 1, + alts->desc.bAlternateSetting)) + return 1; + } + + /* Roland/BOSS implicit feedback with vendor spec class */ + if (attr == USB_ENDPOINT_SYNC_ASYNC && + alts->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC && + alts->desc.bInterfaceProtocol == 2 && + alts->desc.bNumEndpoints == 1 && + USB_ID_VENDOR(chip->usb_id) == 0x0582 /* Roland */) { + if (add_roland_implicit_fb(chip, fmt, + alts->desc.bInterfaceNumber + 1, + alts->desc.bAlternateSetting)) + return 1; + } + + /* Try the generic implicit fb if available */ + if (chip->generic_implicit_fb) + return add_generic_implicit_fb(chip, fmt, alts); + + /* No quirk */ + return 0; +} + +/* same for capture, but only handling FIXED entry */ +static int audioformat_capture_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + struct usb_host_interface *alts) +{ + const struct snd_usb_implicit_fb_match *p; + + p = find_implicit_fb_entry(chip, capture_implicit_fb_quirks, alts); + if (p && p->type == IMPLICIT_FB_FIXED) + return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, p->iface, + NULL); + return 0; +} + +/* + * Parse altset and set up implicit feedback endpoint on the audioformat + */ +int snd_usb_parse_implicit_fb_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + struct usb_host_interface *alts) +{ + if (fmt->endpoint & USB_DIR_IN) + return audioformat_capture_quirk(chip, fmt, alts); + else + return audioformat_implicit_fb_quirk(chip, fmt, alts); +} + +/* + * Return the score of matching two audioformats. + * Veto the audioformat if: + * - It has no channels for some reason. + * - Requested PCM format is not supported. + * - Requested sample rate is not supported. + */ +static int match_endpoint_audioformats(struct snd_usb_substream *subs, + const struct audioformat *fp, + int rate, int channels, + snd_pcm_format_t pcm_format) +{ + int i, score; + + if (fp->channels < 1) + return 0; + + if (!(fp->formats & pcm_format_to_bits(pcm_format))) + return 0; + + if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) { + if (rate < fp->rate_min || rate > fp->rate_max) + return 0; + } else { + for (i = 0; i < fp->nr_rates; i++) { + if (fp->rate_table[i] == rate) + break; + } + if (i >= fp->nr_rates) + return 0; + } + + score = 1; + if (fp->channels == channels) + score++; + + return score; +} + +static struct snd_usb_substream * +find_matching_substream(struct snd_usb_audio *chip, int stream, int ep_num, + int fmt_type) +{ + struct snd_usb_stream *as; + struct snd_usb_substream *subs; + + list_for_each_entry(as, &chip->pcm_list, list) { + subs = &as->substream[stream]; + if (as->fmt_type == fmt_type && subs->ep_num == ep_num) + return subs; + } + + return NULL; +} + +/* + * Return the audioformat that is suitable for the implicit fb + */ +const struct audioformat * +snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, + const struct audioformat *target, + const struct snd_pcm_hw_params *params, + int stream) +{ + struct snd_usb_substream *subs; + const struct audioformat *fp, *sync_fmt; + int score, high_score; + + /* When sharing the same altset, use the original audioformat */ + if (target->iface == target->sync_iface && + target->altsetting == target->sync_altsetting) + return target; + + subs = find_matching_substream(chip, stream, target->sync_ep, + target->fmt_type); + if (!subs) + return NULL; + + sync_fmt = NULL; + high_score = 0; + list_for_each_entry(fp, &subs->fmt_list, list) { + score = match_endpoint_audioformats(subs, fp, + params_rate(params), + params_channels(params), + params_format(params)); + if (score > high_score) { + sync_fmt = fp; + high_score = score; + } + } + + return sync_fmt; +} + diff --git a/sound/usb/implicit.h b/sound/usb/implicit.h new file mode 100644 index 000000000000..ccb415a0ea86 --- /dev/null +++ b/sound/usb/implicit.h @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef __USBAUDIO_IMPLICIT_H +#define __USBAUDIO_IMPLICIT_H + +int snd_usb_parse_implicit_fb_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + struct usb_host_interface *alts); +const struct audioformat * +snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, + const struct audioformat *target, + const struct snd_pcm_hw_params *params, + int stream); + +#endif /* __USBAUDIO_IMPLICIT_H */ diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 81e987eaf063..12b15ed59eaa 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -3454,48 +3454,6 @@ static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer) return 0; } -static int keep_iface_ctl_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = mixer->chip->keep_iface; - return 0; -} - -static int keep_iface_ctl_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol); - bool keep_iface = !!ucontrol->value.integer.value[0]; - - if (mixer->chip->keep_iface == keep_iface) - return 0; - mixer->chip->keep_iface = keep_iface; - return 1; -} - -static const struct snd_kcontrol_new keep_iface_ctl = { - .iface = SNDRV_CTL_ELEM_IFACE_CARD, - .name = "Keep Interface", - .info = snd_ctl_boolean_mono_info, - .get = keep_iface_ctl_get, - .put = keep_iface_ctl_put, -}; - -static int create_keep_iface_ctl(struct usb_mixer_interface *mixer) -{ - struct snd_kcontrol *kctl = snd_ctl_new1(&keep_iface_ctl, mixer); - - /* need only one control per card */ - if (snd_ctl_find_id(mixer->chip->card, &kctl->id)) { - snd_ctl_free_one(kctl); - return 0; - } - - return snd_ctl_add(mixer->chip->card, kctl); -} - int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, int ignore_error) { @@ -3548,10 +3506,6 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, if (err < 0) goto _error; - err = create_keep_iface_ctl(mixer); - if (err < 0) - goto _error; - err = snd_usb_mixer_apply_create_quirk(mixer); if (err < 0) goto _error; diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index c369c81e74c4..a7212f16660e 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -561,7 +561,8 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { }, { /* ASUS ROG Strix */ .id = USB_ID(0x0b05, 0x1917), - .map = asus_rog_map, + .map = trx40_mobo_map, + .connector_map = trx40_mobo_connector_map, }, { /* MSI TRX40 Creator */ .id = USB_ID(0x0db0, 0x0d64), diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index 92b1a6d9c931..bd63a9ce6a70 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -607,7 +607,7 @@ static int snd_us16x08_eq_put(struct snd_kcontrol *kcontrol, static int snd_us16x08_meter_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { - uinfo->count = 1; + uinfo->count = 34; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->value.integer.max = 0x7FFF; uinfo->value.integer.min = 0; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index a860303cc522..56079901769f 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -17,13 +17,13 @@ #include "usbaudio.h" #include "card.h" #include "quirks.h" -#include "debug.h" #include "endpoint.h" #include "helper.h" #include "pcm.h" #include "clock.h" #include "power.h" #include "media.h" +#include "implicit.h" #define SUBSTREAM_FLAG_DATA_EP_STARTED 0 #define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 @@ -81,30 +81,34 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream /* * find a matching audio format */ -static struct audioformat *find_format(struct snd_usb_substream *subs) +static const struct audioformat * +find_format(struct list_head *fmt_list_head, snd_pcm_format_t format, + unsigned int rate, unsigned int channels, bool strict_match, + struct snd_usb_substream *subs) { - struct audioformat *fp; - struct audioformat *found = NULL; + const struct audioformat *fp; + const struct audioformat *found = NULL; int cur_attr = 0, attr; - list_for_each_entry(fp, &subs->fmt_list, list) { - if (!(fp->formats & pcm_format_to_bits(subs->pcm_format))) - continue; - if (fp->channels != subs->channels) - continue; - if (subs->cur_rate < fp->rate_min || - subs->cur_rate > fp->rate_max) + list_for_each_entry(fp, fmt_list_head, list) { + if (strict_match) { + if (!(fp->formats & pcm_format_to_bits(format))) + continue; + if (fp->channels != channels) + continue; + } + if (rate < fp->rate_min || rate > fp->rate_max) continue; - if (! (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { + if (!(fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) { unsigned int i; for (i = 0; i < fp->nr_rates; i++) - if (fp->rate_table[i] == subs->cur_rate) + if (fp->rate_table[i] == rate) break; if (i >= fp->nr_rates) continue; } attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE; - if (! found) { + if (!found) { found = fp; cur_attr = attr; continue; @@ -114,7 +118,7 @@ static struct audioformat *find_format(struct snd_usb_substream *subs) * this is a workaround for the case like * M-audio audiophile USB. */ - if (attr != cur_attr) { + if (subs && attr != cur_attr) { if ((attr == USB_ENDPOINT_SYNC_ASYNC && subs->direction == SNDRV_PCM_STREAM_PLAYBACK) || (attr == USB_ENDPOINT_SYNC_ADAPTIVE && @@ -138,36 +142,30 @@ static struct audioformat *find_format(struct snd_usb_substream *subs) return found; } -static int init_pitch_v1(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt) +static const struct audioformat * +find_substream_format(struct snd_usb_substream *subs, + const struct snd_pcm_hw_params *params) +{ + return find_format(&subs->fmt_list, params_format(params), + params_rate(params), params_channels(params), + true, subs); +} + +static int init_pitch_v1(struct snd_usb_audio *chip, int ep) { struct usb_device *dev = chip->dev; - unsigned int ep; unsigned char data[1]; int err; - if (get_iface_desc(alts)->bNumEndpoints < 1) - return -EINVAL; - ep = get_endpoint(alts, 0)->bEndpointAddress; - data[0] = 1; err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, sizeof(data)); - if (err < 0) { - usb_audio_err(chip, "%d:%d: cannot set enable PITCH\n", - iface, ep); - return err; - } - - return 0; + return err; } -static int init_pitch_v2(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt) +static int init_pitch_v2(struct snd_usb_audio *chip, int ep) { struct usb_device *dev = chip->dev; unsigned char data[1]; @@ -178,34 +176,56 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, UAC2_EP_CS_PITCH << 8, 0, data, sizeof(data)); - if (err < 0) { - usb_audio_err(chip, "%d:%d: cannot set enable PITCH (v2)\n", - iface, fmt->altsetting); - return err; - } - - return 0; + return err; } /* * initialize the pitch control and sample rate */ -int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt) +int snd_usb_init_pitch(struct snd_usb_audio *chip, + const struct audioformat *fmt) { + int err; + /* if endpoint doesn't have pitch control, bail out */ if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL)) return 0; + usb_audio_dbg(chip, "enable PITCH for EP 0x%x\n", fmt->endpoint); + switch (fmt->protocol) { case UAC_VERSION_1: + err = init_pitch_v1(chip, fmt->endpoint); + break; + case UAC_VERSION_2: + err = init_pitch_v2(chip, fmt->endpoint); + break; default: - return init_pitch_v1(chip, iface, alts, fmt); + return 0; + } - case UAC_VERSION_2: - return init_pitch_v2(chip, iface, alts, fmt); + if (err < 0) { + usb_audio_err(chip, "failed to enable PITCH for EP 0x%x\n", + fmt->endpoint); + return err; } + + return 0; +} + +static bool stop_endpoints(struct snd_usb_substream *subs) +{ + bool stopped = 0; + + if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { + snd_usb_endpoint_stop(subs->sync_endpoint); + stopped = true; + } + if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { + snd_usb_endpoint_stop(subs->data_endpoint); + stopped = true; + } + return stopped; } static int start_endpoints(struct snd_usb_substream *subs) @@ -216,48 +236,27 @@ static int start_endpoints(struct snd_usb_substream *subs) return -EINVAL; if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { - struct snd_usb_endpoint *ep = subs->data_endpoint; - - dev_dbg(&subs->dev->dev, "Starting data EP @%p\n", ep); - - ep->data_subs = subs; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(subs->data_endpoint); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); - return err; + goto error; } } if (subs->sync_endpoint && !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { - struct snd_usb_endpoint *ep = subs->sync_endpoint; - - if (subs->data_endpoint->iface != subs->sync_endpoint->iface || - subs->data_endpoint->altsetting != subs->sync_endpoint->altsetting) { - err = usb_set_interface(subs->dev, - subs->sync_endpoint->iface, - subs->sync_endpoint->altsetting); - if (err < 0) { - clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); - dev_err(&subs->dev->dev, - "%d:%d: cannot set interface (%d)\n", - subs->sync_endpoint->iface, - subs->sync_endpoint->altsetting, err); - return -EIO; - } - } - - dev_dbg(&subs->dev->dev, "Starting sync EP @%p\n", ep); - - ep->sync_slave = subs->data_endpoint; - err = snd_usb_endpoint_start(ep); + err = snd_usb_endpoint_start(subs->sync_endpoint); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); - return err; + goto error; } } return 0; + + error: + stop_endpoints(subs); + return err; } static void sync_pending_stops(struct snd_usb_substream *subs) @@ -266,15 +265,6 @@ static void sync_pending_stops(struct snd_usb_substream *subs) snd_usb_endpoint_sync_pending_stop(subs->data_endpoint); } -static void stop_endpoints(struct snd_usb_substream *subs) -{ - if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) - snd_usb_endpoint_stop(subs->sync_endpoint); - - if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) - snd_usb_endpoint_stop(subs->data_endpoint); -} - /* PCM sync_stop callback */ static int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream) { @@ -287,193 +277,42 @@ static int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream) return 0; } -static int search_roland_implicit_fb(struct usb_device *dev, int ifnum, - unsigned int altsetting, - struct usb_host_interface **alts, - unsigned int *ep) -{ - struct usb_interface *iface; - struct usb_interface_descriptor *altsd; - struct usb_endpoint_descriptor *epd; - - iface = usb_ifnum_to_if(dev, ifnum); - if (!iface || iface->num_altsetting < altsetting + 1) - return -ENOENT; - *alts = &iface->altsetting[altsetting]; - altsd = get_iface_desc(*alts); - if (altsd->bAlternateSetting != altsetting || - altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC || - (altsd->bInterfaceSubClass != 2 && - altsd->bInterfaceProtocol != 2 ) || - altsd->bNumEndpoints < 1) - return -ENOENT; - epd = get_endpoint(*alts, 0); - if (!usb_endpoint_is_isoc_in(epd) || - (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != - USB_ENDPOINT_USAGE_IMPLICIT_FB) - return -ENOENT; - *ep = epd->bEndpointAddress; - return 0; -} - -/* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk - * applies. Returns 1 if a quirk was found. - */ -static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, - struct usb_device *dev, - struct usb_interface_descriptor *altsd, - unsigned int attr) +/* Set up sync endpoint */ +int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, + struct audioformat *fmt) { + struct usb_device *dev = chip->dev; struct usb_host_interface *alts; - struct usb_interface *iface; - unsigned int ep; - unsigned int ifnum; - - /* Implicit feedback sync EPs consumers are always playback EPs */ - if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK) - return 0; - - switch (subs->stream->chip->usb_id) { - case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ - case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ - case USB_ID(0x22f0, 0x0006): /* Allen&Heath Qu-16 */ - ep = 0x81; - ifnum = 3; - goto add_sync_ep_from_ifnum; - case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ - case USB_ID(0x0763, 0x2081): - ep = 0x81; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x2466, 0x8003): /* Fractal Audio Axe-Fx II */ - case USB_ID(0x0499, 0x172a): /* Yamaha MODX */ - ep = 0x86; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx III */ - ep = 0x81; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x1686, 0xf029): /* Zoom UAC-2 */ - ep = 0x82; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */ - case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ - ep = 0x81; - ifnum = 1; - goto add_sync_ep_from_ifnum; - case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II/IIc */ - /* MicroBook IIc */ - if (altsd->bInterfaceClass == USB_CLASS_AUDIO) - return 0; + struct usb_interface_descriptor *altsd; + unsigned int ep, attr, sync_attr; + bool is_playback; + int err; - /* MicroBook II */ - ep = 0x84; - ifnum = 0; - goto add_sync_ep_from_ifnum; - case USB_ID(0x07fd, 0x0008): /* MOTU M Series */ - case USB_ID(0x31e9, 0x0001): /* Solid State Logic SSL2 */ - case USB_ID(0x31e9, 0x0002): /* Solid State Logic SSL2+ */ - case USB_ID(0x0499, 0x172f): /* Steinberg UR22C */ - case USB_ID(0x0d9a, 0x00df): /* RTX6001 */ - ep = 0x81; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */ - case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */ - ep = 0x82; - ifnum = 0; - goto add_sync_ep_from_ifnum; - case USB_ID(0x0582, 0x01d8): /* BOSS Katana */ - /* BOSS Katana amplifiers do not need quirks */ + alts = snd_usb_get_host_interface(chip, fmt->iface, fmt->altsetting); + if (!alts) return 0; - } - - if (attr == USB_ENDPOINT_SYNC_ASYNC && - altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && - altsd->bInterfaceProtocol == 2 && - altsd->bNumEndpoints == 1 && - USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ && - search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1, - altsd->bAlternateSetting, - &alts, &ep) >= 0) { - goto add_sync_ep; - } - - /* No quirk */ - return 0; - -add_sync_ep_from_ifnum: - iface = usb_ifnum_to_if(dev, ifnum); - - if (!iface || iface->num_altsetting < 2) - return -EINVAL; - - alts = &iface->altsetting[1]; - -add_sync_ep: - subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, - alts, ep, !subs->direction, - SND_USB_ENDPOINT_TYPE_DATA); - if (!subs->sync_endpoint) - return -EINVAL; - - subs->sync_endpoint->is_implicit_feedback = 1; - - subs->data_endpoint->sync_master = subs->sync_endpoint; - - return 1; -} + altsd = get_iface_desc(alts); -static int set_sync_endpoint(struct snd_usb_substream *subs, - struct audioformat *fmt, - struct usb_device *dev, - struct usb_host_interface *alts, - struct usb_interface_descriptor *altsd) -{ - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - unsigned int ep, attr; - bool implicit_fb; - int err; + err = snd_usb_parse_implicit_fb_quirk(chip, fmt, alts); + if (err > 0) + return 0; /* matched */ - /* we need a sync pipe in async OUT or adaptive IN mode */ - /* check the number of EP, since some devices have broken - * descriptors which fool us. if it has only one EP, - * assume it as adaptive-out or sync-in. + /* + * Generic sync EP handling */ - attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; - - if ((is_playback && (attr != USB_ENDPOINT_SYNC_ASYNC)) || - (!is_playback && (attr != USB_ENDPOINT_SYNC_ADAPTIVE))) { - - /* - * In these modes the notion of sync_endpoint is irrelevant. - * Reset pointers to avoid using stale data from previously - * used settings, e.g. when configuration and endpoints were - * changed - */ - - subs->sync_endpoint = NULL; - subs->data_endpoint->sync_master = NULL; - } - - err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr); - if (err < 0) - return err; - - /* endpoint set by quirk */ - if (err > 0) - return 0; if (altsd->bNumEndpoints < 2) return 0; + is_playback = !(get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); + attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; if ((is_playback && (attr == USB_ENDPOINT_SYNC_SYNC || attr == USB_ENDPOINT_SYNC_ADAPTIVE)) || (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE)) return 0; + sync_attr = get_endpoint(alts, 1)->bmAttributes; + /* * In case of illegal SYNC_NONE for OUT endpoint, we keep going to see * if we don't find a sync endpoint, as on M-Audio Transit. In case of @@ -484,7 +323,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, /* ... and check descriptor size before accessing bSynchAddress because there is a version of the SB Audigy 2 NX firmware lacking the audio fields in the endpoint descriptors */ - if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || + if ((sync_attr & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && get_endpoint(alts, 1)->bSynchAddress != 0)) { dev_err(&dev->dev, @@ -511,257 +350,20 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, return -EINVAL; } - implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK) - == USB_ENDPOINT_USAGE_IMPLICIT_FB; + fmt->sync_ep = ep; + fmt->sync_iface = altsd->bInterfaceNumber; + fmt->sync_altsetting = altsd->bAlternateSetting; + fmt->sync_ep_idx = 1; + if ((sync_attr & USB_ENDPOINT_USAGE_MASK) == USB_ENDPOINT_USAGE_IMPLICIT_FB) + fmt->implicit_fb = 1; - subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, - alts, ep, !subs->direction, - implicit_fb ? - SND_USB_ENDPOINT_TYPE_DATA : - SND_USB_ENDPOINT_TYPE_SYNC); - - if (!subs->sync_endpoint) { - if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) - return 0; - return -EINVAL; - } - - subs->sync_endpoint->is_implicit_feedback = implicit_fb; - - subs->data_endpoint->sync_master = subs->sync_endpoint; + dev_dbg(&dev->dev, "%d:%d: found sync_ep=0x%x, iface=%d, alt=%d, implicit_fb=%d\n", + fmt->iface, fmt->altsetting, fmt->sync_ep, fmt->sync_iface, + fmt->sync_altsetting, fmt->implicit_fb); return 0; } -/* - * find a matching format and set up the interface - */ -static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) -{ - struct usb_device *dev = subs->dev; - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - struct usb_interface *iface; - int err; - - iface = usb_ifnum_to_if(dev, fmt->iface); - if (WARN_ON(!iface)) - return -EINVAL; - alts = usb_altnum_to_altsetting(iface, fmt->altsetting); - if (WARN_ON(!alts)) - return -EINVAL; - altsd = get_iface_desc(alts); - - if (fmt == subs->cur_audiofmt && !subs->need_setup_fmt) - return 0; - - /* close the old interface */ - if (subs->interface >= 0 && (subs->interface != fmt->iface || subs->need_setup_fmt)) { - if (!subs->stream->chip->keep_iface) { - err = usb_set_interface(subs->dev, subs->interface, 0); - if (err < 0) { - dev_err(&dev->dev, - "%d:%d: return to setting 0 failed (%d)\n", - fmt->iface, fmt->altsetting, err); - return -EIO; - } - } - subs->interface = -1; - subs->altset_idx = 0; - } - - if (subs->need_setup_fmt) - subs->need_setup_fmt = false; - - /* set interface */ - if (iface->cur_altsetting != alts) { - err = snd_usb_select_mode_quirk(subs, fmt); - if (err < 0) - return -EIO; - - err = usb_set_interface(dev, fmt->iface, fmt->altsetting); - if (err < 0) { - dev_err(&dev->dev, - "%d:%d: usb_set_interface failed (%d)\n", - fmt->iface, fmt->altsetting, err); - return -EIO; - } - dev_dbg(&dev->dev, "setting usb interface %d:%d\n", - fmt->iface, fmt->altsetting); - snd_usb_set_interface_quirk(dev); - } - - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, - alts, fmt->endpoint, subs->direction, - SND_USB_ENDPOINT_TYPE_DATA); - - if (!subs->data_endpoint) - return -EINVAL; - - err = set_sync_endpoint(subs, fmt, dev, alts, altsd); - if (err < 0) - return err; - - err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt); - if (err < 0) - return err; - - subs->cur_audiofmt = fmt; - - snd_usb_set_format_quirk(subs, fmt); - - return 0; -} - -/* - * Return the score of matching two audioformats. - * Veto the audioformat if: - * - It has no channels for some reason. - * - Requested PCM format is not supported. - * - Requested sample rate is not supported. - */ -static int match_endpoint_audioformats(struct snd_usb_substream *subs, - struct audioformat *fp, - struct audioformat *match, int rate, - snd_pcm_format_t pcm_format) -{ - int i; - int score = 0; - - if (fp->channels < 1) { - dev_dbg(&subs->dev->dev, - "%s: (fmt @%p) no channels\n", __func__, fp); - return 0; - } - - if (!(fp->formats & pcm_format_to_bits(pcm_format))) { - dev_dbg(&subs->dev->dev, - "%s: (fmt @%p) no match for format %d\n", __func__, - fp, pcm_format); - return 0; - } - - for (i = 0; i < fp->nr_rates; i++) { - if (fp->rate_table[i] == rate) { - score++; - break; - } - } - if (!score) { - dev_dbg(&subs->dev->dev, - "%s: (fmt @%p) no match for rate %d\n", __func__, - fp, rate); - return 0; - } - - if (fp->channels == match->channels) - score++; - - dev_dbg(&subs->dev->dev, - "%s: (fmt @%p) score %d\n", __func__, fp, score); - - return score; -} - -/* - * Configure the sync ep using the rate and pcm format of the data ep. - */ -static int configure_sync_endpoint(struct snd_usb_substream *subs) -{ - int ret; - struct audioformat *fp; - struct audioformat *sync_fp = NULL; - int cur_score = 0; - int sync_period_bytes = subs->period_bytes; - struct snd_usb_substream *sync_subs = - &subs->stream->substream[subs->direction ^ 1]; - - if (subs->sync_endpoint->type != SND_USB_ENDPOINT_TYPE_DATA || - !subs->stream) - return snd_usb_endpoint_set_params(subs->sync_endpoint, - subs->pcm_format, - subs->channels, - subs->period_bytes, - 0, 0, - subs->cur_rate, - subs->cur_audiofmt, - NULL); - - /* Try to find the best matching audioformat. */ - list_for_each_entry(fp, &sync_subs->fmt_list, list) { - int score = match_endpoint_audioformats(subs, - fp, subs->cur_audiofmt, - subs->cur_rate, subs->pcm_format); - - if (score > cur_score) { - sync_fp = fp; - cur_score = score; - } - } - - if (unlikely(sync_fp == NULL)) { - dev_err(&subs->dev->dev, - "%s: no valid audioformat for sync ep %x found\n", - __func__, sync_subs->ep_num); - return -EINVAL; - } - - /* - * Recalculate the period bytes if channel number differ between - * data and sync ep audioformat. - */ - if (sync_fp->channels != subs->channels) { - sync_period_bytes = (subs->period_bytes / subs->channels) * - sync_fp->channels; - dev_dbg(&subs->dev->dev, - "%s: adjusted sync ep period bytes (%d -> %d)\n", - __func__, subs->period_bytes, sync_period_bytes); - } - - ret = snd_usb_endpoint_set_params(subs->sync_endpoint, - subs->pcm_format, - sync_fp->channels, - sync_period_bytes, - 0, 0, - subs->cur_rate, - sync_fp, - NULL); - - return ret; -} - -/* - * configure endpoint params - * - * called during initial setup and upon resume - */ -static int configure_endpoint(struct snd_usb_substream *subs) -{ - int ret; - - /* format changed */ - stop_endpoints(subs); - sync_pending_stops(subs); - ret = snd_usb_endpoint_set_params(subs->data_endpoint, - subs->pcm_format, - subs->channels, - subs->period_bytes, - subs->period_frames, - subs->buffer_periods, - subs->cur_rate, - subs->cur_audiofmt, - subs->sync_endpoint); - if (ret < 0) - return ret; - - if (subs->sync_endpoint) - ret = configure_sync_endpoint(subs); - - return ret; -} - static int snd_usb_pcm_change_state(struct snd_usb_substream *subs, int state) { int ret; @@ -810,6 +412,45 @@ int snd_usb_pcm_resume(struct snd_usb_stream *as) return 0; } +static void close_endpoints(struct snd_usb_audio *chip, + struct snd_usb_substream *subs) +{ + if (subs->data_endpoint) { + snd_usb_endpoint_set_sync(chip, subs->data_endpoint, NULL); + snd_usb_endpoint_close(chip, subs->data_endpoint); + subs->data_endpoint = NULL; + } + + if (subs->sync_endpoint) { + snd_usb_endpoint_close(chip, subs->sync_endpoint); + subs->sync_endpoint = NULL; + } +} + +static int configure_endpoints(struct snd_usb_audio *chip, + struct snd_usb_substream *subs) +{ + int err; + + if (subs->data_endpoint->need_setup) { + /* stop any running stream beforehand */ + if (stop_endpoints(subs)) + sync_pending_stops(subs); + err = snd_usb_endpoint_configure(chip, subs->data_endpoint); + if (err < 0) + return err; + snd_usb_set_format_quirk(subs, subs->cur_audiofmt); + } + + if (subs->sync_endpoint) { + err = snd_usb_endpoint_configure(chip, subs->sync_endpoint); + if (err < 0) + return err; + } + + return 0; +} + /* * hw_params callback * @@ -824,30 +465,44 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct snd_usb_substream *subs = substream->runtime->private_data; - struct audioformat *fmt; + struct snd_usb_audio *chip = subs->stream->chip; + const struct audioformat *fmt; + const struct audioformat *sync_fmt; int ret; ret = snd_media_start_pipeline(subs); if (ret) return ret; - subs->pcm_format = params_format(hw_params); - subs->period_bytes = params_period_bytes(hw_params); - subs->period_frames = params_period_size(hw_params); - subs->buffer_periods = params_periods(hw_params); - subs->channels = params_channels(hw_params); - subs->cur_rate = params_rate(hw_params); - - fmt = find_format(subs); + fmt = find_substream_format(subs, hw_params); if (!fmt) { - dev_dbg(&subs->dev->dev, - "cannot set format: format = %#x, rate = %d, channels = %d\n", - subs->pcm_format, subs->cur_rate, subs->channels); + usb_audio_dbg(chip, + "cannot find format: format=%s, rate=%d, channels=%d\n", + snd_pcm_format_name(params_format(hw_params)), + params_rate(hw_params), params_channels(hw_params)); ret = -EINVAL; goto stop_pipeline; } - ret = snd_usb_lock_shutdown(subs->stream->chip); + if (fmt->implicit_fb) { + sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt, + hw_params, + !substream->stream); + if (!sync_fmt) { + usb_audio_dbg(chip, + "cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n", + fmt->sync_ep, fmt->sync_iface, + fmt->sync_altsetting, + snd_pcm_format_name(params_format(hw_params)), + params_rate(hw_params), params_channels(hw_params)); + ret = -EINVAL; + goto stop_pipeline; + } + } else { + sync_fmt = fmt; + } + + ret = snd_usb_lock_shutdown(chip); if (ret < 0) goto stop_pipeline; @@ -855,22 +510,47 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto unlock; - ret = set_format(subs, fmt); - if (ret < 0) + if (subs->data_endpoint) { + if (snd_usb_endpoint_compatible(chip, subs->data_endpoint, + fmt, hw_params)) + goto unlock; + close_endpoints(chip, subs); + } + + subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false); + if (!subs->data_endpoint) { + ret = -EINVAL; goto unlock; + } - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - subs->need_setup_ep = true; + if (fmt->sync_ep) { + subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt, + hw_params, + fmt == sync_fmt); + if (!subs->sync_endpoint) { + ret = -EINVAL; + goto unlock; + } + + snd_usb_endpoint_set_sync(chip, subs->data_endpoint, + subs->sync_endpoint); + } + + mutex_lock(&chip->mutex); + subs->cur_audiofmt = fmt; + mutex_unlock(&chip->mutex); + + ret = configure_endpoints(chip, subs); unlock: - snd_usb_unlock_shutdown(subs->stream->chip); if (ret < 0) - goto stop_pipeline; - return ret; + close_endpoints(chip, subs); + snd_usb_unlock_shutdown(chip); stop_pipeline: - snd_media_stop_pipeline(subs); + if (ret < 0) + snd_media_stop_pipeline(subs); + return ret; } @@ -882,17 +562,17 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, static int snd_usb_hw_free(struct snd_pcm_substream *substream) { struct snd_usb_substream *subs = substream->runtime->private_data; + struct snd_usb_audio *chip = subs->stream->chip; snd_media_stop_pipeline(subs); + mutex_lock(&chip->mutex); subs->cur_audiofmt = NULL; - subs->cur_rate = 0; - subs->period_bytes = 0; - if (!snd_usb_lock_shutdown(subs->stream->chip)) { - stop_endpoints(subs); - sync_pending_stops(subs); - snd_usb_endpoint_deactivate(subs->sync_endpoint); - snd_usb_endpoint_deactivate(subs->data_endpoint); - snd_usb_unlock_shutdown(subs->stream->chip); + mutex_unlock(&chip->mutex); + if (!snd_usb_lock_shutdown(chip)) { + if (stop_endpoints(subs)) + sync_pending_stops(subs); + close_endpoints(chip, subs); + snd_usb_unlock_shutdown(chip); } return 0; @@ -907,16 +587,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = runtime->private_data; - struct usb_host_interface *alts; - struct usb_interface *iface; + struct snd_usb_audio *chip = subs->stream->chip; int ret; - if (! subs->cur_audiofmt) { - dev_err(&subs->dev->dev, "no format is specified!\n"); - return -ENXIO; - } - - ret = snd_usb_lock_shutdown(subs->stream->chip); + ret = snd_usb_lock_shutdown(chip); if (ret < 0) return ret; if (snd_BUG_ON(!subs->data_endpoint)) { @@ -924,38 +598,10 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) goto unlock; } - ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0); - if (ret < 0) - goto unlock; - - ret = set_format(subs, subs->cur_audiofmt); + ret = configure_endpoints(chip, subs); if (ret < 0) goto unlock; - if (subs->need_setup_ep) { - - iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface); - alts = &iface->altsetting[subs->cur_audiofmt->altset_idx]; - ret = snd_usb_init_sample_rate(subs->stream->chip, - subs->cur_audiofmt->iface, - alts, - subs->cur_audiofmt, - subs->cur_rate); - if (ret < 0) - goto unlock; - - ret = configure_endpoint(subs); - if (ret < 0) - goto unlock; - subs->need_setup_ep = false; - } - - /* some unit conversions in runtime */ - subs->data_endpoint->maxframesize = - bytes_to_frames(runtime, subs->data_endpoint->maxpacksize); - subs->data_endpoint->curframesize = - bytes_to_frames(runtime, subs->data_endpoint->curpacksize); - /* reset the pointer */ subs->hwptr_done = 0; subs->transfer_done = 0; @@ -969,10 +615,20 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) ret = start_endpoints(subs); unlock: - snd_usb_unlock_shutdown(subs->stream->chip); + snd_usb_unlock_shutdown(chip); return ret; } +/* + * h/w constraints + */ + +#ifdef HW_CONST_DEBUG +#define hwc_debug(fmt, args...) pr_debug(fmt, ##args) +#else +#define hwc_debug(fmt, args...) do { } while(0) +#endif + static const struct snd_pcm_hardware snd_usb_hardware = { .info = SNDRV_PCM_INFO_MMAP | @@ -981,6 +637,8 @@ static const struct snd_pcm_hardware snd_usb_hardware = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE, + .channels_min = 1, + .channels_max = 256, .buffer_bytes_max = 1024 * 1024, .period_bytes_min = 64, .period_bytes_max = 512 * 1024, @@ -990,7 +648,7 @@ static const struct snd_pcm_hardware snd_usb_hardware = static int hw_check_valid_format(struct snd_usb_substream *subs, struct snd_pcm_hw_params *params, - struct audioformat *fp) + const struct audioformat *fp) { struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *ct = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); @@ -1033,33 +691,12 @@ static int hw_check_valid_format(struct snd_usb_substream *subs, return 1; } -static int hw_rule_rate(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) +static int apply_hw_params_minmax(struct snd_interval *it, unsigned int rmin, + unsigned int rmax) { - struct snd_usb_substream *subs = rule->private; - struct audioformat *fp; - struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - unsigned int rmin, rmax; int changed; - hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); - changed = 0; - rmin = rmax = 0; - list_for_each_entry(fp, &subs->fmt_list, list) { - if (!hw_check_valid_format(subs, params, fp)) - continue; - if (changed++) { - if (rmin > fp->rate_min) - rmin = fp->rate_min; - if (rmax < fp->rate_max) - rmax = fp->rate_max; - } else { - rmin = fp->rate_min; - rmax = fp->rate_max; - } - } - - if (!changed) { + if (rmin > rmax) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; @@ -1084,63 +721,65 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, return changed; } +static int hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_usb_substream *subs = rule->private; + const struct audioformat *fp; + struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + unsigned int rmin, rmax, r; + int i; + + hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); + rmin = UINT_MAX; + rmax = 0; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (!hw_check_valid_format(subs, params, fp)) + continue; + if (fp->rate_table && fp->nr_rates) { + for (i = 0; i < fp->nr_rates; i++) { + r = fp->rate_table[i]; + if (!snd_interval_test(it, r)) + continue; + rmin = min(rmin, r); + rmax = max(rmax, r); + } + } else { + rmin = min(rmin, fp->rate_min); + rmax = max(rmax, fp->rate_max); + } + } + + return apply_hw_params_minmax(it, rmin, rmax); +} + static int hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_usb_substream *subs = rule->private; - struct audioformat *fp; + const struct audioformat *fp; struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); unsigned int rmin, rmax; - int changed; hwc_debug("hw_rule_channels: (%d,%d)\n", it->min, it->max); - changed = 0; - rmin = rmax = 0; + rmin = UINT_MAX; + rmax = 0; list_for_each_entry(fp, &subs->fmt_list, list) { if (!hw_check_valid_format(subs, params, fp)) continue; - if (changed++) { - if (rmin > fp->channels) - rmin = fp->channels; - if (rmax < fp->channels) - rmax = fp->channels; - } else { - rmin = fp->channels; - rmax = fp->channels; - } - } - - if (!changed) { - hwc_debug(" --> get empty\n"); - it->empty = 1; - return -EINVAL; + rmin = min(rmin, fp->channels); + rmax = max(rmax, fp->channels); } - changed = 0; - if (it->min < rmin) { - it->min = rmin; - it->openmin = 0; - changed = 1; - } - if (it->max > rmax) { - it->max = rmax; - it->openmax = 0; - changed = 1; - } - if (snd_interval_checkempty(it)) { - it->empty = 1; - return -EINVAL; - } - hwc_debug(" --> (%d, %d) (changed = %d)\n", it->min, it->max, changed); - return changed; + return apply_hw_params_minmax(it, rmin, rmax); } static int hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_usb_substream *subs = rule->private; - struct audioformat *fp; + const struct audioformat *fp; struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); u64 fbits; u32 oldbits[2]; @@ -1171,11 +810,10 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_usb_substream *subs = rule->private; - struct audioformat *fp; + const struct audioformat *fp; struct snd_interval *it; unsigned char min_datainterval; unsigned int pmin; - int changed; it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME); hwc_debug("hw_rule_period_time: (%u,%u)\n", it->min, it->max); @@ -1191,64 +829,69 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params, return -EINVAL; } pmin = 125 * (1 << min_datainterval); - changed = 0; - if (it->min < pmin) { - it->min = pmin; - it->openmin = 0; - changed = 1; - } - if (snd_interval_checkempty(it)) { - it->empty = 1; - return -EINVAL; - } - hwc_debug(" --> (%u,%u) (changed = %d)\n", it->min, it->max, changed); - return changed; + + return apply_hw_params_minmax(it, pmin, UINT_MAX); } -/* - * If the device supports unusual bit rates, does the request meet these? - */ -static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, - struct snd_usb_substream *subs) +/* apply PCM hw constraints from the concurrent sync EP */ +static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime, + struct snd_usb_substream *subs) { - struct audioformat *fp; - int *rate_list; - int count = 0, needs_knot = 0; + struct snd_usb_audio *chip = subs->stream->chip; + struct snd_usb_endpoint *ep; + const struct audioformat *fp; int err; - kfree(subs->rate_list.list); - subs->rate_list.list = NULL; - list_for_each_entry(fp, &subs->fmt_list, list) { - if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) - return 0; - count += fp->nr_rates; - if (fp->rates & SNDRV_PCM_RATE_KNOT) - needs_knot = 1; + ep = snd_usb_get_endpoint(chip, fp->endpoint); + if (ep && ep->cur_rate) + goto found; + if (!fp->implicit_fb) + continue; + /* for the implicit fb, check the sync ep as well */ + ep = snd_usb_get_endpoint(chip, fp->sync_ep); + if (ep && ep->cur_rate) + goto found; } - if (!needs_knot) - return 0; + return 0; - subs->rate_list.list = rate_list = - kmalloc_array(count, sizeof(int), GFP_KERNEL); - if (!subs->rate_list.list) - return -ENOMEM; - subs->rate_list.count = count; - subs->rate_list.mask = 0; - count = 0; - list_for_each_entry(fp, &subs->fmt_list, list) { - int i; - for (i = 0; i < fp->nr_rates; i++) - rate_list[count++] = fp->rate_table[i]; + found: + if (!find_format(&subs->fmt_list, ep->cur_format, ep->cur_rate, + ep->cur_channels, false, NULL)) { + usb_audio_dbg(chip, "EP 0x%x being used, but not applicable\n", + ep->ep_num); + return 0; } - err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &subs->rate_list); + + usb_audio_dbg(chip, "EP 0x%x being used, using fixed params:\n", + ep->ep_num); + usb_audio_dbg(chip, "rate=%d, period_size=%d, periods=%d\n", + ep->cur_rate, ep->cur_period_frames, + ep->cur_buffer_periods); + + runtime->hw.formats = subs->formats; + runtime->hw.rate_min = runtime->hw.rate_max = ep->cur_rate; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + runtime->hw.periods_min = runtime->hw.periods_max = + ep->cur_buffer_periods; + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + -1); if (err < 0) return err; - return 0; -} + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + ep->cur_period_frames, + ep->cur_period_frames); + if (err < 0) + return err; + return 1; /* notify the finding */ +} /* * set up the runtime hardware information. @@ -1256,11 +899,20 @@ static int snd_usb_pcm_check_knot(struct snd_pcm_runtime *runtime, static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) { - struct audioformat *fp; + struct snd_usb_audio *chip = subs->stream->chip; + const struct audioformat *fp; unsigned int pt, ptmin; - int param_period_time_if_needed; + int param_period_time_if_needed = -1; int err; + mutex_lock(&chip->mutex); + err = apply_hw_constraint_from_sync(runtime, subs); + mutex_unlock(&chip->mutex); + if (err < 0) + return err; + if (err > 0) /* found the matching? */ + goto add_extra_rules; + runtime->hw.formats = subs->formats; runtime->hw.rate_min = 0x7fffffff; @@ -1311,6 +963,8 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre -1); if (err < 0) return err; + +add_extra_rules: err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, hw_rule_channels, subs, SNDRV_PCM_HW_PARAM_FORMAT, @@ -1338,11 +992,8 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre if (err < 0) return err; } - err = snd_usb_pcm_check_knot(runtime, subs); - if (err < 0) - return err; - return snd_usb_autoresume(subs->stream->chip); + return 0; } static int snd_usb_pcm_open(struct snd_pcm_substream *substream) @@ -1353,8 +1004,6 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) struct snd_usb_substream *subs = &as->substream[direction]; int ret; - subs->interface = -1; - subs->altset_idx = 0; runtime->hw = snd_usb_hardware; runtime->private_data = subs; subs->pcm_substream = substream; @@ -1366,11 +1015,14 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream) subs->dsd_dop.marker = 1; ret = setup_hw_info(runtime, subs); - if (ret == 0) { - ret = snd_media_stream_init(subs, as->pcm, direction); - if (ret) - snd_usb_autosuspend(subs->stream->chip); - } + if (ret < 0) + return ret; + ret = snd_usb_autoresume(subs->stream->chip); + if (ret < 0) + return ret; + ret = snd_media_stream_init(subs, as->pcm, direction); + if (ret < 0) + snd_usb_autosuspend(subs->stream->chip); return ret; } @@ -1383,11 +1035,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream) snd_media_stop_pipeline(subs); - if (!as->chip->keep_iface && - subs->interface >= 0 && - !snd_usb_lock_shutdown(subs->stream->chip)) { - usb_set_interface(subs->dev, subs->interface, 0); - subs->interface = -1; + if (!snd_usb_lock_shutdown(subs->stream->chip)) { ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1); snd_usb_unlock_shutdown(subs->stream->chip); if (ret < 0) @@ -1603,13 +1251,7 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, spin_lock_irqsave(&subs->lock, flags); subs->frame_limit += ep->max_urb_frames; for (i = 0; i < ctx->packets; i++) { - if (ctx->packet_size[i]) - counts = ctx->packet_size[i]; - else if (ep->sync_master) - counts = snd_usb_endpoint_slave_next_packet_size(ep); - else - counts = snd_usb_endpoint_next_packet_size(ep); - + counts = snd_usb_endpoint_next_packet_size(ep, ctx, i); /* set up descriptor */ urb->iso_frame_desc[i].offset = frames * ep->stride; urb->iso_frame_desc[i].length = counts * ep->stride; @@ -1649,10 +1291,10 @@ static void prepare_playback_urb(struct snd_usb_substream *subs, } bytes = frames * ep->stride; - if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && + if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U16_LE && subs->cur_audiofmt->dsd_dop)) { fill_playback_urb_dsd_dop(subs, urb, bytes); - } else if (unlikely(subs->pcm_format == SNDRV_PCM_FORMAT_DSD_U8 && + } else if (unlikely(ep->cur_format == SNDRV_PCM_FORMAT_DSD_U8 && subs->cur_audiofmt->dsd_bitrev)) { /* bit-reverse the bytes */ u8 *buf = urb->transfer_buffer; @@ -1760,27 +1402,36 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea subs->trigger_tstamp_pending_update = true; fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - subs->data_endpoint->prepare_data_urb = prepare_playback_urb; - subs->data_endpoint->retire_data_urb = retire_playback_urb; + snd_usb_endpoint_set_callback(subs->data_endpoint, + prepare_playback_urb, + retire_playback_urb, + subs); subs->running = 1; + dev_dbg(&subs->dev->dev, "%d:%d Start Playback PCM\n", + subs->cur_audiofmt->iface, + subs->cur_audiofmt->altsetting); return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs); + snd_usb_endpoint_set_callback(subs->data_endpoint, + NULL, NULL, NULL); subs->running = 0; + dev_dbg(&subs->dev->dev, "%d:%d Stop Playback PCM\n", + subs->cur_audiofmt->iface, + subs->cur_audiofmt->altsetting); return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - subs->data_endpoint->prepare_data_urb = NULL; /* keep retire_data_urb for delay calculation */ - subs->data_endpoint->retire_data_urb = retire_playback_urb; + snd_usb_endpoint_set_callback(subs->data_endpoint, + NULL, + retire_playback_urb, + subs); subs->running = 0; + dev_dbg(&subs->dev->dev, "%d:%d Pause Playback PCM\n", + subs->cur_audiofmt->iface, + subs->cur_audiofmt->altsetting); return 0; - case SNDRV_PCM_TRIGGER_SUSPEND: - if (subs->stream->chip->setup_fmt_after_resume_quirk) { - stop_endpoints(subs); - subs->need_setup_fmt = true; - return 0; - } - break; } return -EINVAL; @@ -1797,30 +1448,28 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream err = start_endpoints(subs); if (err < 0) return err; - - subs->data_endpoint->retire_data_urb = retire_capture_urb; + fallthrough; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + snd_usb_endpoint_set_callback(subs->data_endpoint, + NULL, retire_capture_urb, + subs); subs->running = 1; + dev_dbg(&subs->dev->dev, "%d:%d Start Capture PCM\n", + subs->cur_audiofmt->iface, + subs->cur_audiofmt->altsetting); return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs); - subs->data_endpoint->retire_data_urb = NULL; - subs->running = 0; - return 0; + fallthrough; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - subs->data_endpoint->retire_data_urb = NULL; + snd_usb_endpoint_set_callback(subs->data_endpoint, + NULL, NULL, NULL); subs->running = 0; + dev_dbg(&subs->dev->dev, "%d:%d Stop Capture PCM\n", + subs->cur_audiofmt->iface, + subs->cur_audiofmt->altsetting); return 0; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - subs->data_endpoint->retire_data_urb = retire_capture_urb; - subs->running = 1; - return 0; - case SNDRV_PCM_TRIGGER_SUSPEND: - if (subs->stream->chip->setup_fmt_after_resume_quirk) { - stop_endpoints(subs); - subs->need_setup_fmt = true; - return 0; - } - break; } return -EINVAL; diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index 9833627c1eca..06c586467d3f 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -9,10 +9,11 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); int snd_usb_pcm_suspend(struct snd_usb_stream *as); int snd_usb_pcm_resume(struct snd_usb_stream *as); -int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt); +int snd_usb_init_pitch(struct snd_usb_audio *chip, + const struct audioformat *fmt); void snd_usb_preallocate_buffer(struct snd_usb_substream *subs); +int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, + struct audioformat *fmt); #endif /* __USBAUDIO_PCM_H */ diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 889c550c9f29..e9bbaea7b2fa 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -108,7 +108,8 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s snd_pcm_format_name(fmt)); snd_iprintf(buffer, "\n"); snd_iprintf(buffer, " Channels: %d\n", fp->channels); - snd_iprintf(buffer, " Endpoint: %d %s (%s)\n", + snd_iprintf(buffer, " Endpoint: 0x%02x (%d %s) (%s)\n", + fp->endpoint, fp->endpoint & USB_ENDPOINT_NUMBER_MASK, fp->endpoint & USB_DIR_IN ? "IN" : "OUT", sync_types[(fp->ep_attr & USB_ENDPOINT_SYNCTYPE) >> 2]); @@ -150,6 +151,19 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s snd_iprintf(buffer, "\n"); } + if (fp->sync_ep) { + snd_iprintf(buffer, " Sync Endpoint: 0x%02x (%d %s)\n", + fp->sync_ep, + fp->sync_ep & USB_ENDPOINT_NUMBER_MASK, + fp->sync_ep & USB_DIR_IN ? "IN" : "OUT"); + snd_iprintf(buffer, " Sync EP Interface: %d\n", + fp->sync_iface); + snd_iprintf(buffer, " Sync EP Altset: %d\n", + fp->sync_altsetting); + snd_iprintf(buffer, " Implicit Feedback Mode: %s\n", + fp->implicit_fb ? "Yes" : "No"); + } + // snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize); // snd_iprintf(buffer, " EP Attribute = %#x\n", fp->attributes); } @@ -175,32 +189,39 @@ static void proc_dump_ep_status(struct snd_usb_substream *subs, } } -static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer) +static void proc_dump_substream_status(struct snd_usb_audio *chip, + struct snd_usb_substream *subs, + struct snd_info_buffer *buffer) { + mutex_lock(&chip->mutex); if (subs->running) { snd_iprintf(buffer, " Status: Running\n"); - snd_iprintf(buffer, " Interface = %d\n", subs->interface); - snd_iprintf(buffer, " Altset = %d\n", subs->altset_idx); + if (subs->cur_audiofmt) { + snd_iprintf(buffer, " Interface = %d\n", subs->cur_audiofmt->iface); + snd_iprintf(buffer, " Altset = %d\n", subs->cur_audiofmt->altsetting); + } proc_dump_ep_status(subs, subs->data_endpoint, subs->sync_endpoint, buffer); } else { snd_iprintf(buffer, " Status: Stop\n"); } + mutex_unlock(&chip->mutex); } static void proc_pcm_format_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_usb_stream *stream = entry->private_data; + struct snd_usb_audio *chip = stream->chip; - snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name); + snd_iprintf(buffer, "%s : %s\n", chip->card->longname, stream->pcm->name); if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) { snd_iprintf(buffer, "\nPlayback:\n"); - proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); + proc_dump_substream_status(chip, &stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer); } if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) { snd_iprintf(buffer, "\nCapture:\n"); - proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); + proc_dump_substream_status(chip, &stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer); } } diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 3c1697f6b60c..0e11cb96fa8c 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -3256,14 +3256,6 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } } }, -/* Dell WD19 Dock */ -{ - USB_DEVICE(0x0bda, 0x402e), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .ifnum = QUIRK_ANY_INTERFACE, - .type = QUIRK_SETUP_FMT_AFTER_RESUME - } -}, /* MOTU Microbook II */ { USB_DEVICE_VENDOR_SPEC(0x07fd, 0x0004), @@ -3533,6 +3525,119 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), { /* + * PIONEER DJ DDJ-RR + * PCM is 6 channels out & 4 channels in @ 44.1 fixed + */ + USB_DEVICE_VENDOR_SPEC(0x2b73, 0x000d), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 6, //Master, Headphones & Booth + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } + }, + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 4, //2x RCA inputs (CH1 & CH2) + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC| + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } + }, + { + .ifnum = -1 + } + } + } +}, + +{ + /* + * PIONEER DJ DDJ-SR2 + * PCM is 4 channels out, 6 channels in @ 44.1 fixed + * The Feedback for the output is the input + */ + USB_DEVICE_VENDOR_SPEC(0x2b73, 0x001e), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 4, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } + }, + { + .ifnum = 0, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 6, + .iface = 0, + .altsetting = 1, + .altset_idx = 1, + .endpoint = 0x82, + .ep_attr = USB_ENDPOINT_XFER_ISOC| + USB_ENDPOINT_SYNC_ASYNC| + USB_ENDPOINT_USAGE_IMPLICIT_FB, + .rates = SNDRV_PCM_RATE_44100, + .rate_min = 44100, + .rate_max = 44100, + .nr_rates = 1, + .rate_table = (unsigned int[]) { 44100 } + } + }, + { + .ifnum = -1 + } + } + } +}, + +{ + /* * Pioneer DJ DJM-900NXS2 * 10 channels playback & 12 channels capture @ 44.1/48/96kHz S24LE */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index c989ad8052ae..e4a690bb4c99 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -177,8 +177,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, if (fp->maxpacksize == 0) fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); usb_set_interface(chip->dev, fp->iface, 0); - snd_usb_init_pitch(chip, fp->iface, alts, fp); - snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max); + snd_usb_init_pitch(chip, fp); + snd_usb_init_sample_rate(chip, fp, fp->rate_max); return 0; error: @@ -508,16 +508,6 @@ static int create_standard_mixer_quirk(struct snd_usb_audio *chip, return snd_usb_create_mixer(chip, quirk->ifnum, 0); } - -static int setup_fmt_after_resume_quirk(struct snd_usb_audio *chip, - struct usb_interface *iface, - struct usb_driver *driver, - const struct snd_usb_audio_quirk *quirk) -{ - chip->setup_fmt_after_resume_quirk = 1; - return 1; /* Continue with creating streams and mixer */ -} - static int setup_disable_autosuspend(struct snd_usb_audio *chip, struct usb_interface *iface, struct usb_driver *driver, @@ -565,7 +555,6 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip, [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk, [QUIRK_AUDIO_STANDARD_MIXER] = create_standard_mixer_quirk, - [QUIRK_SETUP_FMT_AFTER_RESUME] = setup_fmt_after_resume_quirk, [QUIRK_SETUP_DISABLE_AUTOSUSPEND] = setup_disable_autosuspend, }; @@ -1121,24 +1110,8 @@ free_buf: static int snd_usb_motu_m_series_boot_quirk(struct usb_device *dev) { - int ret; - - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, 0, NULL, 0, 1000); - - if (ret < 0) - return ret; - msleep(2000); - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - 1, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x20, 0, NULL, 0, 1000); - - if (ret < 0) - return ret; - return 0; } @@ -1386,7 +1359,8 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, /* * check if the device uses big-endian samples */ -int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *fp) +int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, + const struct audioformat *fp) { /* it depends on altsetting whether the device is big-endian or not */ switch (chip->usb_id) { @@ -1425,7 +1399,7 @@ enum { }; static void set_format_emu_quirk(struct snd_usb_substream *subs, - struct audioformat *fmt) + const struct audioformat *fmt) { unsigned char emu_samplerate_id = 0; @@ -1434,7 +1408,7 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs, * by playback substream */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) { - if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1) + if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].cur_audiofmt) return; } @@ -1469,13 +1443,13 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs, */ static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs) { - + unsigned int cur_rate = subs->data_endpoint->cur_rate; /* Convert sample rate value to little endian */ u8 sr[3]; - sr[0] = subs->cur_rate & 0xff; - sr[1] = (subs->cur_rate >> 8) & 0xff; - sr[2] = (subs->cur_rate >> 16) & 0xff; + sr[0] = cur_rate & 0xff; + sr[1] = (cur_rate >> 8) & 0xff; + sr[2] = (cur_rate >> 16) & 0xff; /* Configure device */ usb_set_interface(subs->dev, 0, 1); @@ -1487,7 +1461,7 @@ static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs) } void snd_usb_set_format_quirk(struct snd_usb_substream *subs, - struct audioformat *fmt) + const struct audioformat *fmt) { switch (subs->stream->chip->usb_id) { case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */ @@ -1553,13 +1527,13 @@ static bool is_itf_usb_dsd_dac(unsigned int id) return false; } -int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, - struct audioformat *fmt) +int snd_usb_select_mode_quirk(struct snd_usb_audio *chip, + const struct audioformat *fmt) { - struct usb_device *dev = subs->dev; + struct usb_device *dev = chip->dev; int err; - if (is_itf_usb_dsd_dac(subs->stream->chip->usb_id)) { + if (is_itf_usb_dsd_dac(chip->usb_id)) { /* First switch to alt set 0, otherwise the mode switch cmd * will not be accepted by the DAC */ @@ -1622,10 +1596,8 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep) ep->tenor_fb_quirk = 1; } -void snd_usb_set_interface_quirk(struct usb_device *dev) +void snd_usb_set_interface_quirk(struct snd_usb_audio *chip) { - struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev); - if (!chip) return; /* @@ -1672,13 +1644,13 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) msleep(20); - /* Zoom R16/24, Logitech H650e/H570e, Jabra 550a, Kingston HyperX - * needs a tiny delay here, otherwise requests like get/set - * frequency return as failed despite actually succeeding. + /* Zoom R16/24, many Logitech(at least H650e/H570e/BCC950), + * Jabra 550a, Kingston HyperX needs a tiny delay here, + * otherwise requests like get/set frequency return + * as failed despite actually succeeding. */ if ((chip->usb_id == USB_ID(0x1686, 0x00dd) || - chip->usb_id == USB_ID(0x046d, 0x0a46) || - chip->usb_id == USB_ID(0x046d, 0x0a56) || + USB_ID_VENDOR(chip->usb_id) == 0x046d || /* Logitech */ chip->usb_id == USB_ID(0x0b0e, 0x0349) || chip->usb_id == USB_ID(0x0951, 0x16ad)) && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) @@ -1799,6 +1771,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, case 0x25ce: /* Mytek devices */ case 0x278b: /* Rotel? */ case 0x292b: /* Gustard/Ess based devices */ + case 0x2972: /* FiiO devices */ case 0x2ab6: /* T+A devices */ case 0x3353: /* Khadas devices */ case 0x3842: /* EVGA */ diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index c76cf24a640a..67a02303c820 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -26,22 +26,22 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev, unsigned int usb_id); void snd_usb_set_format_quirk(struct snd_usb_substream *subs, - struct audioformat *fmt); + const struct audioformat *fmt); bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip); int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, - struct audioformat *fp); + const struct audioformat *fp); void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep); -void snd_usb_set_interface_quirk(struct usb_device *dev); +void snd_usb_set_interface_quirk(struct snd_usb_audio *chip); void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size); -int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, - struct audioformat *fmt); +int snd_usb_select_mode_quirk(struct snd_usb_audio *chip, + const struct audioformat *fmt); u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct audioformat *fp, diff --git a/sound/usb/stream.c b/sound/usb/stream.c index ca76ba5b5c0b..ee9aa1dcf0d8 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -47,7 +47,6 @@ static void free_substream(struct snd_usb_substream *subs) return; /* not initialized */ list_for_each_entry_safe(fp, n, &subs->fmt_list, list) audioformat_free(fp); - kfree(subs->rate_list.list); kfree(subs->str_pd); snd_media_stream_delete(subs); } @@ -193,16 +192,16 @@ static int usb_chmap_ctl_get(struct snd_kcontrol *kcontrol, struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); struct snd_usb_substream *subs = info->private_data; struct snd_pcm_chmap_elem *chmap = NULL; - int i; + int i = 0; - memset(ucontrol->value.integer.value, 0, - sizeof(ucontrol->value.integer.value)); if (subs->cur_audiofmt) chmap = subs->cur_audiofmt->chmap; if (chmap) { for (i = 0; i < chmap->channels; i++) ucontrol->value.integer.value[i] = chmap->map[i]; } + for (; i < subs->channels_max; i++) + ucontrol->value.integer.value[i] = 0; return 0; } @@ -1194,6 +1193,8 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, continue; } + snd_usb_audioformat_set_sync_ep(chip, fp); + dev_dbg(&dev->dev, "%u:%d: add audio endpoint %#x\n", iface_no, altno, fp->endpoint); if (protocol == UAC_VERSION_3) err = snd_usb_add_audio_stream_v3(chip, stream, fp, pd); @@ -1205,10 +1206,27 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, kfree(pd); return err; } + + /* add endpoints */ + err = snd_usb_add_endpoint(chip, fp->endpoint, + SND_USB_ENDPOINT_TYPE_DATA); + if (err < 0) + return err; + + if (fp->sync_ep) { + err = snd_usb_add_endpoint(chip, fp->sync_ep, + fp->implicit_fb ? + SND_USB_ENDPOINT_TYPE_DATA : + SND_USB_ENDPOINT_TYPE_SYNC); + if (err < 0) + return err; + } + /* try to set the interface... */ + usb_set_interface(chip->dev, iface_no, 0); + snd_usb_init_pitch(chip, fp); + snd_usb_init_sample_rate(chip, fp, fp->rate_max); usb_set_interface(chip->dev, iface_no, altno); - snd_usb_init_pitch(chip, iface_no, alts, fp); - snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max); } return 0; } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 0805b7f21272..980287aadd36 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -35,7 +35,6 @@ struct snd_usb_audio { wait_queue_head_t shutdown_wait; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ unsigned int tx_length_quirk:1; /* Put length specifier in transfers */ - unsigned int setup_fmt_after_resume_quirk:1; /* setup the format to interface after resume */ unsigned int need_delayed_register:1; /* warn for delayed registration */ int num_interfaces; int num_suspended_intf; @@ -52,10 +51,8 @@ struct snd_usb_audio { struct list_head mixer_list; /* list of mixer interfaces */ int setup; /* from the 'device_setup' module param */ + bool generic_implicit_fb; /* from the 'implicit_fb' module param */ bool autoclock; /* from the 'autoclock' module param */ - bool keep_iface; /* keep interface/altset after closing - * or parameter change - */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ struct media_device *media_dev; |