Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 767d75ad authored by Daniel Mack's avatar Daniel Mack Committed by Takashi Iwai
Browse files

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: default avatarDaniel Mack <daniel@caiaq.de>
Cc: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 29088fef
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -350,8 +350,8 @@ int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
		}
		/* try to set the interface... */
		usb_set_interface(chip->dev, iface_no, altno);
		snd_usb_init_pitch(chip->dev, iface_no, alts, fp);
		snd_usb_init_sample_rate(chip->dev, iface_no, alts, fp, fp->rate_max);
		snd_usb_init_pitch(chip, iface_no, alts, fp);
		snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max);
	}
	return 0;
}
+127 −46
Original line number Diff line number Diff line
@@ -107,58 +107,87 @@ static struct audioformat *find_format(struct snd_usb_substream *subs, unsigned
	return found;
}


/*
 * initialize the picth control and sample rate
 */
int snd_usb_init_pitch(struct usb_device *dev, int iface,
static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
			 struct usb_host_interface *alts,
			 struct audioformat *fmt)
{
	struct usb_device *dev = chip->dev;
	unsigned int ep;
	unsigned char data[1];
	int err;

	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 */
	if (!(fmt->attributes & UAC_EP_CS_ATTR_PITCH_CONTROL))
		return 0;

	data[0] = 1;
	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_PITCH_CONTROL << 8, ep, data, 1, 1000)) < 0) {
				   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;
}

/*
 * 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;
	}

int snd_usb_init_sample_rate(struct usb_device *dev, int iface,
	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;
	int err, crate;

	ep = get_endpoint(alts, 0)->bEndpointAddress;
	/* if endpoint has sampling rate control, set it */
	if (fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE) {
		int crate;
	/* 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, 3, 1000)) < 0) {
				   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, 3, 1000)) < 0) {
				   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 */
@@ -168,10 +197,62 @@ int snd_usb_init_sample_rate(struct usb_device *dev, int iface,
		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 audioformat *fmt, int rate)
{
	struct usb_interface_descriptor *altsd = get_iface_desc(alts);

	switch (altsd->bInterfaceProtocol) {
	case UAC_VERSION_1:
		return set_sample_rate_v1(chip, iface, alts, fmt, rate);

	case UAC_VERSION_2:
		return set_sample_rate_v2(chip, iface, alts, fmt, rate);
	}

	return -EINVAL;
}

/*
 * find a matching format and set up the interface
 */
@@ -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)
		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;

	subs->cur_audiofmt = fmt;
@@ -343,7 +424,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
		struct usb_interface *iface;
		iface = usb_ifnum_to_if(subs->dev, fmt->iface);
		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)
			return ret;
		subs->cur_rate = rate;
+2 −2
Original line number Diff line number Diff line
@@ -3,11 +3,11 @@

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 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 audioformat *fmt, int rate);

+2 −2
Original line number Diff line number Diff line
@@ -159,8 +159,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
	fp->datainterval = snd_usb_parse_datainterval(chip, alts);
	fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
	usb_set_interface(chip->dev, fp->iface, 0);
	snd_usb_init_pitch(chip->dev, fp->iface, alts, fp);
	snd_usb_init_sample_rate(chip->dev, fp->iface, alts, fp, fp->rate_max);
	snd_usb_init_pitch(chip, fp->iface, alts, fp);
	snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
	return 0;
}