diff options
-rw-r--r-- | include/linux/fsl_devices.h | 6 | ||||
-rw-r--r-- | sound/soc/codecs/mxc_spdif.c | 102 | ||||
-rw-r--r-- | sound/soc/codecs/mxc_spdif.h | 2 | ||||
-rw-r--r-- | sound/soc/imx/imx-spdif.c | 2 |
4 files changed, 70 insertions, 42 deletions
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 2b8fc9b61efb..41f00552e3aa 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -330,7 +330,11 @@ struct mxc_spdif_platform_data { int spdif_rx; /* S/PDIF rx enabled for this board */ int spdif_clk_44100; /* tx clk mux in SPDIF_REG_STC; -1 for none */ int spdif_clk_48000; /* tx clk mux in SPDIF_REG_STC; -1 for none */ - int spdif_clkid; /* rx clk mux select in SPDIF_REG_SRPC */ + int spdif_div_44100; /* tx clk div in SPDIF_REG_STC */ + int spdif_div_48000; /* tx clk div in SPDIF_REG_STC */ + int spdif_div_32000; /* tx clk div in SPDIF_REG_STC */ + int spdif_rx_clk; /* rx clk mux select in SPDIF_REG_SRPC */ + int (*spdif_clk_set_rate) (struct clk *clk, unsigned long rate); struct clk *spdif_clk; struct clk *spdif_core_clk; struct clk *spdif_audio_clk; diff --git a/sound/soc/codecs/mxc_spdif.c b/sound/soc/codecs/mxc_spdif.c index 3369e3bdcc22..56eac29c54c5 100644 --- a/sound/soc/codecs/mxc_spdif.c +++ b/sound/soc/codecs/mxc_spdif.c @@ -327,6 +327,9 @@ static void spdif_softreset(void) value = __raw_readl(spdif_base_addr + SPDIF_REG_SCR) & 0x1000; } +/* + * Set clock accuracy information in consumer channel status. + */ static int spdif_set_clk_accuracy(enum spdif_clk_accuracy level) { unsigned long value; @@ -394,55 +397,76 @@ static int spdif_get_rxclk_rate(struct clk *bus_clk, enum spdif_gainsel gainsel) return (int)tmpval64; } -static int spdif_set_sample_rate(int src_44100, int src_48000, int sample_rate) +static int spdif_set_sample_rate(struct snd_soc_codec *codec, int sample_rate) { - unsigned long cstatus, stc; - int ret = 0; - - cstatus = __raw_readl(SPDIF_REG_STCSCL + spdif_base_addr) & 0xfffff0; - stc = __raw_readl(SPDIF_REG_STC + spdif_base_addr) & ~0x7FF; + struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); + struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; + unsigned long cstatus, stc, clk = -1, div = 1, cstatus_fs = 0; + int clk_fs; switch (sample_rate) { case 44100: - if (src_44100 < 0) { - pr_info("%s: no defined 44100 clk src\n", __func__); - ret = -1; - } else { - __raw_writel(cstatus, SPDIF_REG_STCSCL + spdif_base_addr); - stc |= (src_44100 << 8) | 0x07; - __raw_writel(stc, SPDIF_REG_STC + spdif_base_addr); - pr_debug("set sample rate to 44100\n"); - } + clk_fs = 44100; + clk = plat_data->spdif_clk_44100; + div = plat_data->spdif_div_44100; + cstatus_fs = 0; break; + case 48000: - if (src_48000 < 0) { - pr_info("%s: no defined 48000 clk src\n", __func__); - ret = -1; - } else { - cstatus |= 0x04; - __raw_writel(cstatus, SPDIF_REG_STCSCL + spdif_base_addr); - stc |= (src_48000 << 8) | 0x07; - __raw_writel(stc, SPDIF_REG_STC + spdif_base_addr); - pr_debug("set sample rate to 48000\n"); - } + clk_fs = 48000; + clk = plat_data->spdif_clk_48000; + div = plat_data->spdif_div_48000; + cstatus_fs = 0x04; break; + case 32000: - if (src_48000 < 0) { - pr_info("%s: no defined 48000 clk src\n", __func__); - ret = -1; - } else { - cstatus |= 0x0c; - __raw_writel(cstatus, SPDIF_REG_STCSCL + spdif_base_addr); - stc |= (src_48000 << 8) | 0x0b; - __raw_writel(stc, SPDIF_REG_STC + spdif_base_addr); - pr_debug("set sample rate to 32000\n"); - } + /* use 48K clk for 32K */ + clk_fs = 48000; + clk = plat_data->spdif_clk_48000; + div = plat_data->spdif_div_32000; + cstatus_fs = 0x0c; break; + + default: + pr_err("%s: unsupported sample rate %d\n", __func__, sample_rate); + return -EINVAL; } + if (clk < 0) { + pr_info("%s: no defined %d clk src\n", __func__, clk_fs); + return -EINVAL; + } + + /* + * The S/PDIF block needs a clock of 64 * fs * div. The S/PDIF block + * will divide by (div). So request 64 * fs * (div+1) which will + * get rounded. + */ + if (plat_data->spdif_clk_set_rate) + plat_data->spdif_clk_set_rate(plat_data->spdif_clk, + 64 * sample_rate * (div + 1)); + +#if MXC_SPDIF_DEBUG + pr_debug("%s wanted spdif clock rate = %d\n", __func__, + (int)(64 * sample_rate * div)); + pr_debug("%s got spdif clock rate = %d\n", __func__, + (int)clk_get_rate(plat_data->spdif_clk)); +#endif + + /* set fs field in consumer channel status */ + cstatus = __raw_readl(SPDIF_REG_STCSCL + spdif_base_addr) & 0xfffff0; + cstatus |= cstatus_fs; + __raw_writel(cstatus, SPDIF_REG_STCSCL + spdif_base_addr); + + /* select clock source and divisor */ + stc = __raw_readl(SPDIF_REG_STC + spdif_base_addr) & ~0x7FF; + stc |= STC_TXCLK_SRC_EN | (clk << STC_TXCLK_SRC_OFFSET) | (div - 1); + __raw_writel(stc, SPDIF_REG_STC + spdif_base_addr); + pr_debug("set sample rate to %d\n", sample_rate); + pr_debug("STCSCL: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STCSCL)); - return ret; + return 0; } static int spdif_set_channel_status(int value, unsigned long reg) @@ -533,9 +557,7 @@ static int mxc_spdif_playback_prepare(struct snd_pcm_substream *substream, ch_status = mxc_spdif_control.ch_status[3]; spdif_set_channel_status(ch_status, SPDIF_REG_STCSCL); spdif_intr_enable(INT_TXFIFO_RESYNC, 1); - err = spdif_set_sample_rate(plat_data->spdif_clk_44100, - plat_data->spdif_clk_48000, - runtime->rate); + err = spdif_set_sample_rate(codec, runtime->rate); if (err < 0) { pr_info("%s - err < 0\n", __func__); return err; @@ -659,7 +681,7 @@ static int mxc_spdif_capture_prepare(struct snd_pcm_substream *substream, INT_LOSS_LOCK, 1); /* setup rx clock source */ - spdif_set_rx_clksrc(plat_data->spdif_clkid, SPDIF_DEFAULT_GAINSEL, 1); + spdif_set_rx_clksrc(plat_data->spdif_rx_clk, SPDIF_DEFAULT_GAINSEL, 1); regval = __raw_readl(SPDIF_REG_SCR + spdif_base_addr); regval |= SCR_DMA_RX_EN; diff --git a/sound/soc/codecs/mxc_spdif.h b/sound/soc/codecs/mxc_spdif.h index 7eb98352d4de..5c167e5bc76e 100644 --- a/sound/soc/codecs/mxc_spdif.h +++ b/sound/soc/codecs/mxc_spdif.h @@ -108,6 +108,8 @@ enum spdif_gainsel { /* SPDIF Clock register */ #define STC_SYSCLK_DIV_OFFSET 11 #define STC_TXCLK_SRC_OFFSET 8 +#define STC_TXCLK_SRC_EN_OFFSET 7 +#define STC_TXCLK_SRC_EN (1 << 7) #define STC_TXCLK_DIV_OFFSET 0 #define SPDIF_CSTATUS_BYTE 6 diff --git a/sound/soc/imx/imx-spdif.c b/sound/soc/imx/imx-spdif.c index f336545f9210..885d8d58aee8 100644 --- a/sound/soc/imx/imx-spdif.c +++ b/sound/soc/imx/imx-spdif.c @@ -113,7 +113,7 @@ static int __init imx_spdif_init(void) return -ENOMEM; } - imx_spdif_snd_device = platform_device_alloc("soc-audio", 1); + imx_spdif_snd_device = platform_device_alloc("soc-audio", -1); if (!imx_spdif_snd_device) { pr_err("%s - failed platform_device_alloc\n", __func__); return -ENOMEM; |