summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/tegra_i2s_audio.c1408
-rw-r--r--arch/arm/mach-tegra/tegra_spdif_audio.c193
-rw-r--r--include/linux/tegra_audio.h36
3 files changed, 461 insertions, 1176 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;
}
diff --git a/arch/arm/mach-tegra/tegra_spdif_audio.c b/arch/arm/mach-tegra/tegra_spdif_audio.c
index fd96f2abf5eb..faa53a24e219 100644
--- a/arch/arm/mach-tegra/tegra_spdif_audio.c
+++ b/arch/arm/mach-tegra/tegra_spdif_audio.c
@@ -48,6 +48,7 @@
#include <linux/delay.h>
#include <linux/tegra_audio.h>
#include <linux/workqueue.h>
+#include <linux/pm.h>
#include <mach/dma.h>
#include <mach/iomap.h>
@@ -69,7 +70,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;
@@ -77,8 +77,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;
@@ -112,6 +110,7 @@ struct audio_driver_state {
bool using_dma;
unsigned long dma_req_sel;
+ bool fifo_init;
int irq; /* for pio mode */
struct spdif_pio_stats pio_stats;
@@ -125,17 +124,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)
@@ -237,7 +236,11 @@ static int spdif_fifo_set_attention_level(unsigned long base,
static void spdif_fifo_enable(unsigned long base, int on)
{
u32 val = spdif_readl(base, SPDIF_CTRL_0);
- val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN);
+
+ if (on && (val & SPDIF_CTRL_0_TX_EN))
+ return;
+
+ val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN | SPDIF_CTRL_0_TU_EN);
val |= on ? (SPDIF_CTRL_0_TX_EN) : 0;
val |= on ? (SPDIF_CTRL_0_TC_EN) : 0;
@@ -347,12 +350,11 @@ static int spdif_set_sample_rate(struct audio_driver_state *state,
unsigned int sample_rate)
{
unsigned int clock_freq = 0;
- unsigned int parent_clock_freq = 0;
struct clk *spdif_clk;
unsigned int ch_sta[] = {
0x0, /* 44.1, default values */
- 0xf << 4, /* bits 36-39, original sample freq -- 44.1 */
+ 0x0,
0x0,
0x0,
0x0,
@@ -362,37 +364,30 @@ static int spdif_set_sample_rate(struct audio_driver_state *state,
switch (sample_rate) {
case 32000:
clock_freq = 4096000; /* 4.0960 MHz */
- parent_clock_freq = 12288000;
ch_sta[0] = 0x3 << 24;
- ch_sta[1] = 0xC << 4;
+ /*ch_sta[1] = 0xC << 4;*/
break;
case 44100:
clock_freq = 5644800; /* 5.6448 MHz */
- parent_clock_freq = 11289600;
ch_sta[0] = 0x0;
- ch_sta[1] = 0xF << 4;
+ /*ch_sta[1] = 0xF << 4;*/
break;
case 48000:
clock_freq = 6144000; /* 6.1440MHz */
- parent_clock_freq = 12288000;
ch_sta[0] = 0x2 << 24;
- ch_sta[1] = 0xD << 4;
+ /*ch_sta[1] = 0xD << 4;*/
break;
case 88200:
clock_freq = 11289600; /* 11.2896 MHz */
- parent_clock_freq = 11289600;
break;
case 96000:
clock_freq = 12288000; /* 12.288 MHz */
- parent_clock_freq = 12288000;
break;
case 176400:
clock_freq = 22579200; /* 22.5792 MHz */
- parent_clock_freq = 11289600;
break;
case 192000:
clock_freq = 24576000; /* 24.5760 MHz */
- parent_clock_freq = 12288000;
break;
default:
return -1;
@@ -423,8 +418,32 @@ 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 spdif_configure(struct platform_device *pdev)
+{
+ struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
+ struct audio_driver_state *state = pdata->driver_data;
+
+ if (!state)
+ return -ENOMEM;
+
+ /* disable interrupts from SPDIF */
+ spdif_writel(state->spdif_base, 0x0, SPDIF_CTRL_0);
+ spdif_fifo_clear(state->spdif_base);
+ spdif_enable_fifos(state->spdif_base, 0);
+
+ spdif_set_bit_mode(state->spdif_base, state->pdata->mode);
+ spdif_set_fifo_packed(state->spdif_base, state->pdata->fifo_fmt);
+
+ spdif_fifo_set_attention_level(state->spdif_base,
+ state->out.spdif_fifo_atn_level);
+
+ spdif_set_sample_rate(state, 44100);
+
+ state->fifo_init = true;
+ return 0;
+}
+
+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 *);
@@ -595,7 +614,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);
@@ -633,9 +651,14 @@ static void setup_dma_tx_request(struct tegra_dma_req *req,
req->to_memory = false;
req->dest_addr = spdif_get_fifo_phy_base(ads->spdif_phys);
req->dest_wrap = 4;
- req->dest_bus_width = 16;
- req->source_bus_width = 32;
+ if (ads->pdata->mode != SPDIF_BIT_MODE_MODE16BIT ||
+ ads->pdata->fifo_fmt)
+ req->dest_bus_width = 32;
+ else
+ req->dest_bus_width = 16;
+
req->source_wrap = 0;
+ req->source_bus_width = 32;
req->req_sel = ads->dma_req_sel;
}
@@ -667,8 +690,6 @@ static int resume_dma_playback(struct audio_stream *aos)
#if 0
spdif_fifo_clear(ads->spdif_base);
#endif
- spdif_fifo_set_attention_level(ads->spdif_base,
- aos->spdif_fifo_atn_level);
req->source_addr = sg_dma_address(&aos->sg);
req->size = sg_dma_len(&aos->sg);
@@ -681,7 +702,12 @@ static int resume_dma_playback(struct audio_stream *aos)
pr_debug("%s resume playback (%d in fifo, writing %d)\n",
__func__, kfifo_len(&aos->fifo), req->size);
- spdif_fifo_enable(ads->spdif_base, 1);
+ if (ads->fifo_init) {
+ spdif_set_bit_mode(ads->spdif_base, ads->pdata->mode);
+ spdif_set_fifo_packed(ads->spdif_base, ads->pdata->fifo_fmt);
+ spdif_fifo_enable(ads->spdif_base, 1);
+ ads->fifo_init = false;
+ }
aos->last_dma_ts = ktime_get_real();
rc = tegra_dma_enqueue_req(aos->dma_chan, req);
@@ -712,6 +738,7 @@ static void stop_dma_playback(struct audio_stream *aos)
}
if (spin == 100)
pr_warn("%s: spinny\n", __func__);
+ ads->fifo_init = true;
}
/* PIO (non-DMA) */
@@ -925,35 +952,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__);
@@ -981,10 +979,9 @@ 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);
+ rc = spdif_set_sample_rate(ads, 44100);
}
mutex_unlock(&ads->out.lock);
@@ -1049,55 +1046,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;
}
@@ -1312,8 +1273,6 @@ static int tegra_spdif_probe(struct platform_device *pdev)
return -EIO;
}
- state->out.spdif_fifo_atn_level = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
-
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no dma resource!\n");
@@ -1330,12 +1289,10 @@ static int tegra_spdif_probe(struct platform_device *pdev)
memset(&state->pio_stats, 0, sizeof(state->pio_stats));
- /* disable interrupts from SPDIF */
- spdif_fifo_clear(state->spdif_base);
- spdif_enable_fifos(state->spdif_base, 0);
- spdif_set_bit_mode(state->spdif_base, state->pdata->mode);
- spdif_set_fifo_packed(state->spdif_base, state->pdata->fifo_fmt);
+ rc = spdif_configure(pdev);
+ if (rc < 0)
+ return rc;
state->out.opened = 0;
state->out.active = false;
@@ -1348,10 +1305,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;
@@ -1405,12 +1359,29 @@ static int tegra_spdif_probe(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int tegra_spdif_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ /* dev_info(&pdev->dev, "%s\n", __func__); */
+ return 0;
+}
+
+static int tegra_spdif_resume(struct platform_device *pdev)
+{
+ return spdif_configure(pdev);
+}
+#endif /* CONFIG_PM */
+
static struct platform_driver tegra_spdif_driver = {
.driver = {
.name = "spdif_out",
.owner = THIS_MODULE,
},
.probe = tegra_spdif_probe,
+#ifdef CONFIG_PM
+ .suspend = tegra_spdif_suspend,
+ .resume = tegra_spdif_resume,
+#endif
};
static int __init tegra_spdif_init(void)
diff --git a/include/linux/tegra_audio.h b/include/linux/tegra_audio.h
index 0ca6250beac4..db4661aacb4f 100644
--- a/include/linux/tegra_audio.h
+++ b/include/linux/tegra_audio.h
@@ -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 *)