From 9fe9efd6924c9a62ebb759025bb8927e398f51f7 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Wed, 14 Oct 2020 14:16:24 +0000 Subject: ASoC: Intel: kbl_rt5663_max98927: Fix kabylake_ssp_fixup function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a copy of commit 5c5f1baee85a ("ASoC: Intel: kbl_rt5663_rt5514_max98927: Fix kabylake_ssp_fixup function") applied to the kbl_rt5663_max98927 board file. Original explanation of the change: kabylake_ssp_fixup function uses snd_soc_dpcm to identify the codecs DAIs. The HW parameters are changed based on the codec DAI of the stream. The earlier approach to get snd_soc_dpcm was using container_of() macro on snd_pcm_hw_params. The structures have been modified over time and snd_soc_dpcm does not have snd_pcm_hw_params as a reference but as a copy. This causes the current driver to crash when used. This patch changes the way snd_soc_dpcm is extracted. snd_soc_pcm_runtime holds 2 dpcm instances (one for playback and one for capture). 2 codecs on the SSP are dmic (capture) and speakers (playback). Based on the stream direction, snd_soc_dpcm is extracted from snd_soc_pcm_runtime. Fixes a boot crash on a HP Chromebook x2: [ 16.582225] BUG: kernel NULL pointer dereference, address: 0000000000000050 [ 16.582231] #PF: supervisor read access in kernel mode [ 16.582233] #PF: error_code(0x0000) - not-present page [ 16.582234] PGD 0 P4D 0 [ 16.582238] Oops: 0000 [#1] PREEMPT SMP PTI [ 16.582241] CPU: 0 PID: 1980 Comm: cras Tainted: G C 5.4.58 #1 [ 16.582243] Hardware name: HP Soraka/Soraka, BIOS Google_Soraka.10431.75.0 08/30/2018 [ 16.582247] RIP: 0010:kabylake_ssp_fixup+0x19/0xbb [snd_soc_kbl_rt5663_max98927] [ 16.582250] Code: c6 6f c5 80 c0 44 89 f2 31 c0 e8 3e c9 4c d6 eb de 0f 1f 44 00 00 55 48 89 e5 41 57 41 56 53 48 89 f3 48 8b 46 c8 48 8b 4e d0 <48> 8b 49 10 4c 8b 78 10 4c 8b 31 4c 89 f7 48 c7 c6 4b c2 80 c0 e8 [ 16.582252] RSP: 0000:ffffaf7e81e0b958 EFLAGS: 00010282 [ 16.582254] RAX: ffffffff96f13e0d RBX: ffffaf7e81e0ba00 RCX: 0000000000000040 [ 16.582256] RDX: ffffaf7e81e0ba00 RSI: ffffaf7e81e0ba00 RDI: ffffa3b208558028 [ 16.582258] RBP: ffffaf7e81e0b970 R08: ffffa3b203b54160 R09: ffffaf7e81e0ba00 [ 16.582259] R10: 0000000000000000 R11: ffffffffc080b345 R12: ffffa3b209fb6e00 [ 16.582261] R13: ffffa3b1b1a47838 R14: ffffa3b1e6197f28 R15: ffffaf7e81e0ba00 [ 16.582263] FS: 00007eb3f25aaf80(0000) GS:ffffa3b236a00000(0000) knlGS:0000000000000000 [ 16.582265] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 16.582267] CR2: 0000000000000050 CR3: 0000000246bc8006 CR4: 00000000003606f0 [ 16.582269] Call Trace: [ 16.582275] snd_soc_link_be_hw_params_fixup+0x21/0x68 [ 16.582278] snd_soc_dai_hw_params+0x25/0x94 [ 16.582282] soc_pcm_hw_params+0x2d8/0x583 [ 16.582288] dpcm_be_dai_hw_params+0x172/0x29e [ 16.582291] dpcm_fe_dai_hw_params+0x9f/0x12f [ 16.582295] snd_pcm_hw_params+0x137/0x41c [ 16.582298] snd_pcm_hw_params_user+0x3c/0x71 [ 16.582301] snd_pcm_common_ioctl+0x2c6/0x565 [ 16.582304] snd_pcm_ioctl+0x32/0x36 [ 16.582307] do_vfs_ioctl+0x506/0x783 [ 16.582311] ksys_ioctl+0x58/0x83 [ 16.582313] __x64_sys_ioctl+0x1a/0x1e [ 16.582316] do_syscall_64+0x54/0x7e [ 16.582319] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 16.582322] RIP: 0033:0x7eb3f1886157 [ 16.582324] Code: 8a 66 90 48 8b 05 11 dd 2b 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e1 dc 2b 00 f7 d8 64 89 01 48 [ 16.582326] RSP: 002b:00007ffff7559818 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 16.582329] RAX: ffffffffffffffda RBX: 00005acc9188b140 RCX: 00007eb3f1886157 [ 16.582330] RDX: 00007ffff7559940 RSI: 00000000c2604111 RDI: 000000000000001e [ 16.582332] RBP: 00007ffff7559840 R08: 0000000000000004 R09: 0000000000000000 [ 16.582333] R10: 0000000000000000 R11: 0000000000000246 R12: 000000000000bb80 [ 16.582335] R13: 00005acc91702e80 R14: 00007ffff7559940 R15: 00005acc91702e80 [ 16.582337] Modules linked in: rfcomm cmac algif_hash algif_skcipher af_alg uinput hid_google_hammer snd_soc_kbl_rt5663_max98927 snd_soc_hdac_hdmi snd_soc_dmic snd_soc_skl_ssp_clk snd_soc_skl snd_soc_sst_ipc snd_soc_sst_dsp snd_soc_hdac_hda snd_soc_acpi_intel_match snd_soc_acpi snd_hda_ext_core snd_intel_dspcfg snd_hda_codec snd_hwdep snd_hda_core ipu3_cio2 ipu3_imgu(C) videobuf2_v4l2 videobuf2_common videobuf2_dma_sg videobuf2_memops snd_soc_rt5663 snd_soc_max98927 snd_soc_rl6231 ov5670 ov13858 acpi_als v4l2_fwnode dw9714 fuse xt_MASQUERADE iio_trig_sysfs cros_ec_light_prox cros_ec_sensors cros_ec_sensors_core cros_ec_sensors_ring industrialio_triggered_buffer kfifo_buf industrialio cros_ec_sensorhub cdc_ether usbnet btusb btrtl btintel btbcm bluetooth ecdh_generic ecc lzo_rle lzo_compress iwlmvm zram iwl7000_mac80211 r8152 mii iwlwifi cfg80211 joydev [ 16.584243] gsmi: Log Shutdown Reason 0x03 [ 16.584246] CR2: 0000000000000050 [ 16.584248] ---[ end trace c8511d090c11edff ]--- Suggested-by: Ɓukasz Majczak Fixes: 2e5894d73789e ("ASoC: pcm: Add support for DAI multicodec") Signed-off-by: Tomasz Figa Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20201014141624.4143453-1-tfiga@chromium.org Signed-off-by: Mark Brown --- sound/soc/intel/boards/kbl_rt5663_max98927.c | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) (limited to 'sound') 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; -- cgit v1.2.3 From 1849a3872f035494639201fdefb394425233647b Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 12 Oct 2020 11:50:05 +0200 Subject: ASoC: Intel: atom: Remove duplicate kconfigs SND_SST_IPC and its _PCI and _ACPI variants all target sound/soc/intel/atom solution alone. SND_SST_IPC is the core component, required for PCI and ACPI based atom platforms both. _PCI and _ACPI target Merrifield/Edison and Baytrial/Cherrytrail platforms respectively. On top of that, there is an equivalent set of configs targeting the same solution: - SND_SST_ATOM_HIFI2_PLATFORM (core) - SND_SST_ATOM_HIFI2_PLATFORM_PCI - SND_SST_ATOM_HIFI2_PLATFORM_ACPI As both sets do the same job - allow for granular platform selection - remove the duplicate set and rely on SND_SST_ATOM_HIFI2_PLATOFRM_XXX configs alone. Signed-off-by: Cezary Rojewski Acked-by: Hans de Goede Link: https://lore.kernel.org/r/20201012095005.29859-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 18 ------------------ sound/soc/intel/atom/Makefile | 2 +- sound/soc/intel/atom/sst/Makefile | 6 +++--- 3 files changed, 4 insertions(+), 22 deletions(-) (limited to 'sound') 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 -- cgit v1.2.3 From 1d159edf19542793851a04202e5b0dd548a9415c Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 12 Oct 2020 12:32:20 +0200 Subject: ASoC: Intel: catpt: Wake up device before configuring SSP port catpt_dai_pcm_new() invoked during new PCM runtime creation configures SSP by sending IPC to DSP firmware. For that to succeed device needs to be up and running. While components default probing behavior - snd_soc_catpt causing machine board module to load just after it - needs no changes, machine board's module may be unloaded and re-loaded at a different time e.g.: when catpt is already asleep. Wake device explicitly in catpt_dai_pcm_new() to ensure communication is established before sending any IPCs, enabling those advanced scenarios in the process. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20201012103221.30759-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index f78018c857b8..ba653ebea7d1 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -667,7 +667,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); -- cgit v1.2.3 From 3d53c6df4299134525ad9e197f480e89bc8b06af Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 12 Oct 2020 12:32:21 +0200 Subject: ASoC: Intel: catpt: Relax clock selection conditions Stress tests show that DSP may occasionally be late with signaling WAIT state when all pins are made use of simultaneously plus start/stop (pause) gets involved. While this isn't tied to standard audio scenarios where only System Pin (playback and capture) is involved, ensure user is not hindered when playing with more advanced scenarios. >From DSP perspective, clock acts as a resource: low clock equals less resources, high clock more resources. Relax clock selection procedure so only low -> high switch is allowed when awaiting WAIT signal times out. Once active stream count decreases, DSP will have more time internally to adjust thus low clock selection becomes possible again. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20201012103221.30759-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/dsp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound') 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; + } } } -- cgit v1.2.3 From eb5a558705c7f63d06b4ddd072898b1ca894e053 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Mon, 19 Oct 2020 12:47:24 +0800 Subject: ASoC: mediatek: mt8183-da7219: fix DAPM paths for rt1015 RT1015's output widget name is "SPO" instead of "Speaker". Fixes it to use the correct names. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20201019044724.1601476-1-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 31 +++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 4d69ea31bfe4..9630637b8ab9 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, -- cgit v1.2.3 From f47d0742515748162d3fc35f04331c5b81c0ed47 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 22 Oct 2020 14:05:18 +0100 Subject: ASoC: codecs: wsa881x: add missing stream rates and format Add missing supported rates and formats for the stream, without which attempt to do playback will fail to find any matching rates/format. Fixes: a0aab9e1404a ("ASoC: codecs: add wsa881x amplifier support") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20201022130518.31723-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wsa881x.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index d39d479e2378..5456124457a7 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, -- cgit v1.2.3 From 3f48b6eba15ea342ef4cb420b580f5ed6605669f Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 23 Oct 2020 10:58:49 +0100 Subject: ASoC: qcom: sdm845: set driver name correctly With the current state of code, we would endup with something like below in /proc/asound/cards for 2 machines based on this driver. Machine 1: 0 [DB845c ]: DB845c - DB845c DB845c Machine 2: 0 [LenovoYOGAC6301]: Lenovo-YOGA-C63 - Lenovo-YOGA-C630-13Q50 LENOVO-81JL-LenovoYOGAC630_13Q50-LNVNB161216 This is not very UCM friendly both w.r.t to common up configs and card identification, and UCM2 became totally not usefull with just one ucm sdm845.conf for two machines which have different setups w.r.t HDMI and other dais. Reasons for such thing is partly because Qualcomm machine drivers never cared to set driver_name. This patch sets up driver name for the this driver to sort out the UCM integration issues! after this patch contents of /proc/asound/cards: Machine 1: 0 [DB845c ]: sdm845 - DB845c DB845c Machine 2: 0 [LenovoYOGAC6301]: sdm845 - Lenovo-YOGA-C630-13Q50 LENOVO-81JL-LenovoYOGAC630_13Q50-LNVNB161216 with this its possible to align with what UCM2 expects and we can have sdm845/DB845.conf sdm845/LENOVO-81JL-LenovoYOGAC630_13Q50-LNVNB161216.conf ... for board variants. This should scale much better! Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20201023095849.22894-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') 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; -- cgit v1.2.3 From 20afe581c9b980848ad097c4d54dde9bec7593ef Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Tue, 20 Oct 2020 17:01:09 +0200 Subject: ASoC: cs42l51: manage mclk shutdown delay A delay must be introduced before the shutdown down of the mclk, as stated in CS42L51 datasheet. Otherwise the codec may produce some noise after the end of DAPM power down sequence. The delay between DAC and CLOCK_SUPPLY widgets is too short. Add a delay in mclk shutdown request to manage the shutdown delay explicitly. From experiments, at least 10ms delay is necessary. Set delay to 20ms as recommended in Documentation/timers/timers-howto.rst when using msleep(). Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20201020150109.482-1-olivier.moysan@st.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l51.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'sound') 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[] = { -- cgit v1.2.3 From 6e5329c6e6032cd997400b43b8299f607a61883e Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 21 Oct 2020 21:24:19 +0300 Subject: ASoC: SOF: loader: handle all SOF_IPC_EXT types Do not emit a warning for extended firmware header fields that are not used by kernel. This creates unnecessary noise to kernel logs like: sof-audio-pci 0000:00:1f.3: warning: unknown ext header type 3 size 0x1c sof-audio-pci 0000:00:1f.3: warning: unknown ext header type 4 size 0x10 Signed-off-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20201021182419.1160391-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/loader.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') 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); -- cgit v1.2.3 From f15cfca818d756dd1c9492530091dfd583359db3 Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Sun, 25 Oct 2020 22:05:47 -0700 Subject: ALSA: usb-audio: Add implicit feedback quirk for Zoom UAC-2 The Zoom UAC-2 USB audio interface provides an async playback endpoint ("1 OUT (ASYNC)") and capture endpoint ("2 IN (ASYNC)"), both with 2-channel S32_LE in 44.1, 48, 88.2, 96, 176.4, or 192 kilosamples/s. The device provides explicit feedback to adjust the host's playback rate, but the feedback appears unstable and biased relative to the device's capture rate. "alsaloop -t 1000" experiences playback underruns and tries to resample the captured audio to match the varying playback rate. Forcing the kernel to use implicit feedback appears to produce more stable results. This causes the host to transmit one playback sample for each capture sample received. (Zoom North America has been notified of this change.) Signed-off-by: Keith Winstein Tested-by: Keith Winstein Cc: BugLink: https://lore.kernel.org/r/20201027071841.GA164525@trolley.csail.mit.edu Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b401ee894e1b..31b1edf53c41 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -352,6 +352,10 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ep = 0x81; ifnum = 2; goto add_sync_ep_from_ifnum; + case USB_ID(0x1686, 0xf029): /* Zoom UAC-2 */ + ep = 0x82; + ifnum = 2; + goto add_sync_ep_from_ifnum; case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */ case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ ep = 0x81; -- cgit v1.2.3 From 8a8de09cb2adc119104f35044d1a840dd47aa9d8 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 27 Oct 2020 16:46:38 +0800 Subject: ALSA: hda/realtek - Fixed HP headset Mic can't be detected System boot with plugged headset. It will not detect headset Mic. It will happen on cold boot restart resume state. Quirk by SSID change to quirk by pin verb. Fixes: 13468bfa8c58 ("ALSA: hda/realtek - set mic to auto detect on a HP AIO machine") Signed-off-by: Kailang Yang Cc: Link: https://lore.kernel.org/r/f42ae1ede1cf47029ae2bef1a42caf03@realtek.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 54 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f2398721ac1e..4f0ea8013bf6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6008,6 +6008,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 +6039,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 +6095,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 +6299,7 @@ enum { ALC256_FIXUP_INTEL_NUC8_RUGGED, ALC255_FIXUP_XIAOMI_HEADSET_MIC, ALC274_FIXUP_HP_MIC, + ALC274_FIXUP_HP_HEADSET_MIC, }; static const struct hda_fixup alc269_fixups[] = { @@ -7664,6 +7687,12 @@ 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 + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7815,7 +7844,6 @@ 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), @@ -8339,6 +8367,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1a, 0x90a70130}, {0x1b, 0x90170110}, {0x21, 0x03211020}), + 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}, -- cgit v1.2.3 From 215a22ed31a1332075866eca07744d442367c04b Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 27 Oct 2020 21:00:36 +0800 Subject: ALSA: hda: Refactor codec PM to use direct-complete optimization Upon system resume, hda_codec_pm_resume() uses hda_codec_force_resume() to resume the codec. However, pm_runtime_force_resume() won't really resume the codec because of pm_runtime_need_not_resume() check. Hence, hda_codec_force_resume() schedules a jackpoll work, which is to really power up the codec. Instead of doing that, we can use direct-complete to make the PM flow more straightforward, and keep codec always suspended through system PM flow if conditions are met. On system suspend, PM core will decide what to do based on hda_codec_pm_prepare(): - If codec is not runtime-suspended, PM core will suspend and resume the device as normal. - If codec is runtime-suspended, PM core will try to keep it suspended. If it's still suspended after system resume, we use hda_codec_pm_complete() to resume codec if it's needed. Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20201027130038.16463-2-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) (limited to 'sound') 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, -- cgit v1.2.3 From f5dac54d9d93826a776dffc848df76746f7135bb Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 27 Oct 2020 21:00:37 +0800 Subject: ALSA: hda: Separate runtime and system suspend Both pm_runtime_force_suspend() and pm_runtime_force_resume() have some implicit checks, so it can make code flow more straightforward if we separate runtime and system suspend callbacks. High Definition Audio Specification, 4.5.9.3 Codec Wake From System S3 states that codec can wake the system up from S3 if WAKEEN is toggled. Since HDA controller has different wakeup settings for runtime and system susend, we also need to explicitly disable direct-complete which can be enabled automatically by PCI core. In addition to that, avoid waking up codec if runtime resume is for system suspend, to not break direct-complete for codecs. While at it, also remove AZX_DCAPS_SUSPEND_SPURIOUS_WAKEUP, as the original bug commit a6630529aecb ("ALSA: hda: Workaround for spurious wakeups on some Intel platforms") solves doesn't happen with this patch. Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20201027130038.16463-3-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_controller.h | 3 +- sound/pci/hda/hda_intel.c | 62 +++++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 29 deletions(-) (limited to 'sound') 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..be83f1747c5f 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 -- cgit v1.2.3 From 9fc149c3bce7bdbb94948a8e6bd025e3b3538603 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 27 Oct 2020 21:00:38 +0800 Subject: ALSA: hda: Reinstate runtime_allow() for all hda controllers The broken jack detection should be fixed by commit a6e7d0a4bdb0 ("ALSA: hda: fix jack detection with Realtek codecs when in D3"), let's try enabling runtime PM by default again. Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20201027130038.16463-4-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index be83f1747c5f..d539f52009a1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2362,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); } -- cgit v1.2.3 From fc0522bbe02fa4beb95c0514ace66b585616f111 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 28 Oct 2020 15:43:39 +0000 Subject: ASoC: codecs: wcd934x: Set digital gain range correctly digital gain range is -84dB min to 40dB max, however this was not correctly specified in the range. Fix this by with correct range! Fixes: 1cde8b822332 ("ASoC: wcd934x: add basic controls") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20201028154340.17090-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd934x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') 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); -- cgit v1.2.3 From 6d6bc54ab4f2404d46078abc04bf4dee4db01def Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Wed, 28 Oct 2020 15:43:40 +0000 Subject: ASoC: codecs: wcd9335: Set digital gain range correctly digital gain range is -84dB min to 40dB max, however this was not correctly specified in the range. Fix this by with correct range! Fixes: 8c4f021d806a ("ASoC: wcd9335: add basic controls") Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20201028154340.17090-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/codecs/wcd9335.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') 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); -- cgit v1.2.3 From cf9d21984da2c8e852320d12c03ddb7d11760a32 Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Tue, 27 Oct 2020 17:04:34 +0530 Subject: ASoC: qcom: lpass-sc7180: Fix MI2S bitwidth field bit positions Update SC7180 lpass_variant structure with proper I2S bitwidth field bit positions, as bitwidth denotes 0 to 1 bits, but previously used only 0 bit. Signed-off-by: V Sujith Kumar Reddy Signed-off-by: Srinivasa Rao Mandadapu Link: https://lore.kernel.org/r/1603798474-4897-1-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-sc7180.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') 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), -- cgit v1.2.3 From 6ec6c3693a389841d8ca952072aea8020da54ef4 Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Mon, 19 Oct 2020 14:36:03 +0530 Subject: ASoC: qcom: lpass-cpu: Fix clock disable failure Disable MI2S bit clock from PAUSE/STOP/SUSPEND usecase instead of shutdown time. Acheive this by invoking clk_disable API from cpu daiops trigger instead of cpu daiops shutdown. Change non-atomic API "clk_prepare_enable" to atomic API "clk_enable" in trigger, as trigger is being called from atomic context. Fixes: 7e6799d8f87d ("ASoC: qcom: lpass-cpu: Enable MI2S BCLK and LRCLK together") Signed-off-by: V Sujith Kumar Reddy Signed-off-by: Srinivasa Rao Mandadapu Link: https://lore.kernel.org/r/1603098363-9251-1-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'sound') 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; } -- cgit v1.2.3 From 158e1886b6262c1d1c96a18c85fac5219b8bf804 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 3 Nov 2020 13:18:07 +0300 Subject: ALSA: hda: prevent undefined shift in snd_hdac_ext_bus_get_link() This is harmless, but the "addr" comes from the user and it could lead to a negative shift or to shift wrapping if it's too high. Fixes: 0b00a5615dc4 ("ALSA: hdac_ext: add hdac extended controller") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20201103101807.GC1127762@mwanda Signed-off-by: Takashi Iwai --- sound/hda/ext/hdac_ext_controller.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') 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++) { -- cgit v1.2.3 From ef9ce66fab959c66d270bbee7ca79b92ee957893 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 3 Nov 2020 15:40:35 +0800 Subject: ALSA: hda/realtek - Enable headphone for ASUS TM420 ASUS TM420 had depop circuit for headphone. It need to turn on by COEF bit. [ fixed the missing enum definition by tiwai ] Signed-off-by: Kailang Yang Cc: Link: https://lore.kernel.org/r/3d6177d7023b4783bf2793861c577ada@realtek.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4f0ea8013bf6..6899089d132e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6300,6 +6300,7 @@ enum { ALC255_FIXUP_XIAOMI_HEADSET_MIC, ALC274_FIXUP_HP_MIC, ALC274_FIXUP_HP_HEADSET_MIC, + ALC256_FIXUP_ASUS_HPE, }; static const struct hda_fixup alc269_fixups[] = { @@ -7693,6 +7694,17 @@ static const struct hda_fixup alc269_fixups[] = { .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 + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7876,6 +7888,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), -- cgit v1.2.3 From 07815a2b3501adeaae6384a25b9c4a9c81dae59f Mon Sep 17 00:00:00 2001 From: Artem Lapkin Date: Tue, 3 Nov 2020 18:08:09 +0800 Subject: ALSA: usb-audio: add usb vendor id as DSD-capable for Khadas devices Khadas audio devices ( USB_ID_VENDOR 0x3353 ) have DSD-capable implementations from XMOS need add new usb vendor id for recognition Signed-off-by: Artem Lapkin Cc: Link: https://lore.kernel.org/r/20201103103311.5435-1-art@khadas.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index b4fa80ef730d..c989ad8052ae 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1800,6 +1800,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) -- cgit v1.2.3 From 26201ddc1373c99b2a67c5774da2f0eecd749b93 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Wed, 4 Nov 2020 22:37:05 +1030 Subject: ALSA: usb-audio: Add implicit feedback quirk for MODX This patch fixes audio distortion on playback for the Yamaha MODX. Signed-off-by: Geoffrey D. Bennett Tested-by: Frank Slotta Cc: Link: https://lore.kernel.org/r/20201104120705.GA19126@b4.vu Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 31b1edf53c41..7227ccb7e57e 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -345,6 +345,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ifnum = 2; goto add_sync_ep_from_ifnum; case USB_ID(0x2466, 0x8003): /* Fractal Audio Axe-Fx II */ + case USB_ID(0x0499, 0x172a): /* Yamaha MODX */ ep = 0x86; ifnum = 2; goto add_sync_ep_from_ifnum; -- cgit v1.2.3 From f9d7c6eb23f7e55e7a0ca5451da06909bdfdd0e4 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Wed, 4 Nov 2020 17:57:38 +0200 Subject: ASoC: mchp-spdiftx: Do not set Validity bit(s) The Validity bits (bit 28) must not be set in order to have the samples valid. Some controllers look for this bit and ignore the samples if it is set. Fixes: 06ca24e98e6b ("ASoC: mchp-spdiftx: add driver for S/PDIF TX Controller") Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20201104155738.68403-1-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-spdiftx.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') 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); -- cgit v1.2.3 From 0938ecae432e7ac8b01080c35dd81d50a1e43033 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Wed, 4 Nov 2020 22:27:17 +1030 Subject: ALSA: usb-audio: Add implicit feedback quirk for Qu-16 This patch fixes audio distortion on playback for the Allen&Heath Qu-16. Signed-off-by: Geoffrey D. Bennett Cc: Link: https://lore.kernel.org/r/20201104115717.GA19046@b4.vu Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 7227ccb7e57e..a860303cc522 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -336,6 +336,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, switch (subs->stream->chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ + case USB_ID(0x22f0, 0x0006): /* Allen&Heath Qu-16 */ ep = 0x81; ifnum = 3; goto add_sync_ep_from_ifnum; -- cgit v1.2.3 From 93bd813c17763177cf87e96c2313bd4dd747d234 Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Thu, 5 Nov 2020 11:08:04 +0800 Subject: ASoC: rt1015: add delay to fix pop noise from speaker Add delay to fix pop noise from speaker. Signed-off-by: Jack Yu Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20201105030804.31115-1-jack.yu@realtek.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt1015.txt | 6 ++++++ include/sound/rt1015.h | 15 +++++++++++++++ sound/soc/codecs/rt1015.c | 20 ++++++++++++++++++++ sound/soc/codecs/rt1015.h | 2 ++ 4 files changed, 43 insertions(+) create mode 100644 include/sound/rt1015.h (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/rt1015.txt b/Documentation/devicetree/bindings/sound/rt1015.txt index fcfd02d8d32f..e498966d436f 100644 --- a/Documentation/devicetree/bindings/sound/rt1015.txt +++ b/Documentation/devicetree/bindings/sound/rt1015.txt @@ -8,10 +8,16 @@ Required properties: - reg : The I2C address of the device. +Optional properties: + +- realtek,power-up-delay-ms + Set a delay time for flush work to be completed, + this value is adjustable depending on platform. Example: rt1015: codec@28 { compatible = "realtek,rt1015"; reg = <0x28>; + realtek,power-up-delay-ms = <50>; }; diff --git a/include/sound/rt1015.h b/include/sound/rt1015.h new file mode 100644 index 000000000000..70a7538d4c89 --- /dev/null +++ b/include/sound/rt1015.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * linux/sound/rt1015.h -- Platform data for RT1015 + * + * Copyright 2020 Realtek Microelectronics + */ + +#ifndef __LINUX_SND_RT1015_H +#define __LINUX_SND_RT1015_H + +struct rt1015_platform_data { + unsigned int power_up_delay_ms; +}; + +#endif diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 25fe2ddedd54..967193518349 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -27,10 +27,15 @@ #include #include #include +#include #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 }, @@ -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 #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; -- cgit v1.2.3 From 446b8185f0c39ac3faadbcd8ac156c50f2fd4ffe Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Mon, 2 Nov 2020 15:00:12 +0800 Subject: ALSA: hda/realtek - Add supported for Lenovo ThinkPad Headset Button Add supported for Lenovo ThinkPad Headset Button. Thinkpad P1 Gen 3 (0x22c1) Thinkpad X1 Extreme Gen 3 (0x22c2) Signed-off-by: Kailang Yang Cc: Link: https://lore.kernel.org/r/f39b11d00340408ca2ed2df9b4fc2a09@realtek.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6899089d132e..c3a02738ead2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6301,6 +6301,7 @@ enum { ALC274_FIXUP_HP_MIC, ALC274_FIXUP_HP_HEADSET_MIC, ALC256_FIXUP_ASUS_HPE, + ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, }; static const struct hda_fixup alc269_fixups[] = { @@ -7705,6 +7706,12 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC294_FIXUP_ASUS_HEADSET_MIC }, + [ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_jack, + .chained = true, + .chain_id = ALC269_FIXUP_THINKPAD_ACPI + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7966,6 +7973,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), -- cgit v1.2.3 From b5acfe152abaa2721c9ca8aa67f941d7de55d24e Mon Sep 17 00:00:00 2001 From: PeiSen Hou Date: Wed, 11 Nov 2020 08:58:59 +0100 Subject: ALSA: hda/realtek: Add some Clove SSID in the ALC293(ALC1220) Fix "use as headset mic, without its own jack detect" problem. [ Minor coding style fixes by tiwai ] Signed-off-by: PeiSen Hou Cc: Link: https://lore.kernel.org/r/481963e4a5694ff19f27ae1e283d79ad@realtek.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 50 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c3a02738ead2..69a952c9e011 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), @@ -7931,11 +7941,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), -- cgit v1.2.3 From a0ccbc5319d57b9efdc55c943a3fde30a0776502 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Fri, 6 Nov 2020 15:20:38 +0800 Subject: ALSA: hda/realtek - Add supported mute Led for HP HP Pavilion x360 Convertible machine, it supported mute led. GPIO4 high will turn on led. The patch will enable control led via GPIO4 pin. Signed-off-by: Kailang Yang Cc: Link: https://lore.kernel.org/r/1ae4d98e92c147b780ace3911c4e1d73@realtek.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 69a952c9e011..371f6b865439 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -4226,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) @@ -6312,6 +6318,7 @@ enum { ALC274_FIXUP_HP_HEADSET_MIC, ALC256_FIXUP_ASUS_HPE, ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK, + ALC287_FIXUP_HP_GPIO_LED, }; static const struct hda_fixup alc269_fixups[] = { @@ -7722,6 +7729,10 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_THINKPAD_ACPI }, + [ALC287_FIXUP_HP_GPIO_LED] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_hp_gpio_led, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7876,6 +7887,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), -- cgit v1.2.3 From 9e885770277d2ed8d85f9cbd4992515ec324242f Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 3 Nov 2020 15:30:51 +0800 Subject: ALSA: hda/realtek - HP Headset Mic can't detect after boot System boot or warm boot with plugged headset. If it turn on power save mode, Headset Mic will lose. This patch will solve this issue. Signed-off-by: Kailang Yang Cc: Link: https://lore.kernel.org/r/1ae4d98e92c147b780ace3911c4e1d73@realtek.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 371f6b865439..739dbaf54517 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6319,6 +6319,7 @@ enum { 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[] = { @@ -7733,6 +7734,10 @@ static const struct hda_fixup alc269_fixups[] = { .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[] = { @@ -8348,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}), @@ -8450,6 +8459,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x1a, 0x90a70130}, {0x1b, 0x90170110}, {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0256, 0x103c, "HP", ALC256_FIXUP_HP_HEADSET_MIC, + {0x14, 0x90170110}, + {0x19, 0x02a11020}, + {0x21, 0x0221101f}), SND_HDA_PIN_QUIRK(0x10ec0274, 0x103c, "HP", ALC274_FIXUP_HP_HEADSET_MIC, {0x17, 0x90170110}, {0x19, 0x03a11030}, -- cgit v1.2.3 From 95a793c3bc75cf888e0e641d656e7d080f487d8b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 13 Nov 2020 18:20:43 +0900 Subject: ALSA: ctl: fix error path at adding user-defined element set When processing request to add/replace user-defined element set, check of given element identifier and decision of numeric identifier is done in "__snd_ctl_add_replace()" helper function. When the result of check is wrong, the helper function returns error code. The error code shall be returned to userspace application. Current implementation includes bug to return zero to userspace application regardless of the result. This commit fixes the bug. Cc: Fixes: e1a7bfe38079 ("ALSA: control: Fix race between adding and removing a user element") Signed-off-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20201113092043.16148-1-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai --- sound/core/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') 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, -- cgit v1.2.3 From 106e6d8df4842d816dae23076c501ae48386afcb Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Fri, 13 Nov 2020 17:21:25 +0800 Subject: ASoC: rt1015: increase the time to detect BCLK To meet the most platform, the detection time should be increased to avoid that the flushing DAC data fails. Signed-off-by: Shuming Fan Link: https://lore.kernel.org/r/20201113092125.19206-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1015.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 967193518349..3db07293c70b 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -544,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); -- cgit v1.2.3 From 02a9c6ee4183af2e438454c55098b828a96085fb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 13 Nov 2020 13:12:41 +0300 Subject: ALSA: firewire: Clean up a locking issue in copy_resp_to_buf() The spin_lock/unlock_irq() functions cannot be nested. The problem is that presumably we would want the IRQs to be re-enabled on the second call the spin_unlock_irq() but instead it will be enabled at the first call so IRQs will be enabled earlier than expected. In this situation the copy_resp_to_buf() function is only called from one function and it is called with IRQs disabled. We can just use the regular spin_lock/unlock() functions. Fixes: 555e8a8f7f14 ("ALSA: fireworks: Add command/response functionality into hwdep interface") Signed-off-by: Dan Carpenter Acked-by: Takashi Sakamoto Cc: Link: https://lore.kernel.org/r/20201113101241.GB168908@mwanda Signed-off-by: Takashi Iwai --- sound/firewire/fireworks/fireworks_transaction.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') 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 -- cgit v1.2.3 From e5633b95dce915c2ade5ce1c90d295d555396c60 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Sun, 15 Nov 2020 23:38:42 +0800 Subject: ALSA: usb-audio: Use ALC1220-VB-DT mapping for ASUS ROG Strix TRX40 mobo ASUS ROG Strix also uses ALC1220-VB-DT, so adjust the mapping and add profile name to let userspace pick correct UCM profile. BugLink: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/issues/1031 Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20201115153843.1109200-1-kai.heng.feng@canonical.com Signed-off-by: Takashi Iwai --- sound/usb/card.c | 4 ++++ sound/usb/mixer_maps.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index fa764b61fe9c..4457214a3ae6 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -379,6 +379,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 */ 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), -- cgit v1.2.3 From d78359b25f7c6759a23189145be8141b6fdfe385 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 16 Nov 2020 16:19:55 +0200 Subject: ALSA: hda: Add Alderlake-S PCI ID and HDMI codec vid Add HD Audio PCI ID and HDMI codec vendor ID for Intel Alder Lake. Signed-off-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20201116141955.2091240-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 +++ sound/pci/hda/patch_hdmi.c | 1 + 2 files changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index d539f52009a1..6852668f1bcb 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2506,6 +2506,9 @@ static const struct pci_device_id azx_ids[] = { /* DG1 */ { PCI_DEVICE(0x8086, 0x490d), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* Alderlake-S */ + { PCI_DEVICE(0x8086, 0x7ad0), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Elkhart Lake */ { PCI_DEVICE(0x8086, 0x4b55), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, diff --git a/sound/pci/hda/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), -- cgit v1.2.3 From 1bd7b0fc0165694897b7d2fb39751a07b98f6bf1 Mon Sep 17 00:00:00 2001 From: Michael Sit Wei Hong Date: Mon, 16 Nov 2020 14:19:01 +0800 Subject: ASoC: Intel: KMB: Fix S24_LE configuration S24_LE is 24 bit audio in 32 bit container configuration Fixing the configuration to match the data arrangement of this audio format. Fixes: c5477e966728 ("ASoC: Intel: Add KeemBay platform driver") Signed-off-by: Michael Sit Wei Hong Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20201116061905.32431-2-michael.wei.hong.sit@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/keembay/kmb_platform.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') 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; -- cgit v1.2.3 From bd6327fda2f3ded85b69b3c3125c99aaa51c7881 Mon Sep 17 00:00:00 2001 From: Srinivasa Rao Mandadapu Date: Sun, 15 Nov 2020 10:26:50 +0530 Subject: ASoC: qcom: lpass-platform: Fix memory leak lpass_pcm_data is not freed in error paths. Free it in error paths to avoid memory leak. Fixes: 022d00ee0b55 ("ASoC: lpass-platform: Fix broken pcm data usage") Signed-off-by: Pavel Machek Signed-off-by: V Sujith Kumar Reddy Signed-off-by: Srinivasa Rao Mandadapu Link: https://lore.kernel.org/r/1605416210-14530-1-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') 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; -- cgit v1.2.3 From aa9e3fa4992d83acb7311fc86d11d0d53e7ffb8e Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 16 Nov 2020 14:33:28 +0100 Subject: ASoC: Intel: catpt: Skip position update for unprepared streams Playing with very low period sizes may lead to timeouts when awaiting RESET_STREAM reply for offload streams. This is caused by NOTIFY_POSITION appearing in the middle of trigger(stop). Stream is unprepared during trigger(stop) where PAUSE_STREAM IPC gets invoked. However, all data that is already mixed in DSP firmware's mixer stream will still be played regardless of the pause. For offload streams, this means possibility for another NOTIFY_POSITION to process. Keep these notifications in check by only handling them when stream is in prepared state. Fixes: a126750fc865 ("ASoC: Intel: catpt: PCM operations") Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20201116133332.8530-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index f78018c857b8..174a786cd776 100644 --- a/sound/soc/intel/catpt/pcm.c +++ b/sound/soc/intel/catpt/pcm.c @@ -534,6 +534,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; -- cgit v1.2.3 From 1072460a1aabacf6ececda98acd3b5ecaad23fd2 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Mon, 16 Nov 2020 14:33:29 +0100 Subject: ASoC: Intel: catpt: Correct clock selection for dai trigger During stream start DSP firmware requires LPCS disabled as that moment in time is resource heavy. Currently high-clock is selected on start of second stream onwards while low-clock is re-selected before stream actually leaves RESUME state i.e. PAUSE_STREAM call. Fix this by always updating clock before RESUME_STREAM and directly after PAUSE_STREAM. Fixes: a126750fc865 ("ASoC: Intel: catpt: PCM operations") Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20201116133332.8530-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/pcm.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c index 174a786cd776..2061978b6cb9 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; -- cgit v1.2.3 From 54a2a3898f469a915510038fe84ef4f083131d3e Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Tue, 17 Nov 2020 13:28:03 +0100 Subject: ALSA: usb-audio: Add delay quirk for all Logitech USB devices Found one more Logitech device, BCC950 ConferenceCam, which needs the same delay here. This makes 3 out of 3 devices I have tried. Therefore, add a delay for all Logitech devices as it does not hurt. Signed-off-by: Joakim Tjernlund Cc: # 4.19.y, 5.4.y Link: https://lore.kernel.org/r/20201117122803.24310-1-joakim.tjernlund@infinera.com Signed-off-by: Takashi Iwai --- sound/usb/quirks.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index c989ad8052ae..c50be2f75f70 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1672,13 +1672,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) -- cgit v1.2.3 From 879ee8b6f2bae0cc4a25536f8841db1dbc969523 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 11 Nov 2020 12:54:34 -0800 Subject: ASOC: Intel: kbl_rt5663_rt5514_max98927: Do not try to disable disabled clock In kabylake_set_bias_level(), enabling mclk may fail if the clock has already been enabled by the firmware. Attempts to disable that clock later will fail with a warning backtrace. mclk already disabled WARNING: CPU: 2 PID: 108 at drivers/clk/clk.c:952 clk_core_disable+0x1b6/0x1cf ... Call Trace: clk_disable+0x2d/0x3a kabylake_set_bias_level+0x72/0xfd [snd_soc_kbl_rt5663_rt5514_max98927] snd_soc_card_set_bias_level+0x2b/0x6f snd_soc_dapm_set_bias_level+0xe1/0x209 dapm_pre_sequence_async+0x63/0x96 async_run_entry_fn+0x3d/0xd1 process_one_work+0x2a9/0x526 ... Only disable the clock if it has been enabled. Fixes: 15747a802075 ("ASoC: eve: implement set_bias_level function for rt5514") Cc: Brent Lu Cc: Curtis Malainey Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20201111205434.207610-1-linux@roeck-us.net Signed-off-by: Mark Brown --- sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') 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 { -- cgit v1.2.3 From 551310e7356cb8af4eb4c618961ad1e7b2f89e19 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Nov 2020 13:04:04 +0100 Subject: ALSA: hda/ca0132: Fix compile warning without PCI CONFIG_PCI=n leads to a compile warning like: sound/pci/hda/patch_ca0132.c:8214:10: warning: no case matching constant switch condition '0' due to the missed handling of QUIRK_NONE in ca0132_mmio_init(). Fix it. Fixes: bf2aa9ccc8e5 ("ALSA: hda/ca0132 - Cleanup ca0132_mmio_init function.") Reported-by: kernel test robot Link: https://lore.kernel.org/r/20201119120404.16833-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') 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; } } -- cgit v1.2.3 From d21b96c8ed2aea7e6b7bf4735e1d2503cfbf4072 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 19 Nov 2020 13:14:40 +0100 Subject: ALSA: mixart: Fix mutex deadlock The code change for switching to non-atomic mode brought the unexpected mutex deadlock in get_msg(). It converted the spinlock with the existing mutex, but there were calls with the already holding the mutex. Since the only place that needs the extra lock is the code path from snd_mixart_send_msg(), remove the mutex lock in get_msg() and apply in the caller side for fixing the mutex deadlock. Fixes: 8d3a8b5cb57d ("ALSA: mixart: Use nonatomic PCM ops") Reported-by: Dan Carpenter Cc: Link: https://lore.kernel.org/r/20201119121440.18945-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') 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"); -- cgit v1.2.3 From bc4e94aa8e72e79598e63a0b73febdcd8aeb541f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:07 +0100 Subject: ALSA: usb-audio: Handle discrete rates properly in hw constraints In the current code, when the device provides the discrete sample rate tables with unusual sample rates, the driver tries to gather the whole values from the audioformat entries and create a hw-constraint rule to restrict with this single rate list. This is rather inefficient and may overlook the rates that are associated only with the certain audioformat entries. This patch improves the hw constraint setup by rewriting the existing hw_rule_rate(). The discrete sample rates (identified by rate_table and nr_rates of format entry) are checked in the existing hw_rule_rate() instead of extra rules; in the case of discrete rates, the function compares with each rate table entry and calculates the min/max values from there. For the contiguous rates, the behavior doesn't change. Along with it, snd_usb_pcm_check_knot() and snb_usb_substream rate_list field become superfluous, thus those are dropped. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 1 - sound/usb/pcm.c | 73 +++++++++++------------------------------------------- sound/usb/stream.c | 1 - 3 files changed, 15 insertions(+), 60 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 5351d7183b1b..3cc668f98f43 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -157,7 +157,6 @@ struct snd_usb_substream { 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/pcm.c b/sound/usb/pcm.c index a860303cc522..3265155df1b5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1039,27 +1039,31 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, 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; + unsigned int rmin, rmax, r; int changed; + int i; hwc_debug("hw_rule_rate: (%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->rate_min) - rmin = fp->rate_min; - if (rmax < fp->rate_max) - rmax = fp->rate_max; + 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 = fp->rate_min; - rmax = fp->rate_max; + rmin = min(rmin, fp->rate_min); + rmax = max(rmax, fp->rate_max); } } - if (!changed) { + if (rmin > rmax) { hwc_debug(" --> get empty\n"); it->empty = 1; return -EINVAL; @@ -1205,50 +1209,6 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params, return changed; } -/* - * 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) -{ - struct audioformat *fp; - int *rate_list; - int count = 0, needs_knot = 0; - 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; - } - if (!needs_knot) - 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]; - } - err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &subs->rate_list); - if (err < 0) - return err; - - return 0; -} - /* * set up the runtime hardware information. @@ -1338,9 +1298,6 @@ 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); } diff --git a/sound/usb/stream.c b/sound/usb/stream.c index ca76ba5b5c0b..f17913e0b5b4 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); } -- cgit v1.2.3 From 4974b7950929e4a28d4eaee48e4ad07f168ac132 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:08 +0100 Subject: ALSA: usb-audio: Don't call usb_set_interface() at trigger callback The PCM trigger callback is atomic, hence we must not call a function like usb_set_interface() there. Calling it from there would lead to a kernel Oops. Fix it by moving the usb_set_interface() call to set_sync_endpoint(). Also, apply the snd_usb_set_interface_quirk() for consistency, too. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 3265155df1b5..380d7275d187 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -232,21 +232,6 @@ static int start_endpoints(struct snd_usb_substream *subs) !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; @@ -530,6 +515,19 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, subs->data_endpoint->sync_master = 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) + return err; + dev_dbg(&dev->dev, "setting usb interface %d:%d\n", + subs->sync_endpoint->iface, + subs->sync_endpoint->altsetting); + snd_usb_set_interface_quirk(dev); + } + return 0; } -- cgit v1.2.3 From 93db51d06b32227319dae2ac289029ccf1b33181 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:09 +0100 Subject: ALSA: usb-audio: Check valid altsetting at parsing rates for UAC2/3 The current driver code assumes blindly that all found sample rates for the same endpoint from the UAC2 and UAC3 descriptors can be used no matter which altsetting, but actually this was wrong: some devices accept only limited sample rates in each altsetting. For determining which altsetting supports which rate, we need to verify each sample rate and check the validity via UAC2_AS_VAL_ALT_SETTINGS. This control reports back the available altsettings as a bitmap. This patch implements the missing piece above, the verification and reconstructs the sample rate tables based on the result. An open question is how to deal with the altsettings that ended up with no valid sample rates after verification. At least, there is a device that showed this problem although the sample rates did work in the later usage (see bug link). For now, we accept such an altset as is, assuming that it's a firmware bug. Reported-by: Dylan Robinson Tested-by: Keith Milner Tested-by: Dylan Robinson BugLink: https://bugzilla.suse.com/show_bug.cgi?id=1178203 Link: https://lore.kernel.org/r/20201123085347.19667-4-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/clock.c | 106 ++++++++++++++++++++++++++++++----------------------- sound/usb/clock.h | 4 ++ sound/usb/format.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 45 deletions(-) (limited to 'sound') diff --git a/sound/usb/clock.c b/sound/usb/clock.c index f3ca59005d91..f174230d07d5 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -560,16 +560,60 @@ static int get_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, return le32_to_cpu(data); } +/* + * 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) +{ + 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, int iface, - struct usb_host_interface *alts, - struct audioformat *fmt, int rate) + struct usb_host_interface *alts, + struct audioformat *fmt, int rate) { struct usb_device *dev = chip->dev; - __le32 data; - int err, cur_rate, prev_rate; + int cur_rate, prev_rate; int clock; - bool writeable; - u32 bmControls; /* First, try to find a valid clock. This may trigger * automatic clock selection if the current clock is not @@ -592,50 +636,22 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, 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", + 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); + 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; } /* Some devices doesn't respond to sample rate changes while the diff --git a/sound/usb/clock.h b/sound/usb/clock.h index 68df0fbe09d0..97597f5a3c18 100644 --- a/sound/usb/clock.h +++ b/sound/usb/clock.h @@ -9,4 +9,8 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, int snd_usb_clock_find_source(struct snd_usb_audio *chip, 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/format.c b/sound/usb/format.c index 3bfead393aa3..3348032daa16 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -417,6 +417,97 @@ 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; + unsigned int rate_min = 0x7fffffff; + unsigned int rate_max = 0; + unsigned int rates = 0; + 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 (rate_min > fp->rate_table[i]) + rate_min = fp->rate_table[i]; + if (rate_max < fp->rate_table[i]) + rate_max = fp->rate_table[i]; + rates |= snd_pcm_rate_to_rate_bit(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; + fp->rate_min = rate_min; + fp->rate_max = rate_max; + fp->rates = rates; + return 0; +} + /* * parse the format descriptor and stores the possible sample rates * on the audioformat table (audio class v2 and v3). @@ -509,6 +600,8 @@ 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); + err_free: kfree(data); err: -- cgit v1.2.3 From 2e43aae2bf5a4ede98cbe0c85ad104dd7ba5dfd2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:10 +0100 Subject: ALSA: usb-audio: Check implicit feedback EP generically for UAC2 It seems that many UAC2 devices are with the implicit feedback, but they couldn't be probed properly because the assumption the driver takes currently isn't applied: they have the single endpoint for both data and implicit-fb streams, while we checked only the classical sync endpoints assigned to the next altsetting in the same interface. This patch extends the search to match with those typical cases where the implicit fb stream is found in the next interface number. While we're at it, slightly refactor the code, not returning 0/-ERROR but use the standard bool to success/failur, which is more intuitive in this particular case. Reported-by: Dylan Robinson Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-5-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 83 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 380d7275d187..2b11c2c837bf 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -272,33 +272,70 @@ 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) +/* Check whether the given iface:altsetting points to an implicit fb source */ +static bool search_generic_implicit_fb(struct usb_device *dev, int ifnum, + unsigned int altsetting, + struct usb_host_interface **altsp, + unsigned int *ep) { struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + struct usb_endpoint_descriptor *epd; + + iface = usb_ifnum_to_if(dev, ifnum); + if (!iface) + return false; + alts = usb_altnum_to_altsetting(iface, altsetting); + if (!alts) + return false; + altsd = get_iface_desc(alts); + if (altsd->bInterfaceClass != USB_CLASS_AUDIO || + altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING || + altsd->bInterfaceProtocol != UAC_VERSION_2 || + altsd->bNumEndpoints < 1) + return false; + epd = get_endpoint(alts, 0); + if (!usb_endpoint_is_isoc_in(epd) || + (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != + USB_ENDPOINT_USAGE_IMPLICIT_FB) + return false; + *ep = epd->bEndpointAddress; + *altsp = alts; + return true; +} + +/* Like the function above, but specific to Roland with vendor class and hack */ +static bool search_roland_implicit_fb(struct usb_device *dev, int ifnum, + unsigned int altsetting, + struct usb_host_interface **altsp, + unsigned int *ep) +{ + struct usb_interface *iface; + struct usb_host_interface *alts; 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 || + if (!iface) + return false; + alts = usb_altnum_to_altsetting(iface, altsetting); + if (!alts) + return false; + altsd = get_iface_desc(alts); + if (altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC || (altsd->bInterfaceSubClass != 2 && - altsd->bInterfaceProtocol != 2 ) || + altsd->bInterfaceProtocol != 2) || altsd->bNumEndpoints < 1) - return -ENOENT; - epd = get_endpoint(*alts, 0); + return false; + 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; + return false; *ep = epd->bEndpointAddress; - return 0; + *altsp = alts; + return true; } /* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk @@ -375,6 +412,19 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, return 0; } + /* Generic UAC2 implicit feedback */ + if (attr == USB_ENDPOINT_SYNC_ASYNC && + altsd->bInterfaceClass == USB_CLASS_AUDIO && + altsd->bInterfaceProtocol == UAC_VERSION_2 && + altsd->bNumEndpoints == 1) { + ifnum = altsd->bInterfaceNumber + 1; + if (search_generic_implicit_fb(dev, ifnum, + altsd->bAlternateSetting, + &alts, &ep)) + goto add_sync_ep; + } + + /* Roland/BOSS implicit feedback with vendor spec class */ if (attr == USB_ENDPOINT_SYNC_ASYNC && altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && altsd->bInterfaceProtocol == 2 && @@ -382,9 +432,8 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ && search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1, altsd->bAlternateSetting, - &alts, &ep) >= 0) { + &alts, &ep)) goto add_sync_ep; - } /* No quirk */ return 0; -- cgit v1.2.3 From c7474d09777c3d321f9f7f6a416f276204926c54 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:11 +0100 Subject: ALSA: usb-audio: Add snd_usb_get_endpoint() helper Factor out the code to obtain snd_usb_endpoint object matching with the given endpoint. It'll be used in the later patch to add the implicit feedback hw-constraint. No functional change by this patch itself. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-6-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 36 +++++++++++++++++++++++++++--------- sound/usb/endpoint.h | 4 ++++ 2 files changed, 31 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index e2f9ce2f5b8b..cf00871fd278 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -439,6 +439,26 @@ exit_clear: clear_bit(ctx->index, &ep->active_mask); } +/* + * Get the existing endpoint object corresponding EP, iface and alt numbers + * Returns NULL if not present. + * Call inside chip->mutex locking for avoiding the race. + */ +struct snd_usb_endpoint * +snd_usb_get_endpoint(struct snd_usb_audio *chip, + int ep_num, int iface, int altsetting) +{ + struct snd_usb_endpoint *ep; + + list_for_each_entry(ep, &chip->ep_list, list) { + if (ep->ep_num == ep_num && + ep->iface == iface && + ep->altsetting == altsetting) + return ep; + } + return NULL; +} + /** * snd_usb_add_endpoint: Add an endpoint to an USB audio chip * @@ -470,15 +490,13 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, 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; - } + ep = snd_usb_get_endpoint(chip, ep_num, + alts->desc.bInterfaceNumber, + alts->desc.bAlternateSetting); + if (ep) { + 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", diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index d23fa0a8c11b..61487095a766 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -5,6 +5,10 @@ #define SND_USB_ENDPOINT_TYPE_DATA 0 #define SND_USB_ENDPOINT_TYPE_SYNC 1 +struct snd_usb_endpoint *snd_usb_get_endpoint(struct snd_usb_audio *chip, + int ep_num, int iface, + int altsetting); + 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); -- cgit v1.2.3 From 1803503fe963afe850b26769f5447f871b1c6f83 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:12 +0100 Subject: ALSA: usb-audio: Set and clear sync EP link properly The sync EP setup isn't cleared at stopping the stream but expected to be cleared at the next stream start. This may leave the sync link setup stale and can spoof wrongly when full duplex streams were running in the implicit fb sync. Let's initialize them properly at start and end of the stream. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-7-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 2b11c2c837bf..8800ec627a73 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -238,6 +238,7 @@ static int start_endpoints(struct snd_usb_substream *subs) err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); + ep->sync_slave = NULL; return err; } } @@ -253,8 +254,10 @@ static void sync_pending_stops(struct snd_usb_substream *subs) static void stop_endpoints(struct snd_usb_substream *subs) { - if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) + if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { snd_usb_endpoint_stop(subs->sync_endpoint); + subs->sync_endpoint->sync_slave = NULL; + } if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) snd_usb_endpoint_stop(subs->data_endpoint); @@ -471,26 +474,10 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, bool implicit_fb; int err; - /* 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. - */ 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; - } + subs->sync_endpoint = NULL; + subs->data_endpoint->sync_master = NULL; err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr); if (err < 0) @@ -939,6 +926,11 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) sync_pending_stops(subs); snd_usb_endpoint_deactivate(subs->sync_endpoint); snd_usb_endpoint_deactivate(subs->data_endpoint); + if (subs->data_endpoint) { + subs->data_endpoint->sync_master = NULL; + subs->data_endpoint = NULL; + } + subs->sync_endpoint = NULL; snd_usb_unlock_shutdown(subs->stream->chip); } -- cgit v1.2.3 From e93e890e16ef5a0605b7cdc52b3bde50d88d7207 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:13 +0100 Subject: ALSA: usb-audio: Improve some debug prints There are a few rooms for improvements wrt the debug prints: - The EP debug print is shown only at starting, not at stopping - The EP debug print contains useless object addresses - Some helpers show the urb and the EP object addresses, too This patch addresses those shortcomings. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-8-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 8 ++++---- sound/usb/pcm.c | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index cf00871fd278..8205a64a734e 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -367,8 +367,8 @@ static void queue_pending_output_urbs(struct snd_usb_endpoint *ep) err = usb_submit_urb(ctx->urb, GFP_ATOMIC); if (err < 0) usb_audio_err(ep->chip, - "Unable to submit urb #%d: %d (urb %p)\n", - ctx->index, err, ctx->urb); + "Unable to submit urb #%d: %d at %s\n", + ctx->index, err, __func__); else set_bit(ctx->index, &ep->active_mask); } @@ -494,8 +494,8 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, alts->desc.bInterfaceNumber, alts->desc.bAlternateSetting); if (ep) { - usb_audio_dbg(ep->chip, "Re-using EP %x in iface %d,%d @%p\n", - ep_num, ep->iface, ep->altsetting, ep); + usb_audio_dbg(ep->chip, "Re-using EP %x in iface %d,%d\n", + ep_num, ep->iface, ep->altsetting); goto __exit_unlock; } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 8800ec627a73..8f4fe65d5c37 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -218,7 +218,7 @@ static int start_endpoints(struct snd_usb_substream *subs) 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); + dev_dbg(&subs->dev->dev, "Starting data EP 0x%x\n", ep->ep_num); ep->data_subs = subs; err = snd_usb_endpoint_start(ep); @@ -232,7 +232,7 @@ static int start_endpoints(struct snd_usb_substream *subs) !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { struct snd_usb_endpoint *ep = subs->sync_endpoint; - dev_dbg(&subs->dev->dev, "Starting sync EP @%p\n", ep); + dev_dbg(&subs->dev->dev, "Starting sync EP 0x%x\n", ep->ep_num); ep->sync_slave = subs->data_endpoint; err = snd_usb_endpoint_start(ep); @@ -255,12 +255,17 @@ static void sync_pending_stops(struct snd_usb_substream *subs) static void stop_endpoints(struct snd_usb_substream *subs) { if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { + dev_dbg(&subs->dev->dev, "Stopping sync EP 0x%x\n", + subs->sync_endpoint->ep_num); snd_usb_endpoint_stop(subs->sync_endpoint); subs->sync_endpoint->sync_slave = NULL; } - if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) + if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { + dev_dbg(&subs->dev->dev, "Stopping data EP 0x%x\n", + subs->data_endpoint->ep_num); snd_usb_endpoint_stop(subs->data_endpoint); + } } /* PCM sync_stop callback */ -- cgit v1.2.3 From f6581c0e5d297cc1e0d7eb7c2603097f532e629a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:14 +0100 Subject: ALSA: usb-audio: Track implicit fb sync endpoint in audioformat list Instead of parsing and evaluating the sync endpoint and the implicit feedback mode at each time the audio stream is opened, let's parse it once at the probe time, as the all needed information can be obtained statically from the descriptor or from the quirk. This patch extends audioformat struct to record the sync endpoint, interface and altsetting as well as the implicit feedback flag, which are filled at parsing the streams. Then, set_sync_endpoint() is much simplified just to follow the already parsed data. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-9-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 4 ++ sound/usb/pcm.c | 149 +++++++++++++++++++++++++++++++++-------------------- sound/usb/pcm.h | 2 + sound/usb/stream.c | 2 + 4 files changed, 100 insertions(+), 57 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 3cc668f98f43..898a283576df 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -22,6 +22,10 @@ struct audioformat { 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 datainterval; /* log_2 of data packet interval */ unsigned char protocol; /* UAC_VERSION_1/2/3 */ unsigned int maxpacksize; /* max. packet size */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 8f4fe65d5c37..fea2764163b4 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -349,21 +349,18 @@ static bool search_roland_implicit_fb(struct usb_device *dev, int ifnum, /* 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) +static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + struct usb_interface *iface, + struct usb_host_interface *alts) { - struct usb_host_interface *alts; - struct usb_interface *iface; + struct usb_device *dev = chip->dev; + struct usb_interface_descriptor *altsd = get_iface_desc(alts); + unsigned int attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; 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) { + switch (chip->usb_id) { case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ case USB_ID(0x22f0, 0x0006): /* Allen&Heath Qu-16 */ @@ -437,11 +434,13 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, 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)) - goto add_sync_ep; + USB_ID_VENDOR(chip->usb_id) == 0x0582 /* Roland */) { + ifnum = altsd->bInterfaceNumber + 1; + if (search_roland_implicit_fb(dev, ifnum, + altsd->bAlternateSetting, + &alts, &ep)) + goto add_sync_ep; + } /* No quirk */ return 0; @@ -450,56 +449,59 @@ add_sync_ep_from_ifnum: iface = usb_ifnum_to_if(dev, ifnum); if (!iface || iface->num_altsetting < 2) - return -EINVAL; + return 0; 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; + fmt->sync_ep = ep; + fmt->sync_iface = ifnum; + fmt->sync_altsetting = alts->desc.bAlternateSetting; + fmt->implicit_fb = 1; + dev_dbg(&dev->dev, "%d:%d: found implicit_fb sync_ep=%x, iface=%d, alt=%d\n", + fmt->iface, fmt->altsetting, fmt->sync_ep, fmt->sync_iface, + fmt->sync_altsetting); return 1; } -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 snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, + struct audioformat *fmt) { - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - unsigned int ep, attr; - bool implicit_fb; + struct usb_device *dev = chip->dev; + struct usb_interface *iface; + struct usb_host_interface *alts; + struct usb_interface_descriptor *altsd; + unsigned int ep, attr, sync_attr; + bool is_playback; int err; - attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; - - 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) + iface = usb_ifnum_to_if(dev, fmt->iface); + if (!iface) + return 0; + alts = usb_altnum_to_altsetting(iface, fmt->altsetting); + if (!alts) return 0; + altsd = get_iface_desc(alts); + + is_playback = !(get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); + if (is_playback) { + err = audioformat_implicit_fb_quirk(chip, fmt, iface, alts); + if (err > 0) + return 0; + } if (altsd->bNumEndpoints < 2) return 0; + 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 @@ -510,7 +512,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, @@ -537,22 +539,57 @@ 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; + if ((sync_attr & USB_ENDPOINT_USAGE_MASK) == USB_ENDPOINT_USAGE_IMPLICIT_FB) + fmt->implicit_fb = 1; + + 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; +} + +static int set_sync_endpoint(struct snd_usb_substream *subs, + struct audioformat *fmt) +{ + struct usb_device *dev = subs->dev; + struct usb_interface *iface; + struct usb_host_interface *alts; + int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int ep; + int err; + + subs->sync_endpoint = NULL; + subs->data_endpoint->sync_master = NULL; + + ep = fmt->sync_ep; + if (!ep) + return 0; + + iface = usb_ifnum_to_if(dev, fmt->sync_iface); + if (!iface) + return 0; + + alts = usb_altnum_to_altsetting(iface, fmt->altsetting); + if (!alts) + return 0; 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); - + fmt->implicit_fb ? + SND_USB_ENDPOINT_TYPE_DATA : + SND_USB_ENDPOINT_TYPE_SYNC); if (!subs->sync_endpoint) { - if (is_playback && attr == USB_ENDPOINT_SYNC_NONE) + if (is_playback && + (fmt->ep_attr & USB_ENDPOINT_SYNCTYPE) == USB_ENDPOINT_SYNC_NONE) return 0; return -EINVAL; } - subs->sync_endpoint->is_implicit_feedback = implicit_fb; + subs->sync_endpoint->is_implicit_feedback = fmt->implicit_fb; subs->data_endpoint->sync_master = subs->sync_endpoint; @@ -579,7 +616,6 @@ 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; @@ -589,7 +625,6 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) 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; @@ -639,7 +674,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (!subs->data_endpoint) return -EINVAL; - err = set_sync_endpoint(subs, fmt, dev, alts, altsd); + err = set_sync_endpoint(subs, fmt); if (err < 0) return err; diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index 9833627c1eca..362782c2df5c 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -14,5 +14,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, 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/stream.c b/sound/usb/stream.c index f17913e0b5b4..7087ee2c8174 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1193,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); -- cgit v1.2.3 From 1865211d6789e8404c75278754f0fa4735165600 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:15 +0100 Subject: ALSA: usb-audio: Move snd_usb_autoresume() call out of setup_hw_info() This is a preliminary work for the upcoming hw-constraint change for the implicit feedback mode. Currently snd_usb_autoresume() is called at the end of setup_hwinfo(). It's a bit confusing; because of this implicit refcount usage, the caller side needs to call snd_usb_autosuspend() later in the error path although it's not seen inside the function. Instead, it's clearer to call both snd_usb_autoresume() and suspend() in the very same function. It's only refactoring and no functional changes. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-10-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index fea2764163b4..78933b6571d0 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1378,7 +1378,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre return err; } - return snd_usb_autoresume(subs->stream->chip); + return 0; } static int snd_usb_pcm_open(struct snd_pcm_substream *substream) @@ -1402,11 +1402,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; } -- cgit v1.2.3 From 5a6c3e11c9c9bc764cd4e2621cdb48cc9afb28a1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:16 +0100 Subject: ALSA: usb-audio: Add hw constraint for implicit fb sync In the current code, there is no check at the stream open time whether the endpoint is being already used by others. In the normal operations, this shouldn't happen, but in the case of the implicit feedback mode, it's a common problem with the full duplex operation, because the capture stream is always opened by the playback stream as an implicit sync source. Although we recently introduced the check of such a conflict of parameters at the PCM hw_params time, it doesn't give any hint at the hw_params itself and just gives the error. This isn't quite comfortable, and it caused problems on many applications. This patch attempts to make the parameter handling easier by introducing the strict hw constraint matching with the counterpart stream that is being used. That said, when an implicit feedback playback stream is running before a capture stream is opened, the capture stream carries the PCM hw-constraint to allow only the same sample rate, format, periods and period frames as the running playback stream. If not opened or there is no conflict of endpoints, the behavior remains as same as before. Note that this kind of "weak link" should work for most cases, but this is no concrete solution; e.g. if an application changes the hw params multiple times while another stream is opened, this would lead to inconsistencies. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-11-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 9 ++++ sound/usb/endpoint.c | 72 ++++++++++++++++++------- sound/usb/pcm.c | 148 +++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 175 insertions(+), 54 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 898a283576df..1f61be98a31d 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -114,6 +114,14 @@ struct snd_usb_endpoint { in a stream */ bool is_implicit_feedback; /* This endpoint is used as implicit feedback */ + /* for hw constraints */ + unsigned int cur_rate; + snd_pcm_format_t cur_format; + unsigned int cur_channels; + unsigned int cur_period_frames; + unsigned int cur_period_bytes; + unsigned int cur_buffer_periods; + spinlock_t lock; struct list_head list; }; @@ -144,6 +152,7 @@ struct snd_usb_substream { unsigned int stream_offset_adj; /* Bytes to drop from beginning of stream (for non-compliant devices) */ unsigned int running: 1; /* running status */ + unsigned int fixed_hw:1; /* fixed hw constraints due to sync EP */ unsigned int hwptr_done; /* processed byte position in the buffer */ unsigned int transfer_done; /* processed frames since last period update */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 8205a64a734e..94490d706013 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -459,6 +459,9 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, 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 * @@ -500,9 +503,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip, } 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); + is_playback ? "playback" : "capture", + ep_type_name(type), + ep_num); ep = kzalloc(sizeof(*ep), GFP_KERNEL); if (!ep) @@ -644,13 +647,14 @@ 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) + snd_pcm_format_t pcm_format, + unsigned int channels, + unsigned int period_bytes, + unsigned int frames_per_period, + unsigned int periods_per_buffer, + unsigned int rate, + 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; @@ -660,6 +664,14 @@ static bool check_ep_params(struct snd_usb_endpoint *ep, usb_pipeout(ep->pipe)); bool ret = 1; + /* matching with the saved parameters? */ + if (ep->cur_rate == rate && + ep->cur_format == pcm_format && + ep->cur_channels == channels && + ep->cur_period_frames == frames_per_period && + ep->cur_buffer_periods == periods_per_buffer) + return true; + if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) { /* * When operating in DSD DOP mode, the size of a sample frame @@ -917,7 +929,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep, * as their corresponding capture endpoint. */ if (usb_pipein(ep->pipe) || - snd_usb_endpoint_implicit_feedback_sink(ep)) { + ep->is_implicit_feedback || + snd_usb_endpoint_implicit_feedback_sink(ep)) { urb_packs = packs_per_ms; /* @@ -1076,12 +1089,17 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, { int err; + usb_audio_dbg(ep->chip, + "Setting params for ep %x (type %s, count %d), rate=%d, format=%s, channels=%d, period_bytes=%d, periods=%d\n", + ep->ep_num, ep_type_name(ep->type), ep->use_count, + rate, snd_pcm_format_name(pcm_format), channels, + period_bytes, buffer_periods); + 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); + check_ep_params(ep, pcm_format, channels, period_bytes, + period_frames, buffer_periods, rate, + fmt, sync_ep); if (!check) { usb_audio_warn(ep->chip, @@ -1134,11 +1152,22 @@ 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(ep->chip, "Set up %d URBS, ret=%d\n", ep->nurbs, err); - return err; + if (err < 0) + return err; + + /* record the current set up in the endpoint (for implicit fb) */ + spin_lock_irq(&ep->lock); + ep->cur_rate = rate; + ep->cur_channels = channels; + ep->cur_format = pcm_format; + ep->cur_period_frames = period_frames; + ep->cur_period_bytes = period_bytes; + ep->cur_buffer_periods = buffer_periods; + spin_unlock_irq(&ep->lock); + + return 0; } /** @@ -1273,6 +1302,11 @@ void snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) deactivate_urbs(ep, true); wait_clear_urbs(ep); + + /* clear the saved hw params */ + spin_lock_irq(&ep->lock); + ep->cur_rate = 0; + spin_unlock_irq(&ep->lock); } /** diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 78933b6571d0..6d1f5277cd90 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -81,30 +81,33 @@ 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 struct audioformat *find_format(struct list_head *fmt_list_head, + snd_pcm_format_t format, + unsigned int rate, + unsigned int channels, + struct snd_usb_substream *subs) { struct audioformat *fp; 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))) + list_for_each_entry(fp, fmt_list_head, list) { + if (!(fp->formats & pcm_format_to_bits(format))) continue; - if (fp->channels != subs->channels) + if (fp->channels != channels) continue; - if (subs->cur_rate < fp->rate_min || - subs->cur_rate > fp->rate_max) + 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 +117,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,6 +141,12 @@ static struct audioformat *find_format(struct snd_usb_substream *subs) return found; } +static struct audioformat *find_substream_format(struct snd_usb_substream *subs) +{ + return find_format(&subs->fmt_list, subs->pcm_format, subs->cur_rate, + subs->channels, subs); +} + static int init_pitch_v1(struct snd_usb_audio *chip, int iface, struct usb_host_interface *alts, struct audioformat *fmt) @@ -744,7 +753,6 @@ static int match_endpoint_audioformats(struct snd_usb_substream *subs, */ static int configure_sync_endpoint(struct snd_usb_substream *subs) { - int ret; struct audioformat *fp; struct audioformat *sync_fp = NULL; int cur_score = 0; @@ -752,16 +760,16 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs) 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); + if (subs->fixed_hw || + !subs->sync_endpoint->is_implicit_feedback) { + sync_fp = subs->cur_audiofmt; + goto configure; + } + + sync_fp = find_format(&sync_subs->fmt_list, subs->pcm_format, + subs->cur_rate, subs->channels, NULL); + if (sync_fp) + goto configure; /* Try to find the best matching audioformat. */ list_for_each_entry(fp, &sync_subs->fmt_list, list) { @@ -794,16 +802,16 @@ static int configure_sync_endpoint(struct snd_usb_substream *subs) __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: + return snd_usb_endpoint_set_params(subs->sync_endpoint, + subs->pcm_format, + sync_fp->channels, + sync_period_bytes, + subs->period_frames, + subs->buffer_periods, + subs->cur_rate, + sync_fp, + NULL); } /* @@ -912,7 +920,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, subs->channels = params_channels(hw_params); subs->cur_rate = params_rate(hw_params); - fmt = find_format(subs); + fmt = find_substream_format(subs); if (!fmt) { dev_dbg(&subs->dev->dev, "cannot set format: format = %#x, rate = %d, channels = %d\n", @@ -956,12 +964,13 @@ 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); subs->cur_audiofmt = NULL; subs->cur_rate = 0; subs->period_bytes = 0; - if (!snd_usb_lock_shutdown(subs->stream->chip)) { + if (!snd_usb_lock_shutdown(chip)) { stop_endpoints(subs); sync_pending_stops(subs); snd_usb_endpoint_deactivate(subs->sync_endpoint); @@ -971,7 +980,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->data_endpoint = NULL; } subs->sync_endpoint = NULL; - snd_usb_unlock_shutdown(subs->stream->chip); + snd_usb_unlock_shutdown(chip); } return 0; @@ -1288,6 +1297,64 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params, return changed; } +/* 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 snd_usb_audio *chip = subs->stream->chip; + struct snd_usb_endpoint *ep; + struct audioformat *fp; + int err; + + subs->fixed_hw = 0; + list_for_each_entry(fp, &subs->fmt_list, list) { + ep = snd_usb_get_endpoint(chip, fp->endpoint, fp->iface, + fp->altsetting); + 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, fp->sync_iface, + fp->sync_altsetting); + if (ep && ep->cur_rate) + goto found; + } + return 0; + + found: + if (!find_format(&subs->fmt_list, ep->cur_format, ep->cur_rate, + ep->cur_channels, NULL)) { + usb_audio_dbg(chip, "EP 0x%x being used, but not applicable\n", + ep->ep_num); + return 0; + } + + usb_audio_dbg(chip, "EP 0x%x being used, using fixed params:\n", + ep->ep_num); + usb_audio_dbg(chip, "rate=%d, format=%s, channels=%d, period_size=%d, periods=%d\n", + ep->cur_rate, snd_pcm_format_name(ep->cur_format), + ep->cur_channels, ep->cur_period_frames, + ep->cur_buffer_periods); + + runtime->hw.formats = pcm_format_to_bits(ep->cur_format); + runtime->hw.rate_min = runtime->hw.rate_max = ep->cur_rate; + runtime->hw.channels_min = runtime->hw.channels_max = + ep->cur_channels; + runtime->hw.rates = SNDRV_PCM_RATE_KNOT; + runtime->hw.periods_min = runtime->hw.periods_max = + ep->cur_buffer_periods; + subs->fixed_hw = 1; + + 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. @@ -1295,11 +1362,20 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params, static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) { + struct snd_usb_audio *chip = subs->stream->chip; 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; @@ -1350,6 +1426,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, -- cgit v1.2.3 From 7726dce14c5e7eaa78567be8c1e417acd6c83415 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:17 +0100 Subject: ALSA: usb-audio: Simplify hw_params rules Several hw_params functions narrows the interval via min/max rule in the very similar way, so factor out those into a helper function and use commonly. No functional changes, just minor code refactoring. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-12-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 110 ++++++++++++++++++++------------------------------------ 1 file changed, 38 insertions(+), 72 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 6d1f5277cd90..ecc6bf9b42f0 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1121,6 +1121,36 @@ static int hw_check_valid_format(struct snd_usb_substream *subs, return 1; } +static int apply_hw_params_minmax(struct snd_interval *it, unsigned int rmin, + unsigned int rmax) +{ + int changed; + + if (rmin > rmax) { + hwc_debug(" --> get empty\n"); + it->empty = 1; + return -EINVAL; + } + + 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; +} + static int hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -1128,7 +1158,6 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, struct audioformat *fp; struct snd_interval *it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); unsigned int rmin, rmax, r; - int changed; int i; hwc_debug("hw_rule_rate: (%d,%d)\n", it->min, it->max); @@ -1151,29 +1180,7 @@ static int hw_rule_rate(struct snd_pcm_hw_params *params, } } - if (rmin > rmax) { - hwc_debug(" --> get empty\n"); - it->empty = 1; - return -EINVAL; - } - - 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); } @@ -1184,48 +1191,18 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params, 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; - } + rmin = min(rmin, fp->channels); + rmax = max(rmax, fp->channels); } - if (!changed) { - hwc_debug(" --> get empty\n"); - it->empty = 1; - return -EINVAL; - } - - 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, @@ -1267,7 +1244,6 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params, 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); @@ -1283,18 +1259,8 @@ 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); } /* apply PCM hw constraints from the concurrent sync EP */ -- cgit v1.2.3 From 7ec827b9465c427511f05bab02dddbcfdd4ecc53 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:18 +0100 Subject: ALSA: usb-audio: Drop debug.h The file debug.h contains a simple macro for debug prints, and it's used only in two places, the format parser and the hw_params rules. The former actually should print a more informative message instead, so the only users are the hw_parmas rules. This patch moves the contents of debug.h into the hw_params rules local code and remove the unneeded includes. Also, the debug print in the format parser is replaced with the information print with more useful information, and the raw printk() call is replaced with pr_debug(). Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-13-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.c | 1 - sound/usb/debug.h | 16 ---------------- sound/usb/format.c | 5 +++-- sound/usb/pcm.c | 11 ++++++++++- 4 files changed, 13 insertions(+), 20 deletions(-) delete mode 100644 sound/usb/debug.h (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 4457214a3ae6..096dd8e3c64b 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" 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/format.c b/sound/usb/format.c index 3348032daa16..7641716f0c6c 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" @@ -227,7 +226,9 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof fp->nr_rates++; } 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; } } else { diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ecc6bf9b42f0..d83a6a6ac023 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -17,7 +17,6 @@ #include "usbaudio.h" #include "card.h" #include "quirks.h" -#include "debug.h" #include "endpoint.h" #include "helper.h" #include "pcm.h" @@ -1061,6 +1060,16 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) 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 | -- cgit v1.2.3 From 5fd255f4fe9707b5274e60d94d4aa64536f67ec3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:19 +0100 Subject: ALSA: usb-audio: Avoid doubly initialization for implicit fb The implicit feedback mode initializes both the main data stream and the sync data stream. When a sync stream was already opened, this would result in the doubly initialization and might screw up things. Add the check of already opened sync streams and skip the unnecessary initialization. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-14-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index d83a6a6ac023..8ae7d2fdba0d 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -601,8 +601,9 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, subs->data_endpoint->sync_master = subs->sync_endpoint; - if (subs->data_endpoint->iface != subs->sync_endpoint->iface || - subs->data_endpoint->altsetting != subs->sync_endpoint->altsetting) { + if (!subs->sync_endpoint->use_count && + (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); @@ -625,6 +626,7 @@ 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 *iface; + struct snd_usb_endpoint *ep; int err; iface = usb_ifnum_to_if(dev, fmt->iface); @@ -637,6 +639,14 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (fmt == subs->cur_audiofmt && !subs->need_setup_fmt) return 0; + /* shared EP with implicit fb */ + if (fmt->implicit_fb && !subs->need_setup_fmt) { + ep = snd_usb_get_endpoint(subs->stream->chip, fmt->endpoint, + fmt->iface, fmt->altsetting); + if (ep && ep->use_count > 0) + goto add_data_ep; + } + /* close the old interface */ if (subs->interface >= 0 && (subs->interface != fmt->iface || subs->need_setup_fmt)) { if (!subs->stream->chip->keep_iface) { @@ -673,6 +683,9 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) snd_usb_set_interface_quirk(dev); } + subs->need_setup_ep = true; + + add_data_ep: subs->interface = fmt->iface; subs->altset_idx = fmt->altset_idx; subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, @@ -686,9 +699,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) if (err < 0) return err; - err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt); - if (err < 0) - return err; + if (subs->need_setup_ep) { + err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt); + if (err < 0) + return err; + } subs->cur_audiofmt = fmt; @@ -940,10 +955,6 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto unlock; - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - subs->need_setup_ep = true; - unlock: snd_usb_unlock_shutdown(subs->stream->chip); if (ret < 0) -- cgit v1.2.3 From 54cb31901b831befb4f9347dd002dcc8ff2cc263 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:20 +0100 Subject: ALSA: usb-audio: Create endpoint objects at parsing phase Currently snd_usb_endpoint objects are created at first when the substream is opened and tries to assign the endpoints corresponding to the matching audioformat. But since basically the all endpoints have been already parsed and the information have been obtained, we may create the endpoint objects statically at the init phase. It's easier to manage for the implicit fb case, for example. This patch changes the endpoint object management and lets the parser to create the all endpoint objects. This change shouldn't bring any functional changes. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-15-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 87 ++++++++++++++++++++-------------------------------- sound/usb/endpoint.h | 10 +++--- sound/usb/pcm.c | 27 ++++++++-------- sound/usb/stream.c | 16 ++++++++++ 4 files changed, 67 insertions(+), 73 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 94490d706013..eb459db511f8 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -440,22 +440,19 @@ exit_clear: } /* - * Get the existing endpoint object corresponding EP, iface and alt numbers + * Get the existing endpoint object corresponding EP * Returns NULL if not present. - * Call inside chip->mutex locking for avoiding the race. */ struct snd_usb_endpoint * -snd_usb_get_endpoint(struct snd_usb_audio *chip, - int ep_num, int iface, int altsetting) +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 && - ep->iface == iface && - ep->altsetting == altsetting) + if (ep->ep_num == ep_num) return ep; } + return NULL; } @@ -466,14 +463,13 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, * 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(). @@ -481,74 +477,59 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, * 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; - - if (WARN_ON(!alts)) - return NULL; + bool is_playback; - mutex_lock(&chip->mutex); - - ep = snd_usb_get_endpoint(chip, ep_num, - alts->desc.bInterfaceNumber, - alts->desc.bAlternateSetting); - if (ep) { - usb_audio_dbg(ep->chip, "Re-using EP %x in iface %d,%d\n", - ep_num, ep->iface, ep->altsetting); - goto __exit_unlock; - } + ep = snd_usb_get_endpoint(chip, ep_num); + if (ep) + return 0; - usb_audio_dbg(chip, "Creating new %s %s endpoint #%x\n", - is_playback ? "playback" : "capture", + 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; + list_add_tail(&ep->list, &chip->ep_list); + return 0; +} + +/* Set up syncinterval and maxsyncsize for a sync EP */ +void snd_usb_endpoint_set_syncinterval(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep, + struct usb_host_interface *alts) +{ + struct usb_endpoint_descriptor *desc = get_endpoint(alts, 1); + + if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC) { + 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 (get_endpoint(alts, 1)->bInterval >= 1 && - get_endpoint(alts, 1)->bInterval <= 16) - ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1; + else if (desc->bInterval >= 1 && desc->bInterval <= 16) + ep->syncinterval = desc->bInterval - 1; else ep->syncinterval = 3; - ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize); + ep->syncmaxsize = le16_to_cpu(desc->wMaxPacketSize); } - - list_add_tail(&ep->list, &chip->ep_list); - - ep->is_implicit_feedback = 0; - -__exit_unlock: - mutex_unlock(&chip->mutex); - - return ep; } /* diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 61487095a766..76b6de7de991 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -6,12 +6,9 @@ #define SND_USB_ENDPOINT_TYPE_SYNC 1 struct snd_usb_endpoint *snd_usb_get_endpoint(struct snd_usb_audio *chip, - int ep_num, int iface, - int altsetting); + int ep_num); -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); int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, snd_pcm_format_t pcm_format, @@ -34,6 +31,9 @@ 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_endpoint_set_syncinterval(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep, + struct usb_host_interface *alts); void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, struct snd_usb_endpoint *sender, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 8ae7d2fdba0d..03b1a02bcff4 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -585,11 +585,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, if (!alts) return 0; - subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, - alts, ep, !subs->direction, - fmt->implicit_fb ? - SND_USB_ENDPOINT_TYPE_DATA : - SND_USB_ENDPOINT_TYPE_SYNC); + subs->sync_endpoint = snd_usb_get_endpoint(subs->stream->chip, ep); if (!subs->sync_endpoint) { if (is_playback && (fmt->ep_attr & USB_ENDPOINT_SYNCTYPE) == USB_ENDPOINT_SYNC_NONE) @@ -597,10 +593,14 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, return -EINVAL; } + subs->sync_endpoint->iface = fmt->sync_iface; + subs->sync_endpoint->altsetting = fmt->sync_altsetting; subs->sync_endpoint->is_implicit_feedback = fmt->implicit_fb; subs->data_endpoint->sync_master = subs->sync_endpoint; + snd_usb_endpoint_set_syncinterval(subs->stream->chip, subs->sync_endpoint, alts); + if (!subs->sync_endpoint->use_count && (subs->data_endpoint->iface != subs->sync_endpoint->iface || subs->data_endpoint->altsetting != subs->sync_endpoint->altsetting)) { @@ -641,8 +641,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* shared EP with implicit fb */ if (fmt->implicit_fb && !subs->need_setup_fmt) { - ep = snd_usb_get_endpoint(subs->stream->chip, fmt->endpoint, - fmt->iface, fmt->altsetting); + ep = snd_usb_get_endpoint(subs->stream->chip, fmt->endpoint); if (ep && ep->use_count > 0) goto add_data_ep; } @@ -688,12 +687,12 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) add_data_ep: 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); - + subs->data_endpoint = snd_usb_get_endpoint(subs->stream->chip, + fmt->endpoint); if (!subs->data_endpoint) return -EINVAL; + subs->data_endpoint->iface = fmt->iface; + subs->data_endpoint->altsetting = fmt->altsetting; err = set_sync_endpoint(subs, fmt); if (err < 0) @@ -1294,15 +1293,13 @@ static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime, subs->fixed_hw = 0; list_for_each_entry(fp, &subs->fmt_list, list) { - ep = snd_usb_get_endpoint(chip, fp->endpoint, fp->iface, - fp->altsetting); + 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, fp->sync_iface, - fp->sync_altsetting); + ep = snd_usb_get_endpoint(chip, fp->sync_ep); if (ep && ep->cur_rate) goto found; } diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 7087ee2c8174..816fd3e5aada 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1206,6 +1206,22 @@ 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, altno); snd_usb_init_pitch(chip, iface_no, alts, fp); -- cgit v1.2.3 From 982150560c7c1310d8826abf73857b76000e7db8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:21 +0100 Subject: ALSA: usb-audio: Drop keep_interface flag again This behavior turned out to be invalid from the USB spec POV and shouldn't be applied. As it's an optional flag that is set only via an card control element that must be hardly used, let's drop it again. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-16-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/mixer.c | 46 ---------------------------------------------- sound/usb/pcm.c | 17 +++++++---------- sound/usb/usbaudio.h | 3 --- 3 files changed, 7 insertions(+), 59 deletions(-) (limited to 'sound') 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/pcm.c b/sound/usb/pcm.c index 03b1a02bcff4..392aa1cba61c 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -648,14 +648,12 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* 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; - } + 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; @@ -1483,8 +1481,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 && + if (subs->interface >= 0 && !snd_usb_lock_shutdown(subs->stream->chip)) { usb_set_interface(subs->dev, subs->interface, 0); subs->interface = -1; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 0805b7f21272..aa017a93f7bd 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -53,9 +53,6 @@ struct snd_usb_audio { int setup; /* from the 'device_setup' 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; -- cgit v1.2.3 From e42a09bc520e94375501a8f8e769b867d8063d9d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:22 +0100 Subject: ALSA: usb-audio: Add snd_usb_get_host_interface() helper Add a helper function to retrieve the usb_host_interface object from the given interface and altsetting number pair, which is a commonly used procedure in the driver code. No functional changes, just minor code refactoring. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-17-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/helper.c | 10 ++++++++++ sound/usb/helper.h | 3 +++ sound/usb/pcm.c | 38 +++++++++++--------------------------- 3 files changed, 24 insertions(+), 27 deletions(-) (limited to 'sound') 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/pcm.c b/sound/usb/pcm.c index 392aa1cba61c..b0961ebd71f4 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -289,20 +289,16 @@ static int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream) } /* Check whether the given iface:altsetting points to an implicit fb source */ -static bool search_generic_implicit_fb(struct usb_device *dev, int ifnum, +static bool search_generic_implicit_fb(struct snd_usb_audio *chip, int ifnum, unsigned int altsetting, struct usb_host_interface **altsp, unsigned int *ep) { - struct usb_interface *iface; struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; struct usb_endpoint_descriptor *epd; - iface = usb_ifnum_to_if(dev, ifnum); - if (!iface) - return false; - alts = usb_altnum_to_altsetting(iface, altsetting); + alts = snd_usb_get_host_interface(chip, ifnum, altsetting); if (!alts) return false; altsd = get_iface_desc(alts); @@ -322,20 +318,16 @@ static bool search_generic_implicit_fb(struct usb_device *dev, int ifnum, } /* Like the function above, but specific to Roland with vendor class and hack */ -static bool search_roland_implicit_fb(struct usb_device *dev, int ifnum, +static bool search_roland_implicit_fb(struct snd_usb_audio *chip, int ifnum, unsigned int altsetting, struct usb_host_interface **altsp, unsigned int *ep) { - struct usb_interface *iface; struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; struct usb_endpoint_descriptor *epd; - iface = usb_ifnum_to_if(dev, ifnum); - if (!iface) - return false; - alts = usb_altnum_to_altsetting(iface, altsetting); + alts = snd_usb_get_host_interface(chip, ifnum, altsetting); if (!alts) return false; altsd = get_iface_desc(alts); @@ -359,11 +351,11 @@ static bool search_roland_implicit_fb(struct usb_device *dev, int ifnum, */ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, struct audioformat *fmt, - struct usb_interface *iface, struct usb_host_interface *alts) { struct usb_device *dev = chip->dev; struct usb_interface_descriptor *altsd = get_iface_desc(alts); + struct usb_interface *iface; unsigned int attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; unsigned int ep; unsigned int ifnum; @@ -431,7 +423,7 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, altsd->bInterfaceProtocol == UAC_VERSION_2 && altsd->bNumEndpoints == 1) { ifnum = altsd->bInterfaceNumber + 1; - if (search_generic_implicit_fb(dev, ifnum, + if (search_generic_implicit_fb(chip, ifnum, altsd->bAlternateSetting, &alts, &ep)) goto add_sync_ep; @@ -444,7 +436,7 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, altsd->bNumEndpoints == 1 && USB_ID_VENDOR(chip->usb_id) == 0x0582 /* Roland */) { ifnum = altsd->bInterfaceNumber + 1; - if (search_roland_implicit_fb(dev, ifnum, + if (search_roland_implicit_fb(chip, ifnum, altsd->bAlternateSetting, &alts, &ep)) goto add_sync_ep; @@ -477,24 +469,20 @@ int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, struct audioformat *fmt) { struct usb_device *dev = chip->dev; - struct usb_interface *iface; struct usb_host_interface *alts; struct usb_interface_descriptor *altsd; unsigned int ep, attr, sync_attr; bool is_playback; int err; - iface = usb_ifnum_to_if(dev, fmt->iface); - if (!iface) - return 0; - alts = usb_altnum_to_altsetting(iface, fmt->altsetting); + alts = snd_usb_get_host_interface(chip, fmt->iface, fmt->altsetting); if (!alts) return 0; altsd = get_iface_desc(alts); is_playback = !(get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); if (is_playback) { - err = audioformat_implicit_fb_quirk(chip, fmt, iface, alts); + err = audioformat_implicit_fb_quirk(chip, fmt, alts); if (err > 0) return 0; } @@ -564,7 +552,6 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, struct audioformat *fmt) { struct usb_device *dev = subs->dev; - struct usb_interface *iface; struct usb_host_interface *alts; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; unsigned int ep; @@ -577,11 +564,8 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, if (!ep) return 0; - iface = usb_ifnum_to_if(dev, fmt->sync_iface); - if (!iface) - return 0; - - alts = usb_altnum_to_altsetting(iface, fmt->altsetting); + alts = snd_usb_get_host_interface(subs->stream->chip, fmt->sync_iface, + fmt->altsetting); if (!alts) return 0; -- cgit v1.2.3 From c7f902015e1e86117e6cd3dde17d5964d88a9559 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:23 +0100 Subject: ALSA: usb-audio: Don't set altsetting before initializing sample rate Setting the active altsetting at changing sample rate seems unrecommended. The host should deselect the altsetting at first before that, then select it again. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-18-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/stream.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 816fd3e5aada..4501e042a944 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1223,9 +1223,10 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, } /* try to set the interface... */ - usb_set_interface(chip->dev, iface_no, altno); + usb_set_interface(chip->dev, iface_no, 0); snd_usb_init_pitch(chip, iface_no, alts, fp); snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max); + usb_set_interface(chip->dev, iface_no, altno); } return 0; } -- cgit v1.2.3 From d767aba2023c80ff0247b45526358d9a30af8293 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:24 +0100 Subject: ALSA: usb-audio: Pass snd_usb_audio object to quirk functions A preliminary patch for the later big change. Just a minor code refactoring. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-19-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/clock.c | 4 ++-- sound/usb/pcm.c | 17 +++++++++-------- sound/usb/quirks.c | 10 ++++------ sound/usb/quirks.h | 4 ++-- 4 files changed, 17 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/usb/clock.c b/sound/usb/clock.c index f174230d07d5..3298a654ce96 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -658,9 +658,9 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, * interface is active. */ if (rate != prev_rate) { usb_set_interface(dev, iface, 0); - snd_usb_set_interface_quirk(dev); + snd_usb_set_interface_quirk(chip); usb_set_interface(dev, iface, fmt->altsetting); - snd_usb_set_interface_quirk(dev); + snd_usb_set_interface_quirk(chip); } validation: diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index b0961ebd71f4..2518d4c82ad5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -553,6 +553,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, { struct usb_device *dev = subs->dev; struct usb_host_interface *alts; + struct snd_usb_audio *chip = subs->stream->chip; int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; unsigned int ep; int err; @@ -569,7 +570,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, if (!alts) return 0; - subs->sync_endpoint = snd_usb_get_endpoint(subs->stream->chip, ep); + subs->sync_endpoint = snd_usb_get_endpoint(chip, ep); if (!subs->sync_endpoint) { if (is_playback && (fmt->ep_attr & USB_ENDPOINT_SYNCTYPE) == USB_ENDPOINT_SYNC_NONE) @@ -596,7 +597,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, dev_dbg(&dev->dev, "setting usb interface %d:%d\n", subs->sync_endpoint->iface, subs->sync_endpoint->altsetting); - snd_usb_set_interface_quirk(dev); + snd_usb_set_interface_quirk(chip); } return 0; @@ -608,6 +609,7 @@ static int set_sync_endpoint(struct snd_usb_substream *subs, static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) { struct usb_device *dev = subs->dev; + struct snd_usb_audio *chip = subs->stream->chip; struct usb_host_interface *alts; struct usb_interface *iface; struct snd_usb_endpoint *ep; @@ -625,7 +627,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* shared EP with implicit fb */ if (fmt->implicit_fb && !subs->need_setup_fmt) { - ep = snd_usb_get_endpoint(subs->stream->chip, fmt->endpoint); + ep = snd_usb_get_endpoint(chip, fmt->endpoint); if (ep && ep->use_count > 0) goto add_data_ep; } @@ -648,7 +650,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* set interface */ if (iface->cur_altsetting != alts) { - err = snd_usb_select_mode_quirk(subs, fmt); + err = snd_usb_select_mode_quirk(chip, fmt); if (err < 0) return -EIO; @@ -661,7 +663,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } dev_dbg(&dev->dev, "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); - snd_usb_set_interface_quirk(dev); + snd_usb_set_interface_quirk(chip); } subs->need_setup_ep = true; @@ -669,8 +671,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) add_data_ep: subs->interface = fmt->iface; subs->altset_idx = fmt->altset_idx; - subs->data_endpoint = snd_usb_get_endpoint(subs->stream->chip, - fmt->endpoint); + subs->data_endpoint = snd_usb_get_endpoint(chip, fmt->endpoint); if (!subs->data_endpoint) return -EINVAL; subs->data_endpoint->iface = fmt->iface; @@ -681,7 +682,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) return err; if (subs->need_setup_ep) { - err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt); + err = snd_usb_init_pitch(chip, fmt->iface, alts, fmt); if (err < 0) return err; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index c50be2f75f70..bb4c1ae0a4a7 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1553,13 +1553,13 @@ static bool is_itf_usb_dsd_dac(unsigned int id) return false; } -int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, +int snd_usb_select_mode_quirk(struct snd_usb_audio *chip, 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 +1622,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; /* diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index c76cf24a640a..011f22cf2bf6 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -35,12 +35,12 @@ int snd_usb_is_big_endian_format(struct snd_usb_audio *chip, 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, +int snd_usb_select_mode_quirk(struct snd_usb_audio *chip, struct audioformat *fmt); u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, -- cgit v1.2.3 From 953a446b50fd6c68f0a40f0cd79a2a903faf3243 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:25 +0100 Subject: ALSA: usb-audio: Simplify snd_usb_init_sample_rate() arguments A preliminary change for the later big changes. This is a minor code refactoring to drop the unnecessary arguments that can be retrieved in a different way. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-20-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/clock.c | 29 +++++++++++++++-------------- sound/usb/clock.h | 3 +-- sound/usb/pcm.c | 2 -- sound/usb/quirks.c | 2 +- sound/usb/stream.c | 2 +- 5 files changed, 18 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 3298a654ce96..f25da11fce3a 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -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, +static int set_sample_rate_v1(struct snd_usb_audio *chip, 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 */ } @@ -607,8 +610,7 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, return get_sample_rate_v2v3(chip, fmt->iface, fmt->altsetting, clock); } -static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, +static int set_sample_rate_v2v3(struct snd_usb_audio *chip, struct audioformat *fmt, int rate) { struct usb_device *dev = chip->dev; @@ -632,7 +634,7 @@ 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; @@ -640,7 +642,7 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, if (cur_rate < 0) { usb_audio_err(chip, "%d:%d: cannot set freq %d (v2/v3): err %d\n", - iface, fmt->altsetting, rate, cur_rate); + fmt->iface, fmt->altsetting, rate, cur_rate); return cur_rate; } @@ -657,9 +659,9 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, int iface, /* Some devices doesn't respond to sample rate changes while the * interface is active. */ if (rate != prev_rate) { - usb_set_interface(dev, iface, 0); + usb_set_interface(dev, fmt->iface, 0); snd_usb_set_interface_quirk(chip); - usb_set_interface(dev, iface, fmt->altsetting); + usb_set_interface(dev, fmt->iface, fmt->altsetting); snd_usb_set_interface_quirk(chip); } @@ -670,14 +672,13 @@ validation: return 0; } -int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, +int snd_usb_init_sample_rate(struct snd_usb_audio *chip, struct audioformat *fmt, int rate) { 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) { @@ -688,7 +689,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 97597f5a3c18..8d406ed294d6 100644 --- a/sound/usb/clock.h +++ b/sound/usb/clock.h @@ -2,8 +2,7 @@ #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, +int snd_usb_init_sample_rate(struct snd_usb_audio *chip, struct audioformat *fmt, int rate); int snd_usb_clock_find_source(struct snd_usb_audio *chip, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 2518d4c82ad5..38b461bdca86 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1017,8 +1017,6 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) 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) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index bb4c1ae0a4a7..018c6f241ffb 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -178,7 +178,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip, 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_sample_rate(chip, fp, fp->rate_max); return 0; error: diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 4501e042a944..23e881985123 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1225,7 +1225,7 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, /* try to set the interface... */ usb_set_interface(chip->dev, iface_no, 0); snd_usb_init_pitch(chip, iface_no, alts, fp); - snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max); + snd_usb_init_sample_rate(chip, fp, fp->rate_max); usb_set_interface(chip->dev, iface_no, altno); } return 0; -- cgit v1.2.3 From 73037c8dc1c8cf994a38fedba4a5af7e6da5e4f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:26 +0100 Subject: ALSA: usb-audio: Simplify snd_usb_init_pitch() arguments A preliminary change for the later big changes. This is a minor code refactoring to drop the unnecessary arguments that can be retrieved in a different way. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-21-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 23 +++++++++-------------- sound/usb/pcm.h | 3 +-- sound/usb/quirks.c | 2 +- sound/usb/stream.c | 2 +- 4 files changed, 12 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 38b461bdca86..0998be109af3 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -146,8 +146,7 @@ static struct audioformat *find_substream_format(struct snd_usb_substream *subs) subs->channels, subs); } -static int init_pitch_v1(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, +static int init_pitch_v1(struct snd_usb_audio *chip, struct audioformat *fmt) { struct usb_device *dev = chip->dev; @@ -155,9 +154,7 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface, unsigned char data[1]; int err; - if (get_iface_desc(alts)->bNumEndpoints < 1) - return -EINVAL; - ep = get_endpoint(alts, 0)->bEndpointAddress; + ep = fmt->endpoint; data[0] = 1; err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, @@ -166,15 +163,14 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface, data, sizeof(data)); if (err < 0) { usb_audio_err(chip, "%d:%d: cannot set enable PITCH\n", - iface, ep); + fmt->iface, ep); return err; } return 0; } -static int init_pitch_v2(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, +static int init_pitch_v2(struct snd_usb_audio *chip, struct audioformat *fmt) { struct usb_device *dev = chip->dev; @@ -188,7 +184,7 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface, data, sizeof(data)); if (err < 0) { usb_audio_err(chip, "%d:%d: cannot set enable PITCH (v2)\n", - iface, fmt->altsetting); + fmt->iface, fmt->altsetting); return err; } @@ -198,8 +194,7 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface, /* * initialize the pitch control and sample rate */ -int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, - struct usb_host_interface *alts, +int snd_usb_init_pitch(struct snd_usb_audio *chip, struct audioformat *fmt) { /* if endpoint doesn't have pitch control, bail out */ @@ -209,10 +204,10 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, switch (fmt->protocol) { case UAC_VERSION_1: default: - return init_pitch_v1(chip, iface, alts, fmt); + return init_pitch_v1(chip, fmt); case UAC_VERSION_2: - return init_pitch_v2(chip, iface, alts, fmt); + return init_pitch_v2(chip, fmt); } } @@ -682,7 +677,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) return err; if (subs->need_setup_ep) { - err = snd_usb_init_pitch(chip, fmt->iface, alts, fmt); + err = snd_usb_init_pitch(chip, fmt); if (err < 0) return err; } diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index 362782c2df5c..a4f784225abc 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -9,8 +9,7 @@ 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, +int snd_usb_init_pitch(struct snd_usb_audio *chip, struct audioformat *fmt); void snd_usb_preallocate_buffer(struct snd_usb_substream *subs); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 018c6f241ffb..013ab93fb640 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -177,7 +177,7 @@ 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_pitch(chip, fp); snd_usb_init_sample_rate(chip, fp, fp->rate_max); return 0; diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 23e881985123..7f58c7625cd4 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -1224,7 +1224,7 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip, /* try to set the interface... */ usb_set_interface(chip->dev, iface_no, 0); - snd_usb_init_pitch(chip, iface_no, alts, fp); + snd_usb_init_pitch(chip, fp); snd_usb_init_sample_rate(chip, fp, fp->rate_max); usb_set_interface(chip->dev, iface_no, altno); } -- cgit v1.2.3 From 57234bc1038517437d5c589595caf77b2118529e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:27 +0100 Subject: ALSA: usb-audio: Stop both endpoints properly at error start_endpoints() may leave the data endpoint running if an error happens at starting the sync endpoint. We should stop both streams properly, instead. While we're at it, move the debug prints into the endpoint.c that is a more suitable place. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-22-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 6 ++++++ sound/usb/pcm.c | 39 +++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index eb459db511f8..0cc7e9c01263 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1172,6 +1172,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (atomic_read(&ep->chip->shutdown)) return -EBADFD; + usb_audio_dbg(ep->chip, "Starting %s EP 0x%x (count %d)\n", + ep_type_name(ep->type), ep->ep_num, ep->use_count); + /* already running? */ if (++ep->use_count != 1) return 0; @@ -1254,6 +1257,9 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (!ep) return; + usb_audio_dbg(ep->chip, "Stopping %s EP 0x%x (count %d)\n", + ep_type_name(ep->type), ep->ep_num, ep->use_count); + if (snd_BUG_ON(ep->use_count == 0)) return; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 0998be109af3..c4e39aa92a84 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -211,6 +211,17 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, } } +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); + subs->sync_endpoint->sync_slave = NULL; + } + + if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) + snd_usb_endpoint_stop(subs->data_endpoint); +} + static int start_endpoints(struct snd_usb_substream *subs) { int err; @@ -221,13 +232,11 @@ static int start_endpoints(struct snd_usb_substream *subs) 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 0x%x\n", ep->ep_num); - ep->data_subs = subs; err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); - return err; + goto error; } } @@ -235,18 +244,20 @@ static int start_endpoints(struct snd_usb_substream *subs) !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { struct snd_usb_endpoint *ep = subs->sync_endpoint; - dev_dbg(&subs->dev->dev, "Starting sync EP 0x%x\n", ep->ep_num); - ep->sync_slave = subs->data_endpoint; err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags); ep->sync_slave = NULL; - return err; + goto error; } } return 0; + + error: + stop_endpoints(subs); + return err; } static void sync_pending_stops(struct snd_usb_substream *subs) @@ -255,22 +266,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)) { - dev_dbg(&subs->dev->dev, "Stopping sync EP 0x%x\n", - subs->sync_endpoint->ep_num); - snd_usb_endpoint_stop(subs->sync_endpoint); - subs->sync_endpoint->sync_slave = NULL; - } - - if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { - dev_dbg(&subs->dev->dev, "Stopping data EP 0x%x\n", - subs->data_endpoint->ep_num); - snd_usb_endpoint_stop(subs->data_endpoint); - } -} - /* PCM sync_stop callback */ static int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream) { -- cgit v1.2.3 From 96e221f379e887f58d29969f10ed330ae1be4d80 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:28 +0100 Subject: ALSA: usb-audio: Set callbacks via snd_usb_endpoint_set_callback() The prepare_data_urb and retire_data_urb fields of the endpoint object are set dynamically at PCM trigger start/stop. Those are evaluated in the endpoint handler, but there can be a race, especially if two different PCM substreams are handling the same endpoint for the implicit feedback case. Also, the data_subs field of the endpoint is set and accessed dynamically, too, which has the same risk. As a slight improvement for the concurrency, this patch introduces the function to set the callbacks and the data in a shot with the memory barrier. In the reader side, it's also fetched with the memory barrier. There is still a room of race if prepare and retire callbacks are set during executing the URB completion. But such an inconsistency may happen only for the implicit fb source, i.e. it's only about the capture stream. And luckily, the capture stream never sets the prepare callback, hence the problem doesn't happen practically. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-23-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 60 +++++++++++++++++++++++++++++++++++----------------- sound/usb/endpoint.h | 7 ++++++ sound/usb/pcm.c | 33 ++++++++++++++++------------- 3 files changed, 66 insertions(+), 34 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 0cc7e9c01263..7012fdafc3d8 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -169,11 +169,20 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep) return ret; } +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 retire_inbound_urb(struct snd_usb_endpoint *ep, @@ -189,8 +198,7 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep, if (ep->sync_slave) snd_usb_handle_sync_urb(ep->sync_slave, 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, @@ -244,17 +252,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: @@ -381,7 +389,7 @@ 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; + struct snd_usb_substream *data_subs; unsigned long flags; int err; @@ -430,10 +438,9 @@ 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); - } + data_subs = READ_ONCE(ep->data_subs); + if (data_subs && data_subs->pcm_substream) + snd_pcm_stop_xrun(data_subs->pcm_substream); exit_clear: clear_bit(ctx->index, &ep->active_mask); @@ -532,6 +539,24 @@ void snd_usb_endpoint_set_syncinterval(struct snd_usb_audio *chip, } } +/* + * 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); +} + /* * wait until all urbs are processed. */ @@ -554,10 +579,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; + snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); return 0; } @@ -607,8 +630,7 @@ 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); diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 76b6de7de991..e2fddb3dcf7a 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -20,6 +20,13 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct audioformat *fmt, 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); diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c4e39aa92a84..32237623de96 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -232,7 +232,6 @@ static int start_endpoints(struct snd_usb_substream *subs) if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) { struct snd_usb_endpoint *ep = subs->data_endpoint; - ep->data_subs = subs; err = snd_usb_endpoint_start(ep); if (err < 0) { clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags); @@ -1830,18 +1829,24 @@ 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; return 0; case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs); + snd_usb_endpoint_set_callback(subs->data_endpoint, + NULL, NULL, NULL); subs->running = 0; 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; return 0; case SNDRV_PCM_TRIGGER_SUSPEND: @@ -1867,23 +1872,21 @@ 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; return 0; 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; 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); -- cgit v1.2.3 From 75c16b5147ee42270b18b5f32bc3f17f8b74b5eb Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:29 +0100 Subject: ALSA: usb-audio: Always set up the parameters after resume The commit 92adc96f8eec ("ALSA: usb-audio: set the interface format after resume on Dell WD19") introduced the workaround for the broken setup after the resume specifically on a Dell dock model. However, the full setup should have been performed after the resume on all devices, as we can't guarantee the same state. So this patch removes the conditional check and applies the workaround always. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-24-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 20 ++++++-------------- sound/usb/quirks-table.h | 8 -------- sound/usb/quirks.c | 11 ----------- sound/usb/usbaudio.h | 1 - 4 files changed, 6 insertions(+), 34 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 32237623de96..ac6385a4eb70 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1835,6 +1835,9 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea subs); subs->running = 1; return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: + subs->need_setup_fmt = true; + fallthrough; case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs); snd_usb_endpoint_set_callback(subs->data_endpoint, @@ -1849,13 +1852,6 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea subs); subs->running = 0; 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; @@ -1879,6 +1875,9 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream subs); subs->running = 1; return 0; + case SNDRV_PCM_TRIGGER_SUSPEND: + subs->need_setup_fmt = true; + fallthrough; case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs); fallthrough; @@ -1887,13 +1886,6 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream NULL, NULL, NULL); subs->running = 0; 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/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 013ab93fb640..7e7f258011ff 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -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, }; diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index aa017a93f7bd..cc338e8e2597 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; -- cgit v1.2.3 From 61cc2d775e0941ca61b9666760a656919d80077a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:30 +0100 Subject: ALSA: usb-audio: Fix EP matching for continuous rates The function to evaluate the match of the parameters with an EP assumes only the discrete rate tables and doesn't handle the continuous rates properly. This patch fixes match_endpoint_audioformats() to handle the continuous rates. Also the almost useless debug prints there are dropped. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-25-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ac6385a4eb70..45a692512d27 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -695,41 +695,30 @@ static int match_endpoint_audioformats(struct snd_usb_substream *subs, struct audioformat *match, int rate, snd_pcm_format_t pcm_format) { - int i; - int score = 0; + int i, score; - if (fp->channels < 1) { - dev_dbg(&subs->dev->dev, - "%s: (fmt @%p) no channels\n", __func__, fp); + if (fp->channels < 1) 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); + if (!(fp->formats & pcm_format_to_bits(pcm_format))) return 0; - } - for (i = 0; i < fp->nr_rates; i++) { - if (fp->rate_table[i] == rate) { - score++; - break; + 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 (!score) { - dev_dbg(&subs->dev->dev, - "%s: (fmt @%p) no match for rate %d\n", __func__, - fp, rate); - return 0; + if (i >= fp->nr_rates) + return 0; } + score = 1; if (fp->channels == match->channels) score++; - dev_dbg(&subs->dev->dev, - "%s: (fmt @%p) score %d\n", __func__, fp, score); - return score; } -- cgit v1.2.3 From bf6313a0ff766925462e97b4e733d5952de02367 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:31 +0100 Subject: ALSA: usb-audio: Refactor endpoint management This is an intensive surgery for the endpoint and stream management for achieving more robust and clean code. The goals of this patch are: - More clear endpoint resource changes - The interface altsetting control in a single place Below are brief description of the whole changes. First off, most of the endpoint operations are moved into endpoint.c, so that the snd_usb_endpoint object is only referred in other places. The endpoint object is acquired and released via the new functions snd_usb_endpoint_open() and snd_usb_endpoint_close() that are called at PCM hw_params and hw_free callbacks, respectively. Those are ref-counted and EPs can manage the multiple opens. The open callback receives the audioformat and hw_params arguments, and those are used for initializing the EP parameters; especially the endpoint, interface and altset numbers are read from there, as well as the PCM parameters like the format, rate and channels. Those are stored in snd_usb_endpoint object. If it's the secondary open, the function checks whether the given parameters are compatible with the already opened EP setup, too. The coupling with a sync EP (including an implicit feedback sync) is done by the sole snd_usb_endpoint_set_sync() call. The configuration of each endpoint is done in a single shot via snd_usb_endpoint_configure() call. This is the place where most of PCM configurations are done. A few flags and special handling in the snd_usb_substream are dropped along with this change. A significant difference wrt the configuration from the previous code is the order of USB host interface setups. Now the interface is always disabled at beginning and (re-)enabled at the last step of snd_usb_endpoint_configure(), in order to be compliant with the standard UAC2/3. For UAC1, the interface is set before the parameter setups since there seem devices that require it (e.g. Yamaha THR10), just like how it was done in the previous driver code. The start/stop are almost same as before, also single-shots. The URB callbacks need to be set via snd_usb_endpoint_set_callback() like the previous code at the trigger phase, too. Finally, the flag for the re-setup is set at the device suspend through the full EP list, instead of PCM trigger. This catches the overlooked cases where the PCM hasn't been running yet but the device needs the full setup after resume. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-26-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.c | 8 +- sound/usb/card.h | 11 +- sound/usb/clock.c | 13 +- sound/usb/endpoint.c | 662 ++++++++++++++++++++++++++------------------------- sound/usb/endpoint.h | 40 ++-- sound/usb/pcm.c | 616 +++++++++++++++++++---------------------------- 6 files changed, 616 insertions(+), 734 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 096dd8e3c64b..58958afcec93 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -980,6 +980,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; @@ -987,11 +988,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 1f61be98a31d..66a249ae138f 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -26,6 +26,7 @@ struct audioformat { 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 */ @@ -58,6 +59,7 @@ struct snd_urb_ctx { struct snd_usb_endpoint { struct snd_usb_audio *chip; + int opened; /* open refcount; protect with chip->mutex */ int use_count; int ep_num; /* the referenced endpoint number */ int type; /* SND_USB_ENDPOINT_TYPE_* */ @@ -110,14 +112,18 @@ struct snd_usb_endpoint { unsigned char silence_value; unsigned int stride; int iface, altsetting; + unsigned char ep_idx; /* endpoint array index */ 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 */ + 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; @@ -152,7 +158,6 @@ struct snd_usb_substream { unsigned int stream_offset_adj; /* Bytes to drop from beginning of stream (for non-compliant devices) */ unsigned int running: 1; /* running status */ - unsigned int fixed_hw:1; /* fixed hw constraints due to sync EP */ unsigned int hwptr_done; /* processed byte position in the buffer */ unsigned int transfer_done; /* processed frames since last period update */ @@ -163,8 +168,6 @@ 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) */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index f25da11fce3a..b869a711afbf 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -613,7 +613,6 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, static int set_sample_rate_v2v3(struct snd_usb_audio *chip, struct audioformat *fmt, int rate) { - struct usb_device *dev = chip->dev; int cur_rate, prev_rate; int clock; @@ -656,15 +655,6 @@ static int set_sample_rate_v2v3(struct snd_usb_audio *chip, return -ENXIO; } - /* Some devices doesn't respond to sample rate changes while the - * interface is active. */ - if (rate != prev_rate) { - usb_set_interface(dev, fmt->iface, 0); - snd_usb_set_interface_quirk(chip); - usb_set_interface(dev, fmt->iface, fmt->altsetting); - snd_usb_set_interface_quirk(chip); - } - validation: /* validate clock after rate change */ if (!uac_clock_source_is_valid(chip, fmt, clock)) @@ -675,6 +665,9 @@ validation: int snd_usb_init_sample_rate(struct snd_usb_audio *chip, 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: diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 7012fdafc3d8..eee74313603e 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,10 +117,7 @@ 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); } /* @@ -185,18 +183,24 @@ static void retire_outbound_urb(struct snd_usb_endpoint *ep, 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_slave; 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_slave = READ_ONCE(ep->sync_slave); + if (sync_slave) + snd_usb_handle_sync_urb(sync_slave, ep, urb); call_retire_callback(ep, urb); } @@ -518,25 +522,155 @@ int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, int type) } /* Set up syncinterval and maxsyncsize for a sync EP */ -void snd_usb_endpoint_set_syncinterval(struct snd_usb_audio *chip, - struct snd_usb_endpoint *ep, - struct usb_host_interface *alts) +static void endpoint_set_syncinterval(struct snd_usb_audio *chip, + struct snd_usb_endpoint *ep) { - struct usb_endpoint_descriptor *desc = get_endpoint(alts, 1); - - if (ep->type == SND_USB_ENDPOINT_TYPE_SYNC) { - 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; + 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); +} + +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; + + 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, + 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; - ep->syncmaxsize = le16_to_cpu(desc->wMaxPacketSize); + 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_master = sync_ep; } /* @@ -557,6 +691,54 @@ void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, 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 = -1; + 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. */ @@ -646,219 +828,36 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) ep->nurbs = 0; } -/* - * 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, - unsigned int rate, - 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; - - /* matching with the saved parameters? */ - if (ep->cur_rate == rate && - ep->cur_format == pcm_format && - ep->cur_channels == channels && - ep->cur_period_frames == frames_per_period && - ep->cur_buffer_periods == periods_per_buffer) - return true; - - 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; @@ -911,16 +910,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_master && !ep->implicit_fb_sync) max_packs_per_urb = min(max_packs_per_urb, - 1U << sync_ep->syncinterval); + 1U << ep->sync_master->syncinterval); max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); /* @@ -931,9 +930,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) || - ep->is_implicit_feedback || - snd_usb_endpoint_implicit_feedback_sink(ep)) { + if (usb_pipein(ep->pipe) || ep->implicit_fb_sync) { urb_packs = packs_per_ms; /* @@ -942,7 +939,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; @@ -951,7 +948,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; @@ -966,12 +963,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_master) 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, @@ -980,13 +977,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 */ @@ -1004,7 +1001,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; @@ -1028,9 +1025,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; @@ -1063,60 +1064,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; - usb_audio_dbg(ep->chip, - "Setting params for ep %x (type %s, count %d), rate=%d, format=%s, channels=%d, period_bytes=%d, periods=%d\n", - ep->ep_num, ep_type_name(ep->type), ep->use_count, - rate, snd_pcm_format_name(pcm_format), channels, - period_bytes, buffer_periods); - - if (ep->use_count != 0) { - bool check = ep->is_implicit_feedback && - check_ep_params(ep, pcm_format, channels, period_bytes, - period_frames, buffer_periods, rate, - 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); @@ -1124,17 +1084,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; @@ -1144,9 +1104,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); @@ -1155,24 +1113,92 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, err = -EINVAL; } - usb_audio_dbg(ep->chip, "Set up %d URBS, ret=%d\n", ep->nurbs, err); + usb_audio_dbg(chip, "Set up %d URBS, ret=%d\n", ep->nurbs, err); if (err < 0) return err; - /* record the current set up in the endpoint (for implicit fb) */ - spin_lock_irq(&ep->lock); - ep->cur_rate = rate; - ep->cur_channels = channels; - ep->cur_format = pcm_format; - ep->cur_period_frames = period_frames; - ep->cur_period_bytes = period_bytes; - ep->cur_buffer_periods = buffer_periods; - spin_unlock_irq(&ep->lock); + /* 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; +} + /** * snd_usb_endpoint_start: start an snd_usb_endpoint * @@ -1194,6 +1220,9 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (atomic_read(&ep->chip->shutdown)) return -EBADFD; + if (ep->sync_master) + WRITE_ONCE(ep->sync_master->sync_slave, ep); + usb_audio_dbg(ep->chip, "Starting %s EP 0x%x (count %d)\n", ep_type_name(ep->type), ep->ep_num, ep->use_count); @@ -1226,6 +1255,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; } @@ -1251,9 +1281,13 @@ 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: + if (ep->sync_master) + WRITE_ONCE(ep->sync_master->sync_slave, NULL); clear_bit(EP_FLAG_RUNNING, &ep->flags); ep->use_count--; deactivate_urbs(ep, false); @@ -1285,39 +1319,15 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (snd_BUG_ON(ep->use_count == 0)) return; + if (ep->sync_master) + WRITE_ONCE(ep->sync_master->sync_slave, NULL); + if (--ep->use_count == 0) { deactivate_urbs(ep, false); set_bit(EP_FLAG_STOPPING, &ep->flags); } } -/** - * 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) - return; - - if (ep->use_count != 0) - return; - - deactivate_urbs(ep, true); - wait_clear_urbs(ep); - - /* clear the saved hw params */ - spin_lock_irq(&ep->lock); - ep->cur_rate = 0; - spin_unlock_irq(&ep->lock); -} - /** * snd_usb_endpoint_release: Tear down an snd_usb_endpoint * @@ -1343,7 +1353,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 @@ -1353,9 +1363,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; diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index e2fddb3dcf7a..2bfa6d3e029c 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -10,16 +10,25 @@ struct snd_usb_endpoint *snd_usb_get_endpoint(struct snd_usb_audio *chip, int snd_usb_add_endpoint(struct snd_usb_audio *chip, int ep_num, 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); - +struct snd_usb_endpoint * +snd_usb_endpoint_open(struct snd_usb_audio *chip, + 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), @@ -27,23 +36,16 @@ void snd_usb_endpoint_set_callback(struct snd_usb_endpoint *ep, struct urb *urb), struct snd_usb_substream *data_subs); -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep); +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_endpoint_set_syncinterval(struct snd_usb_audio *chip, - struct snd_usb_endpoint *ep, - struct usb_host_interface *alts); - -void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, - struct snd_usb_endpoint *sender, - const struct urb *urb); #endif /* __USBAUDIO_ENDPOINT_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 45a692512d27..e80e8cf1e863 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -80,21 +80,22 @@ 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 list_head *fmt_list_head, - snd_pcm_format_t format, - unsigned int rate, - unsigned int channels, - struct snd_usb_substream *subs) +static 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; int cur_attr = 0, attr; list_for_each_entry(fp, fmt_list_head, list) { - if (!(fp->formats & pcm_format_to_bits(format))) - continue; - if (fp->channels != channels) - continue; + 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)) { @@ -140,38 +141,30 @@ static struct audioformat *find_format(struct list_head *fmt_list_head, return found; } -static struct audioformat *find_substream_format(struct snd_usb_substream *subs) +static struct audioformat * +find_substream_format(struct snd_usb_substream *subs, + const struct snd_pcm_hw_params *params) { - return find_format(&subs->fmt_list, subs->pcm_format, subs->cur_rate, - subs->channels, subs); + 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, - struct audioformat *fmt) +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; - ep = fmt->endpoint; - 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", - fmt->iface, ep); - return err; - } - - return 0; + return err; } -static int init_pitch_v2(struct snd_usb_audio *chip, - 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]; @@ -182,13 +175,7 @@ static int init_pitch_v2(struct snd_usb_audio *chip, 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", - fmt->iface, fmt->altsetting); - return err; - } - - return 0; + return err; } /* @@ -197,29 +184,47 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int snd_usb_init_pitch(struct snd_usb_audio *chip, 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, fmt); + return 0; + } - case UAC_VERSION_2: - return init_pitch_v2(chip, fmt); + if (err < 0) { + usb_audio_err(chip, "failed to enable PITCH for EP 0x%x\n", + fmt->endpoint); + return err; } + + return 0; } -static void stop_endpoints(struct snd_usb_substream *subs) +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); - subs->sync_endpoint->sync_slave = NULL; + stopped = true; } - - if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) + 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) @@ -230,9 +235,7 @@ 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; - - 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); goto error; @@ -241,13 +244,9 @@ static int start_endpoints(struct snd_usb_substream *subs) if (subs->sync_endpoint && !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) { - struct snd_usb_endpoint *ep = subs->sync_endpoint; - - 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); - ep->sync_slave = NULL; goto error; } } @@ -446,6 +445,7 @@ add_sync_ep: fmt->sync_ep = ep; fmt->sync_iface = ifnum; fmt->sync_altsetting = alts->desc.bAlternateSetting; + fmt->sync_ep_idx = 0; fmt->implicit_fb = 1; dev_dbg(&dev->dev, "%d:%d: found implicit_fb sync_ep=%x, iface=%d, alt=%d\n", fmt->iface, fmt->altsetting, fmt->sync_ep, fmt->sync_iface, @@ -527,6 +527,7 @@ int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, 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; @@ -537,152 +538,6 @@ int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, return 0; } -static int set_sync_endpoint(struct snd_usb_substream *subs, - struct audioformat *fmt) -{ - struct usb_device *dev = subs->dev; - struct usb_host_interface *alts; - struct snd_usb_audio *chip = subs->stream->chip; - int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK; - unsigned int ep; - int err; - - subs->sync_endpoint = NULL; - subs->data_endpoint->sync_master = NULL; - - ep = fmt->sync_ep; - if (!ep) - return 0; - - alts = snd_usb_get_host_interface(subs->stream->chip, fmt->sync_iface, - fmt->altsetting); - if (!alts) - return 0; - - subs->sync_endpoint = snd_usb_get_endpoint(chip, ep); - if (!subs->sync_endpoint) { - if (is_playback && - (fmt->ep_attr & USB_ENDPOINT_SYNCTYPE) == USB_ENDPOINT_SYNC_NONE) - return 0; - return -EINVAL; - } - - subs->sync_endpoint->iface = fmt->sync_iface; - subs->sync_endpoint->altsetting = fmt->sync_altsetting; - subs->sync_endpoint->is_implicit_feedback = fmt->implicit_fb; - - subs->data_endpoint->sync_master = subs->sync_endpoint; - - snd_usb_endpoint_set_syncinterval(subs->stream->chip, subs->sync_endpoint, alts); - - if (!subs->sync_endpoint->use_count && - (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) - return err; - dev_dbg(&dev->dev, "setting usb interface %d:%d\n", - subs->sync_endpoint->iface, - subs->sync_endpoint->altsetting); - snd_usb_set_interface_quirk(chip); - } - - 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 snd_usb_audio *chip = subs->stream->chip; - struct usb_host_interface *alts; - struct usb_interface *iface; - struct snd_usb_endpoint *ep; - 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; - - if (fmt == subs->cur_audiofmt && !subs->need_setup_fmt) - return 0; - - /* shared EP with implicit fb */ - if (fmt->implicit_fb && !subs->need_setup_fmt) { - ep = snd_usb_get_endpoint(chip, fmt->endpoint); - if (ep && ep->use_count > 0) - goto add_data_ep; - } - - /* close the old interface */ - if (subs->interface >= 0 && (subs->interface != fmt->iface || subs->need_setup_fmt)) { - 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(chip, 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(chip); - } - - subs->need_setup_ep = true; - - add_data_ep: - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - subs->data_endpoint = snd_usb_get_endpoint(chip, fmt->endpoint); - if (!subs->data_endpoint) - return -EINVAL; - subs->data_endpoint->iface = fmt->iface; - subs->data_endpoint->altsetting = fmt->altsetting; - - err = set_sync_endpoint(subs, fmt); - if (err < 0) - return err; - - if (subs->need_setup_ep) { - err = snd_usb_init_pitch(chip, 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: @@ -691,8 +546,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) * - Requested sample rate is not supported. */ static int match_endpoint_audioformats(struct snd_usb_substream *subs, - struct audioformat *fp, - struct audioformat *match, int rate, + const struct audioformat *fp, + int rate, int channels, snd_pcm_format_t pcm_format) { int i, score; @@ -716,108 +571,12 @@ static int match_endpoint_audioformats(struct snd_usb_substream *subs, } score = 1; - if (fp->channels == match->channels) + if (fp->channels == channels) 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) -{ - 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->fixed_hw || - !subs->sync_endpoint->is_implicit_feedback) { - sync_fp = subs->cur_audiofmt; - goto configure; - } - - sync_fp = find_format(&sync_subs->fmt_list, subs->pcm_format, - subs->cur_rate, subs->channels, NULL); - if (sync_fp) - goto configure; - - /* 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); - } - - configure: - return snd_usb_endpoint_set_params(subs->sync_endpoint, - subs->pcm_format, - sync_fp->channels, - sync_period_bytes, - subs->period_frames, - subs->buffer_periods, - subs->cur_rate, - sync_fp, - NULL); -} - -/* - * 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; @@ -866,6 +625,92 @@ int snd_usb_pcm_resume(struct snd_usb_stream *as) return 0; } +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; +} + +static struct audioformat * +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; + struct audioformat *fp, *sync_fmt; + int score, high_score; + + 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; +} + +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 * @@ -880,30 +725,45 @@ 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 snd_usb_audio *chip = subs->stream->chip; struct audioformat *fmt; + 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_substream_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 && + (fmt->iface != fmt->sync_iface || + fmt->altsetting != fmt->sync_altsetting)) { + sync_fmt = 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; @@ -911,18 +771,56 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto unlock; - ret = set_format(subs, fmt); + 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; + } + + 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); + } + + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; + subs->cur_audiofmt = fmt; + + ret = configure_endpoints(chip, subs); if (ret < 0) goto unlock; + 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); + 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; } @@ -941,15 +839,9 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_rate = 0; subs->period_bytes = 0; if (!snd_usb_lock_shutdown(chip)) { - stop_endpoints(subs); - sync_pending_stops(subs); - snd_usb_endpoint_deactivate(subs->sync_endpoint); - snd_usb_endpoint_deactivate(subs->data_endpoint); - if (subs->data_endpoint) { - subs->data_endpoint->sync_master = NULL; - subs->data_endpoint = NULL; - } - subs->sync_endpoint = NULL; + if (stop_endpoints(subs)) + sync_pending_stops(subs); + close_endpoints(chip, subs); snd_usb_unlock_shutdown(chip); } @@ -965,16 +857,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)) { @@ -982,36 +868,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, - 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; @@ -1025,7 +885,7 @@ 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; } @@ -1047,6 +907,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, @@ -1250,7 +1112,6 @@ static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime, struct audioformat *fp; int err; - subs->fixed_hw = 0; list_for_each_entry(fp, &subs->fmt_list, list) { ep = snd_usb_get_endpoint(chip, fp->endpoint); if (ep && ep->cur_rate) @@ -1266,7 +1127,7 @@ static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime, found: if (!find_format(&subs->fmt_list, ep->cur_format, ep->cur_rate, - ep->cur_channels, NULL)) { + ep->cur_channels, false, NULL)) { usb_audio_dbg(chip, "EP 0x%x being used, but not applicable\n", ep->ep_num); return 0; @@ -1274,19 +1135,23 @@ static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime, usb_audio_dbg(chip, "EP 0x%x being used, using fixed params:\n", ep->ep_num); - usb_audio_dbg(chip, "rate=%d, format=%s, channels=%d, period_size=%d, periods=%d\n", - ep->cur_rate, snd_pcm_format_name(ep->cur_format), - ep->cur_channels, ep->cur_period_frames, + 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 = pcm_format_to_bits(ep->cur_format); + runtime->hw.formats = subs->formats; runtime->hw.rate_min = runtime->hw.rate_max = ep->cur_rate; - runtime->hw.channels_min = runtime->hw.channels_max = - ep->cur_channels; runtime->hw.rates = SNDRV_PCM_RATE_KNOT; runtime->hw.periods_min = runtime->hw.periods_max = ep->cur_buffer_periods; - subs->fixed_hw = 1; + + 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; err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, @@ -1442,9 +1307,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream) snd_media_stop_pipeline(subs); - if (subs->interface >= 0 && - !snd_usb_lock_shutdown(subs->stream->chip)) { - usb_set_interface(subs->dev, subs->interface, 0); + if (!snd_usb_lock_shutdown(subs->stream->chip)) { subs->interface = -1; ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1); snd_usb_unlock_shutdown(subs->stream->chip); @@ -1823,15 +1686,19 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea 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: - subs->need_setup_fmt = true; - fallthrough; 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: /* keep retire_data_urb for delay calculation */ @@ -1840,6 +1707,9 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea 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; } @@ -1863,10 +1733,11 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream 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: - subs->need_setup_fmt = true; - fallthrough; case SNDRV_PCM_TRIGGER_STOP: stop_endpoints(subs); fallthrough; @@ -1874,6 +1745,9 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream 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; } -- cgit v1.2.3 From c15871e17fc6efb98176b92b4152019876dbec24 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:32 +0100 Subject: ALSA: usb-audio: Fix possible stall of implicit fb packet ring-buffer The implicit feedback mode uses a ring buffer for storing the received packet sizes from the feedback source, and the code has a slight flaw; when a playback stream stalls by some reason and the URBs aren't processed, the next_packet FIFO might become empty, but the driver can't distinguish whether it's empty or full because it's managed with read_poss and write_pos. This patch addresses those by changing the next_packet array management. Instead of keeping read and write positions, now the head position and the queued amount are kept. It's easier to understand about the emptiness. Also, the URB active flag is now cleared before calling queue_pending_output_urbs() for avoiding (theoretically) possible inconsistency. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-27-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 5 ++-- sound/usb/endpoint.c | 83 +++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 66a249ae138f..cde492e9581a 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -80,8 +80,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 */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index eee74313603e..b8f06a75fc2a 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -328,6 +328,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 @@ -352,17 +385,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); @@ -377,12 +407,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 at %s\n", ctx->index, err, __func__); - else - set_bit(ctx->index, &ep->active_mask); + notify_xrun(ep); + return; + } + + set_bit(ctx->index, &ep->active_mask); } } @@ -393,7 +426,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_usb_substream *data_subs; unsigned long flags; int err; @@ -418,10 +450,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); @@ -442,9 +474,7 @@ static void snd_complete_urb(struct urb *urb) return; usb_audio_err(ep->chip, "cannot submit urb (err = %d)\n", err); - data_subs = READ_ONCE(ep->data_subs); - if (data_subs && data_subs->pcm_substream) - snd_pcm_stop_xrun(data_subs->pcm_substream); + notify_xrun(ep); exit_clear: clear_bit(ctx->index, &ep->active_mask); @@ -789,8 +819,8 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force) clear_bit(EP_FLAG_RUNNING, &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)) { @@ -1402,7 +1432,16 @@ static 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 @@ -1423,8 +1462,6 @@ static 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); -- cgit v1.2.3 From cab941b7e5cf054502b01f776db724400ee5c1b6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:33 +0100 Subject: ALSA: usb-audio: Constify audioformat pointer references The audioformat is referred in many places but most of usages are read-only. Let's add const prefix in the possible places. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-28-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 4 ++-- sound/usb/clock.c | 16 ++++++++-------- sound/usb/clock.h | 4 ++-- sound/usb/endpoint.c | 2 +- sound/usb/endpoint.h | 2 +- sound/usb/pcm.c | 32 ++++++++++++++++---------------- sound/usb/pcm.h | 2 +- sound/usb/quirks.c | 9 +++++---- sound/usb/quirks.h | 6 +++--- 9 files changed, 39 insertions(+), 38 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index cde492e9581a..53f0ce61f858 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -120,7 +120,7 @@ struct snd_usb_endpoint { bool need_setup; /* (re-)need for configure? */ /* for hw constraints */ - struct audioformat *cur_audiofmt; + const struct audioformat *cur_audiofmt; unsigned int cur_rate; snd_pcm_format_t cur_format; unsigned int cur_channels; @@ -142,7 +142,7 @@ struct snd_usb_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) */ diff --git a/sound/usb/clock.c b/sound/usb/clock.c index b869a711afbf..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)); @@ -482,7 +482,7 @@ int snd_usb_clock_find_source(struct snd_usb_audio *chip, } static int set_sample_rate_v1(struct snd_usb_audio *chip, - struct audioformat *fmt, int rate) + const struct audioformat *fmt, int rate) { struct usb_device *dev = chip->dev; struct usb_host_interface *alts; @@ -611,7 +611,7 @@ int snd_usb_set_sample_rate_v2v3(struct snd_usb_audio *chip, } static int set_sample_rate_v2v3(struct snd_usb_audio *chip, - struct audioformat *fmt, int rate) + const struct audioformat *fmt, int rate) { int cur_rate, prev_rate; int clock; @@ -663,7 +663,7 @@ validation: } int snd_usb_init_sample_rate(struct snd_usb_audio *chip, - struct audioformat *fmt, int rate) + 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); diff --git a/sound/usb/clock.h b/sound/usb/clock.h index 8d406ed294d6..ed9fc2dc0510 100644 --- a/sound/usb/clock.h +++ b/sound/usb/clock.h @@ -3,10 +3,10 @@ #define __USBAUDIO_CLOCK_H int snd_usb_init_sample_rate(struct snd_usb_audio *chip, - struct audioformat *fmt, int rate); + 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, diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index b8f06a75fc2a..49fb934ee432 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -623,7 +623,7 @@ bool snd_usb_endpoint_compatible(struct snd_usb_audio *chip, */ struct snd_usb_endpoint * snd_usb_endpoint_open(struct snd_usb_audio *chip, - struct audioformat *fp, + const struct audioformat *fp, const struct snd_pcm_hw_params *params, bool is_sync_ep) { diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 2bfa6d3e029c..201011d89659 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -12,7 +12,7 @@ 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, - struct audioformat *fp, + 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, diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index e80e8cf1e863..fc028492dd1a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -80,13 +80,13 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream /* * find a matching audio format */ -static struct audioformat * +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, fmt_list_head, list) { @@ -141,7 +141,7 @@ find_format(struct list_head *fmt_list_head, snd_pcm_format_t format, return found; } -static struct audioformat * +static const struct audioformat * find_substream_format(struct snd_usb_substream *subs, const struct snd_pcm_hw_params *params) { @@ -182,7 +182,7 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int ep) * initialize the pitch control and sample rate */ int snd_usb_init_pitch(struct snd_usb_audio *chip, - struct audioformat *fmt) + const struct audioformat *fmt) { int err; @@ -641,14 +641,14 @@ find_matching_substream(struct snd_usb_audio *chip, int stream, int ep_num, return NULL; } -static struct audioformat * +static const struct audioformat * 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; - struct audioformat *fp, *sync_fmt; + const struct audioformat *fp, *sync_fmt; int score, high_score; subs = find_matching_substream(chip, stream, target->sync_ep, @@ -726,8 +726,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, { struct snd_usb_substream *subs = substream->runtime->private_data; struct snd_usb_audio *chip = subs->stream->chip; - struct audioformat *fmt; - struct audioformat *sync_fmt; + const struct audioformat *fmt; + const struct audioformat *sync_fmt; int ret; ret = snd_media_start_pipeline(subs); @@ -918,7 +918,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); @@ -995,7 +995,7 @@ static int hw_rule_rate(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_RATE); unsigned int rmin, rmax, r; int i; @@ -1028,7 +1028,7 @@ 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; @@ -1049,7 +1049,7 @@ 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]; @@ -1080,7 +1080,7 @@ 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; @@ -1109,7 +1109,7 @@ static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime, { struct snd_usb_audio *chip = subs->stream->chip; struct snd_usb_endpoint *ep; - struct audioformat *fp; + const struct audioformat *fp; int err; list_for_each_entry(fp, &subs->fmt_list, list) { @@ -1170,7 +1170,7 @@ static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime, static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs) { struct snd_usb_audio *chip = subs->stream->chip; - struct audioformat *fp; + const struct audioformat *fp; unsigned int pt, ptmin; int param_period_time_if_needed = -1; int err; diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index a4f784225abc..06c586467d3f 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -10,7 +10,7 @@ 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, - struct audioformat *fmt); + 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, diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 7e7f258011ff..5510c8a98447 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1375,7 +1375,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) { @@ -1414,7 +1415,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; @@ -1476,7 +1477,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 */ @@ -1543,7 +1544,7 @@ static bool is_itf_usb_dsd_dac(unsigned int id) } int snd_usb_select_mode_quirk(struct snd_usb_audio *chip, - struct audioformat *fmt) + const struct audioformat *fmt) { struct usb_device *dev = chip->dev; int err; diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h index 011f22cf2bf6..67a02303c820 100644 --- a/sound/usb/quirks.h +++ b/sound/usb/quirks.h @@ -26,12 +26,12 @@ 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); @@ -41,7 +41,7 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, __u16 index, void *data, __u16 size); int snd_usb_select_mode_quirk(struct snd_usb_audio *chip, - struct audioformat *fmt); + const struct audioformat *fmt); u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, struct audioformat *fp, -- cgit v1.2.3 From 43b81e84068d26d630b63fa877e682909a0102fe Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:34 +0100 Subject: ALSA: usb-audio: Use atomic_t for endpoint use_count The endpoint objects may be started/stopped concurrently by different substreams in the case of implicit feedback mode, while the current code handles the reference counter without any protection. This patch changes the refcount to atomic_t for avoiding the inconsistency. We need no reference_t here as the refcount goes only up to 2. Also the name "use_count" is renamed to "running" since this is about actually the running status, not the open refcount. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-29-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 2 +- sound/usb/endpoint.c | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 53f0ce61f858..f58c3769b058 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -60,7 +60,7 @@ struct snd_usb_endpoint { struct snd_usb_audio *chip; int opened; /* open refcount; protect with chip->mutex */ - int use_count; + atomic_t running; /* running status */ int ep_num; /* the referenced endpoint number */ int type; /* SND_USB_ENDPOINT_TYPE_* */ unsigned long flags; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 49fb934ee432..4d733b2d8287 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1234,7 +1234,7 @@ unlock: * * @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. * @@ -1253,11 +1253,12 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (ep->sync_master) WRITE_ONCE(ep->sync_master->sync_slave, ep); - usb_audio_dbg(ep->chip, "Starting %s EP 0x%x (count %d)\n", - ep_type_name(ep->type), ep->ep_num, ep->use_count); + 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 */ @@ -1319,7 +1320,7 @@ __error: if (ep->sync_master) WRITE_ONCE(ep->sync_master->sync_slave, NULL); clear_bit(EP_FLAG_RUNNING, &ep->flags); - ep->use_count--; + atomic_dec(&ep->running); deactivate_urbs(ep, false); return -EPIPE; } @@ -1329,7 +1330,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. * @@ -1343,16 +1344,17 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (!ep) return; - usb_audio_dbg(ep->chip, "Stopping %s EP 0x%x (count %d)\n", - ep_type_name(ep->type), ep->ep_num, ep->use_count); + 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)); - if (snd_BUG_ON(ep->use_count == 0)) + if (snd_BUG_ON(!atomic_read(&ep->running))) return; if (ep->sync_master) WRITE_ONCE(ep->sync_master->sync_slave, NULL); - if (--ep->use_count == 0) { + if (!atomic_dec_return(&ep->running)) { deactivate_urbs(ep, false); set_bit(EP_FLAG_STOPPING, &ep->flags); } @@ -1363,7 +1365,7 @@ void snd_usb_endpoint_stop(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) @@ -1410,7 +1412,7 @@ static 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; -- cgit v1.2.3 From d0f09d1e4a88647695739d2ff4268e9fdcf5b35d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:35 +0100 Subject: ALSA: usb-audio: Refactoring endpoint URB deactivation Minor code refactoring to consolidate the URB deactivation code in endpoint.c. A slight behavior change is that the error handling in snd_usb_endpoint_start() leaves EP_FLAG_STOPPING now. This should be synced with the later PCM sync_stop callback. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-30-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 4d733b2d8287..35c84c2264e1 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -777,6 +777,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) @@ -802,22 +805,31 @@ 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_head = 0; ep->next_packet_queued = 0; @@ -831,6 +843,9 @@ static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force) } } + out: + if (wait_sync) + return wait_clear_urbs(ep); return 0; } @@ -845,8 +860,7 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) 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]); @@ -1261,9 +1275,6 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) 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; @@ -1317,11 +1328,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) return 0; __error: - if (ep->sync_master) - WRITE_ONCE(ep->sync_master->sync_slave, NULL); - clear_bit(EP_FLAG_RUNNING, &ep->flags); - atomic_dec(&ep->running); - deactivate_urbs(ep, false); + snd_usb_endpoint_stop(ep); return -EPIPE; } @@ -1354,10 +1361,8 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (ep->sync_master) WRITE_ONCE(ep->sync_master->sync_slave, NULL); - if (!atomic_dec_return(&ep->running)) { - deactivate_urbs(ep, false); - set_bit(EP_FLAG_STOPPING, &ep->flags); - } + if (!atomic_dec_return(&ep->running)) + stop_and_unlink_urbs(ep, false, false); } /** -- cgit v1.2.3 From 6aa719d15a1903eb3fd0e052ae53f3b024ad4d05 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:36 +0100 Subject: ALSA: usb-audio: Drop unneeded snd_usb_substream fields Some fields like interface and alt_idx in snd_usb_substream are mostly useless now as they can be referred via either cur_audiofmt or data_endpoint assigned to the substream. Drop those, and also assure the concurrency about the access of cur_audiofmt field. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-31-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.c | 1 - sound/usb/card.h | 8 -------- sound/usb/pcm.c | 24 ++++++------------------ sound/usb/proc.c | 19 +++++++++++++------ sound/usb/quirks.c | 10 +++++----- 5 files changed, 24 insertions(+), 38 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 58958afcec93..7940b3bff5bc 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -124,7 +124,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; } diff --git a/sound/usb/card.h b/sound/usb/card.h index f58c3769b058..1dd7a514d1d5 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -140,18 +140,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 */ 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) */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index fc028492dd1a..95a6a854dc55 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -797,20 +797,11 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, subs->sync_endpoint); } - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; + mutex_lock(&chip->mutex); subs->cur_audiofmt = fmt; + mutex_unlock(&chip->mutex); ret = configure_endpoints(chip, subs); - if (ret < 0) - goto unlock; - - 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); unlock: if (ret < 0) @@ -835,9 +826,9 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) 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; + mutex_unlock(&chip->mutex); if (!snd_usb_lock_shutdown(chip)) { if (stop_endpoints(subs)) sync_pending_stops(subs); @@ -1274,8 +1265,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; @@ -1308,7 +1297,6 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream) snd_media_stop_pipeline(subs); if (!snd_usb_lock_shutdown(subs->stream->chip)) { - subs->interface = -1; ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D1); snd_usb_unlock_shutdown(subs->stream->chip); if (ret < 0) @@ -1570,10 +1558,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; diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 889c550c9f29..447ba32e281c 100644 --- a/sound/usb/proc.c +++ b/sound/usb/proc.c @@ -175,32 +175,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.c b/sound/usb/quirks.c index 5510c8a98447..02f3f6ed9390 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1424,7 +1424,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; } @@ -1459,13 +1459,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); -- cgit v1.2.3 From 3d58760f4d0015cc1e7765b580daa007d759d86b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:37 +0100 Subject: ALSA: usb-audio: Unify the code for the next packet size calculation There are two places calculating the next packet size for the playback stream in the exactly same way. Provide the single helper for this purpose and use it from both places gracefully. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-32-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 40 ++++++++++++++++++++++++---------------- sound/usb/endpoint.h | 4 ++-- sound/usb/pcm.c | 8 +------- 3 files changed, 27 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 35c84c2264e1..5d618724bd75 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -121,13 +121,13 @@ int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep) } /* - * 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; @@ -145,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; @@ -167,6 +166,21 @@ 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_master) + 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) { @@ -223,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; diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 201011d89659..11e3bb839fd7 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -45,7 +45,7 @@ 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); +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/pcm.c b/sound/usb/pcm.c index 95a6a854dc55..5953e22a72c5 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1512,13 +1512,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; -- cgit v1.2.3 From 13ee03361fc5c9284067f4bfaaea4ebadbd874cf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:38 +0100 Subject: ALSA: usb-audio: Simplify rate_min/max and rates set up There are multiple places in format.c performing the similar code for setting the rate_min, rate_max and rates fields. This patch unifies those in a helper function and calls it at the end of the parser phase so that all rate_table entries have been already determined. No functional changes, just a minor code refactoring. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-33-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/format.c | 53 ++++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) (limited to 'sound') diff --git a/sound/usb/format.c b/sound/usb/format.c index 7641716f0c6c..93459ba228d3 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -164,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). @@ -198,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) @@ -217,13 +233,7 @@ 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) { usb_audio_info(chip, @@ -231,6 +241,7 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof fp->iface, fp->altsetting); return -EINVAL; } + set_rate_table_min_max(fp); } else { /* continuous rates */ fp->rates = SNDRV_PCM_RATE_CONTINUOUS; @@ -336,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]); @@ -373,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"); @@ -459,9 +462,6 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip, struct usb_device *dev = chip->dev; unsigned int *table; unsigned int nr_rates; - unsigned int rate_min = 0x7fffffff; - unsigned int rate_max = 0; - unsigned int rates = 0; int i, err; table = kcalloc(fp->nr_rates, sizeof(*table), GFP_KERNEL); @@ -478,14 +478,8 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip, if (err < 0) continue; - if (check_valid_altsetting_v2v3(chip, fp->iface, fp->altsetting)) { + if (check_valid_altsetting_v2v3(chip, fp->iface, fp->altsetting)) table[nr_rates++] = fp->rate_table[i]; - if (rate_min > fp->rate_table[i]) - rate_min = fp->rate_table[i]; - if (rate_max < fp->rate_table[i]) - rate_max = fp->rate_table[i]; - rates |= snd_pcm_rate_to_rate_bit(fp->rate_table[i]); - } } if (!nr_rates) { @@ -503,9 +497,6 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip, kfree(fp->rate_table); fp->rate_table = table; fp->nr_rates = nr_rates; - fp->rate_min = rate_min; - fp->rate_max = rate_max; - fp->rates = rates; return 0; } @@ -602,6 +593,10 @@ static int parse_audio_format_rates_v2v3(struct snd_usb_audio *chip, 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); -- cgit v1.2.3 From 53837b4ac2bd33ede5cd799940341ce5ea7b2902 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:39 +0100 Subject: ALSA: usb-audio: Replace slave/master terms Follow the inclusive terminology, just replace sync_master/sync_slave with sync_source/sync_sink. It's also a bit clearer from its meaning, too. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-34-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 4 ++-- sound/usb/endpoint.c | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 1dd7a514d1d5..3e16b9288693 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -71,8 +71,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]; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 5d618724bd75..5f1d5f1ed8db 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -175,7 +175,7 @@ int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep, { if (ctx->packet_size[idx]) return ctx->packet_size[idx]; - else if (ep->sync_master) + else if (ep->sync_source) return slave_next_packet_size(ep); else return next_packet_size(ep); @@ -205,16 +205,16 @@ 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_slave; + struct snd_usb_endpoint *sync_sink; if (unlikely(ep->skip_packets > 0)) { ep->skip_packets--; return; } - sync_slave = READ_ONCE(ep->sync_slave); - if (sync_slave) - snd_usb_handle_sync_urb(sync_slave, ep, urb); + sync_sink = READ_ONCE(ep->sync_sink); + if (sync_sink) + snd_usb_handle_sync_urb(sync_sink, ep, urb); call_retire_callback(ep, urb); } @@ -708,7 +708,7 @@ 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_master = sync_ep; + data_ep->sync_source = sync_ep; } /* @@ -802,7 +802,7 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep) alive, ep->ep_num); clear_bit(EP_FLAG_STOPPING, &ep->flags); - ep->sync_slave = NULL; + ep->sync_sink = NULL; snd_usb_endpoint_set_callback(ep, NULL, NULL, NULL); return 0; @@ -969,9 +969,9 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) packs_per_ms = 1; max_packs_per_urb = MAX_PACKS; } - if (ep->sync_master && !ep->implicit_fb_sync) + if (ep->sync_source && !ep->implicit_fb_sync) max_packs_per_urb = min(max_packs_per_urb, - 1U << ep->sync_master->syncinterval); + 1U << ep->sync_source->syncinterval); max_packs_per_urb = max(1u, max_packs_per_urb >> ep->datainterval); /* @@ -1015,7 +1015,7 @@ 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 (ep->sync_master) + if (ep->sync_source) minsize -= minsize >> 3; minsize = max(minsize, 1u); @@ -1272,8 +1272,8 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep) if (atomic_read(&ep->chip->shutdown)) return -EBADFD; - if (ep->sync_master) - WRITE_ONCE(ep->sync_master->sync_slave, ep); + 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, @@ -1366,8 +1366,8 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) if (snd_BUG_ON(!atomic_read(&ep->running))) return; - if (ep->sync_master) - WRITE_ONCE(ep->sync_master->sync_slave, NULL); + if (ep->sync_source) + WRITE_ONCE(ep->sync_source->sync_sink, NULL); if (!atomic_dec_return(&ep->running)) stop_and_unlink_urbs(ep, false, false); -- cgit v1.2.3 From 89fa3f686c10c9edde0f41e5a1c71afa0e43ff87 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:40 +0100 Subject: ALSA: usb-audio: Use unsigned char for iface and altsettings fields Just for consistency, use unsigned char for iface and altsetting in allover places. Also rearrange the field positions of snd_usb_endpiont and tidy up with some comments. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-35-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.h | 11 +++++++---- sound/usb/endpoint.c | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.h b/sound/usb/card.h index 3e16b9288693..6a027c349194 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -16,7 +16,7 @@ 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 */ @@ -63,7 +63,12 @@ struct snd_usb_endpoint { 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); @@ -112,8 +117,6 @@ struct snd_usb_endpoint { unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */ unsigned char silence_value; unsigned int stride; - int iface, altsetting; - unsigned char ep_idx; /* endpoint array index */ int skip_packets; /* quirks for devices to ignore the first n packets in a stream */ bool implicit_fb_sync; /* syncs with implicit feedback */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 5f1d5f1ed8db..162da7a50046 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -762,7 +762,7 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip, ep->ep_num, ep->opened); if (!--ep->opened) { endpoint_set_interface(chip, ep, false); - ep->iface = -1; + ep->iface = 0; ep->altsetting = 0; ep->cur_audiofmt = NULL; ep->cur_rate = 0; -- cgit v1.2.3 From 8ec08b1a5cf8e4de1db8919a27d0e2a801b09d24 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:41 +0100 Subject: ALSA: usb-audio: Show sync endpoint information in proc outputs Now the sync endpoints have been parsed at the beginning and won't be changed dynamically, let's show them in the proc outputs for helping debugging. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-36-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/proc.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/proc.c b/sound/usb/proc.c index 447ba32e281c..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); } -- cgit v1.2.3 From 6234fdc1cede3c1deda8324bdbb8274c0f4ff192 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:42 +0100 Subject: ALSA: usb-audio: Quirk for BOSS GT-001 The capture stream of BOSS GT-001 seems always requiring to be tied with the playback stream. OTOH, the playback stream of this device doesn't seem working in the implicit fb mode, per se, since the playback must be running before the capture stream. This patch tries to address the points above: - Avoid the implicit fb mode for the playback - Set up a fake sync EP for the capture stream with the hard-coded playback stream using the implicit fb mode Reported-by: Keith Milner Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-37-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'sound') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 5953e22a72c5..676acc20595b 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -403,6 +403,9 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, case USB_ID(0x0582, 0x01d8): /* BOSS Katana */ /* BOSS Katana amplifiers do not need quirks */ return 0; + case USB_ID(0x0582, 0x01e5): /* BOSS GT-001 */ + /* BOSS GT-001 needs no implicit fb for playback */ + return 0; } /* Generic UAC2 implicit feedback */ @@ -454,6 +457,30 @@ add_sync_ep: return 1; } +static int audioformat_capture_quirk(struct snd_usb_audio *chip, + struct audioformat *fmt, + struct usb_host_interface *alts) +{ + struct usb_device *dev = chip->dev; + + switch (chip->usb_id) { + case USB_ID(0x0582, 0x01e5): /* BOSS GT-001 */ + if (!snd_usb_get_host_interface(chip, 0x01, 0x01)) + return 0; + fmt->sync_ep = 0x0d; + fmt->sync_iface = 0x01; + fmt->sync_altsetting = 0x01; + fmt->sync_ep_idx = 0; + fmt->implicit_fb = 1; + dev_dbg(&dev->dev, "%d:%d: added fake capture sync sync_ep=%x, iface=%d, alt=%d\n", + fmt->iface, fmt->altsetting, fmt->sync_ep, fmt->sync_iface, + fmt->sync_altsetting); + return 1; + } + return 0; + +} + int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, struct audioformat *fmt) { @@ -474,6 +501,10 @@ int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, err = audioformat_implicit_fb_quirk(chip, fmt, alts); if (err > 0) return 0; + } else { + err = audioformat_capture_quirk(chip, fmt, alts); + if (err > 0) + return 0; } if (altsd->bNumEndpoints < 2) -- cgit v1.2.3 From 9fddc15e803945a744f357a4d1c94301e1ed6681 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:43 +0100 Subject: ALSA: usb-audio: Factor out the implicit feedback quirk code The code dealing with the implicit feedback mode grew recently, and it's becoming messy. As we receive more and more devices that need the similar handling, it's better to be processed through a table instead of the open code. This patch moves the code that is relevant with parsing the implicit feedback mode and some helpers into another file, implicit.c. The detection and the setup of the implicit feedback sync EPs are rewritten to use the ID/class matching table instead. There should be no functional changes. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-38-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/Makefile | 1 + sound/usb/implicit.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++ sound/usb/implicit.h | 14 +++ sound/usb/pcm.c | 320 +++-------------------------------------------- 4 files changed, 376 insertions(+), 306 deletions(-) create mode 100644 sound/usb/implicit.c create mode 100644 sound/usb/implicit.h (limited to 'sound') 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/implicit.c b/sound/usb/implicit.c new file mode 100644 index 000000000000..bc7edecff946 --- /dev/null +++ b/sound/usb/implicit.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Special handling for implicit feedback mode +// + +#include +#include +#include +#include + +#include +#include +#include + +#include "usbaudio.h" +#include "card.h" +#include "helper.h" +#include "implicit.h" + +enum { + IMPLICIT_FB_NONE, + 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_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[] = { + /* Fixed EP */ + IMPLICIT_FB_FIXED_DEV(0x1397, 0x0001, 0x81, 1), /* Behringer UFX1604 */ + IMPLICIT_FB_FIXED_DEV(0x1397, 0x0002, 0x81, 1), /* Behringer UFX1204 */ + IMPLICIT_FB_FIXED_DEV(0x0763, 0x2080, 0x81, 2), /* M-Audio FastTrack Ultra */ + IMPLICIT_FB_FIXED_DEV(0x0763, 0x2081, 0x81, 2), /* M-Audio FastTrack Ultra */ + IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */ + IMPLICIT_FB_FIXED_DEV(0x07fd, 0x0008, 0x81, 2), /* MOTU M Series */ + 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(0x0763, 0x2030, 0x81, 3), /* M-Audio Fast Track C400 */ + IMPLICIT_FB_FIXED_DEV(0x0763, 0x2031, 0x81, 3), /* M-Audio Fast Track C600 */ + 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 */ + IMPLICIT_FB_SKIP_DEV(0x0582, 0x01d8), /* BOSS Katana */ + + /* No quirk for playback but with capture quirk (see below) */ + IMPLICIT_FB_SKIP_DEV(0x0582, 0x01e5), /* BOSS GT-001 */ + + {} /* terminator */ +}; + +/* Implicit feedback quirk table for capture */ +static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { + 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 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_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; + } + + /* 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/pcm.c b/sound/usb/pcm.c index 676acc20595b..56079901769f 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -23,6 +23,7 @@ #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 @@ -276,211 +277,7 @@ static int snd_usb_pcm_sync_stop(struct snd_pcm_substream *substream) return 0; } -/* Check whether the given iface:altsetting points to an implicit fb source */ -static bool search_generic_implicit_fb(struct snd_usb_audio *chip, int ifnum, - unsigned int altsetting, - struct usb_host_interface **altsp, - unsigned int *ep) -{ - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - struct usb_endpoint_descriptor *epd; - - alts = snd_usb_get_host_interface(chip, ifnum, altsetting); - if (!alts) - return false; - altsd = get_iface_desc(alts); - if (altsd->bInterfaceClass != USB_CLASS_AUDIO || - altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING || - altsd->bInterfaceProtocol != UAC_VERSION_2 || - altsd->bNumEndpoints < 1) - return false; - epd = get_endpoint(alts, 0); - if (!usb_endpoint_is_isoc_in(epd) || - (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != - USB_ENDPOINT_USAGE_IMPLICIT_FB) - return false; - *ep = epd->bEndpointAddress; - *altsp = alts; - return true; -} - -/* Like the function above, but specific to Roland with vendor class and hack */ -static bool search_roland_implicit_fb(struct snd_usb_audio *chip, int ifnum, - unsigned int altsetting, - struct usb_host_interface **altsp, - unsigned int *ep) -{ - struct usb_host_interface *alts; - struct usb_interface_descriptor *altsd; - struct usb_endpoint_descriptor *epd; - - alts = snd_usb_get_host_interface(chip, ifnum, altsetting); - if (!alts) - return false; - altsd = get_iface_desc(alts); - if (altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC || - (altsd->bInterfaceSubClass != 2 && - altsd->bInterfaceProtocol != 2) || - altsd->bNumEndpoints < 1) - return false; - epd = get_endpoint(alts, 0); - if (!usb_endpoint_is_isoc_in(epd) || - (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != - USB_ENDPOINT_USAGE_IMPLICIT_FB) - return false; - *ep = epd->bEndpointAddress; - *altsp = alts; - return true; -} - -/* 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) -{ - struct usb_device *dev = chip->dev; - struct usb_interface_descriptor *altsd = get_iface_desc(alts); - struct usb_interface *iface; - unsigned int attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; - unsigned int ep; - unsigned int ifnum; - - switch (chip->usb_id) { - case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ - case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ - case USB_ID(0x22f0, 0x0006): /* Allen&Heath Qu-16 */ - ep = 0x81; - ifnum = 3; - goto add_sync_ep_from_ifnum; - case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ - case USB_ID(0x0763, 0x2081): - ep = 0x81; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x2466, 0x8003): /* Fractal Audio Axe-Fx II */ - case USB_ID(0x0499, 0x172a): /* Yamaha MODX */ - ep = 0x86; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx III */ - ep = 0x81; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x1686, 0xf029): /* Zoom UAC-2 */ - ep = 0x82; - ifnum = 2; - goto add_sync_ep_from_ifnum; - case USB_ID(0x1397, 0x0001): /* Behringer UFX1604 */ - case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ - ep = 0x81; - ifnum = 1; - goto add_sync_ep_from_ifnum; - case USB_ID(0x07fd, 0x0004): /* MOTU MicroBook II/IIc */ - /* MicroBook IIc */ - if (altsd->bInterfaceClass == USB_CLASS_AUDIO) - return 0; - - /* 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 */ - return 0; - case USB_ID(0x0582, 0x01e5): /* BOSS GT-001 */ - /* BOSS GT-001 needs no implicit fb for playback */ - return 0; - } - - /* Generic UAC2 implicit feedback */ - if (attr == USB_ENDPOINT_SYNC_ASYNC && - altsd->bInterfaceClass == USB_CLASS_AUDIO && - altsd->bInterfaceProtocol == UAC_VERSION_2 && - altsd->bNumEndpoints == 1) { - ifnum = altsd->bInterfaceNumber + 1; - if (search_generic_implicit_fb(chip, ifnum, - altsd->bAlternateSetting, - &alts, &ep)) - goto add_sync_ep; - } - - /* Roland/BOSS implicit feedback with vendor spec class */ - if (attr == USB_ENDPOINT_SYNC_ASYNC && - altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && - altsd->bInterfaceProtocol == 2 && - altsd->bNumEndpoints == 1 && - USB_ID_VENDOR(chip->usb_id) == 0x0582 /* Roland */) { - ifnum = altsd->bInterfaceNumber + 1; - if (search_roland_implicit_fb(chip, ifnum, - altsd->bAlternateSetting, - &alts, &ep)) - 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 0; - - alts = &iface->altsetting[1]; - -add_sync_ep: - fmt->sync_ep = ep; - fmt->sync_iface = ifnum; - fmt->sync_altsetting = alts->desc.bAlternateSetting; - fmt->sync_ep_idx = 0; - fmt->implicit_fb = 1; - dev_dbg(&dev->dev, "%d:%d: found implicit_fb sync_ep=%x, iface=%d, alt=%d\n", - fmt->iface, fmt->altsetting, fmt->sync_ep, fmt->sync_iface, - fmt->sync_altsetting); - - return 1; -} - -static int audioformat_capture_quirk(struct snd_usb_audio *chip, - struct audioformat *fmt, - struct usb_host_interface *alts) -{ - struct usb_device *dev = chip->dev; - - switch (chip->usb_id) { - case USB_ID(0x0582, 0x01e5): /* BOSS GT-001 */ - if (!snd_usb_get_host_interface(chip, 0x01, 0x01)) - return 0; - fmt->sync_ep = 0x0d; - fmt->sync_iface = 0x01; - fmt->sync_altsetting = 0x01; - fmt->sync_ep_idx = 0; - fmt->implicit_fb = 1; - dev_dbg(&dev->dev, "%d:%d: added fake capture sync sync_ep=%x, iface=%d, alt=%d\n", - fmt->iface, fmt->altsetting, fmt->sync_ep, fmt->sync_iface, - fmt->sync_altsetting); - return 1; - } - return 0; - -} - +/* Set up sync endpoint */ int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, struct audioformat *fmt) { @@ -496,20 +293,18 @@ int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, return 0; altsd = get_iface_desc(alts); - is_playback = !(get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN); - if (is_playback) { - err = audioformat_implicit_fb_quirk(chip, fmt, alts); - if (err > 0) - return 0; - } else { - err = audioformat_capture_quirk(chip, fmt, alts); - if (err > 0) - return 0; - } + err = snd_usb_parse_implicit_fb_quirk(chip, fmt, alts); + if (err > 0) + return 0; /* matched */ + + /* + * Generic sync EP handling + */ 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)) || @@ -569,45 +364,6 @@ int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip, 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, - 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 int snd_usb_pcm_change_state(struct snd_usb_substream *subs, int state) { int ret; @@ -656,53 +412,6 @@ int snd_usb_pcm_resume(struct snd_usb_stream *as) return 0; } -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; -} - -static const struct audioformat * -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; - - 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; -} - static void close_endpoints(struct snd_usb_audio *chip, struct snd_usb_substream *subs) { @@ -775,11 +484,10 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, goto stop_pipeline; } - if (fmt->implicit_fb && - (fmt->iface != fmt->sync_iface || - fmt->altsetting != fmt->sync_altsetting)) { - sync_fmt = find_implicit_fb_sync_format(chip, fmt, hw_params, - !substream->stream); + 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", -- cgit v1.2.3 From 83b7dcbc51c930fc2079ab6c6fc9d719768321f1 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:44 +0100 Subject: ALSA: usb-audio: Add generic implicit fb parsing This patch extends the implicit feedback mode parser code to check the description more generically, so that the quirk entries can be added without the explicit EP and interface numbers. The search is done for the next and the previous interface of the given altset, and if both entries are ASYNC mode and the direction matches, it just takes as the sync endpoint. The generic parser is applicable only for the playback stream. As of now, only a few M-Audio devices have been converted to use this mode. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-39-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/implicit.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index bc7edecff946..16b1fb55b7b9 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -19,6 +19,7 @@ enum { IMPLICIT_FB_NONE, + IMPLICIT_FB_GENERIC, IMPLICIT_FB_FIXED, }; @@ -30,6 +31,8 @@ struct snd_usb_implicit_fb_match { 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) } @@ -38,19 +41,22 @@ struct snd_usb_implicit_fb_match { /* 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(0x0763, 0x2080, 0x81, 2), /* M-Audio FastTrack Ultra */ - IMPLICIT_FB_FIXED_DEV(0x0763, 0x2081, 0x81, 2), /* M-Audio FastTrack Ultra */ IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */ IMPLICIT_FB_FIXED_DEV(0x07fd, 0x0008, 0x81, 2), /* MOTU M Series */ 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(0x0763, 0x2030, 0x81, 3), /* M-Audio Fast Track C400 */ - IMPLICIT_FB_FIXED_DEV(0x0763, 0x2031, 0x81, 3), /* M-Audio Fast Track C600 */ 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 */ @@ -75,7 +81,7 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { {} /* terminator */ }; -/* Implicit feedback quirk table for capture */ +/* 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, 0x01e5, 0x0d, 0x01), /* BOSS GT-001 */ @@ -162,6 +168,47 @@ static int add_roland_implicit_fb(struct snd_usb_audio *chip, 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, @@ -189,6 +236,8 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, 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: -- cgit v1.2.3 From 62abd092f97b33a33fcdb98c30bc01f2b1c55d04 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:45 +0100 Subject: ALSA: usb-audio: Add implicit_fb module option A new module option, implicit_fb, is added to specify the driver looking for the implicit feedback sync. This can be useful for a device that could be working better in the implicit feed back mode and user wants to test it quickly. When this works, we can add the quirk entry easily. Tested-by: Keith Milner Tested-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-40-tiwai@suse.de Signed-off-by: Takashi Iwai --- Documentation/sound/alsa-configuration.rst | 5 +++++ sound/usb/card.c | 4 ++++ sound/usb/implicit.c | 4 ++++ sound/usb/usbaudio.h | 1 + 4 files changed, 14 insertions(+) (limited to 'sound') diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst index c755b1c5e16f..fe52c314b763 100644 --- a/Documentation/sound/alsa-configuration.rst +++ b/Documentation/sound/alsa-configuration.rst @@ -2227,6 +2227,11 @@ quirk_alias Quirk alias list, pass strings like ``0123abcd:5678beef``, which applies the existing quirk for the device 5678:beef to a new device 0123:abcd. +implicit_fb + Apply the generic implicit feedback sync mode. When this is set + and the playback stream sync mode is ASYNC, the driver tries to + tie an adjacent ASYNC capture stream as the implicit feedback + source. use_vmalloc Use vmalloc() for allocations of the PCM buffers (default: yes). For architectures with non-coherent memory like ARM or MIPS, the diff --git a/sound/usb/card.c b/sound/usb/card.c index 7940b3bff5bc..cb0b6582dfee 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -72,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; @@ -97,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); @@ -596,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); diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 16b1fb55b7b9..1d87fcdae95b 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -269,6 +269,10 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, 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; } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index cc338e8e2597..980287aadd36 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -51,6 +51,7 @@ 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 */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ -- cgit v1.2.3 From ad0e6a3511824f8fdbe3eb63dbabc0ef35e732b9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Nov 2020 09:53:46 +0100 Subject: ALSA: usb-audio: Fix quirks for other BOSS devices A few other BOSS devices (BR-80, GT-100v2, Katana) seem requiring the same quirk as BOSS GT-001, i.e. no implicit feedback for playback but tying with capture. Add and correct the corresponding quirk table entries for them. Reported-and-tested-by: Keith Milner Link: https://lore.kernel.org/r/20201123085347.19667-41-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/implicit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 1d87fcdae95b..1f7fb036c5b6 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -72,10 +72,10 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { .type = IMPLICIT_FB_FIXED, .ep_num = 0x84, .iface = 0 }, /* MOTU MicroBook II */ - /* No quirk */ - IMPLICIT_FB_SKIP_DEV(0x0582, 0x01d8), /* BOSS Katana */ - /* 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 */ @@ -83,6 +83,9 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { /* 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 */ -- cgit v1.2.3 From 29b105d947c661e521bf7b1005868c02441732be Mon Sep 17 00:00:00 2001 From: Dylan Robinson Date: Mon, 23 Nov 2020 09:53:47 +0100 Subject: ALSA: usb-audio: Fix MOTU M-Series quirks Now that the usb audio driver correctly finds implicit feedback endpoints, the implicit feedback quirk for the MOTU M-Series is no longer required. This also removes some unnecessary vendor specific messages from the MOTU M-Series boot quirk. The removed vendor specific messages turned on vendor specific interrupts to the host every 32 samples. The only thing the boot quirk needs to do is wait for 2 seconds. Tested-by: Dylan Robinson Signed-off-by: Dylan Robinson Link: https://lore.kernel.org/r/20201123085347.19667-42-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/implicit.c | 1 - sound/usb/quirks.c | 16 ---------------- 2 files changed, 17 deletions(-) (limited to 'sound') diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index 1f7fb036c5b6..386198b36b87 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -52,7 +52,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { 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(0x07fd, 0x0008, 0x81, 2), /* MOTU M Series */ 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 */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 02f3f6ed9390..63cdf3c8c2bc 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1110,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; } -- cgit v1.2.3