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

Commit a1f9bad3 authored by Hemant Kumar's avatar Hemant Kumar Committed by Jack Pham
Browse files

sound: usb: Find alt setting using format and service interval



Audio device supporting burst mode exposes alt settings with
different service intervals. In order to find the requested
format as well as service interval remote entity provides
service interval parameter. Data interval (bInterval - 1) is
calculated using service interval and used to find the matching
alt setting using find_format_and_si().

Change-Id: Ie15c78ab53eb5258085b54bf13459ebef44a44a4
Signed-off-by: default avatarHemant Kumar <hemantk@codeaurora.org>
parent 660877cd
Loading
Loading
Loading
Loading
+68 −2
Original line number Diff line number Diff line
@@ -150,6 +150,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)
@@ -574,7 +637,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
}

int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
	bool enable)
	int datainterval, bool enable)
{
	struct audioformat *fmt;
	struct usb_host_interface *alts;
@@ -594,6 +657,9 @@ int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
	}

	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,
+1 −1
Original line number Diff line number Diff line
@@ -11,6 +11,6 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
		       struct usb_host_interface *alts,
		       struct audioformat *fmt);
int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
	bool enable);
	int datainterval, bool enable);

#endif /* __USBAUDIO_PCM_H */
+47 −3
Original line number Diff line number Diff line
@@ -35,6 +35,10 @@
#include "pcm.h"
#include "usb_audio_qmi_v01.h"

#define BUS_INTERVAL_FULL_SPEED 1000 /* in us */
#define BUS_INTERVAL_HIGHSPEED_AND_ABOVE 125 /* in us */
#define MAX_BINTERVAL_ISOC_EP 16

#define SND_PCM_CARD_NUM_MASK 0xffff0000
#define SND_PCM_DEV_NUM_MASK 0xff00
#define SND_PCM_STREAM_DIRECTION 0xff
@@ -948,6 +952,32 @@ static int info_idx_from_ifnum(int card_num, int intf_num, bool enable)
	return -EINVAL;
}

static int get_data_interval_from_si(struct snd_usb_substream *subs,
	u32 service_interval)
{
	unsigned int bus_intval, bus_intval_mult, binterval;

	if (subs->dev->speed >= USB_SPEED_HIGH)
		bus_intval = BUS_INTERVAL_HIGHSPEED_AND_ABOVE;
	else
		bus_intval = BUS_INTERVAL_FULL_SPEED;

	if (service_interval % bus_intval)
		return -EINVAL;

	bus_intval_mult = service_interval / bus_intval;
	binterval = ffs(bus_intval_mult);
	if (!binterval || binterval > MAX_BINTERVAL_ISOC_EP)
		return -EINVAL;

	/* check if another bit is set then bail out */
	bus_intval_mult = bus_intval_mult >> binterval;
	if (bus_intval_mult)
		return -EINVAL;

	return (binterval - 1);
}

static void handle_uaudio_stream_req(struct qmi_handle *handle,
			struct sockaddr_qrtr *sq,
			struct qmi_txn *txn,
@@ -961,7 +991,7 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
	struct intf_info *info;
	int pcm_format;
	u8 pcm_card_num, pcm_dev_num, direction;
	int info_idx = -EINVAL, ret = 0;
	int info_idx = -EINVAL, datainterval = -EINVAL, ret = 0;

	req_msg = (struct qmi_uaudio_stream_req_msg_v01 *)decoded_msg;

@@ -1028,9 +1058,23 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
	subs->pcm_format = pcm_format;
	subs->channels = req_msg->number_of_ch;
	subs->cur_rate = req_msg->bit_rate;
	if (req_msg->service_interval_valid) {
		ret = get_data_interval_from_si(subs,
						req_msg->service_interval);
		if (ret == -EINVAL) {
			pr_err("%s: invalid service interval %u\n", __func__,
					req_msg->service_interval);
			mutex_unlock(&chip->dev_lock);
			goto response;
		}

		datainterval = ret;
		pr_debug("%s: data interval %u\n", __func__, ret);
	}

	uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;

	ret = snd_usb_enable_audio_stream(subs, req_msg->enable);
	ret = snd_usb_enable_audio_stream(subs, datainterval, req_msg->enable);

	if (!ret && req_msg->enable)
		ret = prepare_qmi_response(subs, req_msg, &resp, info_idx);
@@ -1097,7 +1141,7 @@ static void uaudio_qmi_disconnect_work(struct work_struct *w)
					info->direction);
				continue;
			}
			snd_usb_enable_audio_stream(subs, 0);
			snd_usb_enable_audio_stream(subs, -EINVAL, 0);
		}
		atomic_set(&uadev[idx].in_use, 0);
		mutex_lock(&chip->dev_lock);
+18 −0
Original line number Diff line number Diff line
@@ -367,6 +367,24 @@ struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
		.offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
					   xfer_buff_size),
	},
	{
		.data_type      = QMI_OPT_FLAG,
		.elem_len       = 1,
		.elem_size      = sizeof(uint8_t),
		.is_array       = NO_ARRAY,
		.tlv_type       = 0x14,
		.offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
					   service_interval_valid),
	},
	{
		.data_type      = QMI_UNSIGNED_4_BYTE,
		.elem_len       = 1,
		.elem_size      = sizeof(uint32_t),
		.is_array       = NO_ARRAY,
		.tlv_type       = 0x14,
		.offset         = offsetof(struct qmi_uaudio_stream_req_msg_v01,
					   service_interval),
	},
	{
		.data_type      = QMI_EOTI,
		.is_array       = NO_ARRAY,
+3 −1
Original line number Diff line number Diff line
@@ -99,8 +99,10 @@ struct qmi_uaudio_stream_req_msg_v01 {
	uint32_t bit_rate;
	uint8_t xfer_buff_size_valid;
	uint32_t xfer_buff_size;
	uint8_t service_interval_valid;
	uint32_t service_interval;
};
#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 39
#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46
extern struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[];

struct qmi_uaudio_stream_resp_msg_v01 {