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

Commit f055b384 authored by Hemant Kumar's avatar Hemant Kumar
Browse files

sound: usb: Add snapshot for usb audio qmi driver



This change adds snapshot of usb audio qmi service related files.
A remote entity communicates with usb audio qmi server to enable
or disable particular audio stream on enumerated usb audio device.
This offloads ISOC data transfer operations to remote entity and
allows apps processor to go to power collapse more frequently for
better power saving. This snapshot is taken as of msm-4.14
commit <1216527842f1d> ("Merge "ARM: dts: msm: Add compute
priority mode and cooling devices in sm8150"").

Change-Id: I0d3c1c129723e2327406a61ae7c756736df99872
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent b373c65e
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