[ARM] tegra: i2s: Continuous DMA support
Chris Fries [Thu, 18 Nov 2010 18:42:15 +0000 (12:42 -0600)]
- Refactor DMA interactions to handle continuous single-buffered DMA
- Remove PIO support (obsolete, conflicts with new buffer management)
- Remove sample rate conversion (obsolete)
- Remove error-reporting logic
- Remove TEGRA_AUDIO_IN/OUT_GET/SET_BUF_CONFIG
- Add TEGRA_AUDIO_IN/OUT_GET/SET_NUM_BUFS

Change-Id: I8f21a0bb314aac3b7d1bb4918bda9141e58db38d
Signed-off-by: Iliyan Malchev <malchev@google.com>

arch/arm/mach-tegra/tegra_i2s_audio.c
arch/arm/mach-tegra/tegra_spdif_audio.c
include/linux/tegra_audio.h

index d62bf23..76587e6 100644 (file)
  *
  */
 
+/* TODO:
+       -- replace make I2S_MAX_NUM_BUFS configurable through an ioctl
+*/
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/miscdevice.h>
@@ -40,7 +44,6 @@
 #include <linux/io.h>
 #include <linux/ktime.h>
 #include <linux/sysfs.h>
-#include <linux/pm_qos_params.h>
 #include <linux/delay.h>
 #include <linux/tegra_audio.h>
 #include <linux/pm.h>
 
 #include "clock.h"
 
-#define PCM_BUFFER_MAX_SIZE_ORDER      (PAGE_SHIFT + 2)
-#define PCM_BUFFER_DMA_CHUNK_SIZE_ORDER        PAGE_SHIFT
-#define PCM_BUFFER_THRESHOLD_ORDER     (PCM_BUFFER_MAX_SIZE_ORDER - 1)
-#define PCM_DMA_CHUNK_MIN_SIZE_ORDER   3
-
-#define PCM_IN_BUFFER_PADDING          (1<<6) /* bytes */
+#define PCM_BUFFER_MAX_SIZE_ORDER      PAGE_SHIFT
 
 #define TEGRA_AUDIO_DSP_NONE           0
 #define TEGRA_AUDIO_DSP_PCM            1
 #define TEGRA_AUDIO_DSP_NETWORK                2
 #define TEGRA_AUDIO_DSP_TDM            3
 
+#define I2S_MAX_NUM_BUFS 4
+#define I2S_DEFAULT_TX_NUM_BUFS 2
+#define I2S_DEFAULT_RX_NUM_BUFS 2
+
 /* per stream (input/output) */
 struct audio_stream {
        int opened;
        struct mutex lock;
 
-       struct tegra_audio_buf_config buf_config;
-       bool active; /* is DMA or PIO in progress? */
-       void *buffer;
-       dma_addr_t buf_phys;
-       struct kfifo fifo;
-       struct completion fifo_completion;
-       struct scatterlist sg;
-
-       struct tegra_audio_error_counts errors;
+       bool active; /* is DMA in progress? */
+       int num_bufs;
+       void *buffer[I2S_MAX_NUM_BUFS];
+       dma_addr_t buf_phy[I2S_MAX_NUM_BUFS];
+       struct completion comp[I2S_MAX_NUM_BUFS];
+       struct tegra_dma_req dma_req[I2S_MAX_NUM_BUFS];
+       int last_queued;
 
        int i2s_fifo_atn_level;
 
-       ktime_t last_dma_ts;
        struct tegra_dma_channel *dma_chan;
        bool stop;
        struct completion stop_completion;
-       spinlock_t dma_req_lock; /* guards dma_has_it */
-       int dma_has_it;
-       struct tegra_dma_req dma_req;
+       spinlock_t dma_req_lock;
 
-       struct pm_qos_request_list pm_qos;
        struct work_struct allow_suspend_work;
 };
 
-struct i2s_pio_stats {
-       u32 i2s_interrupt_count;
-       u32 tx_fifo_errors;
-       u32 rx_fifo_errors;
-       u32 tx_fifo_written;
-       u32 rx_fifo_read;
-};
-
-static const int divs_8000[] = { 5, 6, 6, 5 }; /* 8018.(18) Hz */
-static const int divs_11025[] = { 4 };
-static const int divs_22050[] = { 2 };
-static const int divs_44100[] = { 1 };
-static const int divs_16000[] = { 2, 3, 3, 3, 3, 3, 3, 2 }; /* 16036.(36) Hz */
-
 /* per i2s controller */
 struct audio_driver_state {
        struct list_head next;
@@ -118,14 +100,10 @@ struct audio_driver_state {
        phys_addr_t i2s_phys;
        unsigned long i2s_base;
 
-       bool using_dma;
        unsigned long dma_req_sel;
 
-       int irq; /* for pio mode */
-       struct i2s_pio_stats pio_stats;
+       int irq;
        struct tegra_audio_in_config in_config;
-       const int *in_divs;
-       int in_divs_len;
 
        struct miscdevice misc_out;
        struct miscdevice misc_out_ctl;
@@ -140,19 +118,18 @@ struct audio_driver_state {
        unsigned int bit_format;
 };
 
-static inline int buf_size(struct audio_stream *s)
+static inline bool pending_buffer_requests(struct audio_stream *stream)
 {
-       return 1 << s->buf_config.size;
-}
-
-static inline int chunk_size(struct audio_stream *s)
-{
-       return 1 << s->buf_config.chunk;
+       int i;
+       for (i = 0; i < stream->num_bufs; i++)
+               if (!completion_done(&stream->comp[i]))
+                       return true;
+       return false;
 }
 
-static inline int threshold_size(struct audio_stream *s)
+static inline int buf_size(struct audio_stream *s __attribute__((unused)))
 {
-       return 1 << s->buf_config.threshold;
+       return 1 << PCM_BUFFER_MAX_SIZE_ORDER;
 }
 
 static inline struct audio_driver_state *ads_from_misc_out(struct file *file)
@@ -222,16 +199,13 @@ static inline void prevent_suspend(struct audio_stream *as)
 {
        pr_debug("%s\n", __func__);
        cancel_work_sync(&as->allow_suspend_work);
-       pm_qos_update_request(&as->pm_qos, 0);
 }
 
 static void allow_suspend_worker(struct work_struct *w)
 {
        struct audio_stream *as = container_of(w,
                        struct audio_stream, allow_suspend_work);
-
        pr_debug("%s\n", __func__);
-       pm_qos_update_request(&as->pm_qos, PM_QOS_DEFAULT_VALUE);
 }
 
 static inline void allow_suspend(struct audio_stream *as)
@@ -344,6 +318,7 @@ static void i2s_fifo_enable(unsigned long base, int fifo, int on)
        i2s_writel(base, val, I2S_I2S_CTRL_0);
 }
 
+#if 0
 static bool i2s_is_fifo_enabled(unsigned long base, int fifo)
 {
        u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
@@ -351,6 +326,7 @@ static bool i2s_is_fifo_enabled(unsigned long base, int fifo)
                return !!(val & I2S_I2S_CTRL_FIFO1_ENABLE);
        return !!(val & I2S_I2S_CTRL_FIFO2_ENABLE);
 }
+#endif
 
 static void i2s_fifo_clear(unsigned long base, int fifo)
 {
@@ -496,6 +472,7 @@ static void i2s_set_left_right_control_polarity(unsigned long base,
        i2s_writel(base, val, I2S_I2S_CTRL_0);
 }
 
+#if 0
 static void i2s_set_fifo_irq_on_err(unsigned long base, int fifo, int on)
 {
        u32 val = i2s_readl(base, I2S_I2S_CTRL_0);
@@ -521,6 +498,7 @@ static void i2s_set_fifo_irq_on_qe(unsigned long base, int fifo, int on)
        }
        i2s_writel(base, val, I2S_I2S_CTRL_0);
 }
+#endif
 
 static void i2s_enable_fifos(unsigned long base, int on)
 {
@@ -607,10 +585,10 @@ static int i2s_configure(struct platform_device *pdev)
        master_clk = state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP ?
                        state->pdata->dsp_master_clk :
                        state->pdata->i2s_master_clk;
-#define I2S_CLK_FUDGE_FACTOR 2  /* Todo, fix this! */
+#define I2S_CLK_TO_BITCLK_RATIO 2  /* Todo, Bitclk based on 2X clock? */
        if (master)
                i2s_set_channel_bit_count(state->i2s_base, master_clk,
-                       clk_get_rate(i2s_clk)*I2S_CLK_FUDGE_FACTOR);
+                       clk_get_rate(i2s_clk)*I2S_CLK_TO_BITCLK_RATIO);
        i2s_set_master(state->i2s_base, master);
 
        i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1);
@@ -626,81 +604,47 @@ static int i2s_configure(struct platform_device *pdev)
        return 0;
 }
 
-static int init_stream_buffer(struct audio_stream *,
-               struct tegra_audio_buf_config *cfg, unsigned);
+static int init_stream_buffer(struct audio_stream *, int);
 
 static int setup_dma(struct audio_driver_state *);
 static void tear_down_dma(struct audio_driver_state *);
-static int start_dma_playback(struct audio_stream *);
 static void stop_dma_playback(struct audio_stream *);
-static int start_dma_recording(struct audio_stream *);
-static int resume_dma_recording(struct audio_stream *);
+static int start_dma_recording(struct audio_stream *, int);
 static void stop_dma_recording(struct audio_stream *);
 
-static int setup_pio(struct audio_driver_state *);
-static void tear_down_pio(struct audio_driver_state *);
-static int start_pio_playback(struct audio_stream *);
-static void stop_pio_playback(struct audio_stream *);
-static int start_pio_recording(struct audio_stream *);
-static void stop_pio_recording(struct audio_stream *);
-
 struct sound_ops {
        int (*setup)(struct audio_driver_state *);
        void (*tear_down)(struct audio_driver_state *);
-       int (*start_playback)(struct audio_stream *);
        void (*stop_playback)(struct audio_stream *);
-       int (*start_recording)(struct audio_stream *);
+       int (*start_recording)(struct audio_stream *, int);
        void (*stop_recording)(struct audio_stream *);
 };
 
 static const struct sound_ops dma_sound_ops = {
        .setup = setup_dma,
        .tear_down = tear_down_dma,
-       .start_playback = start_dma_playback,
        .stop_playback = stop_dma_playback,
        .start_recording = start_dma_recording,
        .stop_recording = stop_dma_recording,
 };
 
-static const struct sound_ops pio_sound_ops = {
-       .setup = setup_pio,
-       .tear_down = tear_down_pio,
-       .start_playback = start_pio_playback,
-       .stop_playback = stop_pio_playback,
-       .start_recording = start_pio_recording,
-       .stop_recording = stop_pio_recording,
-};
-
 static const struct sound_ops *sound_ops = &dma_sound_ops;
 
