Merge tag 'v3.10.97' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / sound / core / compress_offload.c
index 99db892d7299056568e5432bdd660ea14d3143c6..68f891e3f144a934b1b0b8617efa7aa47130ff86 100644 (file)
 #include <sound/compress_offload.h>
 #include <sound/compress_driver.h>
 
+/* struct snd_compr_codec_caps overflows the ioctl bit size for some
+ * architectures, so we need to disable the relevant ioctls.
+ */
+#if _IOC_SIZEBITS < 14
+#define COMPR_CODEC_CAPS_OVERFLOW
+#endif
+
 /* TODO:
  * - add substream support for multiple devices in case of
  *     SND_DYNAMIC_MINORS is not used
@@ -133,7 +140,7 @@ static int snd_compr_open(struct inode *inode, struct file *f)
                kfree(data);
        }
        snd_card_unref(compr->card);
-       return 0;
+       return ret;
 }
 
 static int snd_compr_free(struct inode *inode, struct file *f)
@@ -427,6 +434,7 @@ out:
        return retval;
 }
 
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
 static int
 snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
 {
@@ -450,6 +458,7 @@ out:
        kfree(caps);
        return retval;
 }
+#endif /* !COMPR_CODEC_CAPS_OVERFLOW */
 
 /* revisit this with snd_pcm_preallocate_xxx */
 static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
@@ -480,7 +489,7 @@ static int snd_compress_check_input(struct snd_compr_params *params)
 {
        /* first let's check the buffer parameter's */
        if (params->buffer.fragment_size == 0 ||
-                       params->buffer.fragments > SIZE_MAX / params->buffer.fragment_size)
+           params->buffer.fragments > INT_MAX / params->buffer.fragment_size)
                return -EINVAL;
 
        /* now codec parameters */
@@ -668,14 +677,48 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
                return -EPERM;
        retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
        if (!retval) {
-               stream->runtime->state = SNDRV_PCM_STATE_SETUP;
-               wake_up(&stream->runtime->sleep);
+               snd_compr_drain_notify(stream);
                stream->runtime->total_bytes_available = 0;
                stream->runtime->total_bytes_transferred = 0;
        }
        return retval;
 }
 
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+       int ret;
+
+       /*
+        * We are called with lock held. So drop the lock while we wait for
+        * drain complete notfication from the driver
+        *
+        * It is expected that driver will notify the drain completion and then
+        * stream will be moved to SETUP state, even if draining resulted in an
+        * error. We can trigger next track after this.
+        */
+       stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+       mutex_unlock(&stream->device->lock);
+
+       /* we wait for drain to complete here, drain can return when
+        * interruption occurred, wait returned error or success.
+        * For the first two cases we don't do anything different here and
+        * return after waking up
+        */
+
+       ret = wait_event_interruptible(stream->runtime->sleep,
+                       (stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
+       if (ret == -ERESTARTSYS)
+               pr_debug("wait aborted by a signal");
+       else if (ret)
+               pr_debug("wait for drain failed with %d\n", ret);
+
+
+       wake_up(&stream->runtime->sleep);
+       mutex_lock(&stream->device->lock);
+
+       return ret;
+}
+
 static int snd_compr_drain(struct snd_compr_stream *stream)
 {
        int retval;
@@ -683,12 +726,15 @@ static int snd_compr_drain(struct snd_compr_stream *stream)
        if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
                        stream->runtime->state == SNDRV_PCM_STATE_SETUP)
                return -EPERM;
+
        retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
-       if (!retval) {
-               stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+       if (retval) {
+               pr_debug("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
                wake_up(&stream->runtime->sleep);
+               return retval;
        }
-       return retval;
+
+       return snd_compress_wait_for_drain(stream);
 }
 
 static int snd_compr_next_track(struct snd_compr_stream *stream)
@@ -724,9 +770,14 @@ static int snd_compr_partial_drain(struct snd_compr_stream *stream)
                return -EPERM;
 
        retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+       if (retval) {
+               pr_debug("Partial drain returned failure\n");
+               wake_up(&stream->runtime->sleep);
+               return retval;
+       }
 
        stream->next_track = false;
-       return retval;
+       return snd_compress_wait_for_drain(stream);
 }
 
 static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
@@ -743,15 +794,17 @@ static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
        mutex_lock(&stream->device->lock);
        switch (_IOC_NR(cmd)) {
        case _IOC_NR(SNDRV_COMPRESS_IOCTL_VERSION):
-               put_user(SNDRV_COMPRESS_VERSION,
+               retval = put_user(SNDRV_COMPRESS_VERSION,
                                (int __user *)arg) ? -EFAULT : 0;
                break;
        case _IOC_NR(SNDRV_COMPRESS_GET_CAPS):
                retval = snd_compr_get_caps(stream, arg);
                break;
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
        case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
                retval = snd_compr_get_codec_caps(stream, arg);
                break;
+#endif
        case _IOC_NR(SNDRV_COMPRESS_SET_PARAMS):
                retval = snd_compr_set_params(stream, arg);
                break;
@@ -837,7 +890,8 @@ static int snd_compress_dev_disconnect(struct snd_device *device)
        struct snd_compr *compr;
 
        compr = device->device_data;
-       snd_unregister_device(compr->direction, compr->card, compr->device);
+       snd_unregister_device(SNDRV_DEVICE_TYPE_COMPRESS, compr->card,
+               compr->device);
        return 0;
 }