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

Commit f27cab0c authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "sound: usb: Add snapshot for usb audio qmi driver"

parents c88f91dd f055b384
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -162,5 +162,14 @@ config SND_BCD2000

source "sound/usb/line6/Kconfig"

config SND_USB_AUDIO_QMI
	tristate "USB Audio QMI Service driver"
	depends on QCOM_QMI_HELPERS
	help
	  Starts USB Audio QMI server to communicate with remote entity
	  to perform operations like enable or disable particular audio
	  stream on a connected USB device, subsystem restart and
	  device disconnect.

endif	# SND_USB
+1 −0
Original line number Diff line number Diff line
@@ -29,3 +29,4 @@ obj-$(CONFIG_SND_USB_US122L) += snd-usbmidi-lib.o

obj-$(CONFIG_SND) += misc/ usx2y/ caiaq/ 6fire/ hiface/ bcd2000/
obj-$(CONFIG_SND_USB_LINE6)	+= line6/
obj-$(CONFIG_SND_USB_AUDIO_QMI) += usb_audio_qmi_v01.o usb_audio_qmi_svc.o
+71 −0
Original line number Diff line number Diff line
@@ -119,6 +119,71 @@ static DEFINE_MUTEX(register_mutex);
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
static struct usb_driver usb_audio_driver;

struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
	unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
	**uchip, void (*disconnect_cb)(struct snd_usb_audio *chip))
{
	int idx;
	struct snd_usb_stream *as;
	struct snd_usb_substream *subs = NULL;
	struct snd_usb_audio *chip = NULL;

	mutex_lock(&register_mutex);
	/*
	 * legacy audio snd card number assignment is dynamic. Hence
	 * search using chip->card->number
	 */
	for (idx = 0; idx < SNDRV_CARDS; idx++) {
		if (!usb_chip[idx])
			continue;
		if (usb_chip[idx]->card->number == card_num) {
			chip = usb_chip[idx];
			break;
		}
	}

	if (!chip || atomic_read(&chip->shutdown)) {
		pr_debug("%s: instance of usb crad # %d does not exist\n",
			__func__, card_num);
		goto err;
	}

	if (pcm_idx >= chip->pcm_devs) {
		pr_err("%s: invalid pcm dev number %u > %d\n", __func__,
			pcm_idx, chip->pcm_devs);
		goto err;
	}

	if (direction > SNDRV_PCM_STREAM_CAPTURE) {
		pr_err("%s: invalid direction %u\n", __func__, direction);
		goto err;
	}

	list_for_each_entry(as, &chip->pcm_list, list) {
		if (as->pcm_index == pcm_idx) {
			subs = &as->substream[direction];
			if (subs->interface < 0 && !subs->data_endpoint &&
				!subs->sync_endpoint) {
				pr_debug("%s: stream disconnected, bail out\n",
					__func__);
				subs = NULL;
				goto err;
			}
			goto done;
		}
	}

done:
	chip->card_num = card_num;
	chip->disconnect_cb = disconnect_cb;
err:
	*uchip = chip;
	if (!subs)
		pr_debug("%s: substream instance not found\n", __func__);
	mutex_unlock(&register_mutex);
	return subs;
}

/*
 * disconnect streams
 * called from usb_audio_disconnect()
@@ -366,6 +431,7 @@ static void snd_usb_audio_free(struct snd_card *card)
	list_for_each_entry_safe(ep, n, &chip->ep_list, list)
		snd_usb_endpoint_free(ep);

	mutex_destroy(&chip->dev_lock);
	mutex_destroy(&chip->mutex);
	if (!atomic_read(&chip->shutdown))
		dev_set_drvdata(&chip->dev->dev, NULL);
@@ -493,6 +559,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,

	chip = card->private_data;
	mutex_init(&chip->mutex);
	mutex_init(&chip->dev_lock);
	init_waitqueue_head(&chip->shutdown_wait);
	chip->index = idx;
	chip->dev = dev;
@@ -688,6 +755,8 @@ static int usb_audio_probe(struct usb_interface *intf,
	usb_chip[chip->index] = chip;
	chip->num_interfaces++;
	usb_set_intfdata(intf, chip);
	intf->needs_remote_wakeup = 1;
	usb_enable_autosuspend(chip->dev);
	atomic_dec(&chip->active);
	mutex_unlock(&register_mutex);
	return 0;
@@ -717,6 +786,8 @@ static void usb_audio_disconnect(struct usb_interface *intf)
		return;

	card = chip->card;
	if (chip->disconnect_cb)
		chip->disconnect_cb(chip);

	mutex_lock(&register_mutex);
	if (atomic_inc_return(&chip->shutdown) == 1) {
+4 −0
Original line number Diff line number Diff line
@@ -171,4 +171,8 @@ struct snd_usb_stream {
	struct list_head list;
};

struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
	unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
	**uchip, void (*disconnect_cb)(struct snd_usb_audio *chip));

#endif /* __USBAUDIO_CARD_H */
+130 −0
Original line number Diff line number Diff line
@@ -149,6 +149,69 @@ static struct audioformat *find_format(struct snd_usb_substream *subs)
	return found;
}

