~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/usb/line6/playback.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Line 6 Linux USB driver
  4  *
  5  * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at)
  6  */
  7 
  8 #include <linux/slab.h>
  9 #include <sound/core.h>
 10 #include <sound/pcm.h>
 11 #include <sound/pcm_params.h>
 12 
 13 #include "capture.h"
 14 #include "driver.h"
 15 #include "pcm.h"
 16 #include "playback.h"
 17 
 18 /*
 19         Software stereo volume control.
 20 */
 21 static void change_volume(struct urb *urb_out, int volume[],
 22                           int bytes_per_frame)
 23 {
 24         int chn = 0;
 25 
 26         if (volume[0] == 256 && volume[1] == 256)
 27                 return;         /* maximum volume - no change */
 28 
 29         if (bytes_per_frame == 4) {
 30                 __le16 *p, *buf_end;
 31 
 32                 p = (__le16 *)urb_out->transfer_buffer;
 33                 buf_end = p + urb_out->transfer_buffer_length / sizeof(*p);
 34 
 35                 for (; p < buf_end; ++p) {
 36                         short pv = le16_to_cpu(*p);
 37                         int val = (pv * volume[chn & 1]) >> 8;
 38                         pv = clamp(val, -0x8000, 0x7fff);
 39                         *p = cpu_to_le16(pv);
 40                         ++chn;
 41                 }
 42         } else if (bytes_per_frame == 6) {
 43                 unsigned char *p, *buf_end;
 44 
 45                 p = (unsigned char *)urb_out->transfer_buffer;
 46                 buf_end = p + urb_out->transfer_buffer_length;
 47 
 48                 for (; p < buf_end; p += 3) {
 49                         int val;
 50 
 51                         val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);
 52                         val = (val * volume[chn & 1]) >> 8;
 53                         val = clamp(val, -0x800000, 0x7fffff);
 54                         p[0] = val;
 55                         p[1] = val >> 8;
 56                         p[2] = val >> 16;
 57                         ++chn;
 58                 }
 59         }
 60 }
 61 
 62 /*
 63         Create signal for impulse response test.
 64 */
 65 static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm,
 66                                        struct urb *urb_out, int bytes_per_frame)
 67 {
 68         int frames = urb_out->transfer_buffer_length / bytes_per_frame;
 69 
 70         if (bytes_per_frame == 4) {
 71                 int i;
 72                 short *pi = (short *)line6pcm->prev_fbuf;
 73                 short *po = (short *)urb_out->transfer_buffer;
 74 
 75                 for (i = 0; i < frames; ++i) {
 76                         po[0] = pi[0];
 77                         po[1] = 0;
 78                         pi += 2;
 79                         po += 2;
 80                 }
 81         } else if (bytes_per_frame == 6) {
 82                 int i, j;
 83                 unsigned char *pi = line6pcm->prev_fbuf;
 84                 unsigned char *po = urb_out->transfer_buffer;
 85 
 86                 for (i = 0; i < frames; ++i) {
 87                         for (j = 0; j < bytes_per_frame / 2; ++j)
 88                                 po[j] = pi[j];
 89 
 90                         for (; j < bytes_per_frame; ++j)
 91                                 po[j] = 0;
 92 
 93                         pi += bytes_per_frame;
 94                         po += bytes_per_frame;
 95                 }
 96         }
 97         if (--line6pcm->impulse_count <= 0) {
 98                 ((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame -
 99                                                               1] =
100                     line6pcm->impulse_volume;
101                 line6pcm->impulse_count = line6pcm->impulse_period;
102         }
103 }
104 
105 /*
106         Add signal to buffer for software monitoring.
107 */
108 static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
109                                int volume, int bytes_per_frame)
110 {
111         if (volume == 0)
112                 return;         /* zero volume - no change */
113 
114         if (bytes_per_frame == 4) {
115                 __le16 *pi, *po, *buf_end;
116 
117                 pi = (__le16 *)signal;
118                 po = (__le16 *)urb_out->transfer_buffer;
119                 buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
120 
121                 for (; po < buf_end; ++pi, ++po) {
122                         short pov = le16_to_cpu(*po);
123                         short piv = le16_to_cpu(*pi);
124                         int val = pov + ((piv * volume) >> 8);
125                         pov = clamp(val, -0x8000, 0x7fff);
126                         *po = cpu_to_le16(pov);
127                 }
128         }
129 
130         /*
131            We don't need to handle devices with 6 bytes per frame here
132            since they all support hardware monitoring.
133          */
134 }
135 
136 /*
137         Find a free URB, prepare audio data, and submit URB.
138         must be called in line6pcm->out.lock context
139 */
140 static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
141 {
142         int index;
143         int i, urb_size, urb_frames;
144         int ret;
145         const int bytes_per_frame =
146                 line6pcm->properties->bytes_per_channel *
147                 line6pcm->properties->playback_hw.channels_max;
148         const int frame_increment =
149                 line6pcm->properties->rates.rats[0].num_min;
150         const int frame_factor =
151                 line6pcm->properties->rates.rats[0].den *
152                 (line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
153         struct urb *urb_out;
154 
155         index = find_first_zero_bit(&line6pcm->out.active_urbs,
156                                     line6pcm->line6->iso_buffers);
157 
158         if (index < 0 || index >= line6pcm->line6->iso_buffers) {
159                 dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
160                 return -EINVAL;
161         }
162 
163         urb_out = line6pcm->out.urbs[index];
164         urb_size = 0;
165 
166         /* TODO: this may not work for LINE6_ISO_PACKETS != 1 */
167         for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
168                 /* compute frame size for given sampling rate */
169                 int fsize = 0;
170                 struct usb_iso_packet_descriptor *fout =
171                     &urb_out->iso_frame_desc[i];
172 
173                 fsize = line6pcm->prev_fsize;
174                 if (fsize == 0) {
175                         int n;
176 
177                         line6pcm->out.count += frame_increment;
178                         n = line6pcm->out.count / frame_factor;
179                         line6pcm->out.count -= n * frame_factor;
180                         fsize = n;
181                 }
182 
183                 fsize *= bytes_per_frame;
184 
185                 fout->offset = urb_size;
186                 fout->length = fsize;
187                 urb_size += fsize;
188         }
189 
190         if (urb_size == 0) {
191                 /* can't determine URB size */
192                 dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n");
193                 return -EINVAL;
194         }
195 
196         urb_frames = urb_size / bytes_per_frame;
197         urb_out->transfer_buffer =
198             line6pcm->out.buffer +
199             index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;
200         urb_out->transfer_buffer_length = urb_size;
201         urb_out->context = line6pcm;
202 
203         if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running) &&
204             !test_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags)) {
205                 struct snd_pcm_runtime *runtime =
206                     get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
207 
208                 if (line6pcm->out.pos + urb_frames > runtime->buffer_size) {
209                         /*
210                            The transferred area goes over buffer boundary,
211                            copy the data to the temp buffer.
212                          */
213                         int len;
214 
215                         len = runtime->buffer_size - line6pcm->out.pos;
216 
217                         if (len > 0) {
218                                 memcpy(urb_out->transfer_buffer,
219                                        runtime->dma_area +
220                                        line6pcm->out.pos * bytes_per_frame,
221                                        len * bytes_per_frame);
222                                 memcpy(urb_out->transfer_buffer +
223                                        len * bytes_per_frame, runtime->dma_area,
224                                        (urb_frames - len) * bytes_per_frame);
225                         } else
226                                 dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n",
227                                         len);
228                 } else {
229                         memcpy(urb_out->transfer_buffer,
230                                runtime->dma_area +
231                                line6pcm->out.pos * bytes_per_frame,
232                                urb_out->transfer_buffer_length);
233                 }
234 
235                 line6pcm->out.pos += urb_frames;
236                 if (line6pcm->out.pos >= runtime->buffer_size)
237                         line6pcm->out.pos -= runtime->buffer_size;
238 
239                 change_volume(urb_out, line6pcm->volume_playback,
240                               bytes_per_frame);
241         } else {
242                 memset(urb_out->transfer_buffer, 0,
243                        urb_out->transfer_buffer_length);
244         }
245 
246         spin_lock_nested(&line6pcm->in.lock, SINGLE_DEPTH_NESTING);
247         if (line6pcm->prev_fbuf) {
248                 if (test_bit(LINE6_STREAM_IMPULSE, &line6pcm->out.running)) {
249                         create_impulse_test_signal(line6pcm, urb_out,
250                                                    bytes_per_frame);
251                         if (test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) {
252                                 line6_capture_copy(line6pcm,
253                                                    urb_out->transfer_buffer,
254                                                    urb_out->
255                                                    transfer_buffer_length);
256                                 line6_capture_check_period(line6pcm,
257                                         urb_out->transfer_buffer_length);
258                         }
259                 } else {
260                         if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON)
261                             && line6pcm->out.running && line6pcm->in.running)
262                                 add_monitor_signal(urb_out, line6pcm->prev_fbuf,
263                                                    line6pcm->volume_monitor,
264                                                    bytes_per_frame);
265                 }
266                 line6pcm->prev_fbuf = NULL;
267                 line6pcm->prev_fsize = 0;
268         }
269         spin_unlock(&line6pcm->in.lock);
270 
271         ret = usb_submit_urb(urb_out, GFP_ATOMIC);
272 
273         if (ret == 0)
274                 set_bit(index, &line6pcm->out.active_urbs);
275         else
276                 dev_err(line6pcm->line6->ifcdev,
277                         "URB out #%d submission failed (%d)\n", index, ret);
278 
279         return 0;
280 }
281 
282 /*
283         Submit all currently available playback URBs.
284         must be called in line6pcm->out.lock context
285  */
286 int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
287 {
288         int ret = 0, i;
289 
290         for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
291                 ret = submit_audio_out_urb(line6pcm);
292                 if (ret < 0)
293                         break;
294         }
295 
296         return ret;
297 }
298 
299 /*
300         Callback for completed playback URB.
301 */
302 static void audio_out_callback(struct urb *urb)
303 {
304         int i, index, length = 0, shutdown = 0;
305         unsigned long flags;
306         struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
307         struct snd_pcm_substream *substream =
308             get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
309         const int bytes_per_frame =
310                 line6pcm->properties->bytes_per_channel *
311                 line6pcm->properties->playback_hw.channels_max;
312 
313 #if USE_CLEAR_BUFFER_WORKAROUND
314         memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
315 #endif
316 
317         line6pcm->out.last_frame = urb->start_frame;
318 
319         /* find index of URB */
320         for (index = 0; index < line6pcm->line6->iso_buffers; index++)
321                 if (urb == line6pcm->out.urbs[index])
322                         break;
323 
324         if (index >= line6pcm->line6->iso_buffers)
325                 return;         /* URB has been unlinked asynchronously */
326 
327         for (i = 0; i < LINE6_ISO_PACKETS; i++)
328                 length += urb->iso_frame_desc[i].length;
329 
330         spin_lock_irqsave(&line6pcm->out.lock, flags);
331 
332         if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
333                 struct snd_pcm_runtime *runtime = substream->runtime;
334 
335                 line6pcm->out.pos_done +=
336                     length / bytes_per_frame;
337 
338                 if (line6pcm->out.pos_done >= runtime->buffer_size)
339                         line6pcm->out.pos_done -= runtime->buffer_size;
340         }
341 
342         clear_bit(index, &line6pcm->out.active_urbs);
343 
344         for (i = 0; i < LINE6_ISO_PACKETS; i++)
345                 if (urb->iso_frame_desc[i].status == -EXDEV) {
346                         shutdown = 1;
347                         break;
348                 }
349 
350         if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs))
351                 shutdown = 1;
352 
353         if (!shutdown) {
354                 submit_audio_out_urb(line6pcm);
355 
356                 if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
357                         line6pcm->out.bytes += length;
358                         if (line6pcm->out.bytes >= line6pcm->out.period) {
359                                 line6pcm->out.bytes %= line6pcm->out.period;
360                                 spin_unlock(&line6pcm->out.lock);
361                                 snd_pcm_period_elapsed(substream);
362                                 spin_lock(&line6pcm->out.lock);
363                         }
364                 }
365         }
366         spin_unlock_irqrestore(&line6pcm->out.lock, flags);
367 }
368 
369 /* open playback callback */
370 static int snd_line6_playback_open(struct snd_pcm_substream *substream)
371 {
372         int err;
373         struct snd_pcm_runtime *runtime = substream->runtime;
374         struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
375 
376         err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
377                                             &line6pcm->properties->rates);
378         if (err < 0)
379                 return err;
380 
381         runtime->hw = line6pcm->properties->playback_hw;
382         return 0;
383 }
384 
385 /* close playback callback */
386 static int snd_line6_playback_close(struct snd_pcm_substream *substream)
387 {
388         return 0;
389 }
390 
391 /* playback operators */
392 const struct snd_pcm_ops snd_line6_playback_ops = {
393         .open = snd_line6_playback_open,
394         .close = snd_line6_playback_close,
395         .hw_params = snd_line6_hw_params,
396         .hw_free = snd_line6_hw_free,
397         .prepare = snd_line6_prepare,
398         .trigger = snd_line6_trigger,
399         .pointer = snd_line6_pointer,
400 };
401 
402 int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
403 {
404         struct usb_line6 *line6 = line6pcm->line6;
405         int i;
406 
407         line6pcm->out.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *),
408                                      GFP_KERNEL);
409         if (line6pcm->out.urbs == NULL)
410                 return -ENOMEM;
411 
412         /* create audio URBs and fill in constant values: */
413         for (i = 0; i < line6->iso_buffers; ++i) {
414                 struct urb *urb;
415 
416                 /* URB for audio out: */
417                 urb = line6pcm->out.urbs[i] =
418                     usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
419 
420                 if (urb == NULL)
421                         return -ENOMEM;
422 
423                 urb->dev = line6->usbdev;
424                 urb->pipe =
425                     usb_sndisocpipe(line6->usbdev,
426                                     line6->properties->ep_audio_w &
427                                     USB_ENDPOINT_NUMBER_MASK);
428                 urb->transfer_flags = URB_ISO_ASAP;
429                 urb->start_frame = -1;
430                 urb->number_of_packets = LINE6_ISO_PACKETS;
431                 urb->interval = LINE6_ISO_INTERVAL;
432                 urb->error_count = 0;
433                 urb->complete = audio_out_callback;
434                 if (usb_urb_ep_type_check(urb))
435                         return -EINVAL;
436         }
437 
438         return 0;
439 }
440 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php