Merge branch 'linux-tegra-2.6.36' into android-tegra-2.6.36
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-tegra / tegra_i2s_audio.c
index a1bcc90b7519f28f974974517e97adb919947fb9..f8a67f1e2043aec7c91d15e49276068d4b0cd73e 100644 (file)
 #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>
 #include <linux/pm.h>
+#include <linux/workqueue.h>
+
 #include <mach/dma.h>
 #include <mach/iomap.h>
 #include <mach/i2s.h>
@@ -90,6 +93,9 @@ struct audio_stream {
        struct tegra_dma_req dma_req;
 
        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 {
@@ -218,13 +224,24 @@ static inline struct audio_driver_state *ads_from_in(
 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 inline void allow_suspend(struct audio_stream *as)
+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);
+}
+
+static inline void allow_suspend(struct audio_stream *as)
+{
+       schedule_work(&as->allow_suspend_work);
 }
 
 #define I2S_I2S_FIFO_TX_BUSY   I2S_I2S_STATUS_FIFO1_BSY
@@ -386,13 +403,15 @@ static int i2s_set_dsp_mode(unsigned long base, unsigned int mode)
        if (mode != TEGRA_AUDIO_DSP_PCM) {
                /* Disable PCM mode */
                val = i2s_readl(base, I2S_I2S_PCM_CTRL_0);
-               val &= ~(I2S_I2S_PCM_CTRL_TRM_MODE|I2S_I2S_PCM_CTRL_RCV_MODE);
+               val &= ~(I2S_I2S_PCM_CTRL_TRM_MODE |
+                               I2S_I2S_PCM_CTRL_RCV_MODE);
                i2s_writel(base, val, I2S_I2S_PCM_CTRL_0);
        }
        if (mode != TEGRA_AUDIO_DSP_NETWORK) {
                /* Disable Network mode */
                val = i2s_readl(base, I2S_I2S_NW_CTRL_0);
-               val &= ~(I2S_I2S_NW_CTRL_TRM_TLPHY_MODE|I2S_I2S_NW_CTRL_RCV_TLPHY_MODE);
+               val &= ~(I2S_I2S_NW_CTRL_TRM_TLPHY_MODE |
+                               I2S_I2S_NW_CTRL_RCV_TLPHY_MODE);
                i2s_writel(base, val, I2S_I2S_NW_CTRL_0);
        }
 
@@ -401,13 +420,15 @@ static int i2s_set_dsp_mode(unsigned long base, unsigned int mode)
        case TEGRA_AUDIO_DSP_NETWORK:
                /* Set DSP Network (Telephony) Mode */
                val = i2s_readl(base, I2S_I2S_NW_CTRL_0);
-               val |= I2S_I2S_NW_CTRL_TRM_TLPHY_MODE|I2S_I2S_NW_CTRL_RCV_TLPHY_MODE;
+               val |= I2S_I2S_NW_CTRL_TRM_TLPHY_MODE |
+                               I2S_I2S_NW_CTRL_RCV_TLPHY_MODE;
                i2s_writel(base, val, I2S_I2S_NW_CTRL_0);
                break;
        case TEGRA_AUDIO_DSP_PCM:
                /* Set DSP PCM Mode */
                val = i2s_readl(base, I2S_I2S_PCM_CTRL_0);
-               val |= I2S_I2S_PCM_CTRL_TRM_MODE|I2S_I2S_PCM_CTRL_RCV_MODE;
+               val |= I2S_I2S_PCM_CTRL_TRM_MODE |
+                               I2S_I2S_PCM_CTRL_RCV_MODE;
                i2s_writel(base, val, I2S_I2S_PCM_CTRL_0);
                break;
        }
@@ -558,6 +579,58 @@ static inline u32 i2s_get_fifo_full_empty_count(unsigned long base, int fifo)
        return val & I2S_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK;
 }
 
+static int i2s_configure(struct platform_device *pdev)
+{
+       struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
+       struct audio_driver_state *state = pdata->driver_data;
+       bool master;
+       struct clk *i2s_clk;
+       int master_clk;
+
+       /* dev_info(&pdev->dev, "%s\n", __func__); */
+
+       if (!state)
+               return -ENOMEM;
+
+       /* disable interrupts from I2S */
+       i2s_enable_fifos(state->i2s_base, 0);
+       i2s_fifo_clear(state->i2s_base, I2S_FIFO_TX);
+       i2s_fifo_clear(state->i2s_base, I2S_FIFO_RX);
+       i2s_set_left_right_control_polarity(state->i2s_base, 0); /* default */
+
+       i2s_clk = clk_get(&pdev->dev, NULL);
+       if (!i2s_clk) {
+               dev_err(&pdev->dev, "%s: could not get i2s clock\n",
+                       __func__);
+               return -EIO;
+       }
+
+       master = state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP ?
+                       state->pdata->dsp_master : state->pdata->i2s_master;
+
+
+       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! */
+       if (master)
+               i2s_set_channel_bit_count(state->i2s_base, master_clk,
+                       clk_get_rate(i2s_clk)*I2S_CLK_FUDGE_FACTOR);
+       i2s_set_master(state->i2s_base, master);
+
+       i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1);
+       i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_RX, 0);
+
+       if (state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
+               i2s_set_bit_format(state->i2s_base, I2S_BIT_FORMAT_DSP);
+       else
+               i2s_set_bit_format(state->i2s_base, state->pdata->mode);
+       i2s_set_bit_size(state->i2s_base, state->pdata->bit_size);
+       i2s_set_fifo_format(state->i2s_base, state->pdata->fifo_fmt);
+
+       return 0;
+}
+
 static int init_stream_buffer(struct audio_stream *,
                struct tegra_audio_buf_config *cfg, unsigned);
 
