summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra_spdif_audio.c
diff options
context:
space:
mode:
authorScott Peterson <speterson@nvidia.com>2010-11-09 16:22:27 -0600
committerIliyan Malchev <malchev@google.com>2010-11-22 15:41:17 -0800
commit3707e1c4af5f9af1a45c27a5894ac23741c05d80 (patch)
tree555461ed6902996e6f71641808ea12d79d484c9d /arch/arm/mach-tegra/tegra_spdif_audio.c
parent230015ed6a5c9c7847b215130b72fe97f77fb459 (diff)
[ARM] tegra_spdif_audio: Enable 16 bit packed mode
Enable 16 bit packed fifo format mode to work avoid channel swapping when dma underrun occurs. Added suspend/resume functionality so spdif registers are restored. Change-Id: Id80e7903b81fd3b3b84af08dba40196121eb6cb7 Signed-off-by: Iliyan Malchev <malchev@google.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra_spdif_audio.c')
-rw-r--r--arch/arm/mach-tegra/tegra_spdif_audio.c97
1 files changed, 71 insertions, 26 deletions
diff --git a/arch/arm/mach-tegra/tegra_spdif_audio.c b/arch/arm/mach-tegra/tegra_spdif_audio.c
index bde82c1c4f32..dc8a09b1ff2f 100644
--- a/arch/arm/mach-tegra/tegra_spdif_audio.c
+++ b/arch/arm/mach-tegra/tegra_spdif_audio.c
@@ -47,6 +47,7 @@
#include <linux/delay.h>
#include <linux/tegra_audio.h>
#include <linux/workqueue.h>
+#include <linux/pm.h>
#include <mach/dma.h>
#include <mach/iomap.h>
@@ -106,6 +107,7 @@ struct audio_driver_state {
bool using_dma;
unsigned long dma_req_sel;
+ bool fifo_init;
int irq; /* for pio mode */
struct spdif_pio_stats pio_stats;
@@ -229,7 +231,11 @@ static int spdif_fifo_set_attention_level(unsigned long base,
static void spdif_fifo_enable(unsigned long base, int on)
{
u32 val = spdif_readl(base, SPDIF_CTRL_0);
- val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN);
+
+ if (on && (val & SPDIF_CTRL_0_TX_EN))
+ return;
+
+ val &= ~(SPDIF_CTRL_0_TX_EN | SPDIF_CTRL_0_TC_EN | SPDIF_CTRL_0_TU_EN);
val |= on ? (SPDIF_CTRL_0_TX_EN) : 0;
val |= on ? (SPDIF_CTRL_0_TC_EN) : 0;
@@ -339,12 +345,11 @@ static int spdif_set_sample_rate(struct audio_driver_state *state,
unsigned int sample_rate)
{
unsigned int clock_freq = 0;
- unsigned int parent_clock_freq = 0;
struct clk *spdif_clk;
unsigned int ch_sta[] = {
0x0, /* 44.1, default values */
- 0xf << 4, /* bits 36-39, original sample freq -- 44.1 */
+ 0x0,
0x0,
0x0,
0x0,
@@ -354,37 +359,30 @@ static int spdif_set_sample_rate(struct audio_driver_state *state,
switch (sample_rate) {
case 32000:
clock_freq = 4096000; /* 4.0960 MHz */
- parent_clock_freq = 12288000;
ch_sta[0] = 0x3 << 24;
- ch_sta[1] = 0xC << 4;
+ /*ch_sta[1] = 0xC << 4;*/
break;
case 44100:
clock_freq = 5644800; /* 5.6448 MHz */
- parent_clock_freq = 11289600;
ch_sta[0] = 0x0;
- ch_sta[1] = 0xF << 4;
+ /*ch_sta[1] = 0xF << 4;*/
break;
case 48000:
clock_freq = 6144000; /* 6.1440MHz */
- parent_clock_freq = 12288000;
ch_sta[0] = 0x2 << 24;
- ch_sta[1] = 0xD << 4;
+ /*ch_sta[1] = 0xD << 4;*/
break;
case 88200:
clock_freq = 11289600; /* 11.2896 MHz */
- parent_clock_freq = 11289600;
break;
case 96000:
clock_freq = 12288000; /* 12.288 MHz */
- parent_clock_freq = 12288000;
break;
case 176400:
clock_freq = 22579200; /* 22.5792 MHz */
- parent_clock_freq = 11289600;
break;
case 192000:
clock_freq = 24576000; /* 24.5760 MHz */
- parent_clock_freq = 12288000;
break;
default:
return -1;
@@ -415,6 +413,31 @@ static int spdif_set_sample_rate(struct audio_driver_state *state,
return 0;
}
+static int spdif_configure(struct platform_device *pdev)
+{
+ struct tegra_audio_platform_data *pdata = pdev->dev.platform_data;
+ struct audio_driver_state *state = pdata->driver_data;
+
+ if (!state)
+ return -ENOMEM;
+
+ /* disable interrupts from SPDIF */
+ spdif_writel(state->spdif_base, 0x0, SPDIF_CTRL_0);
+ spdif_fifo_clear(state->spdif_base);
+ spdif_enable_fifos(state->spdif_base, 0);
+
+ spdif_set_bit_mode(state->spdif_base, state->pdata->mode);
+ spdif_set_fifo_packed(state->spdif_base, state->pdata->fifo_fmt);
+
+ spdif_fifo_set_attention_level(state->spdif_base,
+ state->out.spdif_fifo_atn_level);
+
+ spdif_set_sample_rate(state, 44100);
+
+ state->fifo_init = true;
+ return 0;
+}
+
static int init_stream_buffer(struct audio_stream *);
static int setup_dma(struct audio_driver_state *);
@@ -623,9 +646,14 @@ static void setup_dma_tx_request(struct tegra_dma_req *req,
req->to_memory = false;
req->dest_addr = spdif_get_fifo_phy_base(ads->spdif_phys);
req->dest_wrap = 4;
- req->dest_bus_width = 16;
- req->source_bus_width = 32;
+ if (ads->pdata->mode != SPDIF_BIT_MODE_MODE16BIT ||
+ ads->pdata->fifo_fmt)
+ req->dest_bus_width = 32;
+ else
+ req->dest_bus_width = 16;
+
req->source_wrap = 0;
+ req->source_bus_width = 32;
req->req_sel = ads->dma_req_sel;
}
@@ -657,8 +685,6 @@ static int resume_dma_playback(struct audio_stream *aos)
#if 0
spdif_fifo_clear(ads->spdif_base);
#endif
- spdif_fifo_set_attention_level(ads->spdif_base,
- aos->spdif_fifo_atn_level);
req->source_addr = sg_dma_address(&aos->sg);
req->size = sg_dma_len(&aos->sg);
@@ -671,7 +697,12 @@ static int resume_dma_playback(struct audio_stream *aos)
pr_debug("%s resume playback (%d in fifo, writing %d)\n",
__func__, kfifo_len(&aos->fifo), req->size);
- spdif_fifo_enable(ads->spdif_base, 1);
+ if (ads->fifo_init) {
+ spdif_set_bit_mode(ads->spdif_base, ads->pdata->mode);
+ spdif_set_fifo_packed(ads->spdif_base, ads->pdata->fifo_fmt);
+ spdif_fifo_enable(ads->spdif_base, 1);
+ ads->fifo_init = false;
+ }
aos->last_dma_ts = ktime_get_real();
rc = tegra_dma_enqueue_req(aos->dma_chan, req);
@@ -702,6 +733,7 @@ static void stop_dma_playback(struct audio_stream *aos)
}
if (spin == 100)
pr_warn("%s: spinny\n", __func__);
+ ads->fifo_init = true;
}
/* PIO (non-DMA) */
@@ -944,7 +976,7 @@ static int tegra_spdif_out_open(struct inode *inode, struct file *file)
ads->out.stop = false;
kfifo_reset(&ads->out.fifo);
- rc = spdif_set_sample_rate(ads, 44100);
+ rc = spdif_set_sample_rate(ads, 44100);
}
mutex_unlock(&ads->out.lock);
@@ -1234,8 +1266,6 @@ static int tegra_spdif_probe(struct platform_device *pdev)
return -EIO;
}
- state->out.spdif_fifo_atn_level = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
-
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no dma resource!\n");
@@ -1252,12 +1282,10 @@ static int tegra_spdif_probe(struct platform_device *pdev)
memset(&state->pio_stats, 0, sizeof(state->pio_stats));
- /* disable interrupts from SPDIF */
- spdif_fifo_clear(state->spdif_base);
- spdif_enable_fifos(state->spdif_base, 0);
- spdif_set_bit_mode(state->spdif_base, state->pdata->mode);
- spdif_set_fifo_packed(state->spdif_base, state->pdata->fifo_fmt);
+ rc = spdif_configure(pdev);
+ if (rc < 0)
+ return rc;
state->out.opened = 0;
state->out.active = false;
@@ -1320,12 +1348,29 @@ static int tegra_spdif_probe(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int tegra_spdif_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ /* dev_info(&pdev->dev, "%s\n", __func__); */
+ return 0;
+}
+
+static int tegra_spdif_resume(struct platform_device *pdev)
+{
+ return spdif_configure(pdev);
+}
+#endif /* CONFIG_PM */
+
static struct platform_driver tegra_spdif_driver = {
.driver = {
.name = "spdif_out",
.owner = THIS_MODULE,
},
.probe = tegra_spdif_probe,
+#ifdef CONFIG_PM
+ .suspend = tegra_spdif_suspend,
+ .resume = tegra_spdif_resume,
+#endif
};
static int __init tegra_spdif_init(void)