/*
 * find a matching audio format as well as non-zero service interval
 */
static struct audioformat *find_format_and_si(struct snd_usb_substream *subs,
	unsigned int datainterval)
{
	unsigned int i;
	struct audioformat *fp;
	struct audioformat *found = NULL;
	int cur_attr = 0, attr;

	list_for_each_entry(fp, &subs->fmt_list, list) {
		if (datainterval != fp->datainterval)
			continue;
		if (!(fp->formats & pcm_format_to_bits(subs->pcm_format)))
			continue;
		if (fp->channels != subs->channels)
			continue;
		if (subs->cur_rate < fp->rate_min ||
		    subs->cur_rate > fp->rate_max)
			continue;
		if (!(fp->rates & SNDRV_PCM_RATE_CONTINUOUS)) {
			for (i = 0; i < fp->nr_rates; i++)
				if (fp->rate_table[i] == subs->cur_rate)
					break;
			if (i >= fp->nr_rates)
				continue;
		}
		attr = fp->ep_attr & USB_ENDPOINT_SYNCTYPE;
		if (!found) {
			found = fp;
			cur_attr = attr;
			continue;
		}
		/* avoid async out and adaptive in if the other method
		 * supports the same format.
		 * this is a workaround for the case like
		 * M-audio audiophile USB.
		 */
		if (attr != cur_attr) {
			if ((attr == USB_ENDPOINT_SYNC_ASYNC &&
			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
			    (attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
			     subs->direction == SNDRV_PCM_STREAM_CAPTURE))
				continue;
			if ((cur_attr == USB_ENDPOINT_SYNC_ASYNC &&
			     subs->direction == SNDRV_PCM_STREAM_PLAYBACK) ||
			    (cur_attr == USB_ENDPOINT_SYNC_ADAPTIVE &&
			     subs->direction == SNDRV_PCM_STREAM_CAPTURE)) {
				found = fp;
				cur_attr = attr;
				continue;
			}
		}
		/* find the format with the largest max. packet size */
		if (fp->maxpacksize > found->maxpacksize) {
			found = fp;
			cur_attr = attr;
		}
	}
	return found;
}

static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
			 struct usb_host_interface *alts,
			 struct audioformat *fmt)
@@ -565,6 +628,73 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
	return 0;
}

int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
	int datainterval, bool enable)
{
	struct audioformat *fmt;
	struct usb_host_interface *alts;
	struct usb_interface *iface;
	int ret;

	if (!enable) {
		if (subs->interface >= 0) {
			usb_set_interface(subs->dev, subs->interface, 0);
			subs->altset_idx = 0;
			subs->interface = -1;
			subs->cur_audiofmt = NULL;
		}

		snd_usb_autosuspend(subs->stream->chip);
		return 0;
	}

	snd_usb_autoresume(subs->stream->chip);
	if (datainterval != -EINVAL)
		fmt = find_format_and_si(subs, datainterval);
	else
		fmt = find_format(subs);
	if (!fmt) {
		dev_err(&subs->dev->dev,
		"cannot set format: format = %#x, rate = %d, channels = %d\n",
			   subs->pcm_format, subs->cur_rate, subs->channels);
		return -EINVAL;
	}

	subs->altset_idx = 0;
	subs->interface = -1;
	if (atomic_read(&subs->stream->chip->shutdown)) {
		ret = -ENODEV;
	} else {
		ret = set_format(subs, fmt);
		if (ret < 0)
			return ret;

		iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
		if (!iface) {
			dev_err(&subs->dev->dev, "Could not get iface %d\n",
				subs->cur_audiofmt->iface);
			return -ENODEV;
		}

		alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
		ret = snd_usb_init_sample_rate(subs->stream->chip,
					       subs->cur_audiofmt->iface,
					       alts,
					       subs->cur_audiofmt,
					       subs->cur_rate);
		if (ret < 0) {
			dev_err(&subs->dev->dev, "failed to set rate %d\n",
				subs->cur_rate);
			return ret;
		}
	}

	subs->interface = fmt->iface;
	subs->altset_idx = fmt->altset_idx;

	return 0;
}

/*
 * Return the score of matching two audioformats.
 * Veto the audioformat if:
Loading