From 5047a0f6d0b6c1c44d0f692531396f1c5b541c74 Mon Sep 17 00:00:00 2001 From: Vinod G Date: Thu, 21 Apr 2011 15:16:40 -0700 Subject: 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 Tested-by: Vinod Gopalakrishnakurup Reviewed-by: Varun Colbert Reviewed-by: Scott Williams Change-Id: I367a048727b5fbe1a8db70a140b6ed1d752cd3a5 --- arch/arm/mach-tegra/spdif.c | 443 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 395 insertions(+), 48 deletions(-) (limited to 'arch/arm/mach-tegra/spdif.c') 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 #include #include +#include +#include #include #include #include @@ -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); + } -- cgit v1.2.3