#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>
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 {
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
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);
}
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;
}
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);
goto done;
}
sound_ops->tear_down(ads);
+ i2s_configure(ads->pdev);
sound_ops->setup(ads);
}
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;
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));
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));
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;
}
}
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;
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);
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);
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 */