Commit 690a863ff03d9a29ace2b752b8f802fba78a842f

Authored by Torstein Hegge
Committed by Takashi Iwai
1 parent 10250911c6

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: Torstein Hegge <hegge@resisty.net>
Acked-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

Showing 1 changed file with 35 additions and 10 deletions Side-by-side Diff

... ... @@ -253,7 +253,7 @@
253 253 {
254 254 struct usb_device *dev = chip->dev;
255 255 unsigned char data[4];
256   - int err, crate;
  256 + int err, cur_rate, prev_rate;
257 257 int clock = snd_usb_clock_find_source(chip, fmt->clock);
258 258  
259 259 if (clock < 0)
... ... @@ -266,6 +266,19 @@
266 266 return -ENXIO;
267 267 }
268 268  
  269 + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
  270 + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
  271 + UAC2_CS_CONTROL_SAM_FREQ << 8,
  272 + snd_usb_ctrl_intf(chip) | (clock << 8),
  273 + data, sizeof(data));
  274 + if (err < 0) {
  275 + snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
  276 + dev->devnum, iface, fmt->altsetting);
  277 + prev_rate = 0;
  278 + } else {
  279 + prev_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
  280 + }
  281 +
269 282 data[0] = rate;
270 283 data[1] = rate >> 8;
271 284 data[2] = rate >> 16;
272 285  
273 286  
... ... @@ -280,19 +293,31 @@
280 293 return err;
281 294 }
282 295  
283   - if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
284   - USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
285   - UAC2_CS_CONTROL_SAM_FREQ << 8,
286   - snd_usb_ctrl_intf(chip) | (clock << 8),
287   - data, sizeof(data))) < 0) {
  296 + err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
  297 + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
  298 + UAC2_CS_CONTROL_SAM_FREQ << 8,
  299 + snd_usb_ctrl_intf(chip) | (clock << 8),
  300 + data, sizeof(data));
  301 + if (err < 0) {
288 302 snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
289 303 dev->devnum, iface, fmt->altsetting);
290   - return err;
  304 + cur_rate = 0;
  305 + } else {
  306 + cur_rate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
291 307 }
292 308  
293   - crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
294   - if (crate != rate)
295   - snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
  309 + if (cur_rate != rate) {
  310 + snd_printd(KERN_WARNING
  311 + "current rate %d is different from the runtime rate %d\n",
  312 + cur_rate, rate);
  313 + }
  314 +
  315 + /* Some devices doesn't respond to sample rate changes while the
  316 + * interface is active. */
  317 + if (rate != prev_rate) {
  318 + usb_set_interface(dev, iface, 0);
  319 + usb_set_interface(dev, iface, fmt->altsetting);
  320 + }
296 321  
297 322 return 0;
298 323 }