summaryrefslogtreecommitdiffstats
path: root/sound/usb/line6/playback.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-01-28 07:24:41 +0100
committerTakashi Iwai <tiwai@suse.de>2015-01-28 07:24:41 +0100
commit5e0ddd07fa8fcbb84faac666d36ff9c37449a849 (patch)
tree29ff039af456f0aed5280943bc9e1593893b5b21 /sound/usb/line6/playback.c
parent1001fb810b1295d0600c0c6bdcb17889460470a5 (diff)
parent247d95ee6dd22e5323ecf7a73ff64110ef2fa2da (diff)
downloadlinux-5e0ddd07fa8fcbb84faac666d36ff9c37449a849.tar.bz2
Merge branch 'topic/line6' into for-next
Diffstat (limited to 'sound/usb/line6/playback.c')
-rw-r--r--sound/usb/line6/playback.c284
1 files changed, 66 insertions, 218 deletions
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 1c9f95a370ff..1708c05f14db 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -37,7 +37,8 @@ static void change_volume(struct urb *urb_out, int volume[],
buf_end = p + urb_out->transfer_buffer_length / sizeof(*p);
for (; p < buf_end; ++p) {
- *p = (*p * volume[chn & 1]) >> 8;
+ int val = (*p * volume[chn & 1]) >> 8;
+ *p = clamp(val, 0x7fff, -0x8000);
++chn;
}
} else if (bytes_per_frame == 6) {
@@ -51,6 +52,7 @@ static void change_volume(struct urb *urb_out, int volume[],
val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);
val = (val * volume[chn & 1]) >> 8;
+ val = clamp(val, 0x7fffff, -0x800000);
p[0] = val;
p[1] = val >> 8;
p[2] = val >> 16;
@@ -118,8 +120,10 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
po = (short *)urb_out->transfer_buffer;
buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
- for (; po < buf_end; ++pi, ++po)
- *po += (*pi * volume) >> 8;
+ for (; po < buf_end; ++pi, ++po) {
+ int val = *po + ((*pi * volume) >> 8);
+ *po = clamp(val, 0x7fff, -0x8000);
+ }
}
/*
@@ -130,11 +134,11 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal,
/*
Find a free URB, prepare audio data, and submit URB.
+ must be called in line6pcm->out.lock context
*/
static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
{
int index;
- unsigned long flags;
int i, urb_size, urb_frames;
int ret;
const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
@@ -145,17 +149,15 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
(USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
struct urb *urb_out;
- spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
index =
- find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS);
+ find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS);
if (index < 0 || index >= LINE6_ISO_BUFFERS) {
- spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL;
}
- urb_out = line6pcm->urb_audio_out[index];
+ urb_out = line6pcm->out.urbs[index];
urb_size = 0;
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
@@ -164,15 +166,13 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i];
- if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)
- fsize = line6pcm->prev_fsize;
-
+ fsize = line6pcm->prev_fsize;
if (fsize == 0) {
int n;
- line6pcm->count_out += frame_increment;
- n = line6pcm->count_out / frame_factor;
- line6pcm->count_out -= n * frame_factor;
+ line6pcm->out.count += frame_increment;
+ n = line6pcm->out.count / frame_factor;
+ line6pcm->out.count -= n * frame_factor;
fsize = n * bytes_per_frame;
}
@@ -183,36 +183,35 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
if (urb_size == 0) {
/* can't determine URB size */
- spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n");
return -EINVAL;
}
urb_frames = urb_size / bytes_per_frame;
urb_out->transfer_buffer =
- line6pcm->buffer_out +
+ line6pcm->out.buffer +
index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
urb_out->transfer_buffer_length = urb_size;
urb_out->context = line6pcm;
- if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) &&
- !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) {
+ if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running) &&
+ !test_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags)) {
struct snd_pcm_runtime *runtime =
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime;
- if (line6pcm->pos_out + urb_frames > runtime->buffer_size) {
+ if (line6pcm->out.pos + urb_frames > runtime->buffer_size) {
/*
The transferred area goes over buffer boundary,
copy the data to the temp buffer.
*/
int len;
- len = runtime->buffer_size - line6pcm->pos_out;
+ len = runtime->buffer_size - line6pcm->out.pos;
if (len > 0) {
memcpy(urb_out->transfer_buffer,
runtime->dma_area +
- line6pcm->pos_out * bytes_per_frame,
+ line6pcm->out.pos * bytes_per_frame,
len * bytes_per_frame);
memcpy(urb_out->transfer_buffer +
len * bytes_per_frame, runtime->dma_area,
@@ -223,26 +222,27 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
} else {
memcpy(urb_out->transfer_buffer,
runtime->dma_area +
- line6pcm->pos_out * bytes_per_frame,
+ line6pcm->out.pos * bytes_per_frame,
urb_out->transfer_buffer_length);
}
- line6pcm->pos_out += urb_frames;
- if (line6pcm->pos_out >= runtime->buffer_size)
- line6pcm->pos_out -= runtime->buffer_size;
+ line6pcm->out.pos += urb_frames;
+ if (line6pcm->out.pos >= runtime->buffer_size)
+ line6pcm->out.pos -= runtime->buffer_size;
+
+ change_volume(urb_out, line6pcm->volume_playback,
+ bytes_per_frame);
} else {
memset(urb_out->transfer_buffer, 0,
urb_out->transfer_buffer_length);
}
- change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame);
-
- if (line6pcm->prev_fbuf != NULL) {
- if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) {
+ spin_lock_nested(&line6pcm->in.lock, SINGLE_DEPTH_NESTING);
+ if (line6pcm->prev_fbuf) {
+ if (test_bit(LINE6_STREAM_IMPULSE, &line6pcm->out.running)) {
create_impulse_test_signal(line6pcm, urb_out,
bytes_per_frame);
- if (line6pcm->flags &
- LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) {
+ if (test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) {
line6_capture_copy(line6pcm,
urb_out->transfer_buffer,
urb_out->
@@ -251,101 +251,43 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
urb_out->transfer_buffer_length);
}
} else {
- if (!
- (line6pcm->line6->
- properties->capabilities & LINE6_CAP_HWMON)
- && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM)
- && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM))
+ if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON)
+ && line6pcm->out.running && line6pcm->in.running)
add_monitor_signal(urb_out, line6pcm->prev_fbuf,
line6pcm->volume_monitor,
bytes_per_frame);
}
+ line6pcm->prev_fbuf = NULL;
+ line6pcm->prev_fsize = 0;
}
+ spin_unlock(&line6pcm->in.lock);
ret = usb_submit_urb(urb_out, GFP_ATOMIC);
if (ret == 0)
- set_bit(index, &line6pcm->active_urb_out);
+ set_bit(index, &line6pcm->out.active_urbs);
else
dev_err(line6pcm->line6->ifcdev,
"URB out #%d submission failed (%d)\n", index, ret);
- spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
return 0;
}
/*
Submit all currently available playback URBs.
-*/
+ must be called in line6pcm->out.lock context
+ */
int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
{
- int ret, i;
+ int ret = 0, i;
for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_out_urb(line6pcm);
if (ret < 0)
- return ret;
- }
-
- return 0;
-}
-
-/*
- Unlink all currently active playback URBs.
-*/
-void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm)
-{
- unsigned int i;
-
- for (i = LINE6_ISO_BUFFERS; i--;) {
- if (test_bit(i, &line6pcm->active_urb_out)) {
- if (!test_and_set_bit(i, &line6pcm->unlink_urb_out)) {
- struct urb *u = line6pcm->urb_audio_out[i];
-
- usb_unlink_urb(u);
- }
- }
- }
-}
-
-/*
- Wait until unlinking of all currently active playback URBs has been
- finished.
-*/
-void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
-{
- int timeout = HZ;
- unsigned int i;
- int alive;
-
- do {
- alive = 0;
- for (i = LINE6_ISO_BUFFERS; i--;) {
- if (test_bit(i, &line6pcm->active_urb_out))
- alive++;
- }
- if (!alive)
break;
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1);
- } while (--timeout > 0);
- if (alive)
- snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
-}
-
-/*
- Unlink all currently active playback URBs, and wait for finishing.
-*/
-void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm)
-{
- line6_unlink_audio_out_urbs(line6pcm);
- line6_wait_clear_audio_out_urbs(line6pcm);
-}
+ }
-void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm)
-{
- kfree(line6pcm->buffer_out);
- line6pcm->buffer_out = NULL;
+ return ret;
}
/*
@@ -363,56 +305,56 @@ static void audio_out_callback(struct urb *urb)
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
#endif
- line6pcm->last_frame_out = urb->start_frame;
+ line6pcm->out.last_frame = urb->start_frame;
/* find index of URB */
- for (index = LINE6_ISO_BUFFERS; index--;)
- if (urb == line6pcm->urb_audio_out[index])
+ for (index = 0; index < LINE6_ISO_BUFFERS; index++)
+ if (urb == line6pcm->out.urbs[index])
break;
- if (index < 0)
+ if (index >= LINE6_ISO_BUFFERS)
return; /* URB has been unlinked asynchronously */
- for (i = LINE6_ISO_PACKETS; i--;)
+ for (i = 0; i < LINE6_ISO_PACKETS; i++)
length += urb->iso_frame_desc[i].length;
- spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
+ spin_lock_irqsave(&line6pcm->out.lock, flags);
- if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) {
+ if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
struct snd_pcm_runtime *runtime = substream->runtime;
- line6pcm->pos_out_done +=
+ line6pcm->out.pos_done +=
length / line6pcm->properties->bytes_per_frame;
- if (line6pcm->pos_out_done >= runtime->buffer_size)
- line6pcm->pos_out_done -= runtime->buffer_size;
+ if (line6pcm->out.pos_done >= runtime->buffer_size)
+ line6pcm->out.pos_done -= runtime->buffer_size;
}
- clear_bit(index, &line6pcm->active_urb_out);
+ clear_bit(index, &line6pcm->out.active_urbs);
- for (i = LINE6_ISO_PACKETS; i--;)
+ for (i = 0; i < LINE6_ISO_PACKETS; i++)
if (urb->iso_frame_desc[i].status == -EXDEV) {
shutdown = 1;
break;
}
- if (test_and_clear_bit(index, &line6pcm->unlink_urb_out))
+ if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs))
shutdown = 1;
- spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
-
if (!shutdown) {
submit_audio_out_urb(line6pcm);
- if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM,
- &line6pcm->flags)) {
- line6pcm->bytes_out += length;
- if (line6pcm->bytes_out >= line6pcm->period_out) {
- line6pcm->bytes_out %= line6pcm->period_out;
+ if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
+ line6pcm->out.bytes += length;
+ if (line6pcm->out.bytes >= line6pcm->out.period) {
+ line6pcm->out.bytes %= line6pcm->out.period;
+ spin_unlock(&line6pcm->out.lock);
snd_pcm_period_elapsed(substream);
+ spin_lock(&line6pcm->out.lock);
}
}
}
+ spin_unlock_irqrestore(&line6pcm->out.lock, flags);
}
/* open playback callback */
@@ -438,110 +380,16 @@ static int snd_line6_playback_close(struct snd_pcm_substream *substream)
return 0;
}
-/* hw_params playback callback */
-static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- int ret;
- struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
-
- /* -- Florian Demski [FD] */
- /* don't ask me why, but this fixes the bug on my machine */
- if (line6pcm == NULL) {
- if (substream->pcm == NULL)
- return -ENOMEM;
- if (substream->pcm->private_data == NULL)
- return -ENOMEM;
- substream->private_data = substream->pcm->private_data;
- line6pcm = snd_pcm_substream_chip(substream);
- }
- /* -- [FD] end */
-
- ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER);
-
- if (ret < 0)
- return ret;
-
- ret = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (ret < 0) {
- line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER);
- return ret;
- }
-
- line6pcm->period_out = params_period_bytes(hw_params);
- return 0;
-}
-
-/* hw_free playback callback */
-static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
-
- line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER);
- return snd_pcm_lib_free_pages(substream);
-}
-
-/* trigger playback callback */
-int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd)
-{
- int err;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- err = line6_pcm_acquire(line6pcm,
- LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM);
-
- if (err < 0)
- return err;
-
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- err = line6_pcm_release(line6pcm,
- LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM);
-
- if (err < 0)
- return err;
-
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags);
- break;
-
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags);
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* playback pointer callback */
-static snd_pcm_uframes_t
-snd_line6_playback_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
-
- return line6pcm->pos_out_done;
-}
-
/* playback operators */
struct snd_pcm_ops snd_line6_playback_ops = {
.open = snd_line6_playback_open,
.close = snd_line6_playback_close,
.ioctl = snd_pcm_lib_ioctl,
- .hw_params = snd_line6_playback_hw_params,
- .hw_free = snd_line6_playback_hw_free,
+ .hw_params = snd_line6_hw_params,
+ .hw_free = snd_line6_hw_free,
.prepare = snd_line6_prepare,
.trigger = snd_line6_trigger,
- .pointer = snd_line6_playback_pointer,
+ .pointer = snd_line6_pointer,
};
int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
@@ -554,7 +402,7 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
struct urb *urb;
/* URB for audio out: */
- urb = line6pcm->urb_audio_out[i] =
+ urb = line6pcm->out.urbs[i] =
usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
if (urb == NULL)