summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2020-12-28 14:16:53 +0000
committerMark Brown <broonie@kernel.org>2020-12-28 14:16:53 +0000
commitf81325a05e9317f09a2e4ec57a52e4e49eb42b54 (patch)
treea5a91589c9ef8e212f2899f1462cfb5c3f0130ef /sound
parent671ee4db952449acde126965bf76817a3159040d (diff)
parent5c8fe583cce542aa0b84adc939ce85293de36e5e (diff)
Merge tag 'v5.11-rc1' into asoc-5.11
Linux 5.11-rc1
Diffstat (limited to 'sound')
-rw-r--r--sound/core/compress_offload.c39
-rw-r--r--sound/core/control.c2
-rw-r--r--sound/core/init.c2
-rw-r--r--sound/core/memalloc.c4
-rw-r--r--sound/core/oss/pcm_oss.c28
-rw-r--r--sound/core/pcm_lib.c4
-rw-r--r--sound/core/pcm_memory.c10
-rw-r--r--sound/core/pcm_native.c9
-rw-r--r--sound/core/rawmidi.c49
-rw-r--r--sound/core/seq/seq_clientmgr.c1
-rw-r--r--sound/core/seq/seq_queue.c27
-rw-r--r--sound/core/seq/seq_queue.h11
-rw-r--r--sound/drivers/aloop.c6
-rw-r--r--sound/drivers/pcsp/pcsp_input.c1
-rw-r--r--sound/firewire/amdtp-stream.h2
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c4
-rw-r--r--sound/isa/sb/sb8_main.c1
-rw-r--r--sound/pci/emu10k1/emu10k1x.c4
-rw-r--r--sound/pci/hda/hda_codec.c2
-rw-r--r--sound/pci/hda/hda_generic.c12
-rw-r--r--sound/pci/hda/hda_generic.h1
-rw-r--r--sound/pci/hda/hda_intel.c3
-rw-r--r--sound/pci/hda/hda_proc.c38
-rw-r--r--sound/pci/hda/hda_sysfs.c2
-rw-r--r--sound/pci/hda/patch_ca0132.c739
-rw-r--r--sound/pci/hda/patch_hdmi.c128
-rw-r--r--sound/pci/hda/patch_realtek.c203
-rw-r--r--sound/pci/mixart/mixart_core.c5
-rw-r--r--sound/pci/rme32.c1
-rw-r--r--sound/pci/rme9652/hdspm.c9
-rw-r--r--sound/pci/rme9652/rme9652.c7
-rw-r--r--sound/ppc/snd_ps3.c10
-rw-r--r--sound/soc/amd/raven/pci-acp3x.c4
-rw-r--r--sound/soc/amd/renoir/rn-pci-acp3x.c32
-rw-r--r--sound/soc/codecs/Kconfig7
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/cros_ec_codec.c2
-rw-r--r--sound/soc/codecs/max98390.c2
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c278
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.h170
-rw-r--r--sound/soc/codecs/rt715-sdca.c936
-rw-r--r--sound/soc/codecs/rt715-sdca.h124
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.c1
-rw-r--r--sound/soc/codecs/wl1273.c1
-rw-r--r--sound/soc/codecs/wm_adsp.c5
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c12
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c4
-rw-r--r--sound/soc/intel/catpt/core.h11
-rw-r--r--sound/soc/intel/catpt/loader.c2
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c1
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c4
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-clocks.c1
-rw-r--r--sound/soc/stm/stm32_adfsdm.c12
-rw-r--r--sound/soc/ti/davinci-mcasp.c1
-rw-r--r--sound/usb/Makefile1
-rw-r--r--sound/usb/card.c21
-rw-r--r--sound/usb/card.h53
-rw-r--r--sound/usb/clock.c158
-rw-r--r--sound/usb/clock.h11
-rw-r--r--sound/usb/debug.h16
-rw-r--r--sound/usb/endpoint.c943
-rw-r--r--sound/usb/endpoint.h57
-rw-r--r--sound/usb/format.c127
-rw-r--r--sound/usb/helper.c10
-rw-r--r--sound/usb/helper.h3
-rw-r--r--sound/usb/implicit.c405
-rw-r--r--sound/usb/implicit.h14
-rw-r--r--sound/usb/mixer.c46
-rw-r--r--sound/usb/mixer_maps.c3
-rw-r--r--sound/usb/mixer_us16x08.c2
-rw-r--r--sound/usb/pcm.c1117
-rw-r--r--sound/usb/pcm.h7
-rw-r--r--sound/usb/proc.c35
-rw-r--r--sound/usb/quirks-table.h121
-rw-r--r--sound/usb/quirks.c71
-rw-r--r--sound/usb/quirks.h10
-rw-r--r--sound/usb/stream.c30
-rw-r--r--sound/usb/usbaudio.h5
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;