@@ -1489,6 +1562,7 @@ static long tegra_audio_ioctl(struct file *file,
                        goto done;
                }
                sound_ops->tear_down(ads);
+               i2s_configure(ads->pdev);
                sound_ops->setup(ads);
        }
 
@@ -1562,7 +1636,7 @@ static long tegra_audio_in_ioctl(struct file *file,
                        break;
                }
 #endif
-               if(cfg.stereo && !ads->pdata->stereo_capture) {
+               if (cfg.stereo && !ads->pdata->stereo_capture) {
                        pr_err("%s: not capable of stereo capture.",
                                __func__);
                        rc = -EINVAL;
@@ -1918,6 +1992,8 @@ static int tegra_audio_out_release(struct inode *inode, struct file *file)
                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));
@@ -1963,6 +2039,8 @@ static int tegra_audio_in_release(struct inode *inode, struct file *file)
        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));
@@ -2341,14 +2419,14 @@ static int tegra_audio_probe(struct platform_device *pdev)
 
        i2s_clk = clk_get(&pdev->dev, NULL);
        if (!i2s_clk) {
-               dev_err(&pdev->dev, "%s: could not get i2s1 clock\n",
+               dev_err(&pdev->dev, "%s: could not get i2s clock\n",
                        __func__);
                return -EIO;
        }
 
        clk_set_rate(i2s_clk, state->pdata->i2s_clk_rate);
        if (clk_enable(i2s_clk)) {
-               dev_err(&pdev->dev, "%s: failed to enable i2s1 clock\n",
+               dev_err(&pdev->dev, "%s: failed to enable i2s clock\n",
                        __func__);
                return -EIO;
        }
@@ -2370,20 +2448,9 @@ static int tegra_audio_probe(struct platform_device *pdev)
        }
        clk_enable(audio_sync_clk);
 
-       i2s_enable_fifos(state->i2s_base, 0);
-       /* disable interrupts from I2S */
-       i2s_fifo_clear(state->i2s_base, I2S_FIFO_TX);
-       i2s_fifo_clear(state->i2s_base, I2S_FIFO_RX);
-       i2s_set_left_right_control_polarity(state->i2s_base, 0); /* default */
-       if (state->pdata->master)
-               i2s_set_channel_bit_count(state->i2s_base, 44100,
-                               clk_get_rate(i2s_clk));
-       i2s_set_master(state->i2s_base, state->pdata->master);
-       i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1);
-       i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_RX, 0);
-       i2s_set_bit_format(state->i2s_base, state->pdata->mode);
-       i2s_set_bit_size(state->i2s_base, state->pdata->bit_size);
-       i2s_set_fifo_format(state->i2s_base, state->pdata->fifo_fmt);
+       rc = i2s_configure(pdev);
+       if (rc < 0)
+               return rc;
 
        if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) {
                state->out.opened = 0;
@@ -2405,9 +2472,16 @@ static int tegra_audio_probe(struct platform_device *pdev)
                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),
+                       "i2s.%d-audio-out", state->pdev->id);
+               wake_lock_init(&state->out.wake_lock, WAKE_LOCK_SUSPEND,
+                       state->out.wake_lock_name);
+
                rc = setup_misc_device(&state->misc_out,
                        &tegra_audio_out_fops,
                        "audio%d_out", state->pdev->id);
@@ -2442,9 +2516,16 @@ static int tegra_audio_probe(struct platform_device *pdev)
                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),
+                       "i2s.%d-audio-in", state->pdev->id);
+               wake_lock_init(&state->in.wake_lock, WAKE_LOCK_SUSPEND,
+                       state->in.wake_lock_name);
+
                rc = setup_misc_device(&state->misc_in,
                        &tegra_audio_in_fops,
                        "audio%d_in", state->pdev->id);
@@ -2515,37 +2596,7 @@ static int tegra_audio_suspend(struct platform_device *pdev, pm_message_t mesg)
 
 static int tegra_audio_resume(struct platform_device *pdev)
 {
-       struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
-       struct audio_driver_state *state = pdata->driver_data;
-
-       /* dev_info(&pdev->dev, "%s\n", __func__); */
-
-       if (!state)
-               return -ENOMEM;
-
-       /* disable interrupts from I2S */
-       i2s_fifo_clear(state->i2s_base, I2S_FIFO_TX);
-       i2s_fifo_clear(state->i2s_base, I2S_FIFO_RX);
-       i2s_enable_fifos(state->i2s_base, 0);
-
-       i2s_set_left_right_control_polarity(state->i2s_base, 0); /* default */
-
-       if (state->pdata->master)
-               i2s_set_channel_bit_count(state->i2s_base, 44100,
-                               state->pdata->i2s_clk_rate);
-       i2s_set_master(state->i2s_base, state->pdata->master);
-
-       i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_TX, 1);
-       i2s_set_fifo_mode(state->i2s_base, I2S_FIFO_RX, 0);
-
-       if (state->bit_format == TEGRA_AUDIO_BIT_FORMAT_DSP)
-               i2s_set_bit_format(state->i2s_base, I2S_BIT_FORMAT_DSP);
-       else
-               i2s_set_bit_format(state->i2s_base, state->pdata->mode);
-       i2s_set_bit_size(state->i2s_base, state->pdata->bit_size);
-       i2s_set_fifo_format(state->i2s_base, state->pdata->fifo_fmt);
-
-       return 0;
+       return i2s_configure(pdev);
 }
 #endif /* CONFIG_PM */