summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/spdif.c
diff options
context:
space:
mode:
authorVinod G <vinodg@nvidia.com>2011-04-21 15:16:40 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-04-26 15:56:17 -0700
commit5047a0f6d0b6c1c44d0f692531396f1c5b541c74 (patch)
treeffcd806160112f222d96be7ccae0c45fe460a333 /arch/arm/mach-tegra/spdif.c
parent23e69c4ccebadc2b1152a5b3973780cd797c939a (diff)
arm: tegra: Fix suspend/resume issue for spdif.
bug 793875 bug 818490 Merge spdif soc code from dev_hc. Add clock and power mode calls to spdif Fix the system suspend/resume issue caused by audio modules. Original-Change-Id: Ie6d9c1e52596fa744dff893fd7340c1fa2f0f058 Reviewed-on: http://git-master/r/28520 Reviewed-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com> Tested-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com> Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Reviewed-by: Scott Williams <scwilliams@nvidia.com> Change-Id: I367a048727b5fbe1a8db70a140b6ed1d752cd3a5
Diffstat (limited to 'arch/arm/mach-tegra/spdif.c')
-rw-r--r--arch/arm/mach-tegra/spdif.c443
1 files changed, 395 insertions, 48 deletions
diff --git a/arch/arm/mach-tegra/spdif.c b/arch/arm/mach-tegra/spdif.c
index 901ba540e692..df2600cb556c 100644
--- a/arch/arm/mach-tegra/spdif.c
+++ b/arch/arm/mach-tegra/spdif.c
@@ -24,6 +24,8 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
#include <mach/iomap.h>
#include <mach/spdif.h>
#include <mach/dma.h>
@@ -38,6 +40,23 @@
#define SPDIF_DEBUG_PRINT(fmt, arg...) do {} while (0)
#endif
+/*FIXME: spdifin clocks for slave are not supported yet */
+
+struct spdif_controller_info {
+
+ struct tegra_spdif_device_context spdev[AUDIO_FIFO_CNT];
+ struct tegra_spdif_clk_info spclk;
+
+ unsigned long base;
+ int refcnt;
+ void* reg_cache;
+};
+
+static struct spdif_controller_info *spinfo = NULL;
+
+void spdif_save_regs(unsigned long base);
+void spdif_restore_regs(unsigned long base);
+
static inline void spdif_writel(unsigned long base, u32 val, u32 reg)
{
SPDIF_DEBUG_PRINT("Spdif Write 0x%lx : %08x\n",base + reg, val);
@@ -51,6 +70,19 @@ static inline u32 spdif_readl(unsigned long base, u32 reg)
return val;
}
+int spdif_set_clock_rate(int mode, int rate)
+{
+ spinfo->spdev[mode].ch_prop.clk_rate = rate;
+
+ /* FIXME: stop clock before change if needed */
+ if (mode == AUDIO_TX_MODE)
+ clk_set_rate(spinfo->spclk.spdifout_clk, rate);
+ else
+ clk_set_rate(spinfo->spclk.spdifin_clk, rate);
+
+ return 0;
+}
+
int spdif_set_bit_mode(unsigned long base, unsigned mode)
{
u32 val = spdif_readl(base, SPDIF_CTRL_0);
@@ -68,8 +100,11 @@ int spdif_set_bit_mode(unsigned long base, unsigned mode)
return 0;
}
-int spdif_set_sample_rate(unsigned long base, unsigned int sample_rate)
+int spdif_set_sample_rate(int fifo_mode, unsigned int sample_rate)
{
+ unsigned int rate = 0;
+ unsigned long base = spinfo->base;
+
unsigned int ch_sta[] = {
0x0, /* 44.1, default values */
0x0,
@@ -108,6 +143,9 @@ int spdif_set_sample_rate(unsigned long base, unsigned int sample_rate)
spdif_writel(base, ch_sta[4], SPDIF_CH_STA_TX_E_0);
spdif_writel(base, ch_sta[5], SPDIF_CH_STA_TX_F_0);
+ rate = sample_rate << 7; /* sr*128 */
+ spdif_set_clock_rate(fifo_mode, rate);
+
return 0;
}
@@ -116,6 +154,267 @@ u32 spdif_get_control(unsigned long base)
return spdif_readl(base, SPDIF_CTRL_0);
}
+int spdif_close(void)
+{
+ SPDIF_DEBUG_PRINT(" %s \n", __func__);
+
+ if (!spinfo)
+ return -ENOENT;
+ spinfo->refcnt--;
+
+ if (spinfo->refcnt == 0) {
+ if (spinfo->spclk.spdifout_clk)
+ clk_put(spinfo->spclk.spdifout_clk);
+
+ if (spinfo->spclk.spdifin_clk)
+ clk_put(spinfo->spclk.spdifin_clk);
+
+ if (spinfo->spclk.hda2codec_clk)
+ clk_put(spinfo->spclk.hda2codec_clk);
+
+ }
+ return 0;
+}
+
+static int spdif_open(void)
+{
+ int err = 0;
+
+ SPDIF_DEBUG_PRINT(" %s ++ \n", __func__);
+ if (!spinfo) {
+
+ spinfo =
+ kzalloc(sizeof(struct spdif_controller_info), GFP_KERNEL);
+
+ if (!spinfo)
+ return -ENOMEM;
+
+ spinfo->spclk.spdifout_clk = clk_get_sys("spdif_out", NULL);
+ if (IS_ERR_OR_NULL(spinfo->spclk.spdifout_clk)) {
+ err = -ENOENT;
+ spinfo->spclk.spdifout_clk = 0;
+ goto fail_spdif_open;
+ }
+
+ spinfo->spclk.spdifin_clk = clk_get_sys("spdif_in", NULL);
+ if (IS_ERR_OR_NULL(spinfo->spclk.spdifin_clk)) {
+ err = -ENOENT;
+ spinfo->spclk.spdifin_clk = 0;
+ goto fail_spdif_open;
+ }
+
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ spinfo->spclk.hda2codec_clk = clk_get_sys("hda2codec_2x", NULL);
+ if (IS_ERR_OR_NULL(spinfo->spclk.hda2codec_clk)) {
+ pr_err("couldn't get hda2codec_2x clock\n");
+ err = -ENODEV;
+ spinfo->spclk.hda2codec_clk = 0;
+ goto fail_spdif_open;
+ }
+
+ spinfo->reg_cache =
+ kzalloc(sizeof(int)*SPDIF_REG_INDEX_MAX, GFP_KERNEL);
+#else
+ spinfo->reg_cache =
+ kzalloc(sizeof(struct spdif_regs_cache), GFP_KERNEL);
+#endif
+
+ if (!spinfo->reg_cache)
+ goto fail_spdif_open;
+
+ spinfo->refcnt++;
+ }
+
+ spinfo->refcnt++;
+
+ SPDIF_DEBUG_PRINT(" %s -- \n", __func__);
+ return err;
+
+fail_spdif_open:
+
+ spdif_close();
+ return err;
+}
+
+int spdif_initialize(unsigned long base, int mode)
+{
+ /* disable interrupts from SPDIF */
+ spdif_writel(base, 0x0, SPDIF_CTRL_0);
+ spdif_fifo_clear(base, mode);
+ spdif_fifo_enable(base, mode, 0);
+
+ spdif_set_bit_mode(base, SPDIF_BIT_MODE_MODE16BIT);
+ spdif_set_fifo_packed(base, 1);
+
+ spdif_set_sample_rate(mode, 44100);
+
+ return 0;
+}
+
+int spdif_init(unsigned long base, int mode,
+ struct tegra_spdif_property* spdifprop)
+{
+ int err = 0;
+
+ SPDIF_DEBUG_PRINT(" %s ++ \n", __func__);
+ if (spdif_open())
+ return err;
+
+ /* open audio_switch first */
+ err = audio_switch_open();
+ if (err) {
+ spdif_close();
+ return err;
+ }
+
+ spinfo->base = base;
+ spinfo->spdev[mode].ch_prop.clk_rate = spdifprop->clk_rate;
+
+ spdif_clock_set_parent(mode, 0);
+
+ err = spdif_clock_enable(mode);
+ if (err) {
+ audio_switch_close();
+ spdif_close();
+ return err;
+ }
+
+ spdif_initialize(base, mode);
+
+ spdif_clock_disable(mode);
+ SPDIF_DEBUG_PRINT(" %s -- \n", __func__);
+ return 0;
+}
+
+
+int spdif_clock_enable(int mode)
+{
+ int err = 0;
+
+ if (!spinfo)
+ return -ENOENT;
+
+ SPDIF_DEBUG_PRINT(" %s ++ \n", __func__);
+
+ err = audio_switch_enable_clock();
+ if (err)
+ return err;
+
+ if (spinfo->spdev[mode].clk_refs > 0) {
+ spinfo->spdev[mode].clk_refs++;
+
+ } else {
+
+ spdif_set_clock_rate(mode,
+ spinfo->spdev[mode].ch_prop.clk_rate);
+
+ if (mode == AUDIO_TX_MODE) {
+ if (clk_enable(spinfo->spclk.spdifout_clk)) {
+ err = PTR_ERR(spinfo->spclk.spdifout_clk);
+ goto spdif_clock_enable_failed;
+ }
+ } else {
+ if (clk_enable(spinfo->spclk.spdifin_clk)) {
+ err = PTR_ERR(spinfo->spclk.spdifin_clk);
+ goto spdif_clock_enable_failed;
+ }
+ }
+
+ if (spinfo->spclk.hda2codec_clk &&
+ clk_enable(spinfo->spclk.hda2codec_clk)) {
+ pr_err("failed to enable hda2codec_2x clock\n");
+ err = PTR_ERR(spinfo->spclk.hda2codec_clk);
+ goto spdif_clock_enable_failed;
+ }
+
+ spinfo->spdev[mode].clk_refs++;
+ }
+
+ SPDIF_DEBUG_PRINT("%s clk cnt 0x%x \n",
+ __func__, spinfo->spdev[mode].clk_refs);
+ return err;
+
+spdif_clock_enable_failed:
+
+ spdif_clock_disable(mode);
+ return err;
+}
+
+int spdif_clock_disable(int mode)
+{
+ SPDIF_DEBUG_PRINT(" %s ++ \n", __func__);
+
+ if (!spinfo)
+ return -ENOENT;
+
+ if (spinfo->spdev[mode].clk_refs > 0) {
+ spinfo->spdev[mode].clk_refs--;
+
+ if (spinfo->spdev[mode].clk_refs == 0) {
+ if (spinfo->spclk.hda2codec_clk)
+ clk_disable(spinfo->spclk.hda2codec_clk);
+ if (mode == AUDIO_TX_MODE) {
+ if (spinfo->spclk.spdifout_clk)
+ clk_disable(spinfo->spclk.spdifout_clk);
+ } else {
+ if (spinfo->spclk.spdifin_clk)
+ clk_disable(spinfo->spclk.spdifin_clk);
+ }
+ }
+ }
+
+ audio_switch_disable_clock();
+
+ SPDIF_DEBUG_PRINT("%s clock count 0x%x \n",
+ __func__, spinfo->spdev[mode].clk_refs);
+ return 0;
+}
+
+int spdif_clock_set_parent(int mode, int parent)
+{
+ /* Fix set the parent properly */
+ struct clk *pll_a_out0_clk = clk_get_sys(NULL, "pll_a_out0");
+
+ if (mode == AUDIO_TX_MODE) {
+ if (spinfo->spclk.spdifout_clk)
+ clk_set_parent(spinfo->spclk.spdifout_clk,
+ pll_a_out0_clk);
+ } else {
+ if (spinfo->spclk.spdifin_clk)
+ clk_set_parent(spinfo->spclk.spdifin_clk,
+ pll_a_out0_clk);
+ }
+
+ return 0;
+}
+
+
+int spdif_suspend(void)
+{
+ SPDIF_DEBUG_PRINT(" %s ++ \n", __func__);
+
+ /* FIXME : check for Rx mode if needed */
+ if (spinfo->spdev[AUDIO_TX_MODE].clk_refs == 0)
+ spdif_clock_enable(AUDIO_TX_MODE);
+
+ spdif_save_regs(spinfo->base);
+ audio_switch_suspend();
+
+ spdif_clock_disable(AUDIO_TX_MODE);
+
+ return 0;
+}
+
+int spdif_resume(void)
+{
+ SPDIF_DEBUG_PRINT(" %s ++ \n", __func__);
+ /* FIXME : check for Rx mode if needed */
+ spdif_clock_enable(AUDIO_TX_MODE);
+ audio_switch_resume();
+ spdif_restore_regs(spinfo->base);
+
+ return 0;
+}
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
void spdif_fifo_write(unsigned long base, int mode, u32 data)
@@ -209,24 +508,70 @@ u32 spdif_get_fifo_full_empty_count(unsigned long base, int mode)
return 0;
}
-int spdif_get_dma_requestor(int ifc, int fifo_mode)
+int spdif_get_dma_requestor(int fifo_mode)
{
return TEGRA_DMA_REQ_SEL_SPD_I;
}
-int spdif_initialize(unsigned long base, int mode)
+int spdif_free_dma_requestor(int fifo_mode)
{
- /* disable interrupts from SPDIF */
- spdif_writel(base, 0x0, SPDIF_CTRL_0);
- spdif_fifo_clear(base, mode);
- spdif_fifo_enable(base, mode, 0);
+ return 0;
+}
- spdif_set_bit_mode(base, SPDIF_BIT_MODE_MODE16BIT);
- spdif_set_fifo_packed(base, 1);
+void spdif_set_fifo_attention(int buffersize, int fifo_mode)
+{
+ int fifoattn = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
+ spinfo->spdev[fifo_mode].fifo_attn = fifoattn;
+}
- spdif_set_sample_rate(base, 44100);
+void spdif_save_regs(unsigned long base)
+{
+ struct spdif_regs_cache *regs =
+ (struct spdif_regs_cache *) spinfo->reg_cache;
- return 0;
+ regs->spdif_ctrl_0 = spdif_readl(base, SPDIF_CTRL_0);
+ regs->spdif_status_0 = spdif_readl(base, SPDIF_STATUS_0);
+ regs->spdif_strobe_ctrl_0 = spdif_readl(base, SPDIF_STROBE_CTRL_0);
+ regs->spdif_data_fifo_scr_0 = spdif_readl(base, SPDIF_DATA_FIFO_CSR_0);
+ regs->spdif_ch_sta_rx_a_0 = spdif_readl(base, SPDIF_CH_STA_RX_A_0);
+ regs->spdif_ch_sta_rx_b_0 = spdif_readl(base, SPDIF_CH_STA_RX_B_0);
+ regs->spdif_ch_sta_rx_c_0 = spdif_readl(base, SPDIF_CH_STA_RX_C_0);
+ regs->spdif_ch_sta_rx_d_0 = spdif_readl(base, SPDIF_CH_STA_RX_D_0);
+ regs->spdif_ch_sta_rx_e_0 = spdif_readl(base, SPDIF_CH_STA_RX_E_0);
+ regs->spdif_ch_sta_rx_f_0 = spdif_readl(base, SPDIF_CH_STA_RX_F_0);
+ regs->spdif_ch_sta_tx_a_0 = spdif_readl(base, SPDIF_CH_STA_TX_A_0);
+ regs->spdif_ch_sta_tx_b_0 = spdif_readl(base, SPDIF_CH_STA_TX_B_0);
+ regs->spdif_ch_sta_tx_c_0 = spdif_readl(base, SPDIF_CH_STA_TX_C_0);
+ regs->spdif_ch_sta_tx_d_0 = spdif_readl(base, SPDIF_CH_STA_TX_D_0);
+ regs->spdif_ch_sta_tx_e_0 = spdif_readl(base, SPDIF_CH_STA_TX_E_0);
+ regs->spdif_ch_sta_tx_f_0 = spdif_readl(base, SPDIF_CH_STA_TX_F_0);
+ regs->spdif_usr_sta_rx_a_0 = spdif_readl(base, SPDIF_USR_STA_RX_A_0);
+ regs->spdif_usr_dat_tx_a_0 = spdif_readl(base, SPDIF_USR_DAT_TX_A_0);
+}
+
+void spdif_restore_regs(unsigned long base)
+{
+ struct spdif_regs_cache *regs =
+ (struct spdif_regs_cache *) spinfo->reg_cache;
+
+ spdif_writel(base, regs->spdif_ctrl_0, SPDIF_CTRL_0);
+ spdif_writel(base, regs->spdif_status_0, SPDIF_STATUS_0);
+ spdif_writel(base, regs->spdif_strobe_ctrl_0, SPDIF_STROBE_CTRL_0);
+ spdif_writel(base, regs->spdif_data_fifo_scr_0, SPDIF_DATA_FIFO_CSR_0);
+ spdif_writel(base, regs->spdif_ch_sta_rx_a_0, SPDIF_CH_STA_RX_A_0);
+ spdif_writel(base, regs->spdif_ch_sta_rx_b_0, SPDIF_CH_STA_RX_B_0);
+ spdif_writel(base, regs->spdif_ch_sta_rx_c_0, SPDIF_CH_STA_RX_C_0);
+ spdif_writel(base, regs->spdif_ch_sta_rx_d_0, SPDIF_CH_STA_RX_D_0);
+ spdif_writel(base, regs->spdif_ch_sta_rx_e_0, SPDIF_CH_STA_RX_E_0);
+ spdif_writel(base, regs->spdif_ch_sta_rx_f_0, SPDIF_CH_STA_RX_F_0);
+ spdif_writel(base, regs->spdif_ch_sta_tx_a_0, SPDIF_CH_STA_TX_A_0);
+ spdif_writel(base, regs->spdif_ch_sta_tx_b_0, SPDIF_CH_STA_TX_B_0);
+ spdif_writel(base, regs->spdif_ch_sta_tx_c_0, SPDIF_CH_STA_TX_C_0);
+ spdif_writel(base, regs->spdif_ch_sta_tx_d_0, SPDIF_CH_STA_TX_D_0);
+ spdif_writel(base, regs->spdif_ch_sta_tx_e_0, SPDIF_CH_STA_TX_E_0);
+ spdif_writel(base, regs->spdif_ch_sta_tx_f_0, SPDIF_CH_STA_TX_F_0);
+ spdif_writel(base, regs->spdif_usr_sta_rx_a_0, SPDIF_USR_STA_RX_A_0);
+ spdif_writel(base, regs->spdif_usr_dat_tx_a_0, SPDIF_USR_DAT_TX_A_0);
}
void spdif_get_all_regs(unsigned long base, struct spdif_regs_cache* regs)
@@ -274,18 +619,9 @@ void spdif_set_all_regs(unsigned long base, struct spdif_regs_cache* regs)
}
#else
-struct spdif_controller_info {
-/*FIXME: add clock here or move the struct to common place */
- int dma_ch[AUDIO_FIFO_CNT];
- int stream_index[AUDIO_FIFO_CNT];
- unsigned int base;
-};
-
-static struct spdif_controller_info spdif_cont_info;
-
static int spdif_get_apbif_channel(int fifo_mode)
{
- return spdif_cont_info.dma_ch[fifo_mode];
+ return spinfo->spdev[fifo_mode].dma_ch;
}
int spdif_set_fifo_packed(unsigned long base, unsigned on)
@@ -307,9 +643,6 @@ int spdif_fifo_set_attention_level(unsigned long base, int mode,
{
int apbif_ifc = spdif_get_apbif_channel(mode);
- /* expected the level as four slots now */
- level = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
-
if (apbif_ifc != -ENOENT)
return apbif_fifo_set_attention_level(apbif_ifc,
mode, (level - 1));
@@ -366,7 +699,7 @@ u32 spdif_get_fifo_full_empty_count(unsigned long base, int mode)
return 0;
}
-int spdif_free_dma_requestor(int ifc, int fifo_mode)
+int spdif_free_dma_requestor(int fifo_mode)
{
int apbif_ifc = spdif_get_apbif_channel(fifo_mode);
@@ -391,10 +724,10 @@ int spdif_set_acif(int fifo_mode, struct audio_cif *cifInfo)
tx_audio_cif->client_bits = AUDIO_BIT_SIZE_16;
if (fifo_mode == AUDIO_TX_MODE)
- audio_switch_set_acif(spdif_cont_info.base +
+ audio_switch_set_acif(spinfo->base +
SPDIF_AUDIOCIF_TXDATA_CTRL_0, tx_audio_cif);
else
- audio_switch_set_acif(spdif_cont_info.base +
+ audio_switch_set_acif(spinfo->base +
SPDIF_AUDIOCIF_RXDATA_CTRL_0, tx_audio_cif);
audio_apbif_set_acif(spdif_get_apbif_channel(fifo_mode),
@@ -403,18 +736,18 @@ int spdif_set_acif(int fifo_mode, struct audio_cif *cifInfo)
return 0;
}
-int spdif_get_dma_requestor(int ifc, int fifo_mode)
+int spdif_get_dma_requestor(int fifo_mode)
{
int dma_index = 0;
- int apbif_ifc = ahubtx0_spdif;
+ int apbif_ifc = ahubrx0_spdif;
if (fifo_mode == AUDIO_RX_MODE)
- apbif_ifc = ahubrx0_spdif;
+ apbif_ifc = ahubtx0_spdif;
dma_index = apbif_get_channel(apbif_ifc, fifo_mode);
if (dma_index != -ENOENT) {
- spdif_cont_info.dma_ch[fifo_mode] = dma_index - 1;
+ spinfo->spdev[fifo_mode].dma_ch = dma_index - 1;
/* FIXME : this need to be called on connection request
*/
spdif_set_acif(fifo_mode, 0);
@@ -423,31 +756,40 @@ int spdif_get_dma_requestor(int ifc, int fifo_mode)
return dma_index;
}
-int spdif_initialize(unsigned long base, int mode)
+void spdif_set_fifo_attention(int buffersize, int fifo_mode)
{
- int err = 0;
-
- spdif_cont_info.dma_ch[0] = -ENOENT;
- spdif_cont_info.dma_ch[1] = -ENOENT;
- spdif_cont_info.base = base;
+ int fifoattn = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
- err = audio_switch_open();
- if (err)
- return err;
+ if (buffersize & 0xF)
+ fifoattn = SPDIF_FIFO_ATN_LVL_ONE_SLOT;
+ else if ((buffersize >> 4) & 0x1)
+ fifoattn = SPDIF_FIFO_ATN_LVL_FOUR_SLOTS;
+ else
+ fifoattn = SPDIF_FIFO_ATN_LVL_EIGHT_SLOTS;
- /* disable interrupts from SPDIF */
- spdif_writel(base, 0x0, SPDIF_CTRL_0);
- spdif_fifo_clear(base, mode);
+ spinfo->spdev[fifo_mode].fifo_attn = fifoattn;
+}
- spdif_fifo_enable(base, mode, 0);
+void spdif_save_regs(unsigned long base)
+{
+ int i = 0;
+ int *regs = (int *) spinfo->reg_cache;
- spdif_set_bit_mode(base, SPDIF_BIT_MODE_MODE16BIT);
- spdif_set_fifo_packed(base, 1);
+ for (i = 0; i <= SPDIF_REG_INDEX_MAX; i++) {
+ regs[i] = spdif_readl(base, (i << 2));
+ }
+}
- spdif_set_sample_rate(base, 44100);
+void spdif_restore_regs(unsigned long base)
+{
+ int i = 0;
+ int *regs = (int *) spinfo->reg_cache;
- return 0;
+ for (i = 0; i <= SPDIF_REG_INDEX_MAX; i++) {
+ spdif_writel(base, regs[i], (i << 2));
+ }
}
+
#endif
void spdif_fifo_enable(unsigned long base, int mode, int on)
@@ -474,5 +816,10 @@ void spdif_fifo_enable(unsigned long base, int mode, int on)
set_reg_mode(val, SPDIF_CTRL_0_RX_EN, on);
}
+ if (on)
+ spdif_fifo_set_attention_level(base, mode,
+ spinfo->spdev[mode].fifo_attn);
+
spdif_writel(base, val, SPDIF_CTRL_0);
+
}