Commit 690a863f authored by Torstein Hegge's avatar Torstein Hegge Committed by Takashi Iwai

ALSA: usb: Work around CM6631 sample rate change bug

The C-Media CM6631 USB receiver doesn't respond to changes in sample rate
while the interface is active. The same behavior is observed in other UAC2
hardware like the VIA VT1731.

Reset the interface after setting the sampling frequency on sample rate
changes, to ensure that the sample rate set by snd_usb_init_sample_rate() is
used. Otherwise, the device will try to use the sample rate of the previous
stream, causing distorted sound on sample rate changes.

The reset is performed for all UAC2 devices, as it should not affect a
standards compliant device, but it is only necessary for C-Media CM6631,
VIA VT1731 and possibly others.

Failure to read sample rate from the device is not handled as an error in
set_sample_rate_v2(), as (permanent or intermittent) failure to read sample
rate isn't essential for a successful sample rate set.
Signed-off-by: default avatarTorstein Hegge <hegge@resisty.net>
Acked-by: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 10250911
...@@ -253,7 +253,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, ...@@ -253,7 +253,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
{ {
struct usb_device *dev = chip->dev; struct usb_device *dev = chip->dev;
unsigned char data[4]; unsigned char data[4];
int err, crate; int err, cur_rate, prev_rate;
int clock = snd_usb_clock_find_source(chip, fmt->clock); int clock = snd_usb_clock_find_source(chip, fmt->clock);
if (clock < 0) if (clock < 0)
...@@ -266,6 +266,19 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, ...@@ -266,6 +266,19 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
return -ENXIO; return -ENXIO;
} }
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8,
snd_usb_ctrl_intf(chip) | (clock << 8),
data, sizeof(data));
if (err < 0) {
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
dev->devnum, iface, fmt->altsetting);
prev_rate = 0;
} else {
prev_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}
data[0] = rate; data[0] = rate;
data[1] = rate >> 8; data[1] = rate >> 8;
data[2] = rate >> 16; data[2] = rate >> 16;
...@@ -280,19 +293,31 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface, ...@@ -280,19 +293,31 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
return err; return err;
} }
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR, err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
UAC2_CS_CONTROL_SAM_FREQ << 8, UAC2_CS_CONTROL_SAM_FREQ << 8,
snd_usb_ctrl_intf(chip) | (clock << 8), snd_usb_ctrl_intf(chip) | (clock << 8),
data, sizeof(data))) < 0) { data, sizeof(data));
if (err < 0) {
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n", snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
dev->devnum, iface, fmt->altsetting); dev->devnum, iface, fmt->altsetting);
return err; cur_rate = 0;
} else {
cur_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
} }
crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); if (cur_rate != rate) {
if (crate != rate) snd_printd(KERN_WARNING
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate); "current rate %d is different from the runtime rate %d\n",
cur_rate, rate);
}
/* Some devices doesn't respond to sample rate changes while the
* interface is active. */
if (rate != prev_rate) {
usb_set_interface(dev, iface, 0);
usb_set_interface(dev, iface, fmt->altsetting);
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment