aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Mack <zonque@gmail.com>2014-08-27 13:09:07 -0400
committerFelipe Balbi <balbi@ti.com>2014-09-02 10:28:00 -0400
commit9bb87f168931cf55738ed2fbda3639575cede886 (patch)
tree55a2a6ba38b24aec42eaed7cc6399e1365e64496
parentec9e43138f1219966850477e056f6eb7fbcc4fa4 (diff)
usb: gadget: f_uac2: send reasonably sized packets
The UAC2 function driver currently responds to all packets at all times with wMaxPacketSize packets. That results in way too fast audio playback as the function driver (which is in fact supposed to define the audio stream pace) delivers as fast as it can. Fix this by sizing each packet correctly with the following steps: a) Set the packet's size by dividing the nominal data rate by the playback endpoint's interval. b) If there is a residual value from the calculation in a), add it to a accumulator to keep track of it across packets. c) If the accumulator has gathered at least the number of bytes that are needed for one sample frame, increase the packet size. This way, the packet size calculation will get rid of any kind of imprecision that would otherwise occur with a simple division over time. Some of the variables that are needed while processing each packet are pre-computed for performance reasons. Signed-off-by: Daniel Mack <zonque@gmail.com> Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--drivers/usb/gadget/function/f_uac2.c69
1 files changed, 65 insertions, 4 deletions
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 246a7784e012..a5a27a504d67 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -92,6 +92,15 @@ struct snd_uac2_chip {
92 92
93 struct snd_card *card; 93 struct snd_card *card;
94 struct snd_pcm *pcm; 94 struct snd_pcm *pcm;
95
96 /* timekeeping for the playback endpoint */
97 unsigned int p_interval;
98 unsigned int p_residue;
99
100 /* pre-calculated values for playback iso completion */
101 unsigned int p_pktsize;
102 unsigned int p_pktsize_residue;
103 unsigned int p_framesize;
95}; 104};
96 105
97#define BUFF_SIZE_MAX (PAGE_SIZE * 16) 106#define BUFF_SIZE_MAX (PAGE_SIZE * 16)
@@ -191,8 +200,29 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
191 200
192 spin_lock_irqsave(&prm->lock, flags); 201 spin_lock_irqsave(&prm->lock, flags);
193 202
194 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 203 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
204 /*
205 * For each IN packet, take the quotient of the current data
206 * rate and the endpoint's interval as the base packet size.
207 * If there is a residue from this division, add it to the
208 * residue accumulator.
209 */
210 req->length = uac2->p_pktsize;
211 uac2->p_residue += uac2->p_pktsize_residue;
212
213 /*
214 * Whenever there are more bytes in the accumulator than we
215 * need to add one more sample frame, increase this packet's
216 * size and decrease the accumulator.
217 */
218 if (uac2->p_residue / uac2->p_interval >= uac2->p_framesize) {
219 req->length += uac2->p_framesize;
220 uac2->p_residue -= uac2->p_framesize *
221 uac2->p_interval;
222 }
223
195 req->actual = req->length; 224 req->actual = req->length;
225 }
196 226
197 pending = prm->hw_ptr % prm->period_size; 227 pending = prm->hw_ptr % prm->period_size;
198 pending += req->actual; 228 pending += req->actual;
@@ -346,6 +376,7 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
346 c_srate = opts->c_srate; 376 c_srate = opts->c_srate;
347 p_chmask = opts->p_chmask; 377 p_chmask = opts->p_chmask;
348 c_chmask = opts->c_chmask; 378 c_chmask = opts->c_chmask;
379 uac2->p_residue = 0;
349 380
350 runtime->hw = uac2_pcm_hardware; 381 runtime->hw = uac2_pcm_hardware;
351 382
@@ -1077,7 +1108,7 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
1077 struct usb_request *req; 1108 struct usb_request *req;
1078 struct usb_ep *ep; 1109 struct usb_ep *ep;
1079 struct uac2_rtd_params *prm; 1110 struct uac2_rtd_params *prm;
1080 int i; 1111 int req_len, i;
1081 1112
1082 /* No i/f has more than 2 alt settings */ 1113 /* No i/f has more than 2 alt settings */
1083 if (alt > 1) { 1114 if (alt > 1) {
@@ -1099,11 +1130,41 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
1099 prm = &uac2->c_prm; 1130 prm = &uac2->c_prm;
1100 config_ep_by_speed(gadget, fn, ep); 1131 config_ep_by_speed(gadget, fn, ep);
1101 agdev->as_out_alt = alt; 1132 agdev->as_out_alt = alt;
1133 req_len = prm->max_psize;
1102 } else if (intf == agdev->as_in_intf) { 1134 } else if (intf == agdev->as_in_intf) {
1135 struct f_uac2_opts *opts = agdev_to_uac2_opts(agdev);
1136 unsigned int factor, rate;
1137 struct usb_endpoint_descriptor *ep_desc;
1138
1103 ep = agdev->in_ep; 1139 ep = agdev->in_ep;
1104 prm = &uac2->p_prm; 1140 prm = &uac2->p_prm;
1105 config_ep_by_speed(gadget, fn, ep); 1141 config_ep_by_speed(gadget, fn, ep);
1106 agdev->as_in_alt = alt; 1142 agdev->as_in_alt = alt;
1143
1144 /* pre-calculate the playback endpoint's interval */
1145 if (gadget->speed == USB_SPEED_FULL) {
1146 ep_desc = &fs_epin_desc;
1147 factor = 1000;
1148 } else {
1149 ep_desc = &hs_epin_desc;
1150 factor = 125;
1151 }
1152
1153 /* pre-compute some values for iso_complete() */
1154 uac2->p_framesize = opts->p_ssize *
1155 num_channels(opts->p_chmask);
1156 rate = opts->p_srate * uac2->p_framesize;
1157 uac2->p_interval = (1 << (ep_desc->bInterval - 1)) * factor;
1158 uac2->p_pktsize = min_t(unsigned int, rate / uac2->p_interval,
1159 prm->max_psize);
1160
1161 if (uac2->p_pktsize < prm->max_psize)
1162 uac2->p_pktsize_residue = rate % uac2->p_interval;
1163 else
1164 uac2->p_pktsize_residue = 0;
1165
1166 req_len = uac2->p_pktsize;
1167 uac2->p_residue = 0;
1107 } else { 1168 } else {
1108 dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); 1169 dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
1109 return -EINVAL; 1170 return -EINVAL;
@@ -1128,9 +1189,9 @@ afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt)
1128 1189
1129 req->zero = 0; 1190 req->zero = 0;
1130 req->context = &prm->ureq[i]; 1191 req->context = &prm->ureq[i];
1131 req->length = prm->max_psize; 1192 req->length = req_len;
1132 req->complete = agdev_iso_complete; 1193 req->complete = agdev_iso_complete;
1133 req->buf = prm->rbuf + i * req->length; 1194 req->buf = prm->rbuf + i * prm->max_psize;
1134 } 1195 }
1135 1196
1136 if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) 1197 if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))