summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2020-11-24 09:12:37 +0100
committerTakashi Iwai <tiwai@suse.de>2020-11-24 09:13:00 +0100
commit43906287380f962a7178059cf8da1e03d94da3e0 (patch)
tree669a295bf922e54c9a221550bb4598c3758ba9a0 /sound
parent9ac05523d38d4c2f40c9e41fc1453146fcd74368 (diff)
parent29b105d947c661e521bf7b1005868c02441732be (diff)
Merge branch 'topic/usb-audio-refactoring' into for-next
Pull the USB audio improvement Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/core/control.c2
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c4
-rw-r--r--sound/hda/ext/hdac_ext_controller.c2
-rw-r--r--sound/pci/hda/hda_codec.c45
-rw-r--r--sound/pci/hda/hda_controller.h3
-rw-r--r--sound/pci/hda/hda_intel.c66
-rw-r--r--sound/pci/hda/patch_ca0132.c2
-rw-r--r--sound/pci/hda/patch_hdmi.c1
-rw-r--r--sound/pci/hda/patch_realtek.c152
-rw-r--r--sound/pci/mixart/mixart_core.c5
-rw-r--r--sound/soc/atmel/mchp-spdiftx.c1
-rw-r--r--sound/soc/codecs/cs42l51.c22
-rw-r--r--sound/soc/codecs/rt1015.c22
-rw-r--r--sound/soc/codecs/rt1015.h2
-rw-r--r--sound/soc/codecs/wcd9335.c2
-rw-r--r--sound/soc/codecs/wcd934x.c2
-rw-r--r--sound/soc/codecs/wsa881x.c2
-rw-r--r--sound/soc/intel/Kconfig18
-rw-r--r--sound/soc/intel/atom/Makefile2
-rw-r--r--sound/soc/intel/atom/sst/Makefile6
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_max98927.c39
-rw-r--r--sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c2
-rw-r--r--sound/soc/intel/catpt/dsp.c9
-rw-r--r--sound/soc/intel/catpt/pcm.c19
-rw-r--r--sound/soc/intel/keembay/kmb_platform.c6
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c31
-rw-r--r--sound/soc/qcom/lpass-cpu.c14
-rw-r--r--sound/soc/qcom/lpass-platform.c5
-rw-r--r--sound/soc/qcom/lpass-sc7180.c2
-rw-r--r--sound/soc/qcom/sdm845.c2
-rw-r--r--sound/soc/sof/loader.c5
-rw-r--r--sound/usb/Makefile1
-rw-r--r--sound/usb/card.c18
-rw-r--r--sound/usb/card.h53
-rw-r--r--sound/usb/clock.c152
-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.c125
-rw-r--r--sound/usb/helper.c10
-rw-r--r--sound/usb/helper.h3
-rw-r--r--sound/usb/implicit.c402
-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/pcm.c1111
-rw-r--r--sound/usb/pcm.h7
-rw-r--r--sound/usb/proc.c35
-rw-r--r--sound/usb/quirks-table.h8
-rw-r--r--sound/usb/quirks.c71
-rw-r--r--sound/usb/quirks.h10
-rw-r--r--sound/usb/stream.c24
-rw-r--r--sound/usb/usbaudio.h5
54 files changed, 2077 insertions, 1543 deletions
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/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/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 4d060d5b1db6..b0c0ef824d7d 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -148,6 +148,8 @@ struct hdac_ext_link *snd_hdac_ext_bus_get_link(struct hdac_bus *bus,
return NULL;
if (bus->idx != bus_idx)
return NULL;
+ if (addr < 0 || addr > 31)
+ return NULL;
list_for_each_entry(hlink, &bus->hlink_list, list) {
for (i = 0; i < HDA_MAX_CODECS; i++) {
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a356c21edb90..4bb58e8b08a8 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -2934,7 +2934,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
snd_hdac_leave_pm(&codec->core);
}
-static int hda_codec_runtime_suspend(struct device *dev)
+static int hda_codec_suspend(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
unsigned int state;
@@ -2953,7 +2953,7 @@ static int hda_codec_runtime_suspend(struct device *dev)
return 0;
}
-static int hda_codec_runtime_resume(struct device *dev)
+static int hda_codec_resume(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
@@ -2967,57 +2967,70 @@ static int hda_codec_runtime_resume(struct device *dev)
pm_runtime_mark_last_busy(dev);
return 0;
}
+
+static int hda_codec_runtime_suspend(struct device *dev)
+{
+ return hda_codec_suspend(dev);
+}
+
+static int hda_codec_runtime_resume(struct device *dev)
+{
+ return hda_codec_resume(dev);
+}
+
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
-static int hda_codec_force_resume(struct device *dev)
+static int hda_codec_pm_prepare(struct device *dev)
+{
+ return pm_runtime_suspended(dev);
+}
+
+static void hda_codec_pm_complete(struct device *dev)
{
struct hda_codec *codec = dev_to_hda_codec(dev);
- int ret;
- ret = pm_runtime_force_resume(dev);
- /* schedule jackpoll work for jack detection update */
- if (codec->jackpoll_interval ||
- (pm_runtime_suspended(dev) && hda_codec_need_resume(codec)))
- schedule_delayed_work(&codec->jackpoll_work,
- codec->jackpoll_interval);
- return ret;
+ if (pm_runtime_suspended(dev) && (codec->jackpoll_interval ||
+ hda_codec_need_resume(codec) || codec->forced_resume))
+ pm_request_resume(dev);
}
static int hda_codec_pm_suspend(struct device *dev)
{
dev->power.power_state = PMSG_SUSPEND;
- return pm_runtime_force_suspend(dev);
+ return hda_codec_suspend(dev);
}
static int hda_codec_pm_resume(struct device *dev)
{
dev->power.power_state = PMSG_RESUME;
- return hda_codec_force_resume(dev);
+ return hda_codec_resume(dev);
}
static int hda_codec_pm_freeze(struct device *dev)
{
dev->power.power_state = PMSG_FREEZE;
- return pm_runtime_force_suspend(dev);
+ return hda_codec_suspend(dev);
}
static int hda_codec_pm_thaw(struct device *dev)
{
dev->power.power_state = PMSG_THAW;
- return hda_codec_force_resume(dev);
+ return hda_codec_resume(dev);
}
static int hda_codec_pm_restore(struct device *dev)
{
dev->power.power_state = PMSG_RESTORE;
- return hda_codec_force_resume(dev);
+ return hda_codec_resume(dev);
}
#endif /* CONFIG_PM_SLEEP */
/* referred in hda_bind.c */
const struct dev_pm_ops hda_codec_driver_pm = {
#ifdef CONFIG_PM_SLEEP
+ .prepare = hda_codec_pm_prepare,
+ .complete = hda_codec_pm_complete,
.suspend = hda_codec_pm_suspend,
.resume = hda_codec_pm_resume,
.freeze = hda_codec_pm_freeze,
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index be63ead8161f..68f9668788ea 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -41,7 +41,7 @@
/* 24 unused */
#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
-#define AZX_DCAPS_SUSPEND_SPURIOUS_WAKEUP (1 << 27) /* Workaround for spurious wakeups after suspend */
+/* 27 unused */
#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */
@@ -143,6 +143,7 @@ struct azx {
unsigned int align_buffer_size:1;
unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by vga_switcheroo */
+ unsigned int pm_prepared:1;
/* GTS present */
unsigned int gts_present:1;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 749b88090970..6852668f1bcb 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -297,8 +297,7 @@ enum {
/* PCH for HSW/BDW; with runtime PM */
/* no i915 binding for this as HSW/BDW has another controller for HDMI */
#define AZX_DCAPS_INTEL_PCH \
- (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME |\
- AZX_DCAPS_SUSPEND_SPURIOUS_WAKEUP)
+ (AZX_DCAPS_INTEL_PCH_BASE | AZX_DCAPS_PM_RUNTIME)
/* HSW HDMI */
#define AZX_DCAPS_INTEL_HASWELL \
@@ -985,7 +984,7 @@ static void __azx_runtime_suspend(struct azx *chip)
display_power(chip, false);
}
-static void __azx_runtime_resume(struct azx *chip, bool from_rt)
+static void __azx_runtime_resume(struct azx *chip)
{
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
struct hdac_bus *bus = azx_bus(chip);
@@ -1002,7 +1001,8 @@ static void __azx_runtime_resume(struct azx *chip, bool from_rt)
azx_init_pci(chip);
hda_intel_init_chip(chip, true);
- if (from_rt) {
+ /* Avoid codec resume if runtime resume is for system suspend */
+ if (!chip->pm_prepared) {
list_for_each_codec(codec, &chip->bus) {
if (codec->relaxed_resume)
continue;
@@ -1018,6 +1018,29 @@ static void __azx_runtime_resume(struct azx *chip, bool from_rt)
}
#ifdef CONFIG_PM_SLEEP
+static int azx_prepare(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ chip = card->private_data;
+ chip->pm_prepared = 1;
+
+ /* HDA controller always requires different WAKEEN for runtime suspend
+ * and system suspend, so don't use direct-complete here.
+ */
+ return 0;
+}
+
+static void azx_complete(struct device *dev)
+{
+ struct snd_card *card = dev_get_drvdata(dev);
+ struct azx *chip;
+
+ chip = card->private_data;
+ chip->pm_prepared = 0;
+}
+
static int azx_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
@@ -1029,15 +1052,7 @@ static int azx_suspend(struct device *dev)
chip = card->private_data;
bus = azx_bus(chip);
- snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
- /* An ugly workaround: direct call of __azx_runtime_suspend() and
- * __azx_runtime_resume() for old Intel platforms that suffer from
- * spurious wakeups after S3 suspend
- */
- if (chip->driver_caps & AZX_DCAPS_SUSPEND_SPURIOUS_WAKEUP)
- __azx_runtime_suspend(chip);
- else
- pm_runtime_force_suspend(dev);
+ __azx_runtime_suspend(chip);
if (bus->irq >= 0) {
free_irq(bus->irq, chip);
bus->irq = -1;
@@ -1066,11 +1081,7 @@ static int azx_resume(struct device *dev)
if (azx_acquire_irq(chip, 1) < 0)
return -EIO;
- if (chip->driver_caps & AZX_DCAPS_SUSPEND_SPURIOUS_WAKEUP)
- __azx_runtime_resume(chip, false);
- else
- pm_runtime_force_resume(dev);
- snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+ __azx_runtime_resume(chip);
trace_azx_resume(chip);
return 0;
@@ -1118,10 +1129,7 @@ static int azx_runtime_suspend(struct device *dev)
chip = card->private_data;
/* enable controller wake up event */
- if (snd_power_get_state(card) == SNDRV_CTL_POWER_D0) {
- azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
- STATESTS_INT_MASK);
- }
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | STATESTS_INT_MASK);
__azx_runtime_suspend(chip);
trace_azx_runtime_suspend(chip);
@@ -1132,18 +1140,14 @@ static int azx_runtime_resume(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
- bool from_rt = snd_power_get_state(card) == SNDRV_CTL_POWER_D0;
if (!azx_is_pm_ready(card))
return 0;
chip = card->private_data;
- __azx_runtime_resume(chip, from_rt);
+ __azx_runtime_resume(chip);
/* disable controller Wake Up event*/
- if (from_rt) {
- azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
- ~STATESTS_INT_MASK);
- }
+ azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & ~STATESTS_INT_MASK);
trace_azx_runtime_resume(chip);
return 0;
@@ -1177,6 +1181,8 @@ static int azx_runtime_idle(struct device *dev)
static const struct dev_pm_ops azx_pm = {
SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume)
#ifdef CONFIG_PM_SLEEP
+ .prepare = azx_prepare,
+ .complete = azx_complete,
.freeze_noirq = azx_freeze_noirq,
.thaw_noirq = azx_thaw_noirq,
#endif
@@ -2356,6 +2362,7 @@ static int azx_probe_continue(struct azx *chip)
if (azx_has_pm_runtime(chip)) {
pm_runtime_use_autosuspend(&pci->dev);
+ pm_runtime_allow(&pci->dev);
pm_runtime_put_autosuspend(&pci->dev);
}
@@ -2499,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/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index e0c38f2735c6..d8370a417e3d 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -9183,6 +9183,8 @@ static void ca0132_mmio_init(struct hda_codec *codec)
case QUIRK_AE5:
ca0132_mmio_init_ae5(codec);
break;
+ default:
+ break;
}
}
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index ccd1df059654..b0068f8ca46d 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -4274,6 +4274,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 f2398721ac1e..739dbaf54517 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2522,13 +2522,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),
@@ -4216,6 +4226,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)
@@ -6008,6 +6024,27 @@ static void alc285_fixup_invalidate_dacs(struct hda_codec *codec,
snd_hda_override_wcaps(codec, 0x03, 0);
}
+static void alc_combo_jack_hp_jd_restart(struct hda_codec *codec)
+{
+ switch (codec->core.vendor_id) {
+ case 0x10ec0274:
+ case 0x10ec0294:
+ case 0x10ec0225:
+ case 0x10ec0295:
+ case 0x10ec0299:
+ alc_update_coef_idx(codec, 0x4a, 0x8000, 1 << 15); /* Reset HP JD */
+ alc_update_coef_idx(codec, 0x4a, 0x8000, 0 << 15);
+ break;
+ case 0x10ec0235:
+ case 0x10ec0236:
+ case 0x10ec0255:
+ case 0x10ec0256:
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */
+ alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15);
+ break;
+ }
+}
+
static void alc295_fixup_chromebook(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -6018,16 +6055,7 @@ static void alc295_fixup_chromebook(struct hda_codec *codec,
spec->ultra_low_power = true;
break;
case HDA_FIXUP_ACT_INIT:
- switch (codec->core.vendor_id) {
- case 0x10ec0295:
- alc_update_coef_idx(codec, 0x4a, 0x8000, 1 << 15); /* Reset HP JD */
- alc_update_coef_idx(codec, 0x4a, 0x8000, 0 << 15);
- break;
- case 0x10ec0236:
- alc_update_coef_idx(codec, 0x1b, 0x8000, 1 << 15); /* Reset HP JD */
- alc_update_coef_idx(codec, 0x1b, 0x8000, 0 << 15);
- break;
- }
+ alc_combo_jack_hp_jd_restart(codec);
break;
}
}
@@ -6083,6 +6111,16 @@ static void alc285_fixup_hp_gpio_amp_init(struct hda_codec *codec,
alc_write_coef_idx(codec, 0x65, 0x0);
}
+static void alc274_fixup_hp_headset_mic(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ switch (action) {
+ case HDA_FIXUP_ACT_INIT:
+ alc_combo_jack_hp_jd_restart(codec);
+ break;
+ }
+}
+
/* for hda_fixup_thinkpad_acpi() */
#include "thinkpad_helper.c"
@@ -6277,6 +6315,11 @@ enum {
ALC256_FIXUP_INTEL_NUC8_RUGGED,
ALC255_FIXUP_XIAOMI_HEADSET_MIC,
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,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -7664,6 +7707,37 @@ static const struct hda_fixup alc269_fixups[] = {
{ }
},
},
+ [ALC274_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_headset_mic,
+ .chained = true,
+ .chain_id = ALC274_FIXUP_HP_MIC
+ },
+ [ALC256_FIXUP_ASUS_HPE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set EAPD high */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0f },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 },
+ { }
+ },
+ .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,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -7815,10 +7889,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT),
- SND_PCI_QUIRK(0x103c, 0x874e, "HP", ALC274_FIXUP_HP_MIC),
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),
@@ -7848,6 +7923,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
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, 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),
SND_PCI_QUIRK(0x1043, 0x1f11, "ASUS Zephyrus G14", ALC289_FIXUP_ASUS_GA401),
SND_PCI_QUIRK(0x1043, 0x1881, "ASUS Zephyrus S/M", ALC294_FIXUP_ASUS_GX502_PINS),
@@ -7883,11 +7959,49 @@ 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(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),
@@ -7925,6 +8039,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),
@@ -8237,6 +8353,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x19, 0x02a11020},
{0x1a, 0x02a11030},
{0x21, 0x0221101f}),
+ 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}),
@@ -8339,6 +8459,14 @@ 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},
+ {0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
{0x14, 0x90170110},
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/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c
index 82c1eecd2528..3bd350afb743 100644
--- a/sound/soc/atmel/mchp-spdiftx.c
+++ b/sound/soc/atmel/mchp-spdiftx.c
@@ -487,7 +487,6 @@ static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream,
}
mchp_spdiftx_channel_status_write(dev);
spin_unlock_irqrestore(&ctrl->lock, flags);
- mr |= SPDIFTX_MR_VALID1 | SPDIFTX_MR_VALID2;
if (dev->gclk_enabled) {
clk_disable_unprepare(dev->gclk);
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 097c4e8d9950..c61b17dc2af8 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -254,8 +254,28 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
&cs42l51_adcr_mux_controls),
};
+static int mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(comp);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(cs42l51->mclk_handle);
+ case SND_SOC_DAPM_POST_PMD:
+ /* Delay mclk shutdown to fulfill power-down sequence requirements */
+ msleep(20);
+ clk_disable_unprepare(cs42l51->mclk_handle);
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = {
- SND_SOC_DAPM_CLOCK_SUPPLY("MCLK")
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, mclk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route cs42l51_routes[] = {
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
index 25fe2ddedd54..3db07293c70b 100644
--- a/sound/soc/codecs/rt1015.c
+++ b/sound/soc/codecs/rt1015.c
@@ -27,10 +27,15 @@
#include <sound/soc-dapm.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <sound/rt1015.h>
#include "rl6231.h"
#include "rt1015.h"
+static const struct rt1015_platform_data i2s_default_platform_data = {
+ .power_up_delay_ms = 50,
+};
+
static const struct reg_default rt1015_reg[] = {
{ 0x0000, 0x0000 },
{ 0x0004, 0xa000 },
@@ -539,7 +544,7 @@ static void rt1015_flush_work(struct work_struct *work)
struct rt1015_priv *rt1015 = container_of(work, struct rt1015_priv,
flush_work.work);
struct snd_soc_component *component = rt1015->component;
- unsigned int val, i = 0, count = 20;
+ unsigned int val, i = 0, count = 200;
while (i < count) {
usleep_range(1000, 1500);
@@ -650,6 +655,7 @@ static int rt1015_amp_drv_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
if (rt1015->hw_config == RT1015_HW_28)
schedule_delayed_work(&rt1015->flush_work, msecs_to_jiffies(10));
+ msleep(rt1015->pdata.power_up_delay_ms);
break;
default:
break;
@@ -1067,9 +1073,16 @@ static struct acpi_device_id rt1015_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt1015_acpi_match);
#endif
+static void rt1015_parse_dt(struct rt1015_priv *rt1015, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,power-up-delay-ms",
+ &rt1015->pdata.power_up_delay_ms);
+}
+
static int rt1015_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct rt1015_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt1015_priv *rt1015;
int ret;
unsigned int val;
@@ -1081,6 +1094,13 @@ static int rt1015_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt1015);
+ rt1015->pdata = i2s_default_platform_data;
+
+ if (pdata)
+ rt1015->pdata = *pdata;
+ else
+ rt1015_parse_dt(rt1015, &i2c->dev);
+
rt1015->regmap = devm_regmap_init_i2c(i2c, &rt1015_regmap);
if (IS_ERR(rt1015->regmap)) {
ret = PTR_ERR(rt1015->regmap);
diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h
index d3fdd30aca6d..15cadb361ec3 100644
--- a/sound/soc/codecs/rt1015.h
+++ b/sound/soc/codecs/rt1015.h
@@ -12,6 +12,7 @@
#ifndef __RT1015_H__
#define __RT1015_H__
+#include <sound/rt1015.h>
#define RT1015_DEVICE_ID_VAL 0x1011
#define RT1015_DEVICE_ID_VAL2 0x1015
@@ -380,6 +381,7 @@ enum {
struct rt1015_priv {
struct snd_soc_component *component;
+ struct rt1015_platform_data pdata;
struct regmap *regmap;
int sysclk;
int sysclk_src;
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index f2d9d52ee171..4d2b1ec7c03b 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -618,7 +618,7 @@ static const char * const sb_tx8_mux_text[] = {
"ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192"
};
-static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0);
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 35697b072367..40f682f5dab8 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -551,7 +551,7 @@ struct wcd_iir_filter_ctl {
struct soc_bytes_ext bytes_ext;
};
-static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
static const DECLARE_TLV_DB_SCALE(ear_pa_gain, 0, 150, 0);
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 68e774e69c85..4530b74f5921 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -1026,6 +1026,8 @@ static struct snd_soc_dai_driver wsa881x_dais[] = {
.id = 0,
.playback = {
.stream_name = "SPKR Playback",
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
.rate_max = 48000,
.rate_min = 48000,
.channels_min = 1,
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index d5bae5d1ab6f..a5b446d5af19 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -15,22 +15,6 @@ config SND_SOC_INTEL_SST_TOPLEVEL
if SND_SOC_INTEL_SST_TOPLEVEL
-config SND_SST_IPC
- tristate
- # This option controls the IPC core for HiFi2 platforms
-
-config SND_SST_IPC_PCI
- tristate
- select SND_SST_IPC
- # This option controls the PCI-based IPC for HiFi2 platforms
- # (Medfield, Merrifield).
-
-config SND_SST_IPC_ACPI
- tristate
- select SND_SST_IPC
- # This option controls the ACPI-based IPC for HiFi2 platforms
- # (Baytrail, Cherrytrail)
-
config SND_SOC_INTEL_SST
tristate
@@ -57,7 +41,6 @@ config SND_SST_ATOM_HIFI2_PLATFORM
config SND_SST_ATOM_HIFI2_PLATFORM_PCI
tristate "PCI HiFi2 (Merrifield) Platforms"
depends on X86 && PCI
- select SND_SST_IPC_PCI
select SND_SST_ATOM_HIFI2_PLATFORM
help
If you have a Intel Merrifield/Edison platform, then
@@ -70,7 +53,6 @@ config SND_SST_ATOM_HIFI2_PLATFORM_ACPI
tristate "ACPI HiFi2 (Baytrail, Cherrytrail) Platforms"
default ACPI
depends on X86 && ACPI && PCI
- select SND_SST_IPC_ACPI
select SND_SST_ATOM_HIFI2_PLATFORM
select SND_SOC_ACPI_INTEL_MATCH
select IOSF_MBI
diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile
index a9326d5ec44c..c66f03f5d8d6 100644
--- a/sound/soc/intel/atom/Makefile
+++ b/sound/soc/intel/atom/Makefile
@@ -6,4 +6,4 @@ snd-soc-sst-atom-hifi2-platform-objs := sst-mfld-platform-pcm.o \
obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-soc-sst-atom-hifi2-platform.o
# DSP driver
-obj-$(CONFIG_SND_SST_IPC) += sst/
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += sst/
diff --git a/sound/soc/intel/atom/sst/Makefile b/sound/soc/intel/atom/sst/Makefile
index f17c905df3e2..5761d30a5f9d 100644
--- a/sound/soc/intel/atom/sst/Makefile
+++ b/sound/soc/intel/atom/sst/Makefile
@@ -3,6 +3,6 @@ snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_
snd-intel-sst-pci-objs += sst_pci.o
snd-intel-sst-acpi-objs += sst_acpi.o
-obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
-obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
-obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c
index 3ea4602dfb3e..9a4b3d0973f6 100644
--- a/sound/soc/intel/boards/kbl_rt5663_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c
@@ -401,17 +401,40 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_interval *chan = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- struct snd_soc_dpcm *dpcm = container_of(
- params, struct snd_soc_dpcm, hw_params);
- struct snd_soc_dai_link *fe_dai_link = dpcm->fe->dai_link;
- struct snd_soc_dai_link *be_dai_link = dpcm->be->dai_link;
+ struct snd_soc_dpcm *dpcm, *rtd_dpcm = NULL;
+
+ /*
+ * The following loop will be called only for playback stream
+ * In this platform, there is only one playback device on every SSP
+ */
+ for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) {
+ rtd_dpcm = dpcm;
+ break;
+ }
+
+ /*
+ * This following loop will be called only for capture stream
+ * In this platform, there is only one capture device on every SSP
+ */
+ for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
+ rtd_dpcm = dpcm;
+ break;
+ }
+
+ if (!rtd_dpcm)
+ return -EINVAL;
+
+ /*
+ * The above 2 loops are mutually exclusive based on the stream direction,
+ * thus rtd_dpcm variable will never be overwritten
+ */
/*
* The ADSP will convert the FE rate to 48k, stereo, 24 bit
*/
- if (!strcmp(fe_dai_link->name, "Kbl Audio Port") ||
- !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") ||
- !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) {
+ if (!strcmp(rtd_dpcm->fe->dai_link->name, "Kbl Audio Port") ||
+ !strcmp(rtd_dpcm->fe->dai_link->name, "Kbl Audio Headset Playback") ||
+ !strcmp(rtd_dpcm->fe->dai_link->name, "Kbl Audio Capture Port")) {
rate->min = rate->max = 48000;
chan->min = chan->max = 2;
snd_mask_none(fmt);
@@ -421,7 +444,7 @@ static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
* The speaker on the SSP0 supports S16_LE and not S24_LE.
* thus changing the mask here
*/
- if (!strcmp(be_dai_link->name, "SSP0-Codec"))
+ if (!strcmp(rtd_dpcm->be->dai_link->name, "SSP0-Codec"))
snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE);
return 0;
diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
index 922cd0176e1f..f95546c184aa 100644
--- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
+++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c
@@ -700,6 +700,8 @@ static int kabylake_set_bias_level(struct snd_soc_card *card,
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (dapm->bias_level == SND_SOC_BIAS_ON) {
+ if (!__clk_is_enabled(priv->mclk))
+ return 0;
dev_dbg(card->dev, "Disable mclk");
clk_disable_unprepare(priv->mclk);
} else {
diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c
index 7d2968571951..9e807b941732 100644
--- a/sound/soc/intel/catpt/dsp.c
+++ b/sound/soc/intel/catpt/dsp.c
@@ -267,9 +267,12 @@ static int catpt_dsp_select_lpclock(struct catpt_dev *cdev, bool lp, bool waiti)
reg, (reg & CATPT_ISD_DCPWM),
500, 10000);
if (ret) {
- dev_err(cdev->dev, "await WAITI timeout\n");
- mutex_unlock(&cdev->clk_mutex);
- return ret;
+ dev_warn(cdev->dev, "await WAITI timeout\n");
+ /* no signal - only high clock selection allowed */
+ if (lp) {
+ mutex_unlock(&cdev->clk_mutex);
+ return 0;
+ }
}
}
diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c
index f78018c857b8..408e64e3b5fb 100644
--- a/sound/soc/intel/catpt/pcm.c
+++ b/sound/soc/intel/catpt/pcm.c
@@ -458,10 +458,6 @@ static int catpt_dai_prepare(struct snd_pcm_substream *substream,
if (ret)
return CATPT_IPC_ERROR(ret);
- ret = catpt_dsp_update_lpclock(cdev);
- if (ret)
- return ret;
-
ret = catpt_dai_apply_usettings(dai, stream);
if (ret)
return ret;
@@ -500,6 +496,7 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
resume_stream:
+ catpt_dsp_update_lpclock(cdev);
ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id);
if (ret)
return CATPT_IPC_ERROR(ret);
@@ -507,11 +504,11 @@ static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP:
stream->prepared = false;
- catpt_dsp_update_lpclock(cdev);
fallthrough;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id);
+ catpt_dsp_update_lpclock(cdev);
if (ret)
return CATPT_IPC_ERROR(ret);
break;
@@ -534,6 +531,8 @@ void catpt_stream_update_position(struct catpt_dev *cdev,
dsppos = bytes_to_frames(r, pos->stream_position);
+ if (!stream->prepared)
+ goto exit;
/* only offload is set_write_pos driven */
if (stream->template->type != CATPT_STRM_TYPE_RENDER)
goto exit;
@@ -667,7 +666,17 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
break;
}
+ /* see if this is a new configuration */
+ if (!memcmp(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)))
+ return 0;
+
+ pm_runtime_get_sync(cdev->dev);
+
ret = catpt_ipc_set_device_format(cdev, &devfmt);
+
+ pm_runtime_mark_last_busy(cdev->dev);
+ pm_runtime_put_autosuspend(cdev->dev);
+
if (ret)
return CATPT_IPC_ERROR(ret);
diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c
index f54b710ee1c2..291a686568c2 100644
--- a/sound/soc/intel/keembay/kmb_platform.c
+++ b/sound/soc/intel/keembay/kmb_platform.c
@@ -487,9 +487,9 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream,
kmb_i2s->xfer_resolution = 0x02;
break;
case SNDRV_PCM_FORMAT_S24_LE:
- config->data_width = 24;
- kmb_i2s->ccr = 0x08;
- kmb_i2s->xfer_resolution = 0x04;
+ config->data_width = 32;
+ kmb_i2s->ccr = 0x14;
+ kmb_i2s->xfer_resolution = 0x05;
break;
case SNDRV_PCM_FORMAT_S32_LE:
config->data_width = 32;
diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
index c2c1eb16fcc0..26e7d9a7198f 100644
--- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
+++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
@@ -630,15 +630,34 @@ static struct snd_soc_codec_conf mt8183_da7219_rt1015_codec_conf[] = {
},
};
+static const struct snd_kcontrol_new mt8183_da7219_rt1015_snd_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Left Spk"),
+ SOC_DAPM_PIN_SWITCH("Right Spk"),
+};
+
+static const
+struct snd_soc_dapm_widget mt8183_da7219_rt1015_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Left Spk", NULL),
+ SND_SOC_DAPM_SPK("Right Spk", NULL),
+ SND_SOC_DAPM_PINCTRL("TDM_OUT_PINCTRL",
+ "aud_tdm_out_on", "aud_tdm_out_off"),
+};
+
+static const struct snd_soc_dapm_route mt8183_da7219_rt1015_dapm_routes[] = {
+ {"Left Spk", NULL, "Left SPO"},
+ {"Right Spk", NULL, "Right SPO"},
+ {"I2S Playback", NULL, "TDM_OUT_PINCTRL"},
+};
+
static struct snd_soc_card mt8183_da7219_rt1015_card = {
.name = "mt8183_da7219_rt1015",
.owner = THIS_MODULE,
- .controls = mt8183_da7219_max98357_snd_controls,
- .num_controls = ARRAY_SIZE(mt8183_da7219_max98357_snd_controls),
- .dapm_widgets = mt8183_da7219_max98357_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(mt8183_da7219_max98357_dapm_widgets),
- .dapm_routes = mt8183_da7219_max98357_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(mt8183_da7219_max98357_dapm_routes),
+ .controls = mt8183_da7219_rt1015_snd_controls,
+ .num_controls = ARRAY_SIZE(mt8183_da7219_rt1015_snd_controls),
+ .dapm_widgets = mt8183_da7219_rt1015_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8183_da7219_rt1015_dapm_widgets),
+ .dapm_routes = mt8183_da7219_rt1015_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt8183_da7219_rt1015_dapm_routes),
.dai_link = mt8183_da7219_dai_links,
.num_links = ARRAY_SIZE(mt8183_da7219_dai_links),
.aux_dev = &mt8183_da7219_max98357_headset_dev,
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index ba2aca301a9b..9d17c87445a9 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -80,6 +80,12 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret);
return ret;
}
+ ret = clk_prepare(drvdata->mi2s_bit_clk[dai->driver->id]);
+ if (ret) {
+ dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
+ clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
+ return ret;
+ }
return 0;
}
@@ -88,9 +94,8 @@ static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
{
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
- clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
-
clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]);
+ clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]);
}
static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
@@ -303,10 +308,10 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
ret);
- ret = clk_prepare_enable(drvdata->mi2s_bit_clk[id]);
+ ret = clk_enable(drvdata->mi2s_bit_clk[id]);
if (ret) {
dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret);
- clk_disable_unprepare(drvdata->mi2s_osr_clk[id]);
+ clk_disable(drvdata->mi2s_osr_clk[id]);
return ret;
}
@@ -324,6 +329,7 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
if (ret)
dev_err(dai->dev, "error writing to i2sctl reg: %d\n",
ret);
+ clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]);
break;
}
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 36d1512ffd1f..7a3fdf89968a 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -122,8 +122,10 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
else
dma_ch = 0;
- if (dma_ch < 0)
+ if (dma_ch < 0) {
+ kfree(data);
return dma_ch;
+ }
if (cpu_dai->driver->id == LPASS_DP_RX) {
map = drvdata->hdmiif_map;
@@ -147,6 +149,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
ret = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret < 0) {
+ kfree(data);
dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
ret);
return -EINVAL;
diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c
index c6292f9e613f..bc998d501600 100644
--- a/sound/soc/qcom/lpass-sc7180.c
+++ b/sound/soc/qcom/lpass-sc7180.c
@@ -188,7 +188,7 @@ static struct lpass_variant sc7180_data = {
.micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
.micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
.wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
- .bitwidth = REG_FIELD_ID(0x1000, 0, 0, 3, 0x1000),
+ .bitwidth = REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000),
.rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000),
.rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000),
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index ab1bf23c21a6..6c2760e27ea6 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -17,6 +17,7 @@
#include "qdsp6/q6afe.h"
#include "../codecs/rt5663.h"
+#define DRIVER_NAME "sdm845"
#define DEFAULT_SAMPLE_RATE_48K 48000
#define DEFAULT_MCLK_RATE 24576000
#define TDM_BCLK_RATE 6144000
@@ -552,6 +553,7 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
+ card->driver_name = DRIVER_NAME;
card->dapm_widgets = sdm845_snd_widgets;
card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets);
card->dev = dev;
diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c
index 68ed454f7ddf..ba9ed66f98bc 100644
--- a/sound/soc/sof/loader.c
+++ b/sound/soc/sof/loader.c
@@ -118,6 +118,11 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
case SOF_IPC_EXT_CC_INFO:
ret = get_cc_info(sdev, ext_hdr);
break;
+ case SOF_IPC_EXT_UNUSED:
+ case SOF_IPC_EXT_PROBE_INFO:
+ case SOF_IPC_EXT_USER_ABI_INFO:
+ /* They are supported but we don't do anything here */
+ break;
default:
dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n",
ext_hdr->type, ext_hdr->hdr.size);
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..cb0b6582dfee 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,10 @@ 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"),
+
/* Dell WD15 Dock */
PROFILE_NAME(0x0bda, 0x4014, "Dell", "WD15 Dock", "Dell-WD15-Dock"),
/* Dell WD19 Dock */
@@ -594,6 +599,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 +983,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 +991,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..e940dcee792b 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,7 +528,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 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 */
}
@@ -560,16 +563,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 +633,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 +662,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 +682,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..93459ba228d3 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"
@@ -165,6 +164,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 +215,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 +233,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 +347,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 +382,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 +421,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 +592,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..386198b36b87
--- /dev/null
+++ b/sound/usb/implicit.c
@@ -0,0 +1,402 @@
+// 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(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, 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, 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/pcm.c b/sound/usb/pcm.c
index b401ee894e1b..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,187 +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 */
- 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 */
- 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(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
@@ -478,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,
@@ -505,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;
@@ -804,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
*
@@ -818,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;
@@ -849,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;
}
@@ -876,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;
@@ -901,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)) {
@@ -918,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;
@@ -963,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 |
@@ -975,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,
@@ -984,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);
@@ -1027,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;
@@ -1078,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];
@@ -1165,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);
@@ -1185,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.
@@ -1250,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;
@@ -1305,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,
@@ -1332,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)
@@ -1347,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;
@@ -1360,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;
}
@@ -1377,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)
@@ -1597,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;
@@ -1643,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;
@@ -1754,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;
@@ -1791,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..85b99c6d3c61 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),
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index b4fa80ef730d..63cdf3c8c2bc 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)
@@ -1800,6 +1772,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
case 0x278b: /* Rotel? */
case 0x292b: /* Gustard/Ess based devices */
case 0x2ab6: /* T+A devices */
+ case 0x3353: /* Khadas devices */
case 0x3842: /* EVGA */
case 0xc502: /* HiBy devices */
if (fp->dsd_raw)
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..7f58c7625cd4 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);
}
@@ -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;