Merge branch 'for-linus' into for-next
Back-merge of 5.11 devel branch for more works on USB-audio. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
commit
c09e28cd12
|
@ -1501,7 +1501,7 @@ Module for Digigram miXart8 sound cards.
|
|||
|
||||
This module supports multiple cards.
|
||||
Note: One miXart8 board will be represented as 4 alsa cards.
|
||||
See MIXART.txt for details.
|
||||
See Documentation/sound/cards/mixart.rst for details.
|
||||
|
||||
When the driver is compiled as a module and the hotplug firmware
|
||||
is supported, the firmware data is loaded via hotplug automatically.
|
||||
|
|
|
@ -1256,6 +1256,8 @@ static struct tegra_clk_init_table init_table[] __initdata = {
|
|||
{ TEGRA30_CLK_I2S3_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
|
||||
{ TEGRA30_CLK_I2S4_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
|
||||
{ TEGRA30_CLK_VIMCLK_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
|
||||
{ TEGRA30_CLK_HDA, TEGRA30_CLK_PLL_P, 102000000, 0 },
|
||||
{ TEGRA30_CLK_HDA2CODEC_2X, TEGRA30_CLK_PLL_P, 48000000, 0 },
|
||||
/* must be the last entry */
|
||||
{ TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 },
|
||||
};
|
||||
|
|
|
@ -611,7 +611,8 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in
|
|||
|
||||
if (info->is_midi) {
|
||||
struct midi_info minf;
|
||||
snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf);
|
||||
if (snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf))
|
||||
return -ENXIO;
|
||||
inf->synth_type = SYNTH_TYPE_MIDI;
|
||||
inf->synth_subtype = 0;
|
||||
inf->nr_voices = 16;
|
||||
|
|
|
@ -88,7 +88,7 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
|
|||
|
||||
/* Set interval to next transaction. */
|
||||
ff->next_ktime[port] = ktime_add_ns(ktime_get(),
|
||||
ff->rx_bytes[port] * 8 * NSEC_PER_SEC / 31250);
|
||||
ff->rx_bytes[port] * 8 * (NSEC_PER_SEC / 31250));
|
||||
|
||||
if (quad_count == 1)
|
||||
tcode = TCODE_WRITE_QUADLET_REQUEST;
|
||||
|
|
|
@ -209,7 +209,7 @@ static void midi_port_work(struct work_struct *work)
|
|||
|
||||
/* Set interval to next transaction. */
|
||||
port->next_ktime = ktime_add_ns(ktime_get(),
|
||||
port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
|
||||
port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
|
||||
|
||||
/* Start this transaction. */
|
||||
port->idling = false;
|
||||
|
|
|
@ -2480,6 +2480,9 @@ static const struct pci_device_id azx_ids[] = {
|
|||
/* CometLake-S */
|
||||
{ PCI_DEVICE(0x8086, 0xa3f0),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* CometLake-R */
|
||||
{ PCI_DEVICE(0x8086, 0xf0c8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* Icelake */
|
||||
{ PCI_DEVICE(0x8086, 0x34c8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
|
@ -2503,6 +2506,9 @@ static const struct pci_device_id azx_ids[] = {
|
|||
/* Alderlake-S */
|
||||
{ PCI_DEVICE(0x8086, 0x7ad0),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* Alderlake-P */
|
||||
{ PCI_DEVICE(0x8086, 0x51c8),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
/* Elkhart Lake */
|
||||
{ PCI_DEVICE(0x8086, 0x4b55),
|
||||
.driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
|
||||
|
@ -2594,7 +2600,8 @@ static const struct pci_device_id azx_ids[] = {
|
|||
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
|
||||
/* ATI HDMI */
|
||||
{ PCI_DEVICE(0x1002, 0x0002),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
|
||||
AZX_DCAPS_PM_RUNTIME },
|
||||
{ PCI_DEVICE(0x1002, 0x1308),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
||||
{ PCI_DEVICE(0x1002, 0x157a),
|
||||
|
@ -2656,9 +2663,11 @@ static const struct pci_device_id azx_ids[] = {
|
|||
{ PCI_DEVICE(0x1002, 0xaab0),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
||||
{ PCI_DEVICE(0x1002, 0xaac0),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
|
||||
AZX_DCAPS_PM_RUNTIME },
|
||||
{ PCI_DEVICE(0x1002, 0xaac8),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
|
||||
AZX_DCAPS_PM_RUNTIME },
|
||||
{ PCI_DEVICE(0x1002, 0xaad8),
|
||||
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
|
||||
AZX_DCAPS_PM_RUNTIME },
|
||||
|
|
|
@ -388,7 +388,7 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
|
|||
* in powers of 2, next available ratio is 16 which can be
|
||||
* used as a limiting factor here.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "nvidia,tegra194-hda"))
|
||||
if (of_device_is_compatible(np, "nvidia,tegra30-hda"))
|
||||
chip->bus.core.sdo_limit = 16;
|
||||
|
||||
/* codec detection */
|
||||
|
|
|
@ -4346,6 +4346,7 @@ 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(0x8086281c, "Alderlake-P 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),
|
||||
|
|
|
@ -6371,6 +6371,7 @@ enum {
|
|||
ALC256_FIXUP_HP_HEADSET_MIC,
|
||||
ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
|
||||
ALC282_FIXUP_ACER_DISABLE_LINEOUT,
|
||||
ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST,
|
||||
};
|
||||
|
||||
static const struct hda_fixup alc269_fixups[] = {
|
||||
|
@ -7808,6 +7809,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_HEADSET_MODE
|
||||
},
|
||||
[ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_limit_int_mic_boost,
|
||||
.chained = true,
|
||||
.chain_id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
||||
|
@ -7826,6 +7833,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
|
||||
SND_PCI_QUIRK(0x1025, 0x1094, "Acer Aspire E5-575T", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST),
|
||||
SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK),
|
||||
|
|
|
@ -113,6 +113,7 @@ static struct via_spec *via_new_spec(struct hda_codec *codec)
|
|||
spec->codec_type = VT1708S;
|
||||
spec->gen.indep_hp = 1;
|
||||
spec->gen.keep_eapd_on = 1;
|
||||
spec->gen.dac_min_mute = 1;
|
||||
spec->gen.pcm_playback_hook = via_playback_pcm_hook;
|
||||
spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
|
||||
codec->power_save_node = 1;
|
||||
|
|
|
@ -450,10 +450,8 @@ lookup_device_name(u32 id)
|
|||
static void snd_usb_audio_free(struct snd_card *card)
|
||||
{
|
||||
struct snd_usb_audio *chip = card->private_data;
|
||||
struct snd_usb_endpoint *ep, *n;
|
||||
|
||||
list_for_each_entry_safe(ep, n, &chip->ep_list, list)
|
||||
snd_usb_endpoint_free(ep);
|
||||
snd_usb_endpoint_free_all(chip);
|
||||
|
||||
mutex_destroy(&chip->mutex);
|
||||
if (!atomic_read(&chip->shutdown))
|
||||
|
@ -610,6 +608,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
|
|||
chip->usb_id = usb_id;
|
||||
INIT_LIST_HEAD(&chip->pcm_list);
|
||||
INIT_LIST_HEAD(&chip->ep_list);
|
||||
INIT_LIST_HEAD(&chip->iface_ref_list);
|
||||
INIT_LIST_HEAD(&chip->midi_list);
|
||||
INIT_LIST_HEAD(&chip->mixer_list);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ struct audioformat {
|
|||
unsigned int frame_size; /* samples per frame for non-audio */
|
||||
unsigned char iface; /* interface number */
|
||||
unsigned char altsetting; /* corresponding alternate setting */
|
||||
unsigned char ep_idx; /* endpoint array index */
|
||||
unsigned char altset_idx; /* array index of altenate setting */
|
||||
unsigned char attributes; /* corresponding attributes of cs endpoint */
|
||||
unsigned char endpoint; /* endpoint */
|
||||
|
@ -42,6 +43,7 @@ struct audioformat {
|
|||
};
|
||||
|
||||
struct snd_usb_substream;
|
||||
struct snd_usb_iface_ref;
|
||||
struct snd_usb_endpoint;
|
||||
struct snd_usb_power_domain;
|
||||
|
||||
|
@ -58,6 +60,7 @@ struct snd_urb_ctx {
|
|||
|
||||
struct snd_usb_endpoint {
|
||||
struct snd_usb_audio *chip;
|
||||
struct snd_usb_iface_ref *iface_ref;
|
||||
|
||||
int opened; /* open refcount; protect with chip->mutex */
|
||||
atomic_t running; /* running status */
|
||||
|
|
|
@ -485,18 +485,9 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip,
|
|||
const struct audioformat *fmt, int rate)
|
||||
{
|
||||
struct usb_device *dev = chip->dev;
|
||||
struct usb_host_interface *alts;
|
||||
unsigned int ep;
|
||||
unsigned char data[3];
|
||||
int err, crate;
|
||||
|
||||
alts = snd_usb_get_host_interface(chip, fmt->iface, fmt->altsetting);
|
||||
if (!alts)
|
||||
return -EINVAL;
|
||||
if (get_iface_desc(alts)->bNumEndpoints < 1)
|
||||
return -EINVAL;
|
||||
ep = get_endpoint(alts, 0)->bEndpointAddress;
|
||||
|
||||
/* if endpoint doesn't have sampling rate control, bail out */
|
||||
if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
|
||||
return 0;
|
||||
|
@ -506,11 +497,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip,
|
|||
data[2] = rate >> 16;
|
||||
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_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data));
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
|
||||
fmt->endpoint, data, sizeof(data));
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
|
||||
fmt->iface, fmt->altsetting, rate, ep);
|
||||
fmt->iface, fmt->altsetting, rate, fmt->endpoint);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -524,11 +515,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip,
|
|||
|
||||
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data));
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
|
||||
fmt->endpoint, data, sizeof(data));
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
|
||||
fmt->iface, fmt->altsetting, ep);
|
||||
fmt->iface, fmt->altsetting, fmt->endpoint);
|
||||
chip->sample_rate_read_error++;
|
||||
return 0; /* some devices don't support reading */
|
||||
}
|
||||
|
|
|
@ -24,6 +24,14 @@
|
|||
#define EP_FLAG_RUNNING 1
|
||||
#define EP_FLAG_STOPPING 2
|
||||
|
||||
/* interface refcounting */
|
||||
struct snd_usb_iface_ref {
|
||||
unsigned char iface;
|
||||
bool need_setup;
|
||||
int opened;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/*
|
||||
* snd_usb_endpoint is a model that abstracts everything related to an
|
||||
* USB endpoint and its streaming.
|
||||
|
@ -488,6 +496,28 @@ exit_clear:
|
|||
clear_bit(ctx->index, &ep->active_mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find or create a refcount object for the given interface
|
||||
*
|
||||
* The objects are released altogether in snd_usb_endpoint_free_all()
|
||||
*/
|
||||
static struct snd_usb_iface_ref *
|
||||
iface_ref_find(struct snd_usb_audio *chip, int iface)
|
||||
{
|
||||
struct snd_usb_iface_ref *ip;
|
||||
|
||||
list_for_each_entry(ip, &chip->iface_ref_list, list)
|
||||
if (ip->iface == iface)
|
||||
return ip;
|
||||
|
||||
ip = kzalloc(sizeof(*ip), GFP_KERNEL);
|
||||
if (!ip)
|
||||
return NULL;
|
||||
ip->iface = iface;
|
||||
list_add_tail(&ip->list, &chip->iface_ref_list);
|
||||
return ip;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the existing endpoint object corresponding EP
|
||||
* Returns NULL if not present.
|
||||
|
@ -520,8 +550,8 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, int ep_num)
|
|||
*
|
||||
* 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().
|
||||
* New endpoints will be added to chip->ep_list and freed by
|
||||
* calling snd_usb_endpoint_free_all().
|
||||
*
|
||||
* For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
|
||||
* bNumEndpoints > 1 beforehand.
|
||||
|
@ -653,11 +683,17 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
|
|||
} else {
|
||||
ep->iface = fp->iface;
|
||||
ep->altsetting = fp->altsetting;
|
||||
ep->ep_idx = 0;
|
||||
ep->ep_idx = fp->ep_idx;
|
||||
}
|
||||
usb_audio_dbg(chip, "Open EP 0x%x, iface=%d:%d, idx=%d\n",
|
||||
ep_num, ep->iface, ep->altsetting, ep->ep_idx);
|
||||
|
||||
ep->iface_ref = iface_ref_find(chip, ep->iface);
|
||||
if (!ep->iface_ref) {
|
||||
ep = NULL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ep->cur_audiofmt = fp;
|
||||
ep->cur_channels = fp->channels;
|
||||
ep->cur_rate = params_rate(params);
|
||||
|
@ -681,6 +717,11 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
|
|||
ep->implicit_fb_sync);
|
||||
|
||||
} else {
|
||||
if (WARN_ON(!ep->iface_ref)) {
|
||||
ep = NULL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!endpoint_compatible(ep, fp, params)) {
|
||||
usb_audio_err(chip, "Incompatible EP setup for 0x%x\n",
|
||||
ep_num);
|
||||
|
@ -692,6 +733,9 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
|
|||
ep_num, ep->opened);
|
||||
}
|
||||
|
||||
if (!ep->iface_ref->opened++)
|
||||
ep->iface_ref->need_setup = true;
|
||||
|
||||
ep->opened++;
|
||||
|
||||
unlock:
|
||||
|
@ -760,12 +804,16 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
|
|||
mutex_lock(&chip->mutex);
|
||||
usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n",
|
||||
ep->ep_num, ep->opened);
|
||||
if (!--ep->opened) {
|
||||
|
||||
if (!--ep->iface_ref->opened)
|
||||
endpoint_set_interface(chip, ep, false);
|
||||
|
||||
if (!--ep->opened) {
|
||||
ep->iface = 0;
|
||||
ep->altsetting = 0;
|
||||
ep->cur_audiofmt = NULL;
|
||||
ep->cur_rate = 0;
|
||||
ep->iface_ref = NULL;
|
||||
usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
|
||||
}
|
||||
mutex_unlock(&chip->mutex);
|
||||
|
@ -775,6 +823,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
|
|||
void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep)
|
||||
{
|
||||
ep->need_setup = true;
|
||||
if (ep->iface_ref)
|
||||
ep->iface_ref->need_setup = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1195,11 +1245,22 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
|
|||
int err = 0;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
if (WARN_ON(!ep->iface_ref))
|
||||
goto unlock;
|
||||
if (!ep->need_setup)
|
||||
goto unlock;
|
||||
|
||||
/* No need to (re-)configure the sync EP belonging to the same altset */
|
||||
if (ep->ep_idx) {
|
||||
/* If the interface has been already set up, just set EP parameters */
|
||||
if (!ep->iface_ref->need_setup) {
|
||||
/* sample rate setup of UAC1 is per endpoint, and we need
|
||||
* to update at each EP configuration
|
||||
*/
|
||||
if (ep->cur_audiofmt->protocol == UAC_VERSION_1) {
|
||||
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;
|
||||
|
@ -1242,6 +1303,8 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
ep->iface_ref->need_setup = false;
|
||||
|
||||
done:
|
||||
ep->need_setup = false;
|
||||
err = 1;
|
||||
|
@ -1387,15 +1450,21 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep)
|
|||
}
|
||||
|
||||
/**
|
||||
* snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
|
||||
* snd_usb_endpoint_free_all: Free the resources of an snd_usb_endpoint
|
||||
* @card: The chip
|
||||
*
|
||||
* @ep: the endpoint to free
|
||||
*
|
||||
* This free all resources of the given ep.
|
||||
* This free all endpoints and those resources
|
||||
*/
|
||||
void snd_usb_endpoint_free(struct snd_usb_endpoint *ep)
|
||||
void snd_usb_endpoint_free_all(struct snd_usb_audio *chip)
|
||||
{
|
||||
kfree(ep);
|
||||
struct snd_usb_endpoint *ep, *en;
|
||||
struct snd_usb_iface_ref *ip, *in;
|
||||
|
||||
list_for_each_entry_safe(ep, en, &chip->ep_list, list)
|
||||
kfree(ep);
|
||||
|
||||
list_for_each_entry_safe(ip, in, &chip->iface_ref_list, list)
|
||||
kfree(ip);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -42,7 +42,7 @@ 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_release(struct snd_usb_endpoint *ep);
|
||||
void snd_usb_endpoint_free(struct snd_usb_endpoint *ep);
|
||||
void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
|
||||
|
||||
int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
|
||||
int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
|
||||
|
|
|
@ -58,8 +58,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
|
|||
IMPLICIT_FB_FIXED_DEV(0x0499, 0x172f, 0x81, 2), /* Steinberg UR22C */
|
||||
IMPLICIT_FB_FIXED_DEV(0x0d9a, 0x00df, 0x81, 2), /* RTX6001 */
|
||||
IMPLICIT_FB_FIXED_DEV(0x22f0, 0x0006, 0x81, 3), /* Allen&Heath Qu-16 */
|
||||
IMPLICIT_FB_FIXED_DEV(0x2b73, 0x000a, 0x82, 0), /* Pioneer DJ DJM-900NXS2 */
|
||||
IMPLICIT_FB_FIXED_DEV(0x2b73, 0x0017, 0x82, 0), /* Pioneer DJ DJM-250MK2 */
|
||||
IMPLICIT_FB_FIXED_DEV(0x1686, 0xf029, 0x82, 2), /* Zoom UAC-2 */
|
||||
IMPLICIT_FB_FIXED_DEV(0x2466, 0x8003, 0x86, 2), /* Fractal Audio Axe-Fx II */
|
||||
IMPLICIT_FB_FIXED_DEV(0x0499, 0x172a, 0x86, 2), /* Yamaha MODX */
|
||||
|
@ -100,7 +98,7 @@ static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = {
|
|||
/* 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,
|
||||
int ep, int ep_idx, int ifnum,
|
||||
const struct usb_host_interface *alts)
|
||||
{
|
||||
struct usb_interface *iface;
|
||||
|
@ -115,7 +113,7 @@ static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip,
|
|||
fmt->sync_ep = ep;
|
||||
fmt->sync_iface = ifnum;
|
||||
fmt->sync_altsetting = alts->desc.bAlternateSetting;
|
||||
fmt->sync_ep_idx = 0;
|
||||
fmt->sync_ep_idx = ep_idx;
|
||||
fmt->implicit_fb = 1;
|
||||
usb_audio_dbg(chip,
|
||||
"%d:%d: added %s implicit_fb sync_ep %x, iface %d:%d\n",
|
||||
|
@ -147,7 +145,7 @@ static int add_generic_uac2_implicit_fb(struct snd_usb_audio *chip,
|
|||
(epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB)
|
||||
return 0;
|
||||
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress,
|
||||
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
|
||||
ifnum, alts);
|
||||
}
|
||||
|
||||
|
@ -173,10 +171,33 @@ static int add_roland_implicit_fb(struct snd_usb_audio *chip,
|
|||
(epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB)
|
||||
return 0;
|
||||
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress,
|
||||
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
|
||||
ifnum, alts);
|
||||
}
|
||||
|
||||
/* Playback and capture EPs on Pioneer devices share the same iface/altset,
|
||||
* but they don't seem working with the implicit fb mode well, hence we
|
||||
* just return as if the sync were already set up.
|
||||
*/
|
||||
static int skip_pioneer_sync_ep(struct snd_usb_audio *chip,
|
||||
struct audioformat *fmt,
|
||||
struct usb_host_interface *alts)
|
||||
{
|
||||
struct usb_endpoint_descriptor *epd;
|
||||
|
||||
if (alts->desc.bNumEndpoints != 2)
|
||||
return 0;
|
||||
|
||||
epd = get_endpoint(alts, 1);
|
||||
if (!usb_endpoint_is_isoc_in(epd) ||
|
||||
(epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC ||
|
||||
((epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
|
||||
USB_ENDPOINT_USAGE_DATA &&
|
||||
(epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB))
|
||||
return 0;
|
||||
return 1; /* don't handle with the implicit fb, just skip sync EP */
|
||||
}
|
||||
|
||||
static int __add_generic_implicit_fb(struct snd_usb_audio *chip,
|
||||
struct audioformat *fmt,
|
||||
|
@ -197,7 +218,7 @@ static int __add_generic_implicit_fb(struct snd_usb_audio *chip,
|
|||
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,
|
||||
return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
|
||||
iface, alts);
|
||||
}
|
||||
|
||||
|
@ -250,7 +271,7 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip,
|
|||
case IMPLICIT_FB_NONE:
|
||||
return 0; /* No quirk */
|
||||
case IMPLICIT_FB_FIXED:
|
||||
return add_implicit_fb_sync_ep(chip, fmt, p->ep_num,
|
||||
return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0,
|
||||
p->iface, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -278,6 +299,14 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip,
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Pioneer devices with vendor spec class */
|
||||
if (attr == USB_ENDPOINT_SYNC_ASYNC &&
|
||||
alts->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
|
||||
USB_ID_VENDOR(chip->usb_id) == 0x2b73 /* Pioneer */) {
|
||||
if (skip_pioneer_sync_ep(chip, fmt, alts))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Try the generic implicit fb if available */
|
||||
if (chip->generic_implicit_fb)
|
||||
return add_generic_implicit_fb(chip, fmt, alts);
|
||||
|
@ -295,8 +324,8 @@ static int audioformat_capture_quirk(struct snd_usb_audio *chip,
|
|||
|
||||
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 add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0,
|
||||
p->iface, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -378,20 +407,19 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
|
|||
int stream)
|
||||
{
|
||||
struct snd_usb_substream *subs;
|
||||
const struct audioformat *fp, *sync_fmt;
|
||||
const struct audioformat *fp, *sync_fmt = NULL;
|
||||
int score, high_score;
|
||||
|
||||
/* When sharing the same altset, use the original audioformat */
|
||||
/* Use the original audioformat as fallback for the shared altset */
|
||||
if (target->iface == target->sync_iface &&
|
||||
target->altsetting == target->sync_altsetting)
|
||||
return target;
|
||||
sync_fmt = target;
|
||||
|
||||
subs = find_matching_substream(chip, stream, target->sync_ep,
|
||||
target->fmt_type);
|
||||
if (!subs)
|
||||
return NULL;
|
||||
return sync_fmt;
|
||||
|
||||
sync_fmt = NULL;
|
||||
high_score = 0;
|
||||
list_for_each_entry(fp, &subs->fmt_list, list) {
|
||||
score = match_endpoint_audioformats(subs, fp,
|
||||
|
|
171
sound/usb/pcm.c
171
sound/usb/pcm.c
|
@ -663,7 +663,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs,
|
|||
check_fmts.bits[1] = (u32)(fp->formats >> 32);
|
||||
snd_mask_intersect(&check_fmts, fmts);
|
||||
if (snd_mask_empty(&check_fmts)) {
|
||||
hwc_debug(" > check: no supported format %d\n", fp->format);
|
||||
hwc_debug(" > check: no supported format 0x%llx\n", fp->formats);
|
||||
return 0;
|
||||
}
|
||||
/* check the channels */
|
||||
|
@ -775,24 +775,11 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
|
|||
return apply_hw_params_minmax(it, rmin, rmax);
|
||||
}
|
||||
|
||||
static int hw_rule_format(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
static int apply_hw_params_format_bits(struct snd_mask *fmt, u64 fbits)
|
||||
{
|
||||
struct snd_usb_substream *subs = rule->private;
|
||||
const struct audioformat *fp;
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
u64 fbits;
|
||||
u32 oldbits[2];
|
||||
int changed;
|
||||
|
||||
hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
|
||||
fbits = 0;
|
||||
list_for_each_entry(fp, &subs->fmt_list, list) {
|
||||
if (!hw_check_valid_format(subs, params, fp))
|
||||
continue;
|
||||
fbits |= fp->formats;
|
||||
}
|
||||
|
||||
oldbits[0] = fmt->bits[0];
|
||||
oldbits[1] = fmt->bits[1];
|
||||
fmt->bits[0] &= (u32)fbits;
|
||||
|
@ -806,6 +793,24 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
|
|||
return changed;
|
||||
}
|
||||
|
||||
static int hw_rule_format(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_usb_substream *subs = rule->private;
|
||||
const struct audioformat *fp;
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
u64 fbits;
|
||||
|
||||
hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
|
||||
fbits = 0;
|
||||
list_for_each_entry(fp, &subs->fmt_list, list) {
|
||||
if (!hw_check_valid_format(subs, params, fp))
|
||||
continue;
|
||||
fbits |= fp->formats;
|
||||
}
|
||||
return apply_hw_params_format_bits(fmt, fbits);
|
||||
}
|
||||
|
||||
static int hw_rule_period_time(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
|
@ -833,64 +838,92 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params,
|
|||
return apply_hw_params_minmax(it, pmin, UINT_MAX);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
/* get the EP or the sync EP for implicit fb when it's already set up */
|
||||
static const struct snd_usb_endpoint *
|
||||
get_sync_ep_from_substream(struct snd_usb_substream *subs)
|
||||
{
|
||||
struct snd_usb_audio *chip = subs->stream->chip;
|
||||
struct snd_usb_endpoint *ep;
|
||||
const struct audioformat *fp;
|
||||
int err;
|
||||
const struct snd_usb_endpoint *ep;
|
||||
|
||||
list_for_each_entry(fp, &subs->fmt_list, list) {
|
||||
ep = snd_usb_get_endpoint(chip, fp->endpoint);
|
||||
if (ep && ep->cur_rate)
|
||||
goto found;
|
||||
return ep;
|
||||
if (!fp->implicit_fb)
|
||||
continue;
|
||||
/* for the implicit fb, check the sync ep as well */
|
||||
ep = snd_usb_get_endpoint(chip, fp->sync_ep);
|
||||
if (ep && ep->cur_rate)
|
||||
goto found;
|
||||
return ep;
|
||||
}
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
found:
|
||||
if (!find_format(&subs->fmt_list, ep->cur_format, ep->cur_rate,
|
||||
ep->cur_channels, false, NULL)) {
|
||||
usb_audio_dbg(chip, "EP 0x%x being used, but not applicable\n",
|
||||
ep->ep_num);
|
||||
/* additional hw constraints for implicit feedback mode */
|
||||
static int hw_rule_format_implicit_fb(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_usb_substream *subs = rule->private;
|
||||
const struct snd_usb_endpoint *ep;
|
||||
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
|
||||
|
||||
ep = get_sync_ep_from_substream(subs);
|
||||
if (!ep)
|
||||
return 0;
|
||||
}
|
||||
|
||||
usb_audio_dbg(chip, "EP 0x%x being used, using fixed params:\n",
|
||||
ep->ep_num);
|
||||
usb_audio_dbg(chip, "rate=%d, period_size=%d, periods=%d\n",
|
||||
ep->cur_rate, ep->cur_period_frames,
|
||||
ep->cur_buffer_periods);
|
||||
hwc_debug("applying %s\n", __func__);
|
||||
return apply_hw_params_format_bits(fmt, 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.rates = SNDRV_PCM_RATE_KNOT;
|
||||
runtime->hw.periods_min = runtime->hw.periods_max =
|
||||
ep->cur_buffer_periods;
|
||||
static int hw_rule_rate_implicit_fb(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_usb_substream *subs = rule->private;
|
||||
const struct snd_usb_endpoint *ep;
|
||||
struct snd_interval *it;
|
||||
|
||||
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;
|
||||
ep = get_sync_ep_from_substream(subs);
|
||||
if (!ep)
|
||||
return 0;
|
||||
|
||||
err = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
ep->cur_period_frames,
|
||||
ep->cur_period_frames);
|
||||
if (err < 0)
|
||||
return err;
|
||||
hwc_debug("applying %s\n", __func__);
|
||||
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
return apply_hw_params_minmax(it, ep->cur_rate, ep->cur_rate);
|
||||
}
|
||||
|
||||
return 1; /* notify the finding */
|
||||
static int hw_rule_period_size_implicit_fb(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_usb_substream *subs = rule->private;
|
||||
const struct snd_usb_endpoint *ep;
|
||||
struct snd_interval *it;
|
||||
|
||||
ep = get_sync_ep_from_substream(subs);
|
||||
if (!ep)
|
||||
return 0;
|
||||
|
||||
hwc_debug("applying %s\n", __func__);
|
||||
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
|
||||
return apply_hw_params_minmax(it, ep->cur_period_frames,
|
||||
ep->cur_period_frames);
|
||||
}
|
||||
|
||||
static int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
struct snd_usb_substream *subs = rule->private;
|
||||
const struct snd_usb_endpoint *ep;
|
||||
struct snd_interval *it;
|
||||
|
||||
ep = get_sync_ep_from_substream(subs);
|
||||
if (!ep)
|
||||
return 0;
|
||||
|
||||
hwc_debug("applying %s\n", __func__);
|
||||
it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
return apply_hw_params_minmax(it, ep->cur_buffer_periods,
|
||||
ep->cur_buffer_periods);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -899,20 +932,11 @@ 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;
|
||||
const struct audioformat *fp;
|
||||
unsigned int pt, ptmin;
|
||||
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;
|
||||
|
@ -964,7 +988,6 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
|
|||
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,
|
||||
|
@ -993,6 +1016,28 @@ add_extra_rules:
|
|||
return err;
|
||||
}
|
||||
|
||||
/* additional hw constraints for implicit fb */
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
|
||||
hw_rule_format_implicit_fb, subs,
|
||||
SNDRV_PCM_HW_PARAM_FORMAT, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
hw_rule_rate_implicit_fb, subs,
|
||||
SNDRV_PCM_HW_PARAM_RATE, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
hw_rule_period_size_implicit_fb, subs,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS,
|
||||
hw_rule_periods_implicit_fb, subs,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS, -1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -3362,6 +3362,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
|
|||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.endpoint = 0x86,
|
||||
.ep_idx = 1,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC|
|
||||
USB_ENDPOINT_SYNC_ASYNC|
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB,
|
||||
|
@ -3450,6 +3451,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
|
|||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.endpoint = 0x82,
|
||||
.ep_idx = 1,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC|
|
||||
USB_ENDPOINT_SYNC_ASYNC|
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB,
|
||||
|
@ -3506,6 +3508,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
|
|||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.endpoint = 0x82,
|
||||
.ep_idx = 1,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC|
|
||||
USB_ENDPOINT_SYNC_ASYNC|
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB,
|
||||
|
@ -3562,6 +3565,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
|
|||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.endpoint = 0x82,
|
||||
.ep_idx = 1,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC|
|
||||
USB_ENDPOINT_SYNC_ASYNC|
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB,
|
||||
|
@ -3619,6 +3623,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
|
|||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.endpoint = 0x82,
|
||||
.ep_idx = 1,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC|
|
||||
USB_ENDPOINT_SYNC_ASYNC|
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB,
|
||||
|
@ -3679,6 +3684,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
|
|||
.altsetting = 1,
|
||||
.altset_idx = 1,
|
||||
.endpoint = 0x82,
|
||||
.ep_idx = 1,
|
||||
.ep_attr = USB_ENDPOINT_XFER_ISOC|
|
||||
USB_ENDPOINT_SYNC_ASYNC|
|
||||
USB_ENDPOINT_USAGE_IMPLICIT_FB,
|
||||
|
|
|
@ -120,6 +120,40 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* create the audio stream and the corresponding endpoints from the fixed
|
||||
* audioformat object; this is used for quirks with the fixed EPs
|
||||
*/
|
||||
static int add_audio_stream_from_fixed_fmt(struct snd_usb_audio *chip,
|
||||
struct audioformat *fp)
|
||||
{
|
||||
int stream, err;
|
||||
|
||||
stream = (fp->endpoint & USB_DIR_IN) ?
|
||||
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
||||
|
||||
snd_usb_audioformat_set_sync_ep(chip, fp);
|
||||
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a stream for an endpoint/altsetting without proper descriptors
|
||||
*/
|
||||
|
@ -131,8 +165,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|||
struct audioformat *fp;
|
||||
struct usb_host_interface *alts;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
int stream, err;
|
||||
unsigned *rate_table = NULL;
|
||||
int err;
|
||||
|
||||
fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
|
||||
if (!fp)
|
||||
|
@ -153,11 +187,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|||
fp->rate_table = rate_table;
|
||||
}
|
||||
|
||||
stream = (fp->endpoint & USB_DIR_IN)
|
||||
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
|
||||
fp->altset_idx >= iface->num_altsetting) {
|
||||
err = -EINVAL;
|
||||
|
@ -165,7 +194,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|||
}
|
||||
alts = &iface->altsetting[fp->altset_idx];
|
||||
altsd = get_iface_desc(alts);
|
||||
if (altsd->bNumEndpoints < 1) {
|
||||
if (altsd->bNumEndpoints <= fp->ep_idx) {
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -175,7 +204,14 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|||
if (fp->datainterval == 0)
|
||||
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
|
||||
if (fp->maxpacksize == 0)
|
||||
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
||||
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, fp->ep_idx)->wMaxPacketSize);
|
||||
if (!fp->fmt_type)
|
||||
fp->fmt_type = UAC_FORMAT_TYPE_I;
|
||||
|
||||
err = add_audio_stream_from_fixed_fmt(chip, fp);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
usb_set_interface(chip->dev, fp->iface, 0);
|
||||
snd_usb_init_pitch(chip, fp);
|
||||
snd_usb_init_sample_rate(chip, fp, fp->rate_max);
|
||||
|
@ -417,7 +453,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
|
|||
struct usb_host_interface *alts;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
struct audioformat *fp;
|
||||
int stream, err;
|
||||
int err;
|
||||
|
||||
/* both PCM and MIDI interfaces have 2 or more altsettings */
|
||||
if (iface->num_altsetting < 2)
|
||||
|
@ -482,9 +518,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
stream = (fp->endpoint & USB_DIR_IN)
|
||||
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
err = add_audio_stream_from_fixed_fmt(chip, fp);
|
||||
if (err < 0) {
|
||||
list_del(&fp->list); /* unlink for avoiding double-free */
|
||||
kfree(fp);
|
||||
|
@ -1436,30 +1470,6 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs,
|
|||
subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pioneer DJ DJM-900NXS2
|
||||
* Device needs to know the sample rate each time substream is started
|
||||
*/
|
||||
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] = cur_rate & 0xff;
|
||||
sr[1] = (cur_rate >> 8) & 0xff;
|
||||
sr[2] = (cur_rate >> 16) & 0xff;
|
||||
|
||||
/* Configure device */
|
||||
usb_set_interface(subs->dev, 0, 1);
|
||||
snd_usb_ctl_msg(subs->stream->chip->dev,
|
||||
usb_rcvctrlpipe(subs->stream->chip->dev, 0),
|
||||
0x01, 0x22, 0x0100, 0x0082, &sr, 0x0003);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
|
||||
const struct audioformat *fmt)
|
||||
{
|
||||
|
@ -1470,10 +1480,6 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
|
|||
case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */
|
||||
set_format_emu_quirk(subs, fmt);
|
||||
break;
|
||||
case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
|
||||
case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */
|
||||
pioneer_djm_set_format_quirk(subs);
|
||||
break;
|
||||
case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */
|
||||
subs->stream_offset_adj = 2;
|
||||
break;
|
||||
|
|
|
@ -44,6 +44,7 @@ struct snd_usb_audio {
|
|||
|
||||
struct list_head pcm_list; /* list of pcm streams */
|
||||
struct list_head ep_list; /* list of audio-related endpoints */
|
||||
struct list_head iface_ref_list; /* list of interface refcounts */
|
||||
int pcm_devs;
|
||||
|
||||
struct list_head midi_list; /* list of midi interfaces */
|
||||
|
|
Loading…
Reference in New Issue