#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>
#define PCM_IN_BUFFER_PADDING (1<<6) /* bytes */
+#define TEGRA_AUDIO_DSP_NONE 0
+#define TEGRA_AUDIO_DSP_PCM 1
+#define TEGRA_AUDIO_DSP_NETWORK 2
+#define TEGRA_AUDIO_DSP_TDM 3
+
/* per stream (input/output) */
struct audio_stream {
int opened;
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
i2s_writel(base, val, I2S_I2S_CTRL_0);
}
+static int i2s_set_dsp_mode(unsigned long base, unsigned int mode)
+{
+ u32 val;
+ if (mode > TEGRA_AUDIO_DSP_TDM) {
+ pr_err("%s: invalid mode %d.\n", __func__, mode);
+ return -EINVAL;
+ }
+ if (mode == TEGRA_AUDIO_DSP_TDM) {
+ pr_err("TEGRA_AUDIO_DSP_TDM not implemented.\n");
+ return -EINVAL;
+ }
+
+ /* Disable unused modes */
+ 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);
+ 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);
+ i2s_writel(base, val, I2S_I2S_NW_CTRL_0);
+ }
+
+ /* Enable the selected mode. */
+ switch (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;
+ 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;
+ i2s_writel(base, val, I2S_I2S_PCM_CTRL_0);
+ break;
+ }
+
+ return 0;
+}
+
static int i2s_set_bit_format(unsigned long base, unsigned fmt)
{
u32 val;
val = i2s_readl(base, I2S_I2S_CTRL_0);
val &= ~I2S_I2S_CTRL_BIT_FORMAT_MASK;
val |= fmt << I2S_BIT_FORMAT_SHIFT;
-
i2s_writel(base, val, I2S_I2S_CTRL_0);
+ /* For DSP format, select DSP PCM mode. */
+ /* PCM mode and Network Mode slot 0 are effectively identical. */
+ if (fmt == I2S_BIT_FORMAT_DSP)
+ i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_PCM);
+ else
+ i2s_set_dsp_mode(base, TEGRA_AUDIO_DSP_NONE);
+
return 0;
}
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);
{
int rc;
pr_debug("%s: wait for completion\n", __func__);
- rc = wait_for_completion_interruptible(
- &as->stop_completion);
+ rc = wait_for_completion_interruptible_timeout(
+ &as->stop_completion, HZ);
+ if (!rc)
+ pr_err("%s: wait timed out", __func__);
allow_suspend(as);
pr_debug("%s: done: %d\n", __func__, rc);
return true;
int rc;
pr_info("%s\n", __func__);
- /* setup audio playback */
- ads->out.buf_phys = dma_map_single(&ads->pdev->dev, ads->out.buffer,
- 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);
- if (!ads->out.dma_chan) {
- pr_err("%s: could not allocate output I2S DMA channel: %ld\n",
- __func__, PTR_ERR(ads->out.dma_chan));
- rc = -ENODEV;
- goto fail_tx;
- }
-
- /* setup audio recording */
- ads->in.buf_phys = dma_map_single(&ads->pdev->dev, ads->in.buffer,
- 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);
- if (!ads->in.dma_chan) {
- pr_err("%s: could not allocate input I2S DMA channel: %ld\n",
- __func__, PTR_ERR(ads->in.dma_chan));
- rc = -ENODEV;
- goto fail_rx;
+ if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) {
+ /* setup audio playback */
+ ads->out.buf_phys = dma_map_single(&ads->pdev->dev,
+ ads->out.buffer,
+ 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);
+ if (!ads->out.dma_chan) {
+ pr_err("%s: error allocating output DMA channel: %ld\n",
+ __func__, PTR_ERR(ads->out.dma_chan));
+ rc = -ENODEV;
+ goto fail_tx;
+ }
+ }
+
+ if ((ads->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) {
+ /* setup audio recording */
+ ads->in.buf_phys = dma_map_single(&ads->pdev->dev,
+ ads->in.buffer,
+ 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);
+ if (!ads->in.dma_chan) {
+ pr_err("%s: error allocating input DMA channel: %ld\n",
+ __func__, PTR_ERR(ads->in.dma_chan));
+ rc = -ENODEV;
+ goto fail_rx;
+ }
}
return 0;
fail_rx:
- dma_unmap_single(&ads->pdev->dev, ads->in.buf_phys,
+ 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);
- tegra_dma_free_channel(ads->in.dma_chan);
- ads->in.dma_chan = 0;
-
+ tegra_dma_free_channel(ads->in.dma_chan);
+ ads->in.dma_chan = 0;
+ }
fail_tx:
- dma_unmap_single(&ads->pdev->dev, ads->out.buf_phys,
+ 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);
- tegra_dma_free_channel(ads->out.dma_chan);
- ads->out.dma_chan = 0;
+ tegra_dma_free_channel(ads->out.dma_chan);
+ ads->out.dma_chan = 0;
+ }
return rc;
}
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;
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;
if (ais->dma_has_it) {
pr_debug("%s: recording already in progress\n", __func__);
- return 0;
+ return -EALREADY;
}
/* Don't send all the data yet. */
if (i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_TX)) {
pr_debug("%s: playback is already in progress\n", __func__);
- return 0;
+ return -EALREADY;
}
pr_debug("%s\n", __func__);
if (i2s_is_fifo_enabled(ads->i2s_base, I2S_FIFO_RX)) {
pr_debug("%s: already started\n", __func__);
- return 0;
+ return -EALREADY;
}
pr_debug("%s: start\n", __func__);
}
rc = start_playback(&ads->out);
- if (rc < 0) {
+ if (rc < 0 && rc != -EALREADY) {
pr_err("%s: could not start playback: %d\n", __func__, rc);
goto done;
}
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;
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);
+ i2s_configure(ads->pdev);
+ sound_ops->setup(ads);
}
+
+done:
+ mutex_unlock(&ads->in.lock);
+ mutex_unlock(&ads->out.lock);
return rc;
}
break;
}
+#ifdef SAMPLE_RATE_CONVERTER_IN_DRIVER
switch (cfg.rate) {
case 8000:
ads->in_divs = divs_8000;
rc = -EINVAL;
break;
}
-
+#endif
+ if (cfg.stereo && !ads->pdata->stereo_capture) {
+ pr_err("%s: not capable of stereo capture.",
+ __func__);
+ rc = -EINVAL;
+ }
if (!rc) {
pr_info("%s: setting input sampling rate to %d, %s\n",
__func__, cfg.rate,
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.
*/
const int *divs, int divs_len,
bool out_stereo)
{
+ /* Todo: Handle mono source streams */
int i, j;
int lsum, rsum;
int di, div;
return bytes_ds;
}
+#endif /*SAMPLE_RATE_CONVERTER_IN_DRIVER*/
static ssize_t downsample_to_user(struct audio_driver_state *ads,
void __user *buf,
BUG_ON(!sgl[i].length);
again:
- ds_now = __downsample_to_user(ads,
+#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);
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));
state->pdata->driver_data = state;
BUG_ON(!state->pdata);
+ if (!(state->pdata->mask &
+ (TEGRA_AUDIO_ENABLE_TX | TEGRA_AUDIO_ENABLE_RX))) {
+ dev_err(&pdev->dev, "neither tx nor rx is enabled!\n");
+ return -EIO;
+ }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no mem resource!\n");
return -EIO;
}
- state->out.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
- state->in.i2s_fifo_atn_level = I2S_FIFO_ATN_LVL_FOUR_SLOTS;
-
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no dma resource!\n");
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);
- /* 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,
- 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);
-
- 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->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->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 = i2s_configure(pdev);
if (rc < 0)
return rc;
- 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);
- if (rc < 0)
- return rc;
+ if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_TX)) {
+ 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);
+ if (rc < 0)
+ return rc;
- pm_qos_add_request(&state->in.pm_qos, PM_QOS_CPU_DMA_LATENCY,
- PM_QOS_DEFAULT_VALUE);
- pm_qos_add_request(&state->out.pm_qos, PM_QOS_CPU_DMA_LATENCY,
+ 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);
- if (request_irq(state->irq, i2s_interrupt,
- IRQF_DISABLED, state->pdev->name, state) < 0) {
- dev_err(&pdev->dev,
- "%s: could not register handler for irq %d\n",
- __func__, state->irq);
- return -EIO;
- }
+ 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,
+ rc = setup_misc_device(&state->misc_out,
&tegra_audio_out_fops,
"audio%d_out", state->pdev->id);
- if (rc < 0)
- return rc;
+ if (rc < 0)
+ return rc;
- rc = setup_misc_device(&state->misc_out_ctl,
- &tegra_audio_out_ctl_fops,
- "audio%d_out_ctl", state->pdev->id);
- if (rc < 0)
- return rc;
+ rc = setup_misc_device(&state->misc_out_ctl,
+ &tegra_audio_out_ctl_fops,
+ "audio%d_out_ctl", state->pdev->id);
+ if (rc < 0)
+ return rc;
+ }
+
+ if ((state->pdata->mask & TEGRA_AUDIO_ENABLE_RX)) {
+ 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);
+ 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,
+ rc = setup_misc_device(&state->misc_in,
&tegra_audio_in_fops,
"audio%d_in", state->pdev->id);
- if (rc < 0)
- return rc;
+ if (rc < 0)
+ return rc;
- rc = setup_misc_device(&state->misc_in_ctl,
+ rc = setup_misc_device(&state->misc_in_ctl,
&tegra_audio_in_ctl_fops,
"audio%d_in_ctl", state->pdev->id);
- if (rc < 0)
- return rc;
+ if (rc < 0)
+ return rc;
+ }
+
+ if (request_irq(state->irq, i2s_interrupt,
+ IRQF_DISABLED, state->pdev->name, state) < 0) {
+ dev_err(&pdev->dev,
+ "%s: could not register handler for irq %d\n",
+ __func__, state->irq);
+ return -EIO;
+ }
rc = setup_misc_device(&state->misc_ctl,
&tegra_audio_ctl_fops,
return 0;
}
+#ifdef CONFIG_PM
+static int tegra_audio_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ /* dev_info(&pdev->dev, "%s\n", __func__); */
+ return 0;
+}
+
+static int tegra_audio_resume(struct platform_device *pdev)
+{
+ return i2s_configure(pdev);
+}
+#endif /* CONFIG_PM */
+
static struct platform_driver tegra_audio_driver = {
.driver = {
.name = "i2s",
.owner = THIS_MODULE,
},
.probe = tegra_audio_probe,
+#ifdef CONFIG_PM
+ .suspend = tegra_audio_suspend,
+ .resume = tegra_audio_resume,
+#endif
};
static int __init tegra_audio_init(void)