summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra_i2s_audio.c
diff options
context:
space:
mode:
authorIliyan Malchev <malchev@google.com>2010-08-11 18:19:47 -0700
committerColin Cross <ccross@android.com>2010-10-06 16:27:38 -0700
commitdada76ee1116155b3195c7dee63332ba74be6cb8 (patch)
tree729152cefdf50d121ed560c00682f1cc16810218 /arch/arm/mach-tegra/tegra_i2s_audio.c
parentaa52607a901122ad91ea69de479d420e4910f072 (diff)
[ARM] tegra_i2s_audio: add software downsampling for recorded data + fixes
downsampling: -- add ioctl()s to downsample recorded data -- supported frequencies are 8kHz, 11.025kHz, 22.05kHz, and 44.1kHz -- downsamping to stereo and mono -- default is 11.025kHz mono fixes: -- fix crashes from dequeuing DMA requests twice 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.c417
1 files changed, 333 insertions, 84 deletions
diff --git a/arch/arm/mach-tegra/tegra_i2s_audio.c b/arch/arm/mach-tegra/tegra_i2s_audio.c
index cb3a33e69dd7..cfdb3fb73a76 100644
--- a/arch/arm/mach-tegra/tegra_i2s_audio.c
+++ b/arch/arm/mach-tegra/tegra_i2s_audio.c
@@ -48,19 +48,21 @@
#include "clock.h"
+
/* per stream (input/output) */
struct audio_out_stream {
int opened;
struct mutex lock;
- bool active;
- spinlock_t pcm_out_lock;
+ bool active; /* is DMA or PIO in progress? */
void *buffer;
dma_addr_t buf_phys;
struct kfifo fifo;
struct completion fifo_completion;
struct tegra_dma_channel *dma_chan;
+ spinlock_t dma_req_lock; /* guards active and dma_has_it */
+ int dma_has_it;
struct tegra_dma_req dma_req;
};
@@ -68,14 +70,15 @@ struct audio_in_stream {
int opened;
struct mutex lock;
- bool active;
- spinlock_t pcm_in_lock;
+ bool active; /* is DMA or PIO in progress? */
void *buffer;
dma_addr_t buf_phys;
struct kfifo fifo;
struct completion fifo_completion;
struct tegra_dma_channel *dma_chan;
+ spinlock_t dma_req_lock; /* guards active and dma_has_it */
+ int dma_has_it;
struct tegra_dma_req dma_req;
};
@@ -87,6 +90,11 @@ struct i2s_pio_stats {
u32 rx_fifo_read;
};
+static const int divs_8000[] = { 5, 6, 6, 5 };
+static const int divs_11025[] = { 4 };
+static const int divs_22050[] = { 2 };
+static const int divs_44100[] = { 1 };
+
/* per i2s controller */
struct audio_driver_state {
struct list_head next;
@@ -102,6 +110,9 @@ struct audio_driver_state {
int irq; /* for pio mode */
struct i2s_pio_stats pio_stats;
bool recording_cancelled;
+ struct tegra_audio_in_config in_config;
+ const int *in_divs;
+ int in_divs_len;
struct miscdevice misc_out;
struct audio_out_stream out;
@@ -427,6 +438,7 @@ static inline u32 i2s_get_fifo_full_empty_count(unsigned long base, int fifo)
#define PCM_OUT_THRESHOLD (PAGE_SIZE*2)
#define PCM_IN_BUFFER_SIZE (PAGE_SIZE*4)
+#define PCM_IN_BUFFER_PADDING 256
#define PCM_IN_DMA_CHUNK (PAGE_SIZE)
#define PCM_IN_THRESHOLD (PAGE_SIZE*2)
@@ -474,80 +486,68 @@ static const struct sound_ops pio_sound_ops = {
static const struct sound_ops *sound_ops = &dma_sound_ops;
-static void start_playback(struct audio_out_stream *aos)
-{
- aos->active = !sound_ops->start_playback(aos);
-}
-
static void start_playback_if_necessary(struct audio_out_stream *aos)
{
unsigned long flags;
- spin_lock_irqsave(&aos->pcm_out_lock, flags);
+ spin_lock_irqsave(&aos->dma_req_lock, flags);
if (!aos->active) {
pr_debug("%s: starting playback\n", __func__);
- start_playback(aos);
+ aos->active = !sound_ops->start_playback(aos);
} else
pr_debug("%s: playback already started\n", __func__);
- spin_unlock_irqrestore(&aos->pcm_out_lock, flags);
-}
-
-static void start_recording(struct audio_in_stream *ais)
-{
- ais->active = !sound_ops->start_recording(ais);
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
}
static void start_recording_if_necessary(struct audio_in_stream *ais)
{
unsigned long flags;
- spin_lock_irqsave(&ais->pcm_in_lock, flags);
+ spin_lock_irqsave(&ais->dma_req_lock, flags);
if (!ais->active) {
pr_info("%s: starting recording\n", __func__);
- start_recording(ais);
+ ais->active = !sound_ops->start_recording(ais);
} else
pr_debug("%s: recording already started\n", __func__);
- spin_unlock_irqrestore(&ais->pcm_in_lock, flags);
-}
-
-static void stop_playback(struct audio_out_stream *aos)
-{
- sound_ops->stop_playback(aos);
- aos->active = false;
-}
-
-static void stop_recording(struct audio_in_stream *ais)
-{
- sound_ops->stop_recording(ais);
- ais->active = false;
+ spin_unlock_irqrestore(&ais->dma_req_lock, flags);
}
static bool stop_playback_if_necessary(struct audio_out_stream *aos)
{
unsigned long flags;
- spin_lock_irqsave(&aos->pcm_out_lock, flags);
+ spin_lock_irqsave(&aos->dma_req_lock, flags);
if (kfifo_is_empty(&aos->fifo)) {
- stop_playback(aos);
- spin_unlock_irqrestore(&aos->pcm_out_lock, flags);
+ sound_ops->stop_playback(aos);
+ aos->active = false;
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
return true;
}
- spin_unlock_irqrestore(&aos->pcm_out_lock, flags);
+ spin_unlock_irqrestore(&aos->dma_req_lock, flags);
return false;
}
-static bool stop_recording_if_necessary(struct audio_in_stream *ais)
+static bool stop_recording_if_necessary_nosync(struct audio_in_stream *ais)
{
- unsigned long flags;
- spin_lock_irqsave(&ais->pcm_in_lock, flags);
- if (kfifo_is_full(&ais->fifo)) {
- stop_recording(ais);
- spin_unlock_irqrestore(&ais->pcm_in_lock, flags);
+ struct audio_driver_state *ads = ads_from_in(ais);
+
+ if (ads->recording_cancelled || kfifo_is_full(&ais->fifo)) {
+ sound_ops->stop_recording(ais);
+ ais->active = false;
return true;
}
- spin_unlock_irqrestore(&ais->pcm_in_lock, flags);
return false;
}
+static bool stop_recording_if_necessary(struct audio_in_stream *ais)
+{
+ unsigned long flags;
+ bool ret;
+ spin_lock_irqsave(&ais->dma_req_lock, flags);
+ ret = stop_recording_if_necessary_nosync(ais);
+ spin_unlock_irqrestore(&ais->dma_req_lock, flags);
+ return ret;
+}
+
static void toggle_dma(struct audio_driver_state *ads)
{
pr_info("%s: %s\n", __func__, ads->using_dma ? "pio" : "dma");
@@ -636,6 +636,7 @@ static void tear_down_dma(struct audio_driver_state *ads)
static void dma_tx_complete_callback(struct tegra_dma_req *req)
{
+ unsigned long flags;
struct audio_out_stream *aos = req->dev;
int count = req->bytes_transferred;
@@ -652,7 +653,9 @@ static void dma_tx_complete_callback(struct tegra_dma_req *req)
if (stop_playback_if_necessary(aos))
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)
@@ -662,31 +665,38 @@ static void dma_rx_complete_threshold(struct tegra_dma_req *req)
static void dma_rx_complete_callback(struct tegra_dma_req *req)
{
+ unsigned long flags;
struct audio_in_stream *ais = req->dev;
int count = req->bytes_transferred;
- pr_debug("%s bytes transferred %d\n", __func__, count);
+ pr_debug("%s bytes transferred %d (%d available in fifo)\n", __func__,
+ count, kfifo_avail(&ais->fifo));
+ BUG_ON(kfifo_avail(&ais->fifo) < count);
__kfifo_add_in(&ais->fifo, count);
if (kfifo_avail(&ais->fifo) < PCM_IN_THRESHOLD &&
- !completion_done(&ais->fifo_completion)) {
- pr_debug("%s: complete\n", __func__);
+ !completion_done(&ais->fifo_completion))
complete(&ais->fifo_completion);
- }
- if (!ais->active) {
- pr_warn("%s: recording has been stopped\n", __func__);
+ spin_lock_irqsave(&ais->dma_req_lock, flags);
+ ais->dma_has_it = false;
+ if (!ais->active || stop_recording_if_necessary_nosync(ais)) {
+ pr_warn("%s: recording has been stopped or cancelled (%d)\n",
+ __func__, ais->active);
+ spin_unlock_irqrestore(&ais->dma_req_lock, flags);
return;
}
- if (stop_recording_if_necessary(ais)) {
- pr_warn("%s: paused recording (input fifo full)\n",
- __func__);
- return;
- }
+ pr_debug("%s: resuming dma recording\n", __func__);
- resume_dma_recording(ais);
+ /* This call will fail if we try to set up a DMA request that's
+ * too small.
+ */
+ ais->active = !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,
@@ -726,8 +736,10 @@ 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_out_stream *aos)
{
+ int rc;
struct audio_driver_state *ads = ads_from_out(aos);
struct tegra_dma_req *req = &aos->dma_req;
@@ -755,14 +767,18 @@ static int resume_dma_playback(struct audio_out_stream *aos)
I2S_FIFO_TX, I2S_FIFO_ATN_LVL_FOUR_SLOTS);
i2s_fifo_enable(ads->i2s_base, I2S_FIFO_TX, 1);
- return tegra_dma_enqueue_req(aos->dma_chan, req);
+ rc = tegra_dma_enqueue_req(aos->dma_chan, req);
+ aos->dma_has_it = !rc;
+ return rc;
}
+/* Called with aos->dma_req_lock taken. */
static int start_dma_playback(struct audio_out_stream *aos)
{
return resume_dma_playback(aos);
}
+/* Called with aos->dma_req_lock taken. */
static void stop_dma_playback(struct audio_out_stream *aos)
{
struct audio_driver_state *ads = ads_from_out(aos);
@@ -773,6 +789,8 @@ static void stop_dma_playback(struct audio_out_stream *aos)
pr_info("%s: spin\n", __func__);
}
+/* 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_in_stream *ais)
{
struct audio_driver_state *ads = ads_from_in(ais);
@@ -781,7 +799,12 @@ static int resume_dma_recording(struct audio_in_stream *ais)
unsigned out = __kfifo_off(&ais->fifo, ais->fifo.out);
unsigned in = __kfifo_off(&ais->fifo, ais->fifo.in);
- pr_debug("%s\n", __func__);
+ pr_debug("%s in %d out %d\n", __func__, in, out);
+
+ if (kfifo_is_full(&ais->fifo)) {
+ pr_err("%s: input fifo is full\n", __func__);
+ return -ENOMEM;
+ }
req->dest_addr = ais->buf_phys + in;
if (out <= in)
@@ -793,13 +816,24 @@ static int resume_dma_recording(struct audio_in_stream *ais)
if (req->size > PCM_OUT_DMA_CHUNK)
req->size = PCM_OUT_DMA_CHUNK;
+ req->size = round_down(req->size, 4);
+
+ if (!req->size) {
+ pr_err("%s: invalid request size %d (in %d out %d)\n", __func__,
+ req->size, in, out);
+ return -ENOMEM;
+ }
+
dma_sync_single_for_device(NULL,
req->dest_addr, req->size, DMA_FROM_DEVICE);
- if (tegra_dma_enqueue_req(ais->dma_chan, &ais->dma_req)) {
+ BUG_ON(ais->dma_has_it);
+ 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;
}
+
i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
i2s_fifo_set_attention_level(ads->i2s_base,
I2S_FIFO_RX, I2S_FIFO_ATN_LVL_TWELVE_SLOTS);
@@ -807,12 +841,14 @@ static int resume_dma_recording(struct audio_in_stream *ais)
return 0;
}
+/* Called with ais->dma_req_lock taken. */
static int start_dma_recording(struct audio_in_stream *ais)
{
pr_info("%s\n", __func__);
return resume_dma_recording(ais);
}
+/* Called with ais->dma_req_lock taken. */
static void stop_dma_recording(struct audio_in_stream *ais)
{
struct audio_driver_state *ads = ads_from_in(ais);
@@ -821,7 +857,7 @@ static void stop_dma_recording(struct audio_in_stream *ais)
i2s_fifo_clear(ads->i2s_base, I2S_FIFO_RX);
while (i2s_get_status(ads->i2s_base) & I2S_I2S_FIFO_RX_BUSY)
pr_debug("%s: spin\n", __func__);
- tegra_dma_dequeue_req(ais->dma_chan, &ais->dma_req);
+ ais->dma_has_it = false;
}
/* PIO (non-DMA) */
@@ -1102,13 +1138,65 @@ static long tegra_audio_in_ioctl(struct file *file,
break;
case TEGRA_AUDIO_IN_STOP:
pr_info("%s: stop recording\n", __func__);
- stop_recording(ais);
ads->recording_cancelled = true;
+ stop_recording_if_necessary(ais);
if (!completion_done(&ais->fifo_completion)) {
pr_info("%s: complete\n", __func__);
complete(&ais->fifo_completion);
}
break;
+ case TEGRA_AUDIO_IN_SET_CONFIG: {
+ struct tegra_audio_in_config cfg;
+
+ if (ais->active) {
+ pr_err("%s: recording in progress\n", __func__);
+ rc = -EBUSY;
+ break;
+ }
+ if (copy_from_user(&cfg, (const void __user *)arg,
+ sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ 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 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;
+ }
+
+ if (!rc) {
+ pr_info("%s: setting input sampling rate to %d, %s\n",
+ __func__, cfg.rate,
+ cfg.stereo ? "stereo" : "mono");
+ ads->in_config = cfg;
+ ads->in_config.stereo = !!ads->in_config.stereo;
+ }
+ }
+ break;
+ case TEGRA_AUDIO_IN_GET_CONFIG:
+ if (copy_to_user((void __user *)arg, &ads->in_config,
+ sizeof(ads->in_config)))
+ rc = -EFAULT;
+ break;
default:
rc = -EINVAL;
}
@@ -1117,11 +1205,160 @@ static long tegra_audio_in_ioctl(struct file *file,
return rc;
}
-ssize_t tegra_audio_read(struct file *file, char __user *buf,
+/* 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)
+{
+ 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 *buf, unsigned int off,
+ int src_size,
+ int dst_size,
+ int *num_consumed)
+{
+ int bytes_ds;
+
+ pr_debug("%s\n", __func__);
+
+ bytes_ds = downsample(ads->in.buffer + off, src_size / sizeof(s16),
+ ads->in.buffer + off, dst_size / sizeof(s16),
+ num_consumed,
+ ads->in_divs, ads->in_divs_len,
+ ads->in_config.stereo) * sizeof(s16);
+
+ if (copy_to_user(buf, ads->in.buffer + off, bytes_ds)) {
+ pr_err("%s: error copying %d bytes to user\n", __func__,
+ bytes_ds);
+ return -EFAULT;
+ }
+
+ *num_consumed *= sizeof(s16);
+
+ kfifo_skip(&ads->in.fifo, *num_consumed);
+
+ pr_debug("%s: generated %d, skipped %d, original in fifo %d\n",
+ __func__, bytes_ds, *num_consumed, src_size);
+
+ return bytes_ds;
+}
+
+static ssize_t downsample_to_user(struct audio_driver_state *ads,
+ void __user *buf,
+ size_t size) /* bytes to write to user buffer */
+{
+ unsigned out;
+ unsigned in;
+ int bytes_consumed_from_fifo;
+ int bytes_ds;
+ int bytes_till_end;
+ bool take_two = false;
+
+ out = __kfifo_off(&ads->in.fifo, ads->in.fifo.out);
+ in = __kfifo_off(&ads->in.fifo, ads->in.fifo.in);
+
+ pr_debug("%s (size %d out %d in %d)\n", __func__, size, out, in);
+
+ if (kfifo_is_empty(&ads->in.fifo)) {
+ pr_debug("%s: input fifo is empty\n", __func__);
+ return 0;
+ }
+
+ if (size == 0) {
+ pr_debug("%s: user buffer is full\n", __func__);
+ return 0;
+ }
+
+ /* Does the fifo have enough bytes? We need a contiguous stretch of
+ * data in the fifo (not wrapping around).
+ */
+ if (out < in) {
+ bytes_ds = __downsample_to_user(ads, buf, out,
+ in - out,
+ size,
+ &bytes_consumed_from_fifo);
+ pr_debug("%s: (out < in) downsampled (%d req, %d actual)"\
+ " -> %d (size %d)\n", __func__,
+ in - out, bytes_consumed_from_fifo,
+ bytes_ds, size);
+ BUG_ON(bytes_ds > size);
+ return bytes_ds;
+ }
+
+ bytes_till_end = kfifo_size(&ads->in.fifo) - out;
+
+again:
+ bytes_ds = __downsample_to_user(ads, buf, out,
+ bytes_till_end,
+ size,
+ &bytes_consumed_from_fifo);
+ pr_debug("%s: (out > in) downsampled (req %d act %d size %d) -> %d\n",
+ __func__, bytes_till_end, bytes_consumed_from_fifo,
+ bytes_ds, size);
+ BUG_ON(bytes_ds > size);
+
+ if (!bytes_ds) {
+ BUG_ON(take_two);
+ take_two = true;
+
+ pr_debug("%s: not enough data till end of fifo\n", __func__);
+
+ if (in < PCM_IN_BUFFER_PADDING)
+ return 0;
+
+ memcpy(ads->in.buffer + PCM_IN_BUFFER_SIZE,
+ ads->in.buffer,
+ PCM_IN_BUFFER_PADDING);
+ bytes_till_end += PCM_IN_BUFFER_PADDING;
+ pr_debug("%s: take two\n", __func__);
+ goto again;
+ }
+
+ 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;
- unsigned nr;
+ ssize_t nr;
struct audio_driver_state *ads = ads_from_misc_in(file);
@@ -1131,7 +1368,7 @@ ssize_t tegra_audio_read(struct file *file, char __user *buf,
pr_err("%s: user size request %d not aligned to 4\n",
__func__, size);
rc = -EINVAL;
- goto done;
+ goto done_err;
}
pr_debug("%s: read %d bytes, %d available\n", __func__,
@@ -1141,47 +1378,51 @@ ssize_t tegra_audio_read(struct file *file, char __user *buf,
start_recording_if_necessary(&ads->in);
again:
- if (!ads->in.active && kfifo_is_empty(&ads->in.fifo)) {
- pr_info("%s: recording has stopped (read %d bytes)\n",
+ if (ads->recording_cancelled ||
+ (!ads->in.active && kfifo_is_empty(&ads->in.fifo))) {
+ pr_debug("%s: recording has been cancelled (read %d bytes)\n",
__func__, total);
- rc = total;
- *off += total;
- goto done;
+ goto done_ok;
}
- rc = kfifo_to_user(&ads->in.fifo, buf + total, size - total, &nr);
- if (rc < 0) {
- pr_err("%s: error copying to user\n", __func__);
- goto done;
- }
-
- total += nr;
+ 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 && ads->in.active) {
+ if (!ads->recording_cancelled)
+ start_recording_if_necessary(&ads->in);
+ mutex_unlock(&ads->in.lock);
pr_debug("%s: sleep (user %d total %d nr %d)\n", __func__,
size, total, nr);
- mutex_unlock(&ads->in.lock);
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;
+ goto done_err;
}
- pr_debug("%s: awake\n", __func__);
goto again;
}
pr_debug("%s: done reading %d bytes, %d available\n", __func__,
total, kfifo_avail(&ads->in.fifo));
+done_ok:
rc = total;
*off += total;
-done:
+done_err:
mutex_unlock(&ads->in.lock);
return rc;
}
@@ -1210,7 +1451,7 @@ static int tegra_audio_out_release(struct inode *inode, struct file *file)
if (ads->out.opened)
ads->out.opened--;
if (!ads->out.opened)
- stop_playback(&ads->out);
+ stop_playback_if_necessary(&ads->out);
mutex_unlock(&ads->out.lock);
return 0;
@@ -1248,7 +1489,7 @@ static int tegra_audio_in_release(struct inode *inode, struct file *file)
ads->in.opened--;
if (!ads->in.opened) {
pr_info("%s: stop recording\n", __func__);
- stop_recording(&ads->in);
+ stop_recording_if_necessary(&ads->in);
}
mutex_unlock(&ads->in.lock);
pr_info("%s: done\n", __func__);
@@ -1476,17 +1717,19 @@ static int tegra_audio_probe(struct platform_device *pdev)
state->out.active = false;
mutex_init(&state->out.lock);
init_completion(&state->out.fifo_completion);
- spin_lock_init(&state->out.pcm_out_lock);
+ 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->in.opened = 0;
state->in.active = false;
mutex_init(&state->in.lock);
init_completion(&state->in.fifo_completion);
- spin_lock_init(&state->in.pcm_in_lock);
+ 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->out.buffer = kmalloc(PCM_OUT_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
if (!state->out.buffer) {
@@ -1494,7 +1737,8 @@ static int tegra_audio_probe(struct platform_device *pdev)
return -ENOMEM;
}
- state->in.buffer = kmalloc(PCM_IN_BUFFER_SIZE, GFP_KERNEL | GFP_DMA);
+ state->in.buffer = kmalloc(PCM_IN_BUFFER_SIZE + PCM_IN_BUFFER_PADDING,
+ GFP_KERNEL | GFP_DMA);
if (!state->in.buffer) {
pr_err("%s: could not allocate input buffer\n", __func__);
return -ENOMEM;
@@ -1544,6 +1788,11 @@ static int tegra_audio_probe(struct platform_device *pdev)
sound_ops = &pio_sound_ops;
sound_ops->setup(state);
+ state->in_config.rate = 11025;
+ state->in_config.stereo = false;
+ state->in_divs = divs_11025;
+ state->in_divs_len = ARRAY_SIZE(divs_11025);
+
setup_tegra_audio_debugfs(state);
return 0;