mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
Merge "sound: usb: Find alt setting using format and service interval" into msm-4.14
This commit is contained in:
commit
90e4b68f54
@ -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",
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user