diff options
author | Iliyan Malchev <malchev@google.com> | 2010-11-22 16:07:44 -0800 |
---|---|---|
committer | Iliyan Malchev <malchev@google.com> | 2010-11-22 16:07:44 -0800 |
commit | b4d50aa63c58b8affa08f60f4dba415040845371 (patch) | |
tree | 86a34d962893ce160d22ea24904c9b1849dd3c93 /arch/arm/mach-tegra/tegra_i2s_audio.c | |
parent | b7b7ed87ac9d4b1bb8a35a2b02f1d0effb668c76 (diff) | |
parent | 3707e1c4af5f9af1a45c27a5894ac23741c05d80 (diff) |
Merge branch 'linux-tegra-2.6.36' into android-tegra-2.6.36
Conflicts:
arch/arm/mach-tegra/tegra_i2s_audio.c
Signed-off-by: Iliyan Malchev <malchev@google.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra_i2s_audio.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra_i2s_audio.c | 1408 |
1 files changed, 370 insertions, 1038 deletions
diff --git a/arch/arm/mach-tegra/tegra_i2s_audio.c b/arch/arm/mach-tegra/tegra_i2s_audio.c index f8a67f1e2043..39c6c0290f25 100644 --- a/arch/arm/mach-tegra/tegra_i2s_audio.c +++ b/arch/arm/mach-tegra/tegra_i2s_audio.c @@ -17,6 +17,10 @@ * */ +/* 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/wakelock.h> #include <linux/delay.h> #include <linux/tegra_audio.h> @@ -55,63 +58,42 @@ #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 wake_lock wake_lock; char wake_lock_name[100]; }; -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; @@ -121,14 +103,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; @@ -143,19 +121,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) @@ -226,16 +203,13 @@ static inline void prevent_suspend(struct audio_stream *as) pr_debug("%s\n", __func__); cancel_work_sync(&as->allow_suspend_work); wake_lock(&as->wake_lock); - 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); wake_unlock(&as->wake_lock); } @@ -349,6 +323,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); @@ -356,6 +331,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) { @@ -501,6 +477,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); @@ -526,6 +503,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) { @@ -612,10 +590,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); @@ -631,81 +609,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; } @@ -713,7 +657,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); @@ -724,27 +670,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; @@ -755,33 +691,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); @@ -790,21 +713,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; @@ -813,14 +739,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)); @@ -832,16 +761,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; } @@ -851,50 +788,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__); @@ -904,60 +834,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, @@ -989,7 +883,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); @@ -1003,65 +896,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; @@ -1080,48 +942,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); @@ -1132,19 +969,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) && @@ -1157,112 +987,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; @@ -1270,126 +994,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; } @@ -1397,60 +1004,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); @@ -1467,43 +1082,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; } @@ -1553,10 +1167,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; @@ -1585,13 +1199,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; @@ -1607,35 +1224,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__); @@ -1655,34 +1243,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; @@ -1692,293 +1280,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) @@ -1988,42 +1402,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 (wake_lock_active(&ads->out.wake_lock)) - pr_err("%s: wake lock is still held!\n", __func__); - 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) @@ -2033,20 +1445,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 (wake_lock_active(&ads->in.wake_lock)) - pr_err("%s: wake lock is still held!\n", __func__); - 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; @@ -2097,56 +1502,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; } @@ -2187,47 +1561,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); @@ -2311,7 +1653,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; @@ -2360,7 +1702,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; @@ -2415,8 +1757,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", @@ -2456,25 +1796,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); snprintf(state->out.wake_lock_name, sizeof(state->out.wake_lock_name), @@ -2499,26 +1838,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); snprintf(state->in.wake_lock_name, sizeof(state->in.wake_lock_name), @@ -2553,9 +1890,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); @@ -2581,8 +1915,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; } |