-static int start_playback(struct audio_stream *aos)
-{
-       int rc;
-       unsigned long flags;
-       spin_lock_irqsave(&aos->dma_req_lock, flags);
-       pr_debug("%s: starting playback\n", __func__);
-       rc = sound_ops->start_playback(aos);
-       spin_unlock_irqrestore(&aos->dma_req_lock, flags);
-       if (!rc)
-               prevent_suspend(aos);
-       return rc;
-}
-
-static int start_recording_if_necessary(struct audio_stream *ais)
+static int start_recording_if_necessary(struct audio_stream *ais, int size)
 {
        int rc = 0;
        bool started = false;
        unsigned long flags;
-
+       prevent_suspend(ais);
        spin_lock_irqsave(&ais->dma_req_lock, flags);
-       if (!ais->stop && !kfifo_is_full(&ais->fifo)) {
-               pr_debug("%s: starting recording\n", __func__);
-               rc = sound_ops->start_recording(ais);
+       if (!ais->stop && !pending_buffer_requests(ais)) {
+               /* pr_debug("%s: starting recording\n", __func__); */
+               rc = sound_ops->start_recording(ais, size);
                started = !rc;
        }
        spin_unlock_irqrestore(&ais->dma_req_lock, flags);
-       if (started)
-               prevent_suspend(ais);
+       if (!started)
+               allow_suspend(ais);
        return rc;
 }
 
@@ -708,7 +652,9 @@ static bool stop_playback_if_necessary(struct audio_stream *aos)
 {
        unsigned long flags;
        spin_lock_irqsave(&aos->dma_req_lock, flags);
-       if (kfifo_is_empty(&aos->fifo)) {
+       pr_debug("%s\n", __func__);
+       if (!pending_buffer_requests(aos)) {
+               pr_debug("%s: no more data to play back\n", __func__);
                sound_ops->stop_playback(aos);
                spin_unlock_irqrestore(&aos->dma_req_lock, flags);
                allow_suspend(aos);
@@ -719,27 +665,17 @@ static bool stop_playback_if_necessary(struct audio_stream *aos)
        return false;
 }
 
-static bool stop_recording_if_necessary_nosync(struct audio_stream *ais)
-{
-       if (ais->stop || kfifo_is_full(&ais->fifo)) {
-               if (kfifo_is_full(&ais->fifo))
-                       ais->errors.full_empty++;  /* overflow */
-               sound_ops->stop_recording(ais);
-               return true;
-       }
-
-       return false;
-}
-
 /* playback and recording */
 static bool wait_till_stopped(struct audio_stream *as)
 {
        int rc;
        pr_debug("%s: wait for completion\n", __func__);
-       rc = wait_for_completion_interruptible_timeout(
+       rc = wait_for_completion_timeout(
                        &as->stop_completion, HZ);
        if (!rc)
                pr_err("%s: wait timed out", __func__);
+       if (rc < 0)
+               pr_err("%s: wait error %d\n", __func__, rc);
        allow_suspend(as);
        pr_debug("%s: done: %d\n", __func__, rc);
        return true;
@@ -750,33 +686,20 @@ static bool wait_till_stopped(struct audio_stream *as)
  */
 static void request_stop_nosync(struct audio_stream *as)
 {
+       int i;
        pr_debug("%s\n", __func__);
        if (!as->stop) {
                as->stop = true;
                wait_till_stopped(as);
-               if (!completion_done(&as->fifo_completion)) {
-                       pr_debug("%s: complete\n", __func__);
-                       complete(&as->fifo_completion);
+               for (i = 0; i < as->num_bufs; i++) {
+                       init_completion(&as->comp[i]);
+                       complete(&as->comp[i]);
                }
        }
-       kfifo_reset(&as->fifo);
        as->active = false; /* applies to recording only */
        pr_debug("%s: done\n", __func__);
 }
 
-static void toggle_dma(struct audio_driver_state *ads)
-{
-       pr_info("%s: %s\n", __func__, ads->using_dma ? "pio" : "dma");
-       sound_ops->tear_down(ads);
-       sound_ops = ads->using_dma ? &pio_sound_ops : &dma_sound_ops;
-       sound_ops->setup(ads);
-       ads->using_dma = !ads->using_dma;
-}
-
-/* DMA */
-
-static int resume_dma_playback(struct audio_stream *aos);
-
 static void setup_dma_tx_request(struct tegra_dma_req *req,
                struct audio_stream *aos);
 
@@ -785,21 +708,24 @@ static void setup_dma_rx_request(struct tegra_dma_req *req,
 
 static int setup_dma(struct audio_driver_state *ads)
 {
-       int rc;
+       int rc, i;
        pr_info("%s\n", __func__);
 
        if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) {
                /* setup audio playback */
-               ads->out.buf_phys = dma_map_single(&ads->pdev->dev,
-                                       ads->out.buffer,
+               for (i = 0; i < ads->out.num_bufs; i++) {
+                       ads->out.buf_phy[i] = dma_map_single(&ads->pdev->dev,
+                                       ads->out.buffer[i],
                                        1 << PCM_BUFFER_MAX_SIZE_ORDER,
                                        DMA_TO_DEVICE);
-               BUG_ON(!ads->out.buf_phys);
-               setup_dma_tx_request(&ads->out.dma_req, &ads->out);
-               ads->out.dma_chan =
-                       tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+                       BUG_ON(!ads->out.buf_phy[i]);
+                       setup_dma_tx_request(&ads->out.dma_req[i], &ads->out);
+                       ads->out.dma_req[i].source_addr = ads->out.buf_phy[i];
+               }
+               ads->out.dma_chan = tegra_dma_allocate_channel(
+                               TEGRA_DMA_MODE_CONTINUOUS_SINGLE);
                if (!ads->out.dma_chan) {
-                       pr_err("%s: error allocating output DMA channel: %ld\n",
+                       pr_err("%s: error alloc output DMA channel: %ld\n",
                                __func__, PTR_ERR(ads->out.dma_chan));
                        rc = -ENODEV;
                        goto fail_tx;
@@ -808,14 +734,17 @@ static int setup_dma(struct audio_driver_state *ads)
 
        if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) {
                /* setup audio recording */
-               ads->in.buf_phys = dma_map_single(&ads->pdev->dev,
-                                       ads->in.buffer,
+               for (i = 0; i < ads->in.num_bufs; i++) {
+                       ads->in.buf_phy[i] = dma_map_single(&ads->pdev->dev,
+                                       ads->in.buffer[i],
                                        1 << PCM_BUFFER_MAX_SIZE_ORDER,
                                        DMA_FROM_DEVICE);
-               BUG_ON(!ads->in.buf_phys);
-               setup_dma_rx_request(&ads->in.dma_req, &ads->in);
-               ads->in.dma_chan =
-                       tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+                       BUG_ON(!ads->in.buf_phy[i]);
+                       setup_dma_rx_request(&ads->in.dma_req[i], &ads->in);
+                       ads->in.dma_req[i].dest_addr = ads->in.buf_phy[i];
+               }
+               ads->in.dma_chan = tegra_dma_allocate_channel(
+                               TEGRA_DMA_MODE_CONTINUOUS_SINGLE);
                if (!ads->in.dma_chan) {
                        pr_err("%s: error allocating input DMA channel: %ld\n",
                                __func__, PTR_ERR(ads->in.dma_chan));
@@ -827,16 +756,24 @@ static int setup_dma(struct audio_driver_state *ads)
        return 0;
 
 fail_rx:
-       if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) {
-               dma_unmap_single(&ads->pdev->dev, ads->in.buf_phys,
-                       1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_FROM_DEVICE);
+       if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX) {
+               for (i = 0; i < ads->in.num_bufs; i++) {
+                       dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i],
+                                       1 << PCM_BUFFER_MAX_SIZE_ORDER,
+                                       DMA_FROM_DEVICE);
+                       ads->in.buf_phy[i] = 0;
+               }
                tegra_dma_free_channel(ads->in.dma_chan);
                ads->in.dma_chan = 0;
        }
 fail_tx:
-       if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) {
-               dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys,
-                       1 << PCM_BUFFER_MAX_SIZE_ORDER, DMA_TO_DEVICE);
+       if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX) {
+               for (i = 0; i < ads->out.num_bufs; i++) {
+                       dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
+                                       1 << PCM_BUFFER_MAX_SIZE_ORDER,
+                                       DMA_TO_DEVICE);
+                       ads->out.buf_phy[i] = 0;
+               }
                tegra_dma_free_channel(ads->out.dma_chan);
                ads->out.dma_chan = 0;
        }
@@ -846,50 +783,43 @@ fail_tx:
 
 static void tear_down_dma(struct audio_driver_state *ads)
 {
+       int i;
        pr_info("%s\n", __func__);
 
-       tegra_dma_free_channel(ads->out.dma_chan);
+       if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX) {
+               tegra_dma_free_channel(ads->out.dma_chan);
+               for (i = 0; i < ads->out.num_bufs; i++) {
+                       dma_unmap_single(&ads->pdev->dev, ads->out.buf_phy[i],
+                                       buf_size(&ads->out),
+                                       DMA_TO_DEVICE);
+                       ads->out.buf_phy[i] = 0;
+               }
+       }
        ads->out.dma_chan = NULL;
-       dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys,
-                               buf_size(&ads->out),
-                               DMA_TO_DEVICE);
-       ads->out.buf_phys = 0;
 
-       tegra_dma_free_channel(ads->in.dma_chan);
+       if (ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX) {
+               tegra_dma_free_channel(ads->in.dma_chan);
+               for (i = 0; i < ads->in.num_bufs; i++) {
+                       dma_unmap_single(&ads->pdev->dev, ads->in.buf_phy[i],
+                                       buf_size(&ads->in),
+                                       DMA_FROM_DEVICE);
+                       ads->in.buf_phy[i] = 0;
+               }
+       }
        ads->in.dma_chan = NULL;
-       dma_unmap_single(&ads->pdev->dev, ads->in.buf_phys,
-                               buf_size(&ads->in),
-                               DMA_FROM_DEVICE);
-       ads->in.buf_phys = 0;
 }
 
 static void dma_tx_complete_callback(struct tegra_dma_req *req)
 {
-       unsigned long flags;
        struct audio_stream *aos = req->dev;
-       int count = req->bytes_transferred;
-       u64 delta_us;
-       u64 max_delay_us = count * 10000 / (4 * 441);
-
-       pr_debug("%s bytes transferred %d\n", __func__, count);
+       unsigned req_num;
 
-       aos->dma_has_it = false;
-       delta_us = ktime_to_us(ktime_sub(ktime_get_real(), aos->last_dma_ts));
-
-       if (delta_us > max_delay_us) {
-               pr_debug("%s: too late by %lld us\n", __func__,
-                       delta_us - max_delay_us);
-               aos->errors.late_dma++;
-       }
+       req_num = req - aos->dma_req;
+       pr_debug("%s: completed buffer %d size %d\n", __func__,
+                       req_num, req->bytes_transferred);
+       BUG_ON(req_num >= aos->num_bufs);
 
-       kfifo_dma_out_finish(&aos->fifo, count);
-       dma_unmap_sg(NULL, &aos->sg, 1, DMA_TO_DEVICE);
-
-       if (!completion_done(&aos->fifo_completion)) {
-               pr_debug("%s: complete (%d avail)\n", __func__,
-                               kfifo_avail(&aos->fifo));
-               complete(&aos->fifo_completion);
-       }
+       complete(&aos->comp[req_num]);
 
        if (stop_playback_if_necessary(aos)) {
                pr_debug("%s: done (stopped)\n", __func__);
@@ -899,60 +829,24 @@ static void dma_tx_complete_callback(struct tegra_dma_req *req)
                }
                return;
        }
-
-       spin_lock_irqsave(&aos->dma_req_lock, flags);
-       resume_dma_playback(aos);
-       spin_unlock_irqrestore(&aos->dma_req_lock, flags);
-}
-
-static void dma_rx_complete_threshold(struct tegra_dma_req *req)
-{
-       pr_debug("%s\n", __func__);
 }
 
 static void dma_rx_complete_callback(struct tegra_dma_req *req)
 {
        unsigned long flags;
        struct audio_stream *ais = req->dev;
-       int count = req->bytes_transferred;
+       unsigned req_num;
 
        spin_lock_irqsave(&ais->dma_req_lock, flags);
 
-       ais->dma_has_it = false;
-
-       pr_debug("%s(%d): transferred %d bytes (%d available in fifo)\n",
-                       __func__,
-                       smp_processor_id(),
-                       count, kfifo_avail(&ais->fifo));
+       req_num = req - ais->dma_req;
+       pr_debug("%s: completed buffer %d size %d\n", __func__,
+                       req_num, req->bytes_transferred);
+       BUG_ON(req_num >= ais->num_bufs);
 
-       BUG_ON(kfifo_avail(&ais->fifo) < count);
-       kfifo_dma_in_finish(&ais->fifo, count);
-       dma_unmap_sg(NULL, &ais->sg, 1, DMA_FROM_DEVICE);
+       complete(&ais->comp[req_num]);
 
-       if (!completion_done(&ais->fifo_completion)) {
-               pr_debug("%s: signalling fifo completion\n", __func__);
-               complete(&ais->fifo_completion);
-       }
-
-       if (stop_recording_if_necessary_nosync(ais)) {
-               spin_unlock_irqrestore(&ais->dma_req_lock, flags);
-               pr_debug("%s: done (stopped)\n", __func__);
-               if (!completion_done(&ais->stop_completion)) {
-                       pr_debug("%s: signalling stop completion\n", __func__);
-                       complete(&ais->stop_completion);
-               }
-               return;
-       }
-
-       pr_debug("%s: resuming dma recording\n", __func__);
-
-       /* This call will fail if we try to set up a DMA request that's
-        * too small.
-        */
-       (void)resume_dma_recording(ais);
        spin_unlock_irqrestore(&ais->dma_req_lock, flags);
-
-       pr_debug("%s: done\n", __func__);
 }
 
 static void setup_dma_tx_request(struct tegra_dma_req *req,
@@ -984,7 +878,6 @@ static void setup_dma_rx_request(struct tegra_dma_req *req,
        memset(req, 0, sizeof(*req));
 
        req->complete = dma_rx_complete_callback;
-       req->threshold = dma_rx_complete_threshold;
        req->dev = ais;
        req->to_memory = true;
        req->source_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_RX);
@@ -998,65 +891,34 @@ static void setup_dma_rx_request(struct tegra_dma_req *req,
        req->req_sel = ads->dma_req_sel;
 }
 
-/* Called with aos->dma_req_lock taken. */
-static int resume_dma_playback(struct audio_stream *aos)
+static int start_playback(struct audio_stream *aos,
+                       struct tegra_dma_req *req)
 {
        int rc;
+       unsigned long flags;
        struct audio_driver_state *ads = ads_from_out(aos);
-       struct tegra_dma_req *req = &aos->dma_req;
-
-       if (aos->dma_has_it) {
-               pr_debug("%s: playback already in progress\n", __func__);
-               return -EALREADY;
-       }
 
-       rc = kfifo_dma_out_prepare(&aos->fifo, &aos->sg,
-                       1, kfifo_len(&aos->fifo));
-       /* stop_playback_if_necessary() already checks to see if the fifo is
-        * empty.
-        */
-       BUG_ON(!rc);
-       rc = dma_map_sg(NULL, &aos->sg, 1, DMA_TO_DEVICE);
-       if (rc < 0) {
-               pr_err("%s: could not map dma memory: %d\n", __func__, rc);
-               return rc;
-       }
+       pr_debug("%s: (writing %d)\n",
+                       __func__, req->size);
 
+       spin_lock_irqsave(&aos->dma_req_lock, flags);
 #if 0
        i2s_fifo_clear(ads->i2s_base, I2S_FIFO_TX);
 #endif
        i2s_fifo_set_attention_level(ads->i2s_base,
                        I2S_FIFO_TX, aos->i2s_fifo_atn_level);
 
-
-       req->source_addr = sg_dma_address(&aos->sg);
-       req->size = sg_dma_len(&aos->sg);
-       dma_sync_single_for_device(NULL,
-                       req->source_addr, req->size, DMA_TO_DEVICE);
-
-       /* Don't send all the data yet. */
-       if (req->size > chunk_size(aos))
-               req->size = chunk_size(aos);
-       pr_debug("%s resume playback (%d in fifo, writing %d)\n",
-                       __func__, kfifo_len(&aos->fifo), req->size);
-
        i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1);
 
-       aos->last_dma_ts = ktime_get_real();
        rc = tegra_dma_enqueue_req(aos->dma_chan, req);
-       aos->dma_has_it = !rc;
-       if (!aos->dma_has_it)
+       spin_unlock_irqrestore(&aos->dma_req_lock, flags);
+
+       if (rc)
                pr_err("%s: could not enqueue TX DMA req\n", __func__);
        return rc;
 }
 
 /* Called with aos->dma_req_lock taken. */
-static int start_dma_playback(struct audio_stream *aos)
-{
-       return resume_dma_playback(aos);
-}
-
-/* Called with aos->dma_req_lock taken. */
 static void stop_dma_playback(struct audio_stream *aos)
 {
        int spin = 0;
@@ -1075,48 +937,23 @@ static void stop_dma_playback(struct audio_stream *aos)
 
 /* This function may be called from either interrupt or process context. */
 /* Called with ais->dma_req_lock taken. */
-static int resume_dma_recording(struct audio_stream *ais)
+static int start_dma_recording(struct audio_stream *ais, int size)
 {
-       int rc;
+       int i;
        struct audio_driver_state *ads = ads_from_in(ais);
-       struct tegra_dma_req *req = &ais->dma_req;
 
-       BUG_ON(kfifo_is_full(&ais->fifo));
-
-       if (ais->dma_has_it) {
-               pr_debug("%s: recording already in progress\n", __func__);
-               return -EALREADY;
-       }
-
-       /* Don't send all the data yet. */
-       if (req->size > chunk_size(ais))
-               req->size = chunk_size(ais);
-       rc = kfifo_dma_in_prepare(&ais->fifo, &ais->sg, 1,
-                       kfifo_avail(&ais->fifo));
-       BUG_ON(!rc);
-       rc = dma_map_sg(NULL, &ais->sg, 1, DMA_FROM_DEVICE);
-       if (rc < 0) {
-               pr_err("%s: coult not map dma for recording: %d\n",
-                               __func__, rc);
-               return rc;
-       }
+       pr_debug("%s\n", __func__);
 
-       req->dest_addr = sg_dma_address(&ais->sg);
-       req->size = round_down(sg_dma_len(&ais->sg), 4);
+       BUG_ON(pending_buffer_requests(ais));
 
-       if (!req->size) {
-               pr_err("%s: invalid request size %d\n", __func__, req->size);
-               return -EIO;
+       for (i = 0; i < ais->num_bufs; i++) {
+               init_completion(&ais->comp[i]);
+               ais->dma_req[i].dest_addr = ais->buf_phy[i];
+               ais->dma_req[i].size = size;
+               tegra_dma_enqueue_req(ais->dma_chan, &ais->dma_req[i]);
        }
 
-       dma_sync_single_for_device(NULL,
-                       req->dest_addr, req->size, DMA_FROM_DEVICE);
-
-       ais->dma_has_it = !tegra_dma_enqueue_req(ais->dma_chan, &ais->dma_req);
-       if (!ais->dma_has_it) {
-               pr_err("%s: could not enqueue RX DMA req\n", __func__);
-               return -EINVAL;
-       }
+       ais->last_queued = ais->num_bufs - 1;
 
 #if 0
        i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
@@ -1127,19 +964,12 @@ static int resume_dma_recording(struct audio_stream *ais)
        return 0;
 }
 
-/* Called with ais->dma_req_lock taken. */
-static int start_dma_recording(struct audio_stream *ais)
-{
-       pr_debug("%s\n", __func__);
-       return resume_dma_recording(ais);
-}
-
-/* Called with ais->dma_req_lock taken. */
 static void stop_dma_recording(struct audio_stream *ais)
 {
        int spin = 0;
        struct audio_driver_state *ads = ads_from_in(ais);
        pr_debug("%s\n", __func__);
+       tegra_dma_cancel(ais->dma_chan);
        i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0);
        i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
        while ((i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_RX_BUSY) &&
@@ -1152,112 +982,6 @@ static void stop_dma_recording(struct audio_stream *ais)
                pr_warn("%s: spinny\n", __func__);
 }
 
-/* PIO (non-DMA) */
-
-static int setup_pio(struct audio_driver_state *ads)
-{
-       pr_info("%s\n", __func__);
-       enable_irq(ads->irq);
-       return 0;
-}
-
-static void tear_down_pio(struct audio_driver_state *ads)
-{
-       pr_info("%s\n", __func__);
-       disable_irq(ads->irq);
-}
-
-static int start_pio_playback(struct audio_stream *aos)
-{
-       struct audio_driver_state *ads = ads_from_out(aos);
-
-       if (i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_TX)) {
-               pr_debug("%s: playback is already in progress\n", __func__);
-               return -EALREADY;
-       }
-
-       pr_debug("%s\n", __func__);
-
-       i2s_fifo_set_attention_level(ads->i2s_base,
-                       I2S_FIFO_TX, aos->i2s_fifo_atn_level);
-#if 0
-       i2s_fifo_clear(ads->i2s_base, I2S_FIFO_TX);
-#endif
-
-       i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1);
-
-       i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_TX, 1);
-       i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_TX, 1);
-
-       return 0;
-}
-
-static void stop_pio_playback(struct audio_stream *aos)
-{
-       struct audio_driver_state *ads = ads_from_out(aos);
-
-       i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_TX, 0);
-       i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_TX, 0);
-       i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 0);
-       while (i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_TX_BUSY)
-               /* spin */;
-
-       pr_debug("%s: interrupts %d\n", __func__,
-                       ads->pio_stats.i2s_interrupt_count);
-       pr_info("%s: sent       %d\n", __func__,
-                       ads->pio_stats.tx_fifo_written);
-       pr_info("%s: tx errors  %d\n", __func__,
-                       ads->pio_stats.tx_fifo_errors);
-
-       memset(&ads->pio_stats, 0, sizeof(ads->pio_stats));
-}
-
-static int start_pio_recording(struct audio_stream *ais)
-{
-       struct audio_driver_state *ads = ads_from_in(ais);
-
-       if (i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_RX)) {
-               pr_debug("%s: already started\n", __func__);
-               return -EALREADY;
-       }
-
-       pr_debug("%s: start\n", __func__);
-
-       i2s_fifo_set_attention_level(ads->i2s_base,
-                       I2S_FIFO_RX, ais->i2s_fifo_atn_level);
-#if 0
-       i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
-#endif
-
-       i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 1);
-
-       i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_RX, 1);
-       i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_RX, 1);
-
-       return 0;
-}
-
-static void stop_pio_recording(struct audio_stream *ais)
-{
-       struct audio_driver_state *ads = ads_from_in(ais);
-
-       pr_debug("%s\n", __func__);
-
-       i2s_set_fifo_irq_on_err(ads->i2s_base, I2S_FIFO_RX, 0);
-       i2s_set_fifo_irq_on_qe(ads->i2s_base, I2S_FIFO_RX, 0);
-       i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0);
-       i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
-
-       pr_debug("%s: interrupts %d\n", __func__,
-                       ads->pio_stats.i2s_interrupt_count);
-       pr_debug("%s: received   %d\n", __func__,
-                       ads->pio_stats.rx_fifo_read);
-       pr_debug("%s: rx errors  %d\n", __func__,
-                       ads->pio_stats.rx_fifo_errors);
-
-       memset(&ads->pio_stats, 0, sizeof(ads->pio_stats));
-}
-
 static irqreturn_t i2s_interrupt(int irq, void *data)
 {
        struct audio_driver_state *ads = data;
@@ -1265,126 +989,9 @@ static irqreturn_t i2s_interrupt(int irq, void *data)
 
        pr_debug("%s: %08x\n", __func__, status);
 
-       ads->pio_stats.i2s_interrupt_count++;
-
-       if (status & I2S_I2S_FIFO_TX_ERR)
-               ads->pio_stats.tx_fifo_errors++;
-
-       if (status & I2S_I2S_FIFO_RX_ERR) {
-               ads->pio_stats.rx_fifo_errors++;
-               ads->in.errors.full_empty++;
-       }
-
        if (status & I2S_FIFO_ERR)
                i2s_ack_status(ads->i2s_base);
 
-       if (status & I2S_I2S_FIFO_TX_QS) {
-               int written;
-               int empty;
-               int len;
-               u16 fifo_buffer[32];
-
-               struct audio_stream *out = &ads->out;
-
-               if (!i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_TX)) {
-                       pr_debug("%s: tx fifo not enabled, skipping\n",
-                               __func__);
-                       goto check_rx;
-               }
-
-               pr_debug("%s tx fifo is ready\n", __func__);
-
-               if (!completion_done(&out->fifo_completion)) {
-                       pr_debug("%s: tx complete (%d avail)\n", __func__,
-                                       kfifo_avail(&out->fifo));
-                       complete(&out->fifo_completion);
-               }
-
-               if (stop_playback_if_necessary(out)) {
-                       pr_debug("%s: done (stopped)\n", __func__);
-                       if (!completion_done(&out->stop_completion)) {
-                               pr_debug("%s: signalling stop completion\n",
-                                       __func__);
-                               complete(&out->stop_completion);
-                       }
-                       goto check_rx;
-               }
-
-               empty = i2s_get_fifo_full_empty_count(ads->i2s_base,
-                               I2S_FIFO_TX);
-
-               len = kfifo_out(&out->fifo, fifo_buffer,
-                               empty * sizeof(u16));
-               len /= sizeof(u16);
-
-               written = 0;
-               while (empty-- && written < len) {
-                       ads->pio_stats.tx_fifo_written += written * sizeof(u16);
-                       i2s_fifo_write(ads->i2s_base,
-                                       I2S_FIFO_TX, fifo_buffer[written++]);
-               }
-
-               /* TODO: Should we check to see if we wrote less than the
-                * FIFO threshold and adjust it if so?
-                */
-
-               if (written) {
-                       /* start the transaction */
-                       pr_debug("%s: enabling fifo (%d samples written)\n",
-                                       __func__, written);
-                       i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1);
-               }
-       }
-
-check_rx:
-       if (status & I2S_I2S_FIFO_RX_QS) {
-               int nr;
-               int full;
-
-               struct audio_stream *in = &ads->in;
-
-               if (!i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_RX)) {
-                       pr_debug("%s: rx fifo not enabled, skipping\n",
-                               __func__);
-                       goto done;
-               }
-
-               i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 0);
-
-               full = i2s_get_fifo_full_empty_count(ads->i2s_base,
-                               I2S_FIFO_RX);
-
-               pr_debug("%s rx fifo is ready (%d samples)\n", __func__, full);
-
-               nr = full;
-               while (nr--) {
-                       u16 sample = i2s_fifo_read(ads->i2s_base, I2S_FIFO_RX);
-                       kfifo_in(&in->fifo, &sample, sizeof(sample));
-               }
-
-               ads->pio_stats.rx_fifo_read += full * sizeof(u16);
-
-               if (!completion_done(&in->fifo_completion)) {
-                       pr_debug("%s: rx complete (%d avail)\n", __func__,
-                                       kfifo_avail(&in->fifo));
-                       complete(&in->fifo_completion);
-               }
-
-               if (stop_recording_if_necessary_nosync(&ads->in)) {
-                       pr_debug("%s: recording cancelled or fifo full\n",
-                               __func__);
-                       if (!completion_done(&ads->in.stop_completion)) {
-                               pr_debug("%s: signalling stop completion\n",
-                                       __func__);
-                               complete(&ads->in.stop_completion);
-                       }
-                       goto done;
-               }
-
-               i2s_fifo_enable(ads->i2s_base, I2S_FIFO_RX, 1);
-       }
-
-done:
        pr_debug("%s: done %08x\n", __func__, i2s_get_status(ads->i2s_base));
        return IRQ_HANDLED;
 }
