Merge "sound: usb: Find alt setting using format and service interval" into msm-4.14

This commit is contained in:
Linux Build Service Account 2018-03-26 16:33:02 -07:00 committed by Gerrit - the friendly Code Review server
commit 90e4b68f54
5 changed files with 137 additions and 7 deletions

View File

@ -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,7 +657,10 @@ int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
}
snd_usb_autoresume(subs->stream->chip);
fmt = find_format(subs);
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",

View File

@ -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 */

View File

@ -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);

View File

@ -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,

View File

@ -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 {