[ARM] tegra_i2s_audio: allow different DMA bus widths
authorChris Fries <C.Fries@motorola.com>
Thu, 14 Oct 2010 21:31:52 +0000 (16:31 -0500)
committerIliyan Malchev <malchev@google.com>
Sun, 17 Oct 2010 04:07:26 +0000 (21:07 -0700)
Added separate APB DMA bus-width controls depending on the audio bus format.

Signed-off-by: Iliyan Malchev <malchev@google.com>
arch/arm/mach-tegra/include/mach/audio.h
arch/arm/mach-tegra/tegra_i2s_audio.c

index 6e1af87a2ef9ef18d4af86d824aaa0635ce2b045..6a6b5d08833dbff3bedde56786b253c6ac6b93b7 100644 (file)
@@ -41,6 +41,8 @@ struct tegra_audio_platform_data {
        int mode; /* I2S, LJM, RJM, etc. */
        int fifo_fmt;
        int bit_size;
+       int i2s_bus_width; /* 32-bit for 16-bit packed I2S */
+       int dsp_bus_width; /* 16-bit for DSP data format */
 
        void *driver_data;
 };
index 47830789a74a216d72c8a6d901cfb7481635b584..dc6e5597a4388bc67c14ed4455624d2afe6a5499 100644 (file)
@@ -835,7 +835,10 @@ static void setup_dma_tx_request(struct tegra_dma_req *req,
        req->to_memory = false;
        req->dest_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_TX);
        req->dest_wrap = 4;
-       req->dest_bus_width = 16;
+       if (ads->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+               req->dest_bus_width = ads->pdata->dsp_bus_width;
+       else
+               req->dest_bus_width = ads->pdata->i2s_bus_width;
        req->source_bus_width = 32;
        req->source_wrap = 0;
        req->req_sel = ads->dma_req_sel;
@@ -854,7 +857,10 @@ static void setup_dma_rx_request(struct tegra_dma_req *req,
        req->to_memory = true;
        req->source_addr = i2s_get_fifo_phy_base(ads->i2s_phys, I2S_FIFO_RX);
        req->source_wrap = 4;
-       req->source_bus_width = 16;
+       if (ads->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+               req->source_bus_width = ads->pdata->dsp_bus_width;
+       else
+               req->source_bus_width = ads->pdata->i2s_bus_width;
        req->dest_bus_width = 32;
        req->dest_wrap = 0;
        req->req_sel = ads->dma_req_sel;
@@ -1375,15 +1381,20 @@ static long tegra_audio_ioctl(struct file *file,
        int rc = 0;
        struct audio_driver_state *ads = ads_from_misc_ctl(file);
        unsigned int mode;
+       bool dma_restart = false;
+
+       mutex_lock(&ads->out.lock);
+       mutex_lock(&ads->in.lock);
 
        switch (cmd) {
        case TEGRA_AUDIO_SET_BIT_FORMAT:
                if (copy_from_user(&mode, (const void __user *)arg,
                                        sizeof(mode))) {
                        rc = -EFAULT;
-                       break;
+                       goto done;
                }
-               switch(mode) {
+               dma_restart = (mode != ads->bit_format);
+               switch (mode) {
                case TEGRA_AUDIO_BIT_FORMAT_DEFAULT:
                        i2s_set_bit_format(ads->i2s_base, ads->pdata->mode);
                        ads->bit_format = mode;
@@ -1395,16 +1406,31 @@ static long tegra_audio_ioctl(struct file *file,
                default:
                        pr_err("%s: Invald PCM mode %d", __func__, mode);
                        rc = -EINVAL;
-                       break;
+                       goto done;
                }
                break;
        case TEGRA_AUDIO_GET_BIT_FORMAT:
                if (copy_to_user((void __user *)arg, &ads->bit_format,
-                               sizeof(mode))) {
+                               sizeof(mode)))
                        rc = -EFAULT;
+               goto done;
+       }
+
+       if (dma_restart && ads->using_dma) {
+               pr_debug("%s: Restarting DMA due to configuration change.\n",
+                       __func__);
+               if (kfifo_len(&ads->out.fifo) || ads->in.active) {
+                       pr_err("%s: dma busy, cannot restart.\n", __func__);
+                       rc = -EBUSY;
+                       goto done;
                }
-               break;
+               sound_ops->tear_down(ads);
+               sound_ops->setup(ads);
        }
+
+done:
+       mutex_unlock(&ads->in.lock);
+       mutex_unlock(&ads->out.lock);
        return rc;
 }