@@ -1392,60 +999,68 @@ done:
 static ssize_t tegra_audio_write(struct file *file,
                const char __user *buf, size_t size, loff_t *off)
 {
-       ssize_t rc = 0, total = 0;
-       unsigned nw = 0;
-
+       ssize_t rc = 0;
+       int out_buf;
+       struct tegra_dma_req *req;
        struct audio_driver_state *ads = ads_from_misc_out(file);
 
        mutex_lock(&ads->out.lock);
 
-       if (!IS_ALIGNED(size, 4)) {
-               pr_err("%s: user size request %d not aligned to 4\n",
-                       __func__, size);
+       if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->out)) {
+               pr_err("%s: invalid user size %d\n", __func__, size);
                rc = -EINVAL;
                goto done;
        }
 
-       pr_debug("%s: write %d bytes, %d available\n", __func__,
-                       size, kfifo_avail(&ads->out.fifo));
+       pr_debug("%s: write %d bytes\n", __func__, size);
 
-again:
        if (ads->out.stop) {
-               pr_info("%s: playback has been cancelled (%d/%d bytes)\n",
-                               __func__, total, size);
+               pr_debug("%s: playback has been cancelled\n", __func__);
                goto done;
        }
 
-       rc = kfifo_from_user(&ads->out.fifo, buf + total, size - total, &nw);
-       if (rc < 0) {
-               pr_err("%s: error copying from user\n", __func__);
+       /* Decide which buf is next. */
+       out_buf = (ads->out.last_queued + 1) % ads->out.num_bufs;
+       req = &ads->out.dma_req[out_buf];
+
+       /* Wait for the buffer to be emptied (complete).  The maximum timeout
+        * value could be calculated dynamically based on buf_size(&ads->out).
+        * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
+        * have ~93ms.
+        */
+       pr_debug("%s: waiting for buffer %d\n", __func__, out_buf);
+       rc = wait_for_completion_interruptible_timeout(
+                               &ads->out.comp[out_buf], HZ);
+       if (!rc) {
+               pr_err("%s: timeout", __func__);
+               rc = -ETIMEDOUT;
+               goto done;
+       } else if (rc < 0) {
+               pr_err("%s: wait error %d", __func__, rc);
                goto done;
        }
 
-       rc = start_playback(&ads->out);
-       if (rc < 0 && rc != -EALREADY) {
-               pr_err("%s: could not start playback: %d\n", __func__, rc);
+       /* Fill the buffer and enqueue it. */
+       pr_debug("%s: acquired buffer %d, copying data\n", __func__, out_buf);
+       rc = copy_from_user(ads->out.buffer[out_buf], buf, size);
+       if (rc) {
+               rc = -EFAULT;
                goto done;
        }
 
-       total += nw;
-       if (total < size) {
-               pr_debug("%s: sleep (user %d total %d nw %d)\n", __func__,
-                               size, total, nw);
-               mutex_unlock(&ads->out.lock);
-               rc = wait_for_completion_interruptible(
-                               &ads->out.fifo_completion);
-               mutex_lock(&ads->out.lock);
-               if (rc == -ERESTARTSYS) {
-                       pr_warn("%s: interrupted\n", __func__);
-                       goto done;
-               }
-               pr_debug("%s: awake\n", __func__);
-               goto again;
-       }
+       prevent_suspend(&ads->out);
+
+       req->size = size;
+       dma_sync_single_for_device(NULL,
+                       req->source_addr, req->size, DMA_TO_DEVICE);
+       ads->out.last_queued = out_buf;
+       init_completion(&ads->out.stop_completion);
 
-       rc = total;
-       *off += total;
+       rc = start_playback(&ads->out, req);
+       if (!rc)
+               rc = size;
+       else
+               allow_suspend(&ads->out);
 
 done:
        mutex_unlock(&ads->out.lock);
@@ -1462,43 +1077,42 @@ static long tegra_audio_out_ioctl(struct file *file,
        mutex_lock(&aos->lock);
 
        switch (cmd) {
-       case TEGRA_AUDIO_OUT_SET_BUF_CONFIG: {
-               struct tegra_audio_buf_config cfg;
-               if (copy_from_user(&cfg, (void __user *)arg, sizeof(cfg))) {
+       case TEGRA_AUDIO_OUT_FLUSH:
+               if (pending_buffer_requests(aos)) {
+                       pr_debug("%s: flushing\n", __func__);
+                       request_stop_nosync(aos);
+                       pr_debug("%s: flushed\n", __func__);
+               }
+               aos->stop = false;
+               break;
+       case TEGRA_AUDIO_OUT_SET_NUM_BUFS: {
+               unsigned int num;
+               if (copy_from_user(&num, (const void __user *)arg,
+                                       sizeof(num))) {
                        rc = -EFAULT;
                        break;
                }
-               if (kfifo_len(&aos->fifo)) {
+               if (!num || num > I2S_MAX_NUM_BUFS) {
+                       pr_err("%s: invalid buffer count %d\n", __func__, num);
+                       rc = -EINVAL;
+                       break;
+               }
+               if (pending_buffer_requests(aos)) {
                        pr_err("%s: playback in progress\n", __func__);
                        rc = -EBUSY;
                        break;
                }
-               rc = init_stream_buffer(aos, &cfg, 0);
+               rc = init_stream_buffer(aos, num);
                if (rc < 0)
                        break;
-               aos->buf_config = cfg;
+               aos->num_bufs = num;
        }
                break;
-       case TEGRA_AUDIO_OUT_GET_BUF_CONFIG:
-               if (copy_to_user((void __user *)arg, &aos->buf_config,
-                               sizeof(aos->buf_config)))
+       case TEGRA_AUDIO_OUT_GET_NUM_BUFS:
+               if (copy_to_user((void __user *)arg,
+                               &aos->num_bufs, sizeof(aos->num_bufs)))
                        rc = -EFAULT;
                break;
-       case TEGRA_AUDIO_OUT_GET_ERROR_COUNT:
-               if (copy_to_user((void __user *)arg, &aos->errors,
-                               sizeof(aos->errors)))
-                       rc = -EFAULT;
-               if (!rc)
-                       memset(&aos->errors, 0, sizeof(aos->errors));
-               break;
-       case TEGRA_AUDIO_OUT_FLUSH:
-               if (kfifo_len(&aos->fifo)) {
-                       pr_debug("%s: flushing\n", __func__);
-                       request_stop_nosync(aos);
-                       pr_debug("%s: flushed\n", __func__);
-               }
-               aos->stop = false;
-               break;
        default:
                rc = -EINVAL;
        }
@@ -1548,10 +1162,10 @@ static long tegra_audio_ioctl(struct file *file,
                goto done;
        }
 
-       if (dma_restart && ads->using_dma) {
+       if (dma_restart) {
                pr_debug("%s: Restarting DMA due to configuration change.\n",
                        __func__);
-               if (kfifo_len(&ads->out.fifo) || ads->in.active) {
+               if (pending_buffer_requests(&ads->out) || ads->in.active) {
                        pr_err("%s: dma busy, cannot restart.\n", __func__);
                        rc = -EBUSY;
                        goto done;
@@ -1580,13 +1194,16 @@ static long tegra_audio_in_ioctl(struct file *file,
        case TEGRA_AUDIO_IN_START:
                pr_debug("%s: start recording\n", __func__);
                ais->stop = false;
-               rc = start_recording_if_necessary(ais);
-               ais->active = !rc || rc == -EALREADY;
                break;
        case TEGRA_AUDIO_IN_STOP:
-               pr_debug("%s: start recording\n", __func__);
-               if (ais->active)
+               pr_debug("%s: stop recording\n", __func__);
+               if (ais->active) {
+                       /* Clean up DMA/I2S, and complete the completion */
+                       sound_ops->stop_recording(ais);
+                       complete(&ais->stop_completion);
+                       /* Set stop flag and allow suspend. */
                        request_stop_nosync(ais);
+               }
                break;
        case TEGRA_AUDIO_IN_SET_CONFIG: {
                struct tegra_audio_in_config cfg;
@@ -1602,35 +1219,6 @@ static long tegra_audio_in_ioctl(struct file *file,
                        break;
                }
 
-#ifdef SAMPLE_RATE_CONVERTER_IN_DRIVER
-               switch (cfg.rate) {
-               case 8000:
-                       ads->in_divs = divs_8000;
-                       ads->in_divs_len = ARRAY_SIZE(divs_8000);
-                       break;
-               case 11025:
-                       ads->in_divs = divs_11025;
-                       ads->in_divs_len = ARRAY_SIZE(divs_11025);
-                       break;
-               case 16000:
-                       ads->in_divs = divs_16000;
-                       ads->in_divs_len = ARRAY_SIZE(divs_16000);
-                       break;
-               case 22050:
-                       ads->in_divs = divs_22050;
-                       ads->in_divs_len = ARRAY_SIZE(divs_22050);
-                       break;
-               case 44100:
-                       ads->in_divs = divs_44100;
-                       ads->in_divs_len = ARRAY_SIZE(divs_44100);
-                       break;
-               default:
-                       pr_err("%s: invalid sampling rate %d\n", __func__,
-                               cfg.rate);
-                       rc = -EINVAL;
-                       break;
-               }
-#endif
                if (cfg.stereo && !ads->pdata->stereo_capture) {
                        pr_err("%s: not capable of stereo capture.",
                                __func__);
@@ -1650,34 +1238,34 @@ static long tegra_audio_in_ioctl(struct file *file,
                                sizeof(ads->in_config)))
                        rc = -EFAULT;
                break;
-       case TEGRA_AUDIO_IN_SET_BUF_CONFIG: {
-               struct tegra_audio_buf_config cfg;
-               if (copy_from_user(&cfg, (void __user *)arg, sizeof(cfg))) {
+       case TEGRA_AUDIO_IN_SET_NUM_BUFS: {
+               unsigned int num;
+               if (copy_from_user(&num, (const void __user *)arg,
+                                       sizeof(num))) {
                        rc = -EFAULT;
                        break;
                }
-               if (ais->active) {
+               if (!num || num > I2S_MAX_NUM_BUFS) {
+                       pr_err("%s: invalid buffer count %d\n", __func__,
+                               num);
+                       rc = -EINVAL;
+                       break;
+               }
+               if (ais->active || pending_buffer_requests(ais)) {
                        pr_err("%s: recording in progress\n", __func__);
                        rc = -EBUSY;
                        break;
                }
-               rc = init_stream_buffer(ais, &cfg, PCM_IN_BUFFER_PADDING);
+               rc = init_stream_buffer(ais, num);
                if (rc < 0)
                        break;
-               ais->buf_config = cfg;
+               ais->num_bufs = num;
        }
                break;
-       case TEGRA_AUDIO_IN_GET_BUF_CONFIG:
-               if (copy_to_user((void __user *)arg, &ais->buf_config,
-                               sizeof(ais->buf_config)))
-                       rc = -EFAULT;
-               break;
-       case TEGRA_AUDIO_IN_GET_ERROR_COUNT:
-               if (copy_to_user((void __user *)arg, &ais->errors,
-                               sizeof(ais->errors)))
+       case TEGRA_AUDIO_IN_GET_NUM_BUFS:
+               if (copy_from_user((void __user *)arg,
+                               &ais->num_bufs, sizeof(ais->num_bufs)))
                        rc = -EFAULT;
-               if (!rc)
-                       memset(&ais->errors, 0, sizeof(ais->errors));
                break;
        default:
                rc = -EINVAL;
@@ -1687,293 +1275,119 @@ static long tegra_audio_in_ioctl(struct file *file,
        return rc;
 }
 
-static ssize_t __i2s_copy_to_user(struct audio_driver_state *ads,
-                               void __user *dst, int dst_size,
-                               void *src, int src_size,
-                               int *num_consumed)
-{
-       int bytes_written = dst_size < src_size ? dst_size : src_size;
-       *num_consumed = bytes_written;
-       if (copy_to_user(dst, src, bytes_written)) {
-               pr_err("%s: error copying %d bytes to user\n", __func__,
-                       bytes_written);
-               return -EFAULT;
-       }
-       return bytes_written;
-}
-
-#ifdef SAMPLE_RATE_CONVERTER_IN_DRIVER
-
-/* downsample a 16-bit 44.1kHz PCM stereo stream to stereo or mono 16-bit PCM
- * stream.
- */
-
-static int downsample(const s16 *in, int in_len,
-               s16 *out, int out_len,
-               int *consumed, /* from input */
-               const int *divs, int divs_len,
-               bool out_stereo)
-{
-       /* Todo: Handle mono source streams */
-       int i, j;
-       int lsum, rsum;
-       int di, div;
-       int oi;
-
-       i = 0;
-       oi = 0;
-       di = 0;
-       div = divs[0];
-       while (i + div * 2 <= in_len && oi + out_stereo < out_len) {
-               for (j = 0, lsum = 0, rsum = 0; j < div; j++) {
-                       lsum += in[i + j * 2];
-                       rsum += in[i + j * 2 + 1];
-               }
-               if (!out_stereo)
-                       out[oi] = (lsum + rsum) / (div * 2);
-               else {
-                       out[oi] = lsum / div;
-                       out[oi + 1] = rsum / div;
-               }
-
-               oi += out_stereo + 1;
-               i += div * 2;
-               div = divs[++di % divs_len];
-       }
-
-       *consumed = i;
-
-       pr_debug("%s: in_len %d out_len %d consumed %d generated %d\n",
-               __func__, in_len, out_len, *consumed, oi);
-       return oi;
-}
-
-static ssize_t __downsample_to_user(struct audio_driver_state *ads,
-                               void __user *dst, int dst_size,
-                               void *src, int src_size,
-                               int *num_consumed)
-{
-       int bytes_ds;
-
-       pr_debug("%s\n", __func__);
-
-       bytes_ds = downsample(src, src_size / sizeof(s16),
-                       src, dst_size / sizeof(s16),
-                       num_consumed,
-                       ads->in_divs, ads->in_divs_len,
-                       ads->in_config.stereo) * sizeof(s16);
-
-       if (copy_to_user(dst, src, bytes_ds)) {
-               pr_err("%s: error copying %d bytes to user\n", __func__,
-                       bytes_ds);
-               return -EFAULT;
-       }
-
-       *num_consumed *= sizeof(s16);
-       BUG_ON(*num_consumed > src_size);
-
-       pr_debug("%s: generated %d, skipped %d, original in fifo %d\n",
-                       __func__, bytes_ds, *num_consumed, src_size);
-
-       return bytes_ds;
-}
-#endif /*SAMPLE_RATE_CONVERTER_IN_DRIVER*/
-
-static ssize_t downsample_to_user(struct audio_driver_state *ads,
-                       void __user *buf,
-                       size_t size) /* bytes to write to user buffer */
-{
-       int i, nr_sg;
-       int bytes_consumed_from_fifo, bc_now;
-       int bytes_ds, ds_now;
-       bool take_two = false;
-
-       struct scatterlist sgl[PCM_BUFFER_MAX_SIZE_ORDER - PAGE_SHIFT];
-       sg_init_table(sgl, ARRAY_SIZE(sgl));
-
-       if (size == 0) {
-               pr_debug("%s: user buffer is full\n", __func__);
-               return 0;
-       }
-
-       if (kfifo_is_empty(&ads->in.fifo)) {
-               pr_debug("%s: input fifo is empty\n", __func__);
-               return 0;
-       }
-
-       nr_sg = kfifo_dma_out_prepare(&ads->in.fifo,
-                               sgl, ARRAY_SIZE(sgl),
-                               kfifo_len(&ads->in.fifo));
-       BUG_ON(!nr_sg);
-
-       pr_debug("%s (fifo size %d)\n", __func__, size);
-
-       bytes_ds = 0;
-       bytes_consumed_from_fifo = 0;
-       for (bytes_ds = 0, i = 0; i < nr_sg; i++) {
-               BUG_ON(!sgl[i].length);
-
-again:
-#ifdef SAMPLE_RATE_CONVERTER_IN_DRIVER
-               ds_now = __downsample_to_user(
-#else
-               ds_now = __i2s_copy_to_user(
-#endif
-                                       ads,
-                                       buf, size,
-                                       sg_virt(&sgl[i]), sgl[i].length,
-                                       &bc_now);
-
-               if (!ds_now && !sg_is_last(sgl + i)) {
-
-                       BUG_ON(bc_now);
-                       BUG_ON(take_two);
-                       take_two = true;
-
-                       /* The assumption is that this sgl entry is at the end
-                        * of the fifo, and there isn't enough space till the
-                        * end of the fifo for at least one target sample to be
-                        * generated.  When this happens, we copy enough bytes
-                        * from the next sgl entry to the end of the buffer of
-                        * the current one, knowing that the copied bytes will
-                        * cause the fifo to wrap around.  We adjust the next
-                        * entry, and continue with the loop.
-                        */
-
-                       BUG_ON(sg_virt(&sgl[i]) + sgl[i].length !=
-                               ads->in.buffer + kfifo_size(&ads->in.fifo));
-
-                       if (sgl[i + 1].length < PCM_IN_BUFFER_PADDING) {
-                               pr_debug("%s: not enough data till end of fifo\n",
-                                               __func__);
-                               return 0;
-                       }
-
-                       memcpy(sg_virt(&sgl[i]) + sgl[i].length,
-                               sg_virt(&sgl[i + 1]),
-                               PCM_IN_BUFFER_PADDING);
-                       sgl[i].length += PCM_IN_BUFFER_PADDING;
-
-                       sg_set_buf(&sgl[i + 1],
-                               sg_virt(&sgl[i + 1]) + PCM_IN_BUFFER_PADDING,
-                               sgl[i + 1].length - PCM_IN_BUFFER_PADDING);
-
-                       goto again;
-               }
-
-               bytes_ds += ds_now;
-               buf += ds_now;
-               BUG_ON(ds_now > size);
-               size -= ds_now;
-               bytes_consumed_from_fifo += bc_now;
-               pr_debug("%s: downsampled (%d req, %d actual)" \
-                       " -> total ds %d (size %d)\n", __func__,
-                       sgl[i].length, bytes_consumed_from_fifo,
-                       bytes_ds, size);
-               if (sg_is_last(sgl + i))
-                       break;
-       }
-
-       kfifo_dma_out_finish(&ads->in.fifo, bytes_consumed_from_fifo);
-
-       return bytes_ds;
-}
-
 static ssize_t tegra_audio_read(struct file *file, char __user *buf,
                        size_t size, loff_t *off)
 {
-       ssize_t rc, total = 0;
-       ssize_t nr;
-
+       ssize_t rc;
+       ssize_t nr = 0;
+       int in_buf;
+       struct tegra_dma_req *req;
        struct audio_driver_state *ads = ads_from_misc_in(file);
 
        mutex_lock(&ads->in.lock);
 
-       if (!IS_ALIGNED(size, 4)) {
-               pr_err("%s: user size request %d not aligned to 4\n",
-                       __func__, size);
+       if (!IS_ALIGNED(size, 4) || size < 4 || size > buf_size(&ads->in)) {
+               pr_err("%s: invalid size %d.\n", __func__, size);
                rc = -EINVAL;
-               goto done_err;
+               goto done;
        }
 
-       pr_debug("%s:%d: read %d bytes, %d available\n", __func__,
-                       smp_processor_id(),
-                       size, kfifo_len(&ads->in.fifo));
+       pr_debug("%s: size %d\n", __func__, size);
 
-again:
        /* If we want recording to stop immediately after it gets cancelled,
         * then we do not want to wait for the fifo to get drained.
         */
-       if (ads->in.stop /* && kfifo_is_empty(&ads->in.fifo) */) {
-               pr_debug("%s: recording has been cancelled (%d/%d bytes)\n",
-                               __func__, total, size);
-               goto done_ok;
+       if (ads->in.stop) {
+               pr_debug("%s: recording has been cancelled\n", __func__);
+               rc = 0;
+               goto done;
        }
 
-       rc = start_recording_if_necessary(&ads->in);
+       /* This function calls prevent_suspend() internally */
+       rc = start_recording_if_necessary(&ads->in, size);
        if (rc < 0 && rc != -EALREADY) {
                pr_err("%s: could not start recording\n", __func__);
-               goto done_err;
+               goto done;
        }
 
        ads->in.active = true;
 
-       nr = 0;
-       do {
-               nr = downsample_to_user(ads, buf + total, size - total);
-               if (nr < 0) {
-                       rc = nr;
-                       goto done_err;
-               }
-               total += nr;
-       } while (nr);
-
-       pr_debug("%s: copied %d bytes to user, total %d/%d\n",
-                       __func__, nr, total, size);
-
-       if (total < size) {
-               mutex_unlock(&ads->in.lock);
-               pr_debug("%s: sleep (user %d total %d nr %d)\n", __func__,
-                               size, total, nr);
-               rc = wait_for_completion_interruptible(
-                               &ads->in.fifo_completion);
-               pr_debug("%s: awake\n", __func__);
-               mutex_lock(&ads->in.lock);
-               if (rc == -ERESTARTSYS) {
-                       pr_warn("%s: interrupted\n", __func__);
-                       goto done_err;
-               }
-               goto again;
+       /* Note that when tegra_audio_read() is called for the first time (or
+        * when all the buffers are empty), then it queues up all
+        * ads->in.num_bufs buffers, and in_buf is set to zero below.
+        */
+       in_buf = (ads->in.last_queued + 1) % ads->in.num_bufs;
+
+       /* Wait for the buffer to be filled (complete).  The maximum timeout
+        * value could be calculated dynamically based on buf_size(&ads->in).
+        * For a buffer size of 16k, at 44.1kHz/stereo/16-bit PCM, you would
+        * have ~93ms.
+        */
+       rc = wait_for_completion_interruptible_timeout(
+                               &ads->in.comp[in_buf], HZ);
+       if (!rc) {
+               pr_err("%s: timeout", __func__);
+               rc = -ETIMEDOUT;
+               goto done;
+       } else if (rc < 0) {
+               pr_err("%s: wait error %d", __func__, rc);
+               goto done;
        }
 
-       pr_debug("%s: done reading %d bytes, %d available\n", __func__,
-                       total, kfifo_avail(&ads->in.fifo));
+       req = &ads->in.dma_req[in_buf];
 
-done_ok:
-       rc = total;
-       *off += total;
+       nr = size > req->size ? req->size : size;
+       req->size = size;
+       dma_sync_single_for_cpu(NULL, ads->in.dma_req[in_buf].dest_addr,
+                               ads->in.dma_req[in_buf].size, DMA_FROM_DEVICE);
+       rc = copy_to_user(buf, ads->in.buffer[in_buf], nr);
+       if (rc) {
+               rc = -EFAULT;
+               goto done;
+       }
+
+       init_completion(&ads->in.stop_completion);
 
-done_err:
+       ads->in.last_queued = in_buf;
+       rc = tegra_dma_enqueue_req(ads->in.dma_chan, req);
+       /* We've successfully enqueued this request before. */
+       BUG_ON(rc);
+
+       rc = nr;
+       *off += nr;
+done:
        mutex_unlock(&ads->in.lock);
+       pr_debug("%s: done %d\n", __func__, rc);
        return rc;
 }
 
 static int tegra_audio_out_open(struct inode *inode, struct file *file)
 {
+       int rc = 0;
+       int i;
        struct audio_driver_state *ads = ads_from_misc_out(file);
 
        pr_debug("%s\n", __func__);
 
        mutex_lock(&ads->out.lock);
-       if (!ads->out.opened++) {
-               pr_debug("%s: resetting fifo and error count\n", __func__);
-               ads->out.stop = false;
-               memset(&ads->out.errors, 0, sizeof(ads->out.errors));
-               kfifo_reset(&ads->out.fifo);
+
+       if (ads->out.opened) {
+               rc = -EBUSY;
+               goto done;
        }
-       mutex_unlock(&ads->out.lock);
 
-       return 0;
+       ads->out.opened = 1;
+       ads->out.stop = false;
+
+       for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+               init_completion(&ads->out.comp[i]);
+               /* TX buf rest state is unqueued, complete. */
+               complete(&ads->out.comp[i]);
+       }
+
+done:
+       mutex_unlock(&ads->out.lock);
+       return rc;
 }
 
 static int tegra_audio_out_release(struct inode *inode, struct file *file)
@@ -1983,40 +1397,40 @@ static int tegra_audio_out_release(struct inode *inode, struct file *file)
        pr_debug("%s\n", __func__);
 
        mutex_lock(&ads->out.lock);
-       if (ads->out.opened)
-               ads->out.opened--;
-       if (!ads->out.opened) {
-               stop_playback_if_necessary(&ads->out);
-               if (kfifo_len(&ads->out.fifo))
-                       pr_err("%s: output fifo is not empty (%d bytes left)\n",
-                               __func__, kfifo_len(&ads->out.fifo));
-               allow_suspend(&ads->out);
-       }
+       ads->out.opened = 0;
+       request_stop_nosync(&ads->out);
+       allow_suspend(&ads->out);
        mutex_unlock(&ads->out.lock);
-
+       pr_debug("%s: done\n", __func__);
        return 0;
 }
 
 static int tegra_audio_in_open(struct inode *inode, struct file *file)
 {
+       int rc = 0;
+       int i;
        struct audio_driver_state *ads = ads_from_misc_in(file);
 
        pr_debug("%s\n", __func__);
 
        mutex_lock(&ads->in.lock);
-       if (!ads->in.opened++) {
-               pr_debug("%s: resetting fifo\n", __func__);
-               /* By default, do not start recording when someone reads from
-                * input device.
-                */
-               ads->in.stop = false;
-               memset(&ads->in.errors, 0, sizeof(ads->in.errors));
-               kfifo_reset(&ads->in.fifo);
+       if (ads->in.opened) {
+               rc = -EBUSY;
+               goto done;
        }
-       mutex_unlock(&ads->in.lock);
 
-       pr_debug("%s: done\n", __func__);
-       return 0;
+       ads->in.opened = 1;
+       ads->in.stop = false;
+
+       for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+               init_completion(&ads->in.comp[i]);
+               /* RX buf rest state is unqueued, complete. */
+               complete(&ads->in.comp[i]);
+       }
+
+done:
+       mutex_unlock(&ads->in.lock);
+       return rc;
 }
 
 static int tegra_audio_in_release(struct inode *inode, struct file *file)
@@ -2026,18 +1440,13 @@ static int tegra_audio_in_release(struct inode *inode, struct file *file)
        pr_debug("%s\n", __func__);
 
        mutex_lock(&ads->in.lock);
-       if (ads->in.opened)
-               ads->in.opened--;
-
-       if (!ads->in.opened) {
-               if (ads->in.active)
-                       request_stop_nosync(&ads->in);
-               if (kfifo_len(&ads->in.fifo))
-                       pr_err("%s: input fifo is not empty (%d bytes left)\n",
-                               __func__, kfifo_len(&ads->in.fifo));
-               allow_suspend(&ads->in);
+       ads->in.opened = 0;
+       if (ads->in.active) {
+               sound_ops->stop_recording(&ads->in);
+               complete(&ads->in.stop_completion);
+               request_stop_nosync(&ads->in);
        }
-
+       allow_suspend(&ads->in);
        mutex_unlock(&ads->in.lock);
        pr_debug("%s: done\n", __func__);
        return 0;
@@ -2088,56 +1497,25 @@ static const struct file_operations tegra_audio_ctl_fops = {
        .unlocked_ioctl = tegra_audio_ioctl,
 };
 
-static int init_stream_buffer(struct audio_stream *s,
-                               struct tegra_audio_buf_config *cfg,
-                               unsigned padding)
+static int init_stream_buffer(struct audio_stream *s, int num)
 {
-       pr_debug("%s (size %d threshold %d chunk %d)\n", __func__,
-               cfg->size, cfg->threshold, cfg->chunk);
-
-       if (cfg->chunk < PCM_DMA_CHUNK_MIN_SIZE_ORDER) {
-               pr_err("%s: chunk %d too small (%d min)\n", __func__,
-                       cfg->chunk, PCM_DMA_CHUNK_MIN_SIZE_ORDER);
-               return -EINVAL;
-       }
-
-       if (cfg->chunk > cfg->size) {
-               pr_err("%s: chunk %d > size %d\n", __func__,
-                               cfg->chunk, cfg->size);
-               return -EINVAL;
-       }
-
-       if (cfg->threshold > cfg->size) {
-               pr_err("%s: threshold %d > size %d\n", __func__,
-                               cfg->threshold, cfg->size);
-               return -EINVAL;
-       }
-
-       if ((1 << cfg->size) < padding) {
-               pr_err("%s: size %d < buffer padding %d (bytes)\n", __func__,
-                       cfg->size, padding);
-               return -EINVAL;
-       }
-
-       if (cfg->size > PCM_BUFFER_MAX_SIZE_ORDER) {
-               pr_err("%s: size %d exceeds max %d\n", __func__,
-                       cfg->size, PCM_BUFFER_MAX_SIZE_ORDER);
-               return -EINVAL;
-       }
-
-       if (!s->buffer) {
-               pr_debug("%s: allocating buffer (size %d, padding %d)\n",
-                               __func__, 1 << cfg->size, padding);
-               s->buffer = kmalloc((1 << cfg->size) + padding,
-                               GFP_KERNEL | GFP_DMA);
-       }
-       if (!s->buffer) {
-               pr_err("%s: could not allocate output buffer\n", __func__);
-               return -ENOMEM;
+       int i, j;
+       pr_debug("%s (num %d)\n", __func__,  num);
+
+       for (i = 0; i < num; i++) {
+               kfree(s->buffer[i]);
+               s->buffer[i] =
+                       kmalloc((1 << PCM_BUFFER_MAX_SIZE_ORDER),
+                                       GFP_KERNEL | GFP_DMA);
+               if (!s->buffer[i]) {
+                       pr_err("%s: could not allocate buffer\n", __func__);
+                       for (j = i - 1; j >= 0; j--) {
+                               kfree(s->buffer[j]);
+                               s->buffer[j] = 0;
+                       }
+                       return -ENOMEM;
+               }
        }
-
-       kfifo_init(&s->fifo, s->buffer, 1 << cfg->size);
-       sg_init_table(&s->sg, 1);
        return 0;
 }
 
@@ -2178,47 +1556,15 @@ static ssize_t dma_toggle_show(struct device *dev,
                                struct device_attribute *attr,
                                char *buf)
 {
-       struct tegra_audio_platform_data *pdata = dev->platform_data;
-       struct audio_driver_state *ads = pdata->driver_data;
-       return sprintf(buf, "%s\n", ads->using_dma ? "dma" : "pio");
+       return sprintf(buf, "dma\n");
 }
 
 static ssize_t dma_toggle_store(struct device *dev,
                        struct device_attribute *attr,
                        const char *buf, size_t count)
 {
-       int use_dma;
-       struct tegra_audio_platform_data *pdata = dev->platform_data;
-       struct audio_driver_state *ads = pdata->driver_data;
-
-       if (count < 4)
-               return -EINVAL;
-
-       use_dma = 0;
-       if (!strncmp(buf, "dma", 3))
-               use_dma = 1;
-       else if (strncmp(buf, "pio", 3)) {
-               dev_err(dev, "%s: invalid string [%s]\n", __func__, buf);
-               return -EINVAL;
-       }
-
-       mutex_lock(&ads->out.lock);
-       mutex_lock(&ads->in.lock);
-       if (kfifo_len(&ads->out.fifo) || ads->in.active) {
-               dev_err(dev, "%s: playback or recording in progress.\n",
-                       __func__);
-               mutex_unlock(&ads->in.lock);
-               mutex_unlock(&ads->out.lock);
-               return -EBUSY;
-       }
-       if (!!use_dma ^ !!ads->using_dma)
-               toggle_dma(ads);
-       else
-               dev_info(dev, "%s: no change\n", __func__);
-       mutex_unlock(&ads->in.lock);
-       mutex_unlock(&ads->out.lock);
-
-       return count;
+       pr_err("%s: Not implemented.", __func__);
+       return 0;
 }
 
 static DEVICE_ATTR(dma_toggle, 0644, dma_toggle_show, dma_toggle_store);
@@ -2302,7 +1648,7 @@ static ssize_t tx_fifo_atn_store(struct device *dev,
        struct tegra_audio_platform_data *pdata = dev->platform_data;
        struct audio_driver_state *ads = pdata->driver_data;
        mutex_lock(&ads->out.lock);
-       if (kfifo_len(&ads->out.fifo)) {
+       if (pending_buffer_requests(&ads->out)) {
                pr_err("%s: playback in progress.\n", __func__);
                rc = -EBUSY;
                goto done;
@@ -2351,7 +1697,7 @@ static DEVICE_ATTR(rx_fifo_atn, 0644, rx_fifo_atn_show, rx_fifo_atn_store);
 
 static int tegra_audio_probe(struct platform_device *pdev)
 {
-       int rc;
+       int rc, i;
        struct resource *res;
        struct clk *i2s_clk, *audio_sync_clk, *dap_mclk;
        struct audio_driver_state *state;
@@ -2406,8 +1752,6 @@ static int tegra_audio_probe(struct platform_device *pdev)
        }
        state->irq = res->start;
 
-       memset(&state->pio_stats, 0, sizeof(state->pio_stats));
-
        i2s_clk = clk_get(&pdev->dev, NULL);
        if (!i2s_clk) {
                dev_err(&pdev->dev, "%s: could not get i2s clock\n",
@@ -2447,25 +1791,24 @@ static int tegra_audio_probe(struct platform_device *pdev)
                state->out.opened = 0;
                state->out.active = false;
                mutex_init(&state->out.lock);
-               init_completion(&state->out.fifo_completion);
                init_completion(&state->out.stop_completion);
                spin_lock_init(&state->out.dma_req_lock);
-               state->out.buf_phys = 0;
                state->out.dma_chan = NULL;
-               state->out.dma_has_it = false;
-
                state->out.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
-               state->out.buffer = 0;
-               state->out.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER;
-               state->out.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER;
-               state->out.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER;
-               rc = init_stream_buffer(&state->out, &state->out.buf_config, 0);
+               state->out.num_bufs = I2S_DEFAULT_TX_NUM_BUFS;
+               for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+                       init_completion(&state->out.comp[i]);
+                       /* TX buf rest state is unqueued, complete. */
+                       complete(&state->out.comp[i]);
+                       state->out.buffer[i] = 0;
+                       state->out.buf_phy[i] = 0;
+               }
+               state->out.last_queued = 0;
+               rc = init_stream_buffer(&state->out, state->out.num_bufs);
                if (rc < 0)
                        return rc;
 
                INIT_WORK(&state->out.allow_suspend_work, allow_suspend_worker);
-               pm_qos_add_request(&state->out.pm_qos, PM_QOS_CPU_DMA_LATENCY,
-                               PM_QOS_DEFAULT_VALUE);
 
                rc = setup_misc_device(&state->misc_out,
                        &tegra_audio_out_fops,
@@ -2484,26 +1827,24 @@ static int tegra_audio_probe(struct platform_device *pdev)
                state->in.opened = 0;
                state->in.active = false;
                mutex_init(&state->in.lock);
-               init_completion(&state->in.fifo_completion);
                init_completion(&state->in.stop_completion);
                spin_lock_init(&state->in.dma_req_lock);
-               state->in.buf_phys = 0;
                state->in.dma_chan = NULL;
-               state->in.dma_has_it = false;
-
                state->in.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
-               state->in.buffer = 0;
-               state->in.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER;
-               state->in.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER;
-               state->in.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER;
-               rc = init_stream_buffer(&state->in, &state->in.buf_config,
-                               PCM_IN_BUFFER_PADDING);
+               state->in.num_bufs = I2S_DEFAULT_RX_NUM_BUFS;
+               for (i = 0; i < I2S_MAX_NUM_BUFS; i++) {
+                       init_completion(&state->in.comp[i]);
+                       /* RX buf rest state is unqueued, complete. */
+                       complete(&state->in.comp[i]);
+                       state->in.buffer[i] = 0;
+                       state->in.buf_phy[i] = 0;
+               }
+               state->in.last_queued = 0;
+               rc = init_stream_buffer(&state->in, state->in.num_bufs);
                if (rc < 0)
                        return rc;
 
                INIT_WORK(&state->in.allow_suspend_work, allow_suspend_worker);
-               pm_qos_add_request(&state->in.pm_qos, PM_QOS_CPU_DMA_LATENCY,
-                                       PM_QOS_DEFAULT_VALUE);
 
                rc = setup_misc_device(&state->misc_in,
                        &tegra_audio_in_fops,
@@ -2532,9 +1873,6 @@ static int tegra_audio_probe(struct platform_device *pdev)
        if (rc < 0)
                return rc;
 
-       state->using_dma = state->pdata->dma_on;
-       if (!state->using_dma)
-               sound_ops = &pio_sound_ops;
        sound_ops->setup(state);
 
        rc = device_create_file(&pdev->dev, &dev_attr_dma_toggle);
@@ -2560,8 +1898,6 @@ static int tegra_audio_probe(struct platform_device *pdev)
 
        state->in_config.rate = 11025;
        state->in_config.stereo = false;
-       state->in_divs = divs_11025;
-       state->in_divs_len = ARRAY_SIZE(divs_11025);
 
        return 0;
 }
index 1c22e45..bde82c1 100644 (file)
@@ -68,7 +68,6 @@ struct audio_stream {
        int opened;
        struct mutex lock;
 
-       struct tegra_audio_buf_config buf_config;
        bool active; /* is DMA or PIO in progress? */
        void *buffer;
        dma_addr_t buf_phys;
@@ -76,8 +75,6 @@ struct audio_stream {
        struct completion fifo_completion;
        struct scatterlist sg;
 
-       struct tegra_audio_error_counts errors;
-
        int spdif_fifo_atn_level;
 
        ktime_t last_dma_ts;
@@ -122,17 +119,17 @@ struct audio_driver_state {
 
 static inline int buf_size(struct audio_stream *s)
 {
-       return 1 << s->buf_config.size;
+       return 1 << PCM_BUFFER_MAX_SIZE_ORDER; 
 }
 
 static inline int chunk_size(struct audio_stream *s)
 {
-       return 1 << s->buf_config.chunk;
+       return 1 << PCM_BUFFER_DMA_CHUNK_SIZE_ORDER;
 }
 
 static inline int threshold_size(struct audio_stream *s)
 {
-       return 1 << s->buf_config.threshold;
+       return 1 << PCM_BUFFER_THRESHOLD_ORDER;
 }
 
 static inline struct audio_driver_state *ads_from_misc_out(struct file *file)
@@ -418,8 +415,7 @@ static int spdif_set_sample_rate(struct audio_driver_state *state,
        return 0;
 }
 
-static int init_stream_buffer(struct audio_stream *,
-               struct tegra_audio_buf_config *cfg, unsigned);
+static int init_stream_buffer(struct audio_stream *);
 
 static int setup_dma(struct audio_driver_state *);
 static void tear_down_dma(struct audio_driver_state *);
@@ -590,7 +586,6 @@ static void dma_tx_complete_callback(struct tegra_dma_req *req)
        if (delta_us > max_delay_us) {
                pr_debug("%s: too late by %lld us\n", __func__,
                        delta_us - max_delay_us);
-               aos->errors.late_dma++;
        }
 
        kfifo_dma_out_finish(&aos->fifo, count);
@@ -920,35 +915,6 @@ static long tegra_spdif_out_ioctl(struct file *file,
        mutex_lock(&aos->lock);
 
        switch (cmd) {
-       case TEGRA_AUDIO_OUT_SET_BUF_CONFIG: {
-               struct tegra_audio_buf_config cfg;
-               if (copy_from_user(&cfg, (void __user *)arg, sizeof(cfg))) {
-                       rc = -EFAULT;
-                       break;
-               }
-               if (kfifo_len(&aos->fifo)) {
-                       pr_err("%s: playback in progress\n", __func__);
-                       rc = -EBUSY;
-                       break;
-               }
-               rc = init_stream_buffer(aos, &cfg, 0);
-               if (rc < 0)
-                       break;
-               aos->buf_config = cfg;
-       }
-               break;
-       case TEGRA_AUDIO_OUT_GET_BUF_CONFIG:
-               if (copy_to_user((void __user *)arg, &aos->buf_config,
-                               sizeof(aos->buf_config)))
-                       rc = -EFAULT;
-               break;
-       case TEGRA_AUDIO_OUT_GET_ERROR_COUNT:
-               if (copy_to_user((void __user *)arg, &aos->errors,
-                               sizeof(aos->errors)))
-                       rc = -EFAULT;
-               if (!rc)
-                       memset(&aos->errors, 0, sizeof(aos->errors));
-               break;
        case TEGRA_AUDIO_OUT_FLUSH:
                if (kfifo_len(&aos->fifo)) {
                        pr_debug("%s: flushing\n", __func__);
@@ -976,7 +942,6 @@ static int tegra_spdif_out_open(struct inode *inode, struct file *file)
        if (!ads->out.opened++) {
                pr_debug("%s: resetting fifo and error count\n", __func__);
                ads->out.stop = false;
-               memset(&ads->out.errors, 0, sizeof(ads->out.errors));
                kfifo_reset(&ads->out.fifo);
 
        rc = spdif_set_sample_rate(ads, 44100);
@@ -1042,55 +1007,19 @@ static const struct file_operations tegra_spdif_out_ctl_fops = {
        .unlocked_ioctl = tegra_spdif_out_ioctl,
 };
 
-static int init_stream_buffer(struct audio_stream *s,
-                               struct tegra_audio_buf_config *cfg,
-                               unsigned padding)
+static int init_stream_buffer(struct audio_stream *s)
 {
-       pr_info("%s (size %d threshold %d chunk %d)\n", __func__,
-               cfg->size, cfg->threshold, cfg->chunk);
-
-       if (cfg->chunk < PCM_DMA_CHUNK_MIN_SIZE_ORDER) {
-               pr_err("%s: chunk %d too small (%d min)\n", __func__,
-                       cfg->chunk, PCM_DMA_CHUNK_MIN_SIZE_ORDER);
-               return -EINVAL;
-       }
-
-       if (cfg->chunk > cfg->size) {
-               pr_err("%s: chunk %d > size %d\n", __func__,
-                               cfg->chunk, cfg->size);
-               return -EINVAL;
-       }
-
-       if (cfg->threshold > cfg->size) {
-               pr_err("%s: threshold %d > size %d\n", __func__,
-                               cfg->threshold, cfg->size);
-               return -EINVAL;
-       }
-
-       if ((1 << cfg->size) < padding) {
-               pr_err("%s: size %d < buffer padding %d (bytes)\n", __func__,
-                       cfg->size, padding);
-               return -EINVAL;
-       }
-
-       if (cfg->size > PCM_BUFFER_MAX_SIZE_ORDER) {
-               pr_err("%s: size %d exceeds max %d\n", __func__,
-                       cfg->size, PCM_BUFFER_MAX_SIZE_ORDER);
-               return -EINVAL;
-       }
+       pr_info("%s\n", __func__);
 
-       if (!s->buffer) {
-               pr_debug("%s: allocating buffer (size %d, padding %d)\n",
-                               __func__, 1 << cfg->size, padding);
-               s->buffer = kmalloc((1 << cfg->size) + padding,
-                               GFP_KERNEL | GFP_DMA);
-       }
+       kfree(s->buffer);
+       s->buffer = kmalloc(1 << PCM_BUFFER_MAX_SIZE_ORDER,
+                       GFP_KERNEL | GFP_DMA);
        if (!s->buffer) {
                pr_err("%s: could not allocate output buffer\n", __func__);
                return -ENOMEM;
        }
 
-       kfifo_init(&s->fifo, s->buffer, 1 << cfg->size);
+       kfifo_init(&s->fifo, s->buffer, 1 << PCM_BUFFER_MAX_SIZE_ORDER);
        sg_init_table(&s->sg, 1);
        return 0;
 }
@@ -1341,10 +1270,7 @@ static int tegra_spdif_probe(struct platform_device *pdev)
        state->out.dma_has_it = false;
 
        state->out.buffer = 0;
-       state->out.buf_config.size = PCM_BUFFER_MAX_SIZE_ORDER;
-       state->out.buf_config.threshold = PCM_BUFFER_THRESHOLD_ORDER;
-       state->out.buf_config.chunk = PCM_BUFFER_DMA_CHUNK_SIZE_ORDER;
-       rc = init_stream_buffer(&state->out, &state->out.buf_config, 0);
+       rc = init_stream_buffer(&state->out);
        if (rc < 0)
                return rc;
 
index 0ca6250..db4661a 100644 (file)
@@ -36,39 +36,21 @@ struct tegra_audio_in_config {
 #define TEGRA_AUDIO_IN_GET_CONFIG      _IOR(TEGRA_AUDIO_MAGIC, 3, \
                        struct tegra_audio_in_config *)
 
-struct tegra_audio_buf_config {
-       unsigned size; /* order */
-       unsigned threshold; /* order */
-       unsigned chunk; /* order */
-};
-
-#define TEGRA_AUDIO_IN_SET_BUF_CONFIG  _IOW(TEGRA_AUDIO_MAGIC, 4, \
-                       const struct tegra_audio_buf_config *)
-#define TEGRA_AUDIO_IN_GET_BUF_CONFIG  _IOR(TEGRA_AUDIO_MAGIC, 5, \
-                       struct tegra_audio_buf_config *)
-
-#define TEGRA_AUDIO_OUT_SET_BUF_CONFIG _IOW(TEGRA_AUDIO_MAGIC, 6, \
-                       const struct tegra_audio_buf_config *)
-#define TEGRA_AUDIO_OUT_GET_BUF_CONFIG _IOR(TEGRA_AUDIO_MAGIC, 7, \
-                       struct tegra_audio_buf_config *)
-
-struct tegra_audio_error_counts {
-       unsigned late_dma;
-       unsigned full_empty; /* empty for playback, full for recording */
-};
-
-#define TEGRA_AUDIO_IN_GET_ERROR_COUNT _IOR(TEGRA_AUDIO_MAGIC, 8, \
-                       struct tegra_audio_error_counts *)
-
-#define TEGRA_AUDIO_OUT_GET_ERROR_COUNT        _IOR(TEGRA_AUDIO_MAGIC, 9, \
-                       struct tegra_audio_error_counts *)
+#define TEGRA_AUDIO_IN_SET_NUM_BUFS    _IOW(TEGRA_AUDIO_MAGIC, 4, \
+                       const unsigned int *)
+#define TEGRA_AUDIO_IN_GET_NUM_BUFS    _IOW(TEGRA_AUDIO_MAGIC, 5, \
+                       unsigned int *)
+#define TEGRA_AUDIO_OUT_SET_NUM_BUFS   _IOW(TEGRA_AUDIO_MAGIC, 6, \
+                       const unsigned int *)
+#define TEGRA_AUDIO_OUT_GET_NUM_BUFS   _IOW(TEGRA_AUDIO_MAGIC, 7, \
+                       unsigned int *)
 
 #define TEGRA_AUDIO_OUT_FLUSH          _IO(TEGRA_AUDIO_MAGIC, 10)
 
 #define TEGRA_AUDIO_BIT_FORMAT_DEFAULT 0
 #define TEGRA_AUDIO_BIT_FORMAT_DSP 1
 #define TEGRA_AUDIO_SET_BIT_FORMAT     _IOW(TEGRA_AUDIO_MAGIC, 11, \
-                       unsigned int *)
+                       const unsigned int *)
 #define TEGRA_AUDIO_GET_BIT_FORMAT     _IOR(TEGRA_AUDIO_MAGIC, 12, \
                        unsigned int *)