ALSA: usb-audio: add support for samplerate setting on v2 devices
Sample rate setting is done with a 4-byte long class request that addresses the interface. Signed-off-by: Daniel Mack <daniel@caiaq.de> Cc: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
committed by
Takashi Iwai
parent
29088fef3e
commit
767d75ad1c
@@ -350,8 +350,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
|
|||||||
}
|
}
|
||||||
/* try to set the interface... */
|
/* try to set the interface... */
|
||||||
usb_set_interface(chip->dev, iface_no, altno);
|
usb_set_interface(chip->dev, iface_no, altno);
|
||||||
snd_usb_init_pitch(chip->dev, iface_no, alts, fp);
|
snd_usb_init_pitch(chip, iface_no, alts, fp);
|
||||||
snd_usb_init_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max);
|
snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
181
sound/usb/pcm.c
181
sound/usb/pcm.c
@@ -107,69 +107,150 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
|
||||||
/*
|
struct usb_host_interface *alts,
|
||||||
* initialize the picth control and sample rate
|
struct audioformat *fmt)
|
||||||
*/
|
|
||||||
int snd_usb_init_pitch(struct usb_device *dev, int iface,
|
|
||||||
struct usb_host_interface *alts,
|
|
||||||
struct audioformat *fmt)
|
|
||||||
{
|
{
|
||||||
|
struct usb_device *dev = chip->dev;
|
||||||
unsigned int ep;
|
unsigned int ep;
|
||||||
unsigned char data[1];
|
unsigned char data[1];
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
ep = get_endpoint(alts, 0)->bEndpointAddress;
|
ep = get_endpoint(alts, 0)->bEndpointAddress;
|
||||||
/* if endpoint has pitch control, enable it */
|
|
||||||
if (fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL) {
|
/* if endpoint doesn't have pitch control, bail out */
|
||||||
data[0] = 1;
|
if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
|
||||||
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
return 0;
|
||||||
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
|
|
||||||
UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) {
|
data[0] = 1;
|
||||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
|
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
||||||
dev->devnum, iface, ep);
|
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
|
||||||
return err;
|
UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
|
||||||
}
|
data, sizeof(data), 1000)) < 0) {
|
||||||
|
snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
|
||||||
|
dev->devnum, iface, ep);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_usb_init_sample_rate(struct usb_device *dev, int iface,
|
/*
|
||||||
|
* initialize the picth control and sample rate
|
||||||
|
*/
|
||||||
|
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
|
||||||
|
struct usb_host_interface *alts,
|
||||||
|
struct audioformat *fmt)
|
||||||
|
{
|
||||||
|
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
|
||||||
|
|
||||||
|
switch (altsd->bInterfaceProtocol) {
|
||||||
|
case UAC_VERSION_1:
|
||||||
|
return init_pitch_v1(chip, iface, alts, fmt);
|
||||||
|
|
||||||
|
case UAC_VERSION_2:
|
||||||
|
/* not implemented yet */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
||||||
|
struct usb_host_interface *alts,
|
||||||
|
struct audioformat *fmt, int rate)
|
||||||
|
{
|
||||||
|
struct usb_device *dev = chip->dev;
|
||||||
|
unsigned int ep;
|
||||||
|
unsigned char data[3];
|
||||||
|
int err, crate;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
|
||||||
|
dev->devnum, iface, fmt->altsetting);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = rate;
|
||||||
|
data[1] = rate >> 8;
|
||||||
|
data[2] = rate >> 16;
|
||||||
|
if ((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), 1000)) < 0) {
|
||||||
|
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
|
||||||
|
dev->devnum, iface, fmt->altsetting, rate, ep);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if ((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), 1000)) < 0) {
|
||||||
|
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
|
||||||
|
dev->devnum, iface, fmt->altsetting, ep);
|
||||||
|
return 0; /* some devices don't support reading */
|
||||||
|
}
|
||||||
|
crate = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||||
|
if (crate != rate) {
|
||||||
|
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
|
||||||
|
// runtime->rate = crate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
||||||
|
struct usb_host_interface *alts,
|
||||||
|
struct audioformat *fmt, int rate)
|
||||||
|
{
|
||||||
|
struct usb_device *dev = chip->dev;
|
||||||
|
unsigned char data[4];
|
||||||
|
int err, crate;
|
||||||
|
|
||||||
|
data[0] = rate;
|
||||||
|
data[1] = rate >> 8;
|
||||||
|
data[2] = rate >> 16;
|
||||||
|
data[3] = rate >> 24;
|
||||||
|
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
|
||||||
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||||
|
0x0100, chip->clock_id << 8,
|
||||||
|
data, sizeof(data), 1000)) < 0) {
|
||||||
|
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
|
||||||
|
dev->devnum, iface, fmt->altsetting, rate);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC2_CS_CUR,
|
||||||
|
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||||
|
0x0100, chip->clock_id << 8,
|
||||||
|
data, sizeof(data), 1000)) < 0) {
|
||||||
|
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
|
||||||
|
dev->devnum, iface, fmt->altsetting);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
crate = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
||||||
|
if (crate != rate)
|
||||||
|
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
||||||
struct usb_host_interface *alts,
|
struct usb_host_interface *alts,
|
||||||
struct audioformat *fmt, int rate)
|
struct audioformat *fmt, int rate)
|
||||||
{
|
{
|
||||||
unsigned int ep;
|
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
|
||||||
unsigned char data[3];
|
|
||||||
int err;
|
|
||||||
|
|
||||||
ep = get_endpoint(alts, 0)->bEndpointAddress;
|
switch (altsd->bInterfaceProtocol) {
|
||||||
/* if endpoint has sampling rate control, set it */
|
case UAC_VERSION_1:
|
||||||
if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) {
|
return set_sample_rate_v1(chip, iface, alts, fmt, rate);
|
||||||
int crate;
|
|
||||||
data[0] = rate;
|
case UAC_VERSION_2:
|
||||||
data[1] = rate >> 8;
|
return set_sample_rate_v2(chip, iface, alts, fmt, rate);
|
||||||
data[2] = rate >> 16;
|
|
||||||
if ((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, 3, 1000)) < 0) {
|
|
||||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
|
|
||||||
dev->devnum, iface, fmt->altsetting, rate, ep);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
if ((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, 3, 1000)) < 0) {
|
|
||||||
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
|
|
||||||
dev->devnum, iface, fmt->altsetting, ep);
|
|
||||||
return 0; /* some devices don't support reading */
|
|
||||||
}
|
|
||||||
crate = data[0] | (data[1] << 8) | (data[2] << 16);
|
|
||||||
if (crate != rate) {
|
|
||||||
snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, rate);
|
|
||||||
// runtime->rate = crate;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -280,7 +361,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
|
|||||||
if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
|
if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
|
||||||
subs->fill_max = 1;
|
subs->fill_max = 1;
|
||||||
|
|
||||||
if ((err = snd_usb_init_pitch(dev, subs->interface, alts, fmt)) < 0)
|
if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
subs->cur_audiofmt = fmt;
|
subs->cur_audiofmt = fmt;
|
||||||
@@ -343,7 +424,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
|
|||||||
struct usb_interface *iface;
|
struct usb_interface *iface;
|
||||||
iface = usb_ifnum_to_if(subs->dev, fmt->iface);
|
iface = usb_ifnum_to_if(subs->dev, fmt->iface);
|
||||||
alts = &iface->altsetting[fmt->altset_idx];
|
alts = &iface->altsetting[fmt->altset_idx];
|
||||||
ret = snd_usb_init_sample_rate(subs->dev, subs->interface, alts, fmt, rate);
|
ret = snd_usb_init_sample_rate(subs->stream->chip, subs->interface, alts, fmt, rate);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
subs->cur_rate = rate;
|
subs->cur_rate = rate;
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
|
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
|
||||||
|
|
||||||
int snd_usb_init_pitch(struct usb_device *dev, int iface,
|
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
|
||||||
struct usb_host_interface *alts,
|
struct usb_host_interface *alts,
|
||||||
struct audioformat *fmt);
|
struct audioformat *fmt);
|
||||||
|
|
||||||
int snd_usb_init_sample_rate(struct usb_device *dev, int iface,
|
int snd_usb_init_sample_rate(struct snd_usb_audio *chip, int iface,
|
||||||
struct usb_host_interface *alts,
|
struct usb_host_interface *alts,
|
||||||
struct audioformat *fmt, int rate);
|
struct audioformat *fmt, int rate);
|
||||||
|
|
||||||
|
@@ -159,8 +159,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|||||||
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
|
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
|
||||||
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
||||||
usb_set_interface(chip->dev, fp->iface, 0);
|
usb_set_interface(chip->dev, fp->iface, 0);
|
||||||
snd_usb_init_pitch(chip->dev, fp->iface, alts, fp);
|
snd_usb_init_pitch(chip, fp->iface, alts, fp);
|
||||||
snd_usb_init_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max);
|
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user