diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/wcd-mbhc-v2.c | 3 | ||||
-rw-r--r-- | sound/soc/codecs/wcd934x/wcd934x.c | 11 | ||||
-rw-r--r-- | sound/soc/msm/apq8096-auto.c | 88 | ||||
-rw-r--r-- | sound/soc/msm/msm-dai-fe.c | 2 | ||||
-rw-r--r-- | sound/soc/msm/msm8998.c | 3 | ||||
-rw-r--r-- | sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 5 | ||||
-rw-r--r-- | sound/soc/msm/sdm660-common.c | 3 | ||||
-rw-r--r-- | sound/usb/Makefile | 3 | ||||
-rw-r--r-- | sound/usb/badd.c | 137 | ||||
-rw-r--r-- | sound/usb/card.c | 39 | ||||
-rw-r--r-- | sound/usb/clock.c | 4 | ||||
-rw-r--r-- | sound/usb/format.c | 49 | ||||
-rw-r--r-- | sound/usb/mixer.c | 496 | ||||
-rw-r--r-- | sound/usb/stream.c | 87 | ||||
-rw-r--r-- | sound/usb/usb_audio_qmi_svc.c | 49 |
15 files changed, 845 insertions, 134 deletions
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 7f9ad8ebcd3d..44141ae5668e 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -2217,7 +2217,7 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); - init_completion(&mbhc->btn_press_compl); + reinit_completion(&mbhc->btn_press_compl); WCD_MBHC_RSC_UNLOCK(mbhc); pr_debug("%s: leave\n", __func__); @@ -2854,6 +2854,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, } mutex_init(&mbhc->hphl_pa_lock); mutex_init(&mbhc->hphr_pa_lock); + init_completion(&mbhc->btn_press_compl); /* Register event notifier */ mbhc->nblock.notifier_call = wcd_event_notify; diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index e502d6340ffb..a0514c8a1ee5 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -5476,7 +5476,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, u32 adc, i, mic_bias_found = 0; int ret = 0; char *mad_input; - bool is_adc2_input = false; + bool is_adc_input = false; tavil_mad_input = ucontrol->value.integer.value[0]; @@ -5524,8 +5524,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); mad_input_widget = mad_amic_input_widget; - if (adc == 2) - is_adc2_input = true; + is_adc_input = true; } else { /* DMIC type input widget*/ mad_input_widget = tavil_conn_mad_text[tavil_mad_input]; @@ -5533,7 +5532,7 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, dev_dbg(codec->dev, "%s: tavil input widget = %s, adc_input = %s\n", __func__, - mad_input_widget, is_adc2_input ? "true" : "false"); + mad_input_widget, is_adc_input ? "true" : "false"); for (i = 0; i < card->num_of_dapm_routes; i++) { if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { @@ -5578,8 +5577,8 @@ static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, 0x0F, tavil_mad_input); snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, 0x07, mic_bias_found); - /* for adc2 input, mad should be in micbias mode with BG enabled */ - if (is_adc2_input) + /* for all adc inputs, mad should be in micbias mode with BG enabled */ + if (is_adc_input) snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, 0x88, 0x88); else diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c index c01150f883ff..e1baca358d5f 100644 --- a/sound/soc/msm/apq8096-auto.c +++ b/sound/soc/msm/apq8096-auto.c @@ -22,6 +22,7 @@ #include <linux/module.h> #include <linux/switch.h> #include <linux/input.h> +#include <linux/qdsp6v2/apr.h> #include <sound/core.h> #include <sound/soc.h> #include <sound/soc-dapm.h> @@ -68,6 +69,7 @@ static int msm_tert_tdm_rx_0_ch = 2; /* ICC STREAM */ static int msm_tert_tdm_rx_1_ch = 2; static int msm_tert_tdm_rx_2_ch = 2; static int msm_tert_tdm_rx_3_ch = 2; +static int msm_tert_tdm_rx_4_ch; static int msm_tert_tdm_tx_0_ch = 6; /* EC_REF1-EC_REF6(6 CHAN) */ static int msm_tert_tdm_tx_1_ch = 1; @@ -94,6 +96,7 @@ static int msm_tert_tdm_rx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_rx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_rx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_rx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_tx_0_bit_format = SNDRV_PCM_FORMAT_S16_LE; static int msm_tert_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE; @@ -836,6 +839,24 @@ static int msm_tert_tdm_rx_3_ch_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_tert_tdm_rx_4_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__, + msm_tert_tdm_rx_4_ch); + ucontrol->value.integer.value[0] = msm_tert_tdm_rx_4_ch - 1; + return 0; +} + +static int msm_tert_tdm_rx_4_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_tert_tdm_rx_4_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s: msm_tert_tdm_rx_4_ch = %d\n", __func__, + msm_tert_tdm_rx_4_ch); + return 0; +} + static int msm_tert_tdm_tx_0_ch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1324,6 +1345,40 @@ static int msm_tert_tdm_rx_3_bit_format_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_tert_tdm_rx_4_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_tert_tdm_rx_4_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_tert_tdm_rx_4_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 0: + default: + msm_tert_tdm_rx_4_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + pr_debug("%s: msm_tert_tdm_rx_4_bit_format = %d\n", + __func__, msm_tert_tdm_rx_4_bit_format); + return 0; +} + static int msm_tert_tdm_tx_0_bit_format_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2020,6 +2075,11 @@ static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, msm_tert_tdm_rx_3_bit_format); break; + case AFE_PORT_ID_TERTIARY_TDM_RX_4: + channels->min = channels->max = msm_tert_tdm_rx_4_ch; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + msm_tert_tdm_rx_4_bit_format); + break; case AFE_PORT_ID_TERTIARY_TDM_TX: channels->min = channels->max = msm_tert_tdm_tx_0_ch; param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, @@ -2600,6 +2660,8 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_tert_tdm_rx_2_ch_get, msm_tert_tdm_rx_2_ch_put), SOC_ENUM_EXT("TERT_TDM_RX_3 Channels", msm_snd_enum[5], msm_tert_tdm_rx_3_ch_get, msm_tert_tdm_rx_3_ch_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Channels", msm_snd_enum[5], + msm_tert_tdm_rx_4_ch_get, msm_tert_tdm_rx_4_ch_put), SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", msm_snd_enum[5], msm_tert_tdm_tx_0_ch_get, msm_tert_tdm_tx_0_ch_put), SOC_ENUM_EXT("TERT_TDM_TX_1 Channels", msm_snd_enum[5], @@ -2648,6 +2710,9 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("TERT_TDM_RX_3 Bit Format", msm_snd_enum[6], msm_tert_tdm_rx_3_bit_format_get, msm_tert_tdm_rx_3_bit_format_put), + SOC_ENUM_EXT("TERT_TDM_RX_4 Bit Format", msm_snd_enum[6], + msm_tert_tdm_rx_4_bit_format_get, + msm_tert_tdm_rx_4_bit_format_put), SOC_ENUM_EXT("TERT_TDM_TX_0 Bit Format", msm_snd_enum[6], msm_tert_tdm_tx_0_bit_format_get, msm_tert_tdm_tx_0_bit_format_put), @@ -4160,6 +4225,20 @@ static struct snd_soc_dai_link apq8096_auto_be_dai_links[] = { .ignore_suspend = 1, }, { + .name = LPASS_BE_TERT_TDM_RX_4, + .stream_name = "Tertiary TDM4 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36904", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_4, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &apq8096_tdm_be_ops, + .ignore_suspend = 1, + }, + { .name = LPASS_BE_TERT_TDM_TX_0, .stream_name = "Tertiary TDM0 Capture", .cpu_dai_name = "msm-dai-q6-tdm.36897", @@ -4606,12 +4685,21 @@ static int apq8096_asoc_machine_probe(struct platform_device *pdev) struct snd_soc_card *card; const struct of_device_id *match; int ret; + enum apr_subsys_state q6_state; if (!pdev->dev.of_node) { dev_err(&pdev->dev, "No platform supplied from device tree\n"); return -EINVAL; } + q6_state = apr_get_q6_state(); + if (q6_state == APR_SUBSYS_DOWN) { + dev_dbg(&pdev->dev, "deferring %s, adsp_state %d\n", + __func__, q6_state); + ret = -EPROBE_DEFER; + goto err; + } + card = populate_snd_card_dailinks(&pdev->dev); if (!card) { dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__); diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c index a7a25ea070da..33443ab151e9 100644 --- a/sound/soc/msm/msm-dai-fe.c +++ b/sound/soc/msm/msm-dai-fe.c @@ -113,7 +113,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE), .channels_min = 1, - .channels_max = 4, + .channels_max = 8, .rate_min = 8000, .rate_max = 48000, }, diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c index ec02cdf3ca3c..0f09c68ab344 100644 --- a/sound/soc/msm/msm8998.c +++ b/sound/soc/msm/msm8998.c @@ -4155,9 +4155,6 @@ static void update_mi2s_clk_val(int dai_id, int stream) mi2s_clk[dai_id].clk_freq_in_hz = mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; } - - if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master) - mi2s_clk[dai_id].clk_freq_in_hz = 0; } static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c index 7ce73484a681..08c78f91920e 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -7933,6 +7933,9 @@ static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX, MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = { @@ -13325,6 +13328,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SLIM7_UL_HL", NULL, "HFP_SLIM7_UL_HL"}, {"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, + {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"}, {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"}, {"MI2S_RX", NULL, "MI2S_DL_HL"}, {"MI2S_UL_HL", NULL, "MI2S_TX"}, @@ -13729,6 +13733,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUX_PCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, {"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"AUX_PCM_RX Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, {"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"}, {"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, diff --git a/sound/soc/msm/sdm660-common.c b/sound/soc/msm/sdm660-common.c index edf5719fbf3e..90b661dff7ec 100644 --- a/sound/soc/msm/sdm660-common.c +++ b/sound/soc/msm/sdm660-common.c @@ -2412,9 +2412,6 @@ static void update_mi2s_clk_val(int dai_id, int stream) mi2s_clk[dai_id].clk_freq_in_hz = mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample; } - - if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master) - mi2s_clk[dai_id].clk_freq_in_hz = 0; } static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) diff --git a/sound/usb/Makefile b/sound/usb/Makefile index d2ac0386d3da..083887ba0346 100644 --- a/sound/usb/Makefile +++ b/sound/usb/Makefile @@ -13,7 +13,8 @@ snd-usb-audio-objs := card.o \ pcm.o \ proc.o \ quirks.o \ - stream.o + stream.o \ + badd.o snd-usbmidi-lib-objs := midi.o diff --git a/sound/usb/badd.c b/sound/usb/badd.c new file mode 100644 index 000000000000..cc6c26cbb624 --- /dev/null +++ b/sound/usb/badd.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/usb.h> +#include <linux/usb/audio.h> +#include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> + +struct uac3_input_terminal_descriptor badd_baif_in_term_desc = { + .bLength = UAC3_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = BADD_IN_TERM_ID_BAIF, + .bCSourceID = BADD_CLOCK_SOURCE, + .wExTerminalDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +struct uac3_input_terminal_descriptor badd_baof_in_term_desc = { + .bLength = UAC3_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = BADD_IN_TERM_ID_BAOF, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = 0x00, + .bCSourceID = BADD_CLOCK_SOURCE, + .bmControls = 0x00000000, + .wExTerminalDescrID = 0x0000, + .wConnectorsDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +struct uac3_output_terminal_descriptor badd_baif_out_term_desc = { + .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = BADD_OUT_TERM_ID_BAIF, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = 0x00, /* No associated terminal */ + .bSourceID = BADD_FU_ID_BAIF, + .bCSourceID = BADD_CLOCK_SOURCE, + .bmControls = 0x00000000, /* No controls */ + .wExTerminalDescrID = 0x0000, + .wConnectorsDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +struct uac3_output_terminal_descriptor badd_baof_out_term_desc = { + .bLength = UAC3_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = BADD_OUT_TERM_ID_BAOF, + .bSourceID = BADD_FU_ID_BAOF, + .bCSourceID = BADD_CLOCK_SOURCE, + .wExTerminalDescrID = 0x0000, + .wTerminalDescrStr = 0x0000 +}; + +__u8 monoControls[] = { + 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00}; + +__u8 stereoControls[] = { + 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00 +}; + +__u8 badd_mu_src_ids[] = {BADD_IN_TERM_ID_BAOF, BADD_FU_ID_BAIOF}; + +struct uac3_mixer_unit_descriptor badd_baiof_mu_desc = { + .bLength = UAC3_DT_MIXER_UNIT_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_MIXER_UNIT_V3, + .bUnitID = BADD_MU_ID_BAIOF, + .bNrInPins = 0x02, + .baSourceID = badd_mu_src_ids, + .bmMixerControls = 0x00, + .bmControls = 0x00000000, + .wMixerDescrStr = 0x0000 +}; + +struct uac3_feature_unit_descriptor badd_baif_fu_desc = { + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3, + .bUnitID = BADD_FU_ID_BAIF, + .bSourceID = BADD_IN_TERM_ID_BAIF, + .wFeatureDescrStr = 0x0000 +}; + +struct uac3_feature_unit_descriptor badd_baof_fu_desc = { + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3, + .bUnitID = BADD_FU_ID_BAOF, + .wFeatureDescrStr = 0x0000 +}; + +struct uac3_feature_unit_descriptor badd_baiof_fu_desc = { + .bLength = 0x0f, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_FEATURE_UNIT_V3, + .bUnitID = BADD_FU_ID_BAIOF, + .bSourceID = BADD_IN_TERM_ID_BAIF, + .bmaControls = monoControls, + .wFeatureDescrStr = 0x0000 +}; + +struct uac3_clock_source_descriptor badd_clock_desc = { + .bLength = UAC3_DT_CLOCK_SRC_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC3_CLOCK_SOURCE, + .bClockID = BADD_CLOCK_SOURCE, + .bmControls = 0x00000001, + .bReferenceTerminal = 0x00, + .wClockSourceStr = 0x0000 +}; + +void *badd_desc_list[] = { + &badd_baif_in_term_desc, + &badd_baof_in_term_desc, + &badd_baiof_mu_desc, + &badd_baif_fu_desc, + &badd_baof_fu_desc, + &badd_baiof_fu_desc, + &badd_clock_desc +}; + diff --git a/sound/usb/card.c b/sound/usb/card.c index 7bc935bab369..23ea575f3bcf 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -45,6 +45,7 @@ #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> #include <linux/module.h> +#include <linux/usb/audio-v3.h> #include <sound/control.h> #include <sound/core.h> @@ -281,7 +282,6 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) struct usb_host_interface *host_iface; struct usb_interface_descriptor *altsd; struct usb_interface *usb_iface; - void *control_header; int i, protocol; usb_iface = usb_ifnum_to_if(dev, ctrlif); @@ -298,16 +298,13 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return -EINVAL; } - control_header = snd_usb_find_csint_desc(host_iface->extra, - host_iface->extralen, - NULL, UAC_HEADER); altsd = get_iface_desc(host_iface); protocol = altsd->bInterfaceProtocol; - if (!control_header) { - dev_err(&dev->dev, "cannot find UAC_HEADER\n"); - return -EINVAL; - } + /* + * UAC 1.0 devices use AC HEADER Desc for linking AS interfaces; + * UAC 2.0 and 3.0 devices use IAD for linking AS interfaces + */ switch (protocol) { default: @@ -317,8 +314,17 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) /* fall through */ case UAC_VERSION_1: { - struct uac1_ac_header_descriptor *h1 = control_header; + void *control_header; + struct uac1_ac_header_descriptor *h1; + + control_header = snd_usb_find_csint_desc(host_iface->extra, + host_iface->extralen, NULL, UAC_HEADER); + if (!control_header) { + dev_err(&dev->dev, "cannot find UAC_HEADER\n"); + return -EINVAL; + } + h1 = control_header; if (!h1->bInCollection) { dev_info(&dev->dev, "skipping empty audio interface (v1)\n"); return -EINVAL; @@ -335,7 +341,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) break; } - case UAC_VERSION_2: { + case UAC_VERSION_2: + case UAC_VERSION_3: { struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc; if (!assoc) { @@ -354,7 +361,8 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) } if (!assoc) { - dev_err(&dev->dev, "Audio class v2 interfaces need an interface association\n"); + dev_err(&dev->dev, "Audio class V%d interfaces need an interface association\n", + protocol); return -EINVAL; } @@ -554,6 +562,15 @@ static int usb_audio_probe(struct usb_interface *intf, struct usb_host_interface *alts; int ifnum; u32 id; + struct usb_interface_assoc_descriptor *assoc; + + assoc = intf->intf_assoc; + if (assoc && assoc->bFunctionClass == USB_CLASS_AUDIO && + assoc->bFunctionProtocol == UAC_VERSION_3 && + assoc->bFunctionSubClass == FULL_ADC_PROFILE) { + dev_info(&dev->dev, "No support for full-fledged ADC 3.0 yet!!\n"); + return -EINVAL; + } alts = &intf->altsetting[0]; ifnum = get_iface_desc(alts)->bInterfaceNumber; diff --git a/sound/usb/clock.c b/sound/usb/clock.c index 7ccbcaf6a147..2cd09ceba5e9 100644 --- a/sound/usb/clock.c +++ b/sound/usb/clock.c @@ -424,6 +424,10 @@ int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface, case UAC_VERSION_2: return set_sample_rate_v2(chip, iface, alts, fmt, rate); + + /* Clock rate is fixed at 48 kHz for BADD devices */ + case UAC_VERSION_3: + return 0; } } diff --git a/sound/usb/format.c b/sound/usb/format.c index 789d19ec035d..2cc3b92f1fba 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -20,6 +20,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> #include <sound/core.h> #include <sound/pcm.h> @@ -69,6 +70,30 @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip, format <<= 1; break; } + + case UAC_VERSION_3: { + switch (fp->maxpacksize) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_16: { + sample_width = BIT_RES_16_BIT; + sample_bytes = SUBSLOTSIZE_16_BIT; + break; + } + + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_24: { + sample_width = BIT_RES_24_BIT; + sample_bytes = SUBSLOTSIZE_24_BIT; + break; + } + } + format = 1 << format; + break; + } } if ((pcm_formats == 0) && @@ -366,6 +391,22 @@ err: return ret; } +static int badd_set_audio_rate_v3(struct snd_usb_audio *chip, + struct audioformat *fp) +{ + unsigned int rate; + + fp->rate_table = kmalloc(sizeof(int), GFP_KERNEL); + if (fp->rate_table == NULL) + return -ENOMEM; + + fp->nr_rates = 1; + rate = BADD_SAMPLING_RATE; + fp->rate_min = fp->rate_max = fp->rate_table[0] = rate; + fp->rates |= snd_pcm_rate_to_rate_bit(rate); + return 0; +} + /* * parse the format type I and III descriptors */ @@ -415,6 +456,9 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, /* fp->channels is already set in this case */ ret = parse_audio_format_rates_v2(chip, fp); break; + case UAC_VERSION_3: + ret = badd_set_audio_rate_v3(chip, fp); + break; } if (fp->channels < 1) { @@ -502,7 +546,10 @@ int snd_usb_parse_audio_format(struct snd_usb_audio *chip, fmt->bFormatType); return -ENOTSUPP; } - fp->fmt_type = fmt->bFormatType; + if (fp->protocol == UAC_VERSION_3) + fp->fmt_type = UAC_FORMAT_TYPE_I; + else + fp->fmt_type = fmt->bFormatType; if (err < 0) return err; #if 1 diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 157c0704817c..b9d9d2e99c78 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -50,6 +50,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> #include <sound/core.h> #include <sound/control.h> @@ -184,6 +185,17 @@ static void *find_audio_control_unit(struct mixer_build *state, /* we just parse the header */ struct uac_feature_unit_descriptor *hdr = NULL; + if (state->mixer->protocol == UAC_VERSION_3) { + int i; + + for (i = 0; i < NUM_BADD_DESCS; i++) { + hdr = (void *)badd_desc_list[i]; + if (hdr->bUnitID == unit) + return hdr; + } + + return NULL; + } while ((hdr = snd_usb_find_desc(state->buffer, state->buflen, hdr, USB_DT_CS_INTERFACE)) != NULL) { if (hdr->bLength >= 4 && @@ -717,7 +729,7 @@ static int check_input_term(struct mixer_build *state, int id, term->channels = d->bNrChannels; term->chconfig = le16_to_cpu(d->wChannelConfig); term->name = d->iTerminal; - } else { /* UAC_VERSION_2 */ + } else if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_input_terminal_descriptor *d = p1; /* call recursively to verify that the @@ -734,6 +746,24 @@ static int check_input_term(struct mixer_build *state, int id, term->channels = d->bNrChannels; term->chconfig = le32_to_cpu(d->bmChannelConfig); term->name = d->iTerminal; + } else { /* UAC_VERSION_3 */ + struct uac3_input_terminal_descriptor *d = p1; + + err = check_input_term(state, + d->bCSourceID, term); + if (err < 0) + return err; + + term->id = id; + term->type = d->wTerminalType; + if (d->wClusterDescrID == CLUSTER_ID_MONO) { + term->channels = NUM_CHANNELS_MONO; + term->chconfig = BADD_CH_CONFIG_MONO; + } else { + term->channels = NUM_CHANNELS_STEREO; + term->chconfig = BADD_CH_CONFIG_STEREO; + } + term->name = d->wTerminalDescrStr; } return 0; case UAC_FEATURE_UNIT: { @@ -751,41 +781,81 @@ static int check_input_term(struct mixer_build *state, int id, return 0; } case UAC_SELECTOR_UNIT: - case UAC2_CLOCK_SELECTOR: { - struct uac_selector_unit_descriptor *d = p1; - /* call recursively to retrieve the channel info */ - err = check_input_term(state, d->baSourceID[0], term); - if (err < 0) - return err; - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->id = id; - term->name = uac_selector_unit_iSelector(d); + /* UAC3_MIXER_UNIT_V3 */ + case UAC2_CLOCK_SELECTOR: + /* UAC3_CLOCK_SOURCE */ { + if (state->mixer->protocol == UAC_VERSION_3 + && hdr[2] == UAC3_CLOCK_SOURCE) { + struct uac3_clock_source_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; + term->id = id; + term->name = d->wClockSourceStr; + } else if (state->mixer->protocol == UAC_VERSION_3 + && hdr[2] == UAC3_MIXER_UNIT_V3) { + struct uac3_mixer_unit_descriptor *d = p1; + + term->type = d->bDescriptorSubtype << 16; + if (d->wClusterDescrID == CLUSTER_ID_MONO) { + term->channels = NUM_CHANNELS_MONO; + term->chconfig = BADD_CH_CONFIG_MONO; + } else { + term->channels = NUM_CHANNELS_STEREO; + term->chconfig = BADD_CH_CONFIG_STEREO; + } + term->name = d->wMixerDescrStr; + } else { + struct uac_selector_unit_descriptor *d = p1; + /* call recursively to retrieve channel info */ + err = check_input_term(state, + d->baSourceID[0], term); + if (err < 0) + return err; + /* virtual type */ + term->type = d->bDescriptorSubtype << 16; + term->id = id; + term->name = uac_selector_unit_iSelector(d); + } return 0; } case UAC1_PROCESSING_UNIT: case UAC1_EXTENSION_UNIT: /* UAC2_PROCESSING_UNIT_V2 */ /* UAC2_EFFECT_UNIT */ + /* UAC3_FEATURE_UNIT_V3 */ case UAC2_EXTENSION_UNIT_V2: { - struct uac_processing_unit_descriptor *d = p1; + if (state->mixer->protocol == UAC_VERSION_3) { + struct uac_feature_unit_descriptor *d = p1; + + id = d->bSourceID; + } else { + struct uac_processing_unit_descriptor *d = p1; + + if (state->mixer->protocol == UAC_VERSION_2 && + hdr[2] == UAC2_EFFECT_UNIT) { + /* UAC2/UAC1 unit IDs overlap here in an + * uncompatible way. Ignore this unit + * for now. + */ + return 0; + } - if (state->mixer->protocol == UAC_VERSION_2 && - hdr[2] == UAC2_EFFECT_UNIT) { - /* UAC2/UAC1 unit IDs overlap here in an - * uncompatible way. Ignore this unit for now. - */ + if (d->bNrInPins) { + id = d->baSourceID[0]; + break; /* continue to parse */ + } + /* virtual type */ + term->type = d->bDescriptorSubtype << 16; + term->channels = + uac_processing_unit_bNrChannels(d); + term->chconfig = + uac_processing_unit_wChannelConfig( + d, state->mixer->protocol); + term->name = uac_processing_unit_iProcessing( + d, state->mixer->protocol); return 0; } - - if (d->bNrInPins) { - id = d->baSourceID[0]; - break; /* continue to parse */ - } - term->type = d->bDescriptorSubtype << 16; /* virtual type */ - term->channels = uac_processing_unit_bNrChannels(d); - term->chconfig = uac_processing_unit_wChannelConfig(d, state->mixer->protocol); - term->name = uac_processing_unit_iProcessing(d, state->mixer->protocol); - return 0; + break; } case UAC2_CLOCK_SOURCE: { struct uac_clock_source_descriptor *d = p1; @@ -1232,12 +1302,18 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, struct usb_feature_control_info *ctl_info; unsigned int len = 0; int mapped_name = 0; - int nameid = uac_feature_unit_iFeature(desc); + int nameid; struct snd_kcontrol *kctl; struct usb_mixer_elem_info *cval; const struct usbmix_name_map *map; unsigned int range; + if (state->mixer->protocol == UAC_VERSION_3) + nameid = ((struct uac3_feature_unit_descriptor *) + raw_desc)->wFeatureDescrStr; + else + nameid = uac_feature_unit_iFeature(desc); + control++; /* change from zero-based to 1-based value */ if (control == UAC_FU_GRAPHIC_EQUALIZER) { @@ -1258,7 +1334,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, ctl_info = &audio_feature_info[control-1]; if (state->mixer->protocol == UAC_VERSION_1) cval->val_type = ctl_info->type; - else /* UAC_VERSION_2 */ + else /* UAC_VERSION_2 or UAC_VERSION_3*/ cval->val_type = ctl_info->type_uac2 >= 0 ? ctl_info->type_uac2 : ctl_info->type; @@ -1381,6 +1457,62 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, snd_usb_mixer_add_control(&cval->head, kctl); } +static int find_num_channels(struct mixer_build *state, int dir) +{ + int num_ch = -EINVAL, num, i, j, wMaxPacketSize; + int ctrlif = get_iface_desc(state->mixer->hostif)->bInterfaceNumber; + struct usb_interface *usb_iface = + usb_ifnum_to_if(state->mixer->chip->dev, ctrlif); + struct usb_interface_assoc_descriptor *assoc = usb_iface->intf_assoc; + struct usb_host_interface *alts; + + for (i = 0; i < assoc->bInterfaceCount; i++) { + int intf = assoc->bFirstInterface + i; + + if (intf != ctrlif) { + struct usb_interface *iface = + usb_ifnum_to_if(state->mixer->chip->dev, intf); + + alts = &iface->altsetting[1]; + if (dir == USB_DIR_OUT && + get_endpoint(alts, 0)->bEndpointAddress & + USB_DIR_IN) + continue; + if (dir == USB_DIR_IN && + !(get_endpoint(alts, 0)->bEndpointAddress & + USB_DIR_IN)) + continue; + num = iface->num_altsetting; + for (j = 1; j < num; j++) { + num_ch = NUM_CHANNELS_MONO; + alts = &iface->altsetting[j]; + wMaxPacketSize = le16_to_cpu( + get_endpoint(alts, 0)-> + wMaxPacketSize); + switch (wMaxPacketSize) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_MONO_24: + break; + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_24: + num_ch = NUM_CHANNELS_STEREO; + break; + } + if (num_ch == NUM_CHANNELS_MONO) + continue; + else + break; + } + } + } + + return num_ch; +} + /* * parse a feature unit * @@ -1412,7 +1544,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } - } else { + } else if (state->mixer->protocol == UAC_VERSION_2) { struct uac2_feature_unit_descriptor *ftr = _ftr; csize = 4; channels = (hdr->bLength - 6) / 4 - 1; @@ -1423,11 +1555,114 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, unitid); return -EINVAL; } + } else { + struct usb_interface *usb_iface = + usb_ifnum_to_if(state->mixer->chip->dev, + get_iface_desc(state->mixer->hostif)->bInterfaceNumber); + struct usb_interface_assoc_descriptor *assoc = + usb_iface->intf_assoc; + + csize = 4; + switch (unitid) { + case BADD_FU_ID_BAIOF: + channels = NUM_CHANNELS_MONO; + bmaControls = monoControls; + badd_baif_in_term_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + + case BADD_FU_ID_BAOF: + switch (assoc->bFunctionSubClass) { + case PROF_HEADPHONE: + case PROF_HEADSET_ADAPTER: + channels = NUM_CHANNELS_STEREO; + bmaControls = stereoControls; + badd_baiof_mu_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + case PROF_SPEAKERPHONE: + channels = NUM_CHANNELS_MONO; + bmaControls = monoControls; + badd_baof_in_term_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + default: + channels = find_num_channels(state, + USB_DIR_OUT); + if (channels < 0) { + usb_audio_err(state->chip, + "unit %u: Cant find num of channels\n", + unitid); + return channels; + } + + bmaControls = (channels == NUM_CHANNELS_MONO) ? + monoControls : stereoControls; + badd_baof_in_term_desc.wClusterDescrID = + (channels == NUM_CHANNELS_MONO) ? + CLUSTER_ID_MONO : CLUSTER_ID_STEREO; + break; + } + break; + + case BADD_FU_ID_BAIF: + switch (assoc->bFunctionSubClass) { + case PROF_HEADSET: + case PROF_HEADSET_ADAPTER: + case PROF_SPEAKERPHONE: + channels = NUM_CHANNELS_MONO; + bmaControls = monoControls; + badd_baif_in_term_desc.wClusterDescrID = + CLUSTER_ID_MONO; + break; + default: + channels = find_num_channels(state, USB_DIR_IN); + if (channels < 0) { + usb_audio_err(state->chip, + "unit %u: Cant find num of channels\n", + unitid); + return channels; + } + + bmaControls = (channels == NUM_CHANNELS_MONO) ? + monoControls : stereoControls; + badd_baif_in_term_desc.wClusterDescrID = + (channels == NUM_CHANNELS_MONO) ? + CLUSTER_ID_MONO : CLUSTER_ID_STEREO; + break; + } + break; + } } /* parse the source unit */ - if ((err = parse_audio_unit(state, hdr->bSourceID)) < 0) - return err; + if (state->mixer->protocol != UAC_VERSION_3) { + err = parse_audio_unit(state, hdr->bSourceID); + if (err < 0) + return err; + } else { + struct usb_interface *usb_iface = + usb_ifnum_to_if(state->mixer->chip->dev, + get_iface_desc(state->mixer->hostif)->bInterfaceNumber); + struct usb_interface_assoc_descriptor *assoc = + usb_iface->intf_assoc; + + switch (unitid) { + case BADD_FU_ID_BAOF: + switch (assoc->bFunctionSubClass) { + case PROF_HEADSET: + case PROF_HEADSET_ADAPTER: + hdr->bSourceID = BADD_MU_ID_BAIOF; + break; + default: + hdr->bSourceID = BADD_IN_TERM_ID_BAOF; + break; + } + } + err = parse_audio_unit(state, hdr->bSourceID); + if (err < 0) + return err; + } /* determine the input source type and name */ err = check_input_term(state, hdr->bSourceID, &iterm); @@ -1481,7 +1716,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0); } - } else { /* UAC_VERSION_2 */ + } else { /* UAC_VERSION_2 or UAC_VERSION_3*/ for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) { unsigned int ch_bits = 0; unsigned int ch_read_only = 0; @@ -1599,12 +1834,19 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int input_pins, num_ins, num_outs; int pin, ich, err; - if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || - !(num_outs = uac_mixer_unit_bNrChannels(desc))) { - usb_audio_err(state->chip, - "invalid MIXER UNIT descriptor %d\n", - unitid); - return -EINVAL; + if (state->mixer->protocol == UAC_VERSION_3) { + input_pins = badd_baiof_mu_desc.bNrInPins; + num_outs = + (badd_baiof_mu_desc.wClusterDescrID == CLUSTER_ID_MONO) ? + NUM_CHANNELS_MONO : NUM_CHANNELS_STEREO; + } else { + if (desc->bLength < 11 || !(input_pins = desc->bNrInPins) || + !(num_outs = uac_mixer_unit_bNrChannels(desc))) { + usb_audio_err(state->chip, + "invalid MIXER UNIT descriptor %d\n", + unitid); + return -EINVAL; + } } num_ins = 0; @@ -1624,9 +1866,14 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, int och, ich_has_controls = 0; for (och = 0; och < num_outs; och++) { - __u8 *c = uac_mixer_unit_bmControls(desc, - state->mixer->protocol); + __u8 *c = NULL; + if (state->mixer->protocol == UAC_VERSION_3) + c = + &(badd_baiof_mu_desc.bmMixerControls); + else + c = uac_mixer_unit_bmControls(desc, + state->mixer->protocol); if (check_matrix_bitmap(c, ich, och, num_outs)) { ich_has_controls = 1; break; @@ -2134,16 +2381,28 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) case UAC_MIXER_UNIT: return parse_audio_mixer_unit(state, unitid, p1); case UAC_SELECTOR_UNIT: + /* UAC3_MIXER_UNIT_V3 has the same value */ case UAC2_CLOCK_SELECTOR: - return parse_audio_selector_unit(state, unitid, p1); + /* UAC3_CLOCK_SOURCE has the same value */ + if (state->mixer->protocol == UAC_VERSION_3 && + p1[2] == UAC3_CLOCK_SOURCE) + return 0; /* NOP */ + else if (state->mixer->protocol == UAC_VERSION_3 + && p1[2] == UAC3_MIXER_UNIT_V3) + return parse_audio_mixer_unit(state, unitid, p1); + else + return parse_audio_selector_unit(state, unitid, p1); case UAC_FEATURE_UNIT: return parse_audio_feature_unit(state, unitid, p1); case UAC1_PROCESSING_UNIT: /* UAC2_EFFECT_UNIT has the same value */ + /* UAC3_FEATURE_UNIT_V3 has the same value */ if (state->mixer->protocol == UAC_VERSION_1) return parse_audio_processing_unit(state, unitid, p1); - else + else if (state->mixer->protocol == UAC_VERSION_2) return 0; /* FIXME - effect units not implemented yet */ + else + return parse_audio_feature_unit(state, unitid, p1); case UAC1_EXTENSION_UNIT: /* UAC2_PROCESSING_UNIT_V2 has the same value */ if (state->mixer->protocol == UAC_VERSION_1) @@ -2178,6 +2437,23 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) return 0; } +static int make_out_term(struct mixer_build state, int wTerminalType) +{ + struct uac3_output_terminal_descriptor *desc = NULL; + + if (wTerminalType == UAC_TERMINAL_STREAMING) + desc = &badd_baif_out_term_desc; + else { + desc = &badd_baof_out_term_desc; + desc->wTerminalType = wTerminalType; + } + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; + state.oterm.type = desc->wTerminalType; + state.oterm.name = desc->wTerminalDescrStr; + return parse_audio_unit(&state, desc->bSourceID); +} + /* * create mixer controls * @@ -2186,9 +2462,8 @@ static int snd_usb_mixer_dev_free(struct snd_device *device) static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) { struct mixer_build state; - int err; + int err = -EINVAL; const struct usbmix_ctl_map *map; - void *p; memset(&state, 0, sizeof(state)); state.chip = mixer->chip; @@ -2206,44 +2481,108 @@ static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer) } } - p = NULL; - while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, - mixer->hostif->extralen, - p, UAC_OUTPUT_TERMINAL)) != NULL) { - if (mixer->protocol == UAC_VERSION_1) { - struct uac1_output_terminal_descriptor *desc = p; - - if (desc->bLength < sizeof(*desc)) - continue; /* invalid descriptor? */ - /* mark terminal ID as visited */ - set_bit(desc->bTerminalID, state.unitbitmap); - state.oterm.id = desc->bTerminalID; - state.oterm.type = le16_to_cpu(desc->wTerminalType); - state.oterm.name = desc->iTerminal; - err = parse_audio_unit(&state, desc->bSourceID); + if (mixer->protocol == UAC_VERSION_3) { + struct usb_interface *usb_iface = + usb_ifnum_to_if(mixer->chip->dev, + get_iface_desc(mixer->hostif)->bInterfaceNumber); + struct usb_interface_assoc_descriptor *assoc = + usb_iface->intf_assoc; + + switch (assoc->bFunctionSubClass) { + case PROF_GENERIC_IO: { + if (assoc->bInterfaceCount == 0x02) { + if (get_endpoint(mixer->hostif, + 0)->bEndpointAddress | USB_DIR_IN) + err = make_out_term(state, + UAC_TERMINAL_STREAMING); + else + err = make_out_term(state, + UAC_OUTPUT_TERMINAL_UNDEFINED); + } else { + err = make_out_term(state, + UAC_OUTPUT_TERMINAL_UNDEFINED); + if (err < 0 && err != -EINVAL) + return err; + err = make_out_term(state, + UAC_TERMINAL_STREAMING); + } + break; + } + + case PROF_HEADPHONE: + err = make_out_term(state, + UAC_OUTPUT_TERMINAL_HEADPHONES); + break; + case PROF_SPEAKER: + err = make_out_term(state, UAC_OUTPUT_TERMINAL_SPEAKER); + break; + case PROF_MICROPHONE: + err = make_out_term(state, UAC_TERMINAL_STREAMING); + break; + case PROF_HEADSET: + case PROF_HEADSET_ADAPTER: + err = make_out_term(state, UAC_BIDIR_TERMINAL_HEADSET); if (err < 0 && err != -EINVAL) return err; - } else { /* UAC_VERSION_2 */ - struct uac2_output_terminal_descriptor *desc = p; - - if (desc->bLength < sizeof(*desc)) - continue; /* invalid descriptor? */ - /* mark terminal ID as visited */ - set_bit(desc->bTerminalID, state.unitbitmap); - state.oterm.id = desc->bTerminalID; - state.oterm.type = le16_to_cpu(desc->wTerminalType); - state.oterm.name = desc->iTerminal; - err = parse_audio_unit(&state, desc->bSourceID); + err = make_out_term(state, UAC_TERMINAL_STREAMING); + break; + case PROF_SPEAKERPHONE: + err = make_out_term(state, + UAC_BIDIR_TERMINAL_SPEAKERPHONE); if (err < 0 && err != -EINVAL) return err; + err = make_out_term(state, UAC_TERMINAL_STREAMING); + break; + } + if (err < 0 && err != -EINVAL) + return err; + } else { + void *p; + + p = NULL; + while ((p = snd_usb_find_csint_desc(mixer->hostif->extra, + mixer->hostif->extralen, p, + UAC_OUTPUT_TERMINAL)) != NULL) { + if (mixer->protocol == UAC_VERSION_1) { + struct uac1_output_terminal_descriptor *desc = + p; + + if (desc->bLength < sizeof(*desc)) + continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; + state.oterm.type = + le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0 && err != -EINVAL) + return err; + } else { /* UAC_VERSION_2 */ + struct uac2_output_terminal_descriptor *desc = + p; + + if (desc->bLength < sizeof(*desc)) + continue; /* invalid descriptor? */ + /* mark terminal ID as visited */ + set_bit(desc->bTerminalID, state.unitbitmap); + state.oterm.id = desc->bTerminalID; + state.oterm.type = + le16_to_cpu(desc->wTerminalType); + state.oterm.name = desc->iTerminal; + err = parse_audio_unit(&state, desc->bSourceID); + if (err < 0 && err != -EINVAL) + return err; - /* - * For UAC2, use the same approach to also add the - * clock selectors - */ - err = parse_audio_unit(&state, desc->bCSourceID); - if (err < 0 && err != -EINVAL) - return err; + /* + * For UAC2, use the same approach to also add + * the clock selectors + */ + err = parse_audio_unit(&state, + desc->bCSourceID); + if (err < 0 && err != -EINVAL) + return err; + } } } @@ -2478,6 +2817,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, case UAC_VERSION_2: mixer->protocol = UAC_VERSION_2; break; + case UAC_VERSION_3: + mixer->protocol = UAC_VERSION_3; + break; } if ((err = snd_usb_mixer_controls(mixer)) < 0 || diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 98b2c28ae46b..2bb503576134 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -20,6 +20,7 @@ #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> +#include <linux/usb/audio-v3.h> #include <sound/core.h> #include <sound/pcm.h> @@ -284,8 +285,6 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, 0 /* terminator */ }; struct snd_pcm_chmap_elem *chmap; - const unsigned int *maps; - int c; if (channels > ARRAY_SIZE(chmap->map)) return NULL; @@ -294,27 +293,42 @@ static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, if (!chmap) return NULL; - maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps; chmap->channels = channels; - c = 0; - if (bits) { - for (; bits && *maps; maps++, bits >>= 1) - if (bits & 1) - chmap->map[c++] = *maps; + if (protocol == UAC_VERSION_3) { + switch (channels) { + case 1: + chmap->map[0] = SNDRV_CHMAP_MONO; + break; + case 2: + chmap->map[0] = SNDRV_CHMAP_FL; + chmap->map[1] = SNDRV_CHMAP_FR; + break; + } } else { - /* If we're missing wChannelConfig, then guess something - to make sure the channel map is not skipped entirely */ - if (channels == 1) - chmap->map[c++] = SNDRV_CHMAP_MONO; - else - for (; c < channels && *maps; maps++) - chmap->map[c++] = *maps; + int c = 0; + const unsigned int *maps = + protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps; + + if (bits) { + for (; bits && *maps; maps++, bits >>= 1) + if (bits & 1) + chmap->map[c++] = *maps; + } else { + /* + * If we're missing wChannelConfig, then guess something + * to make sure the channel map is not skipped entirely + */ + if (channels == 1) + chmap->map[c++] = SNDRV_CHMAP_MONO; + else + for (; c < channels && *maps; maps++) + chmap->map[c++] = *maps; + } + for (; c < channels; c++) + chmap->map[c] = SNDRV_CHMAP_UNKNOWN; } - for (; c < channels; c++) - chmap->map[c] = SNDRV_CHMAP_UNKNOWN; - return chmap; } @@ -411,6 +425,9 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, struct usb_interface_descriptor *altsd = get_iface_desc(alts); int attributes = 0; + if (protocol == UAC_VERSION_3) + return 0; + csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT); /* Creamware Noah has this descriptor after the 2nd endpoint */ @@ -631,6 +648,39 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) iface_no, altno, as->bTerminalLink); continue; } + + case UAC_VERSION_3: { + int wMaxPacketSize; + + format = UAC_FORMAT_TYPE_I_PCM; + clock = BADD_CLOCK_SOURCE; + wMaxPacketSize = le16_to_cpu(get_endpoint(alts, 0) + ->wMaxPacketSize); + switch (wMaxPacketSize) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_MONO_24: { + num_channels = NUM_CHANNELS_MONO; + chconfig = BADD_CH_CONFIG_MONO; + goto populate_fp; + } + + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_24: { + num_channels = NUM_CHANNELS_STEREO; + chconfig = BADD_CH_CONFIG_STEREO; + goto populate_fp; + } + default: + dev_err(&dev->dev, + "%u:%d: invalid wMaxPacketSize\n", + iface_no, altno); + continue; + } + } } /* get format type */ @@ -664,6 +714,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) fp->maxpacksize * 2) continue; +populate_fp: fp = kzalloc(sizeof(*fp), GFP_KERNEL); if (! fp) { dev_err(&dev->dev, "cannot malloc\n"); diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c index a495a7f6cb22..14ea2239fe11 100644 --- a/sound/usb/usb_audio_qmi_svc.c +++ b/sound/usb/usb_audio_qmi_svc.c @@ -28,6 +28,7 @@ #include <linux/iommu.h> #include <linux/qcom_iommu.h> #include <linux/platform_device.h> +#include <linux/usb/audio-v3.h> #include "usbaudio.h" #include "card.h" @@ -428,12 +429,14 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, protocol = altsd->bInterfaceProtocol; /* get format type */ - fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, - UAC_FORMAT_TYPE); - if (!fmt) { - pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", __func__, - subs->interface, subs->altset_idx); - goto err; + if (protocol != UAC_VERSION_3) { + fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, + UAC_FORMAT_TYPE); + if (!fmt) { + pr_err("%s: %u:%d : no UAC_FORMAT_TYPE desc\n", + __func__, subs->interface, subs->altset_idx); + goto err; + } } if (!uadev[card_num].ctrl_intf) { @@ -441,12 +444,15 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, goto err; } - hdr_ptr = snd_usb_find_csint_desc(uadev[card_num].ctrl_intf->extra, - uadev[card_num].ctrl_intf->extralen, - NULL, UAC_HEADER); - if (!hdr_ptr) { - pr_err("%s: no UAC_HEADER desc\n", __func__); - goto err; + if (protocol != UAC_VERSION_3) { + hdr_ptr = snd_usb_find_csint_desc( + uadev[card_num].ctrl_intf->extra, + uadev[card_num].ctrl_intf->extralen, + NULL, UAC_HEADER); + if (!hdr_ptr) { + pr_err("%s: no UAC_HEADER desc\n", __func__); + goto err; + } } if (protocol == UAC_VERSION_1) { @@ -474,6 +480,25 @@ static int prepare_qmi_response(struct snd_usb_substream *subs, resp->usb_audio_spec_revision = ((struct uac2_ac_header_descriptor *)hdr_ptr)->bcdADC; resp->usb_audio_spec_revision_valid = 1; + } else if (protocol == UAC_VERSION_3) { + switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) { + case BADD_MAXPSIZE_SYNC_MONO_16: + case BADD_MAXPSIZE_SYNC_STEREO_16: + case BADD_MAXPSIZE_ASYNC_MONO_16: + case BADD_MAXPSIZE_ASYNC_STEREO_16: { + resp->usb_audio_subslot_size = SUBSLOTSIZE_16_BIT; + break; + } + + case BADD_MAXPSIZE_SYNC_MONO_24: + case BADD_MAXPSIZE_SYNC_STEREO_24: + case BADD_MAXPSIZE_ASYNC_MONO_24: + case BADD_MAXPSIZE_ASYNC_STEREO_24: { + resp->usb_audio_subslot_size = SUBSLOTSIZE_24_BIT; + break; + } + } + resp->usb_audio_subslot_size_valid = 1; } else { pr_err("%s: unknown protocol version %x\n", __func__, protocol); goto err; |