/* * MXC SPDIF ALSA Soc Codec Driver * * Copyright (C) 2007-2011 Freescale Semiconductor, Inc. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mxc_spdif.h" #define MXC_SPDIF_DEBUG 0 static unsigned int gainsel_multi[GAINSEL_MULTI_MAX] = { 24 * 1024, 16 * 1024, 12 * 1024, 8 * 1024, 6 * 1024, 4 * 1024, 3 * 1024, }; /* * SPDIF control structure * Defines channel status, subcode and Q sub */ struct spdif_mixer_control { /* spinlock to access control data */ spinlock_t ctl_lock; /* IEC958 channel tx status bit */ unsigned char ch_status[4]; /* User bits */ unsigned char subcode[2 * SPDIF_UBITS_SIZE]; /* Q subcode part of user bits */ unsigned char qsub[2 * SPDIF_QSUB_SIZE]; /* buffer ptrs for writer */ unsigned int upos; unsigned int qpos; /* ready buffer index of the two buffers */ unsigned int ready_buf; }; struct mxc_spdif_priv { struct snd_soc_codec codec; struct mxc_spdif_platform_data *plat_data; unsigned long __iomem *reg_base; unsigned long reg_phys_base; struct snd_card *card; /* ALSA SPDIF sound card handle */ struct snd_pcm *pcm; /* ALSA spdif driver type handle */ atomic_t dpll_locked; /* DPLL locked status */ }; struct spdif_mixer_control mxc_spdif_control; static unsigned long spdif_base_addr; #if MXC_SPDIF_DEBUG static void dumpregs(void) { unsigned int value, i; for (i = 0 ; i <= 0x38 ; i += 4) { value = readl(spdif_base_addr + i) & 0xffffff; pr_debug("reg 0x%02x = 0x%06x\n", i, value); } i = 0x44; value = readl(spdif_base_addr + i) & 0xffffff; pr_debug("reg 0x%02x = 0x%06x\n", i, value); i = 0x50; value = readl(spdif_base_addr + i) & 0xffffff; pr_debug("reg 0x%02x = 0x%06x\n", i, value); } #else static void dumpregs(void) {} #endif /* define each spdif interrupt handlers */ typedef void (*spdif_irq_func_t) (unsigned int bit, void *desc); /* spdif irq functions declare */ static void spdif_irq_fifo(unsigned int bit, void *devid); static void spdif_irq_dpll_lock(unsigned int bit, void *devid); static void spdif_irq_uq(unsigned int bit, void *devid); static void spdif_irq_bit_error(unsigned int bit, void *devid); static void spdif_irq_sym_error(unsigned int bit, void *devid); static void spdif_irq_valnogood(unsigned int bit, void *devid); static void spdif_irq_cnew(unsigned int bit, void *devid); /* irq function list */ static spdif_irq_func_t spdif_irq_handlers[] = { spdif_irq_fifo, spdif_irq_fifo, spdif_irq_dpll_lock, NULL, spdif_irq_fifo, spdif_irq_uq, spdif_irq_uq, spdif_irq_uq, spdif_irq_uq, spdif_irq_uq, spdif_irq_uq, NULL, NULL, NULL, spdif_irq_bit_error, spdif_irq_sym_error, spdif_irq_valnogood, spdif_irq_cnew, NULL, spdif_irq_fifo, spdif_irq_dpll_lock, }; static void spdif_intr_enable(unsigned long intr, int enable) { unsigned long value; value = __raw_readl(spdif_base_addr + SPDIF_REG_SIE) & 0xffffff; if (enable) value |= intr; else value &= ~intr; __raw_writel(value, spdif_base_addr + SPDIF_REG_SIE); } /* * Get spdif interrupt status and clear the interrupt */ static int spdif_intr_status(void) { unsigned long value; value = __raw_readl(SPDIF_REG_SIS + spdif_base_addr) & 0xffffff; value &= __raw_readl(spdif_base_addr + SPDIF_REG_SIE); __raw_writel(value, SPDIF_REG_SIC + spdif_base_addr); return value; } static irqreturn_t spdif_isr(int irq, void *dev_id) { unsigned long int_stat; int line; int_stat = spdif_intr_status(); while ((line = ffs(int_stat)) != 0) { int_stat &= ~(1UL << (line - 1)); if (spdif_irq_handlers[line - 1] != NULL) spdif_irq_handlers[line - 1] (line - 1, dev_id); } return IRQ_HANDLED; } /* * Rx FIFO Full, Underrun/Overrun interrupts * Tx FIFO Empty, Underrun/Overrun interrupts */ static void spdif_irq_fifo(unsigned int bit, void *devid) { pr_debug("irq %s\n", __func__); } /* * DPLL locked and lock loss interrupt handler */ static void spdif_irq_dpll_lock(unsigned int bit, void *devid) { struct mxc_spdif_priv *spdif_priv = (struct mxc_spdif_priv *)devid; unsigned int locked = __raw_readl(spdif_base_addr + SPDIF_REG_SRPC); if (locked & SRPC_DPLL_LOCKED) { pr_debug("SPDIF Rx dpll locked\n"); atomic_set(&spdif_priv->dpll_locked, 1); } else { /* INT_LOSS_LOCK */ pr_debug("SPDIF Rx dpll loss lock\n"); atomic_set(&spdif_priv->dpll_locked, 0); } } /* * U/Q channel related interrupts handler * * U/QChannel full, overrun interrupts * U/QChannel sync error and frame error interrupts */ static void spdif_irq_uq(unsigned int bit, void *devid) { unsigned long val; int index; struct spdif_mixer_control *ctrl = &mxc_spdif_control; bit = 1 << bit; /* get U/Q channel datas */ switch (bit) { case INT_URX_OV: /* read U data */ pr_debug("User bit receive overrun\n"); case INT_URX_FUL: pr_debug("U bit receive full\n"); if (ctrl->upos >= SPDIF_UBITS_SIZE * 2) { ctrl->upos = 0; } else if (unlikely((ctrl->upos % SPDIF_UBITS_SIZE) + 3 > SPDIF_UBITS_SIZE)) { pr_err("User bit receivce buffer overflow\n"); break; } val = __raw_readl(spdif_base_addr + SPDIF_REG_SQU); ctrl->subcode[ctrl->upos++] = val >> 16; ctrl->subcode[ctrl->upos++] = val >> 8; ctrl->subcode[ctrl->upos++] = val; break; case INT_QRX_OV: /* read Q data */ pr_debug("Q bit receive overrun\n"); case INT_QRX_FUL: pr_debug("Q bit receive full\n"); if (ctrl->qpos >= SPDIF_QSUB_SIZE * 2) { ctrl->qpos = 0; } else if (unlikely((ctrl->qpos % SPDIF_QSUB_SIZE) + 3 > SPDIF_QSUB_SIZE)) { pr_debug("Q bit receivce buffer overflow\n"); break; } val = __raw_readl(spdif_base_addr + SPDIF_REG_SRQ); ctrl->qsub[ctrl->qpos++] = val >> 16; ctrl->qsub[ctrl->qpos++] = val >> 8; ctrl->qsub[ctrl->qpos++] = val; break; case INT_UQ_ERR: /* read U/Q data and do buffer reset */ pr_debug("U/Q bit receive error\n"); val = __raw_readl(spdif_base_addr + SPDIF_REG_SQU); val = __raw_readl(spdif_base_addr + SPDIF_REG_SRQ); /* drop this U/Q buffer */ ctrl->ready_buf = 0; ctrl->upos = 0; ctrl->qpos = 0; break; case INT_UQ_SYNC: /* U/Q buffer reset */ pr_debug("U/Q sync receive\n"); if (ctrl->qpos == 0) break; index = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE; /* set ready to this buffer */ ctrl->ready_buf = index + 1; break; } } /* * SPDIF receiver found parity bit error interrupt handler */ static void spdif_irq_bit_error(unsigned int bit, void *devid) { pr_debug("SPDIF interrupt parity bit error\n"); } /* * SPDIF receiver found illegal symbol interrupt handler */ static void spdif_irq_sym_error(unsigned int bit, void *devid) { pr_debug("SPDIF interrupt symbol error\n"); } /* * SPDIF validity flag no good interrupt handler */ static void spdif_irq_valnogood(unsigned int bit, void *devid) { pr_debug("SPDIF interrupt validate is not good\n"); } /* * SPDIF receive change in value of control channel */ static void spdif_irq_cnew(unsigned int bit, void *devid) { pr_debug("SPDIF interrupt cstatus new\n"); } static void spdif_softreset(void) { unsigned long value = 1; int cycle = 0; __raw_writel(SCR_SOFT_RESET, spdif_base_addr + SPDIF_REG_SCR); while (value && (cycle++ < 10)) value = __raw_readl(spdif_base_addr + SPDIF_REG_SCR) & 0x1000; } static int spdif_set_clk_accuracy(enum spdif_clk_accuracy level) { unsigned long value; value = __raw_readl(SPDIF_REG_STCSCL + spdif_base_addr) & 0xffffcf; value |= (level << 4); __raw_writel(value, SPDIF_REG_STCSCL + spdif_base_addr); pr_debug("STCSCL: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STCSCL)); return 0; } /* * set SPDIF PhaseConfig register for rx clock */ static int spdif_set_rx_clksrc(enum spdif_clk_src clksrc, enum spdif_gainsel gainsel, int dpll_locked) { unsigned long value; if (clksrc > SPDIF_CLK_SRC5 || gainsel > GAINSEL_MULTI_3) return 1; if (dpll_locked) value = clksrc; else value = clksrc + SRPC_CLKSRC_SEL_LOCKED; value = (value << SRPC_CLKSRC_SEL_OFFSET) | (gainsel << SRPC_GAINSEL_OFFSET); __raw_writel(value, spdif_base_addr + SPDIF_REG_SRPC); return 0; } /* * Get RX data clock rate given the SPDIF bus_clk */ static int spdif_get_rxclk_rate(struct clk *bus_clk, enum spdif_gainsel gainsel) { unsigned long freqmeas, phaseconf, busclk_freq = 0; u64 tmpval64; enum spdif_clk_src clksrc; freqmeas = __raw_readl(spdif_base_addr + SPDIF_REG_SRFM); phaseconf = __raw_readl(spdif_base_addr + SPDIF_REG_SRPC); clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0x0F; if (clksrc < 5 && (phaseconf & SRPC_DPLL_LOCKED)) { /* get bus clock from system */ busclk_freq = clk_get_rate(bus_clk); } /* FreqMeas_CLK = (BUS_CLK*FreqMeas[23:0])/2^10/GAINSEL/128 */ tmpval64 = (u64) busclk_freq * freqmeas; do_div(tmpval64, gainsel_multi[gainsel]); do_div(tmpval64, 128 * 1024); pr_debug("FreqMeas: %d\n", (int)freqmeas); pr_debug("busclk_freq: %d\n", (int)busclk_freq); pr_debug("rx rate: %d\n", (int)tmpval64); return (int)tmpval64; } static int spdif_set_sample_rate(int src_44100, int src_48000, 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; 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"); } 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"); } 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"); } break; } pr_debug("STCSCL: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STCSCL)); return ret; } static int spdif_set_channel_status(int value, unsigned long reg) { __raw_writel(value & 0xffffff, reg + spdif_base_addr); return 0; } static unsigned int spdif_playback_rates[] = { 32000, 44100, 48000 }; static unsigned int spdif_capture_rates[] = { 16000, 32000, 44100, 48000, 64000, 96000 }; static struct snd_pcm_hw_constraint_list hw_playback_rates_stereo = { .count = ARRAY_SIZE(spdif_playback_rates), .list = spdif_playback_rates, .mask = 0, }; static struct snd_pcm_hw_constraint_list hw_capture_rates_stereo = { .count = ARRAY_SIZE(spdif_capture_rates), .list = spdif_capture_rates, .mask = 0, }; static int mxc_spdif_playback_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct snd_pcm_runtime *runtime = substream->runtime; struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; int err; if (!plat_data->spdif_tx) return -EINVAL; clk_enable(plat_data->spdif_clk); clk_enable(plat_data->spdif_audio_clk); err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_playback_rates_stereo); if (err < 0) goto failed; err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) goto failed; return 0; failed: clk_disable(plat_data->spdif_clk); return err; } static int mxc_spdif_playback_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; struct snd_pcm_runtime *runtime = substream->runtime; unsigned int ch_status; unsigned long regval; int err; if (!plat_data->spdif_tx) return -EINVAL; regval = __raw_readl(spdif_base_addr + SPDIF_REG_SCR); regval &= 0xfc33e3; regval &= ~SCR_LOW_POWER; regval |= SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_NORMAL | SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP | (2 << SCR_TXFIFO_ESEL_BIT); __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr); /* Default clock source from EXTAL, divider by 8, generate 44.1kHz sample rate */ __raw_writel(0x07, SPDIF_REG_STC + spdif_base_addr); ch_status = ((mxc_spdif_control.ch_status[2] << 16) | (mxc_spdif_control.ch_status[1] << 8) | mxc_spdif_control.ch_status[0]); spdif_set_channel_status(ch_status, SPDIF_REG_STCSCH); 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); if (err < 0) { pr_info("%s - err < 0\n", __func__); return err; } spdif_set_clk_accuracy(SPDIF_CLK_ACCURACY_LEV2); pr_debug("SCR: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SCR)); pr_debug("SIE: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIE)); pr_debug("STC: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STC)); pr_debug("STCSCH: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STCSCH)); pr_debug("STCSCL: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STCSCL)); regval = __raw_readl(SPDIF_REG_SCR + spdif_base_addr); regval |= SCR_DMA_TX_EN; __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr); dumpregs(); return 0; } static int mxc_spdif_playback_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; 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 regval; if (!plat_data->spdif_tx) return -EINVAL; dumpregs(); pr_debug("SIS: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIS)); spdif_intr_status(); spdif_intr_enable(INT_TXFIFO_RESYNC, 0); regval = __raw_readl(SPDIF_REG_SCR + spdif_base_addr) & 0xffffe3; regval |= SCR_TXSEL_OFF; __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr); regval = __raw_readl(SPDIF_REG_STC + spdif_base_addr) & ~0x7FF; regval |= (0x7 << STC_TXCLK_SRC_OFFSET); __raw_writel(regval, SPDIF_REG_STC + spdif_base_addr); regval = __raw_readl(SPDIF_REG_SCR + spdif_base_addr); regval &= ~SCR_DMA_TX_EN; regval |= SCR_LOW_POWER; __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr); clk_disable(plat_data->spdif_audio_clk); clk_disable(plat_data->spdif_clk); return 0; } static int mxc_spdif_capture_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); struct snd_pcm_runtime *runtime = substream->runtime; struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; int err = 0; if (!plat_data->spdif_rx) return -EINVAL; /* enable rx bus clock */ clk_enable(plat_data->spdif_clk); /* set hw param constraints */ err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_capture_rates_stereo); if (err < 0) goto failed; err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) goto failed; /* enable spdif dpll lock interrupt */ spdif_intr_enable(INT_DPLL_LOCKED, 1); return 0; failed: clk_disable(plat_data->spdif_clk); return err; } static int mxc_spdif_capture_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; 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 regval; if (!plat_data->spdif_rx) return -EINVAL; regval = __raw_readl(spdif_base_addr + SPDIF_REG_SCR); /* * initial and reset SPDIF configuration: * RxFIFO off * RxFIFO sel to 8 sample * Autosync * Valid bit set */ regval &= ~(SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO | SCR_LOW_POWER); regval |= (2 << SCR_RXFIFO_FSEL_BIT) | SCR_RXFIFO_AUTOSYNC; __raw_writel(regval, spdif_base_addr + SPDIF_REG_SCR); /* enable interrupts, include DPLL lock */ spdif_intr_enable(INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV | INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR | INT_RX_RESYNC | INT_LOSS_LOCK, 1); /* setup rx clock source */ spdif_set_rx_clksrc(plat_data->spdif_clkid, SPDIF_DEFAULT_GAINSEL, 1); regval = __raw_readl(SPDIF_REG_SCR + spdif_base_addr); regval |= SCR_DMA_RX_EN; __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr); /* Debug: dump registers */ pr_debug("SCR: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SCR)); pr_debug("SIE: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIE)); pr_debug("SRPC: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SRPC)); pr_debug("FreqMeas: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SRFM)); return 0; } static int mxc_spdif_capture_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; 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 regval; if (!plat_data->spdif_rx) return -EINVAL; pr_debug("SIS: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SIS)); pr_debug("SRPC: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SRPC)); pr_debug("FreqMeas: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_SRFM)); spdif_intr_enable(INT_DPLL_LOCKED | INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV | INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR | INT_RX_RESYNC | INT_LOSS_LOCK, 0); regval = __raw_readl(SPDIF_REG_SCR + spdif_base_addr); regval &= SCR_DMA_RX_EN; __raw_writel(regval, SPDIF_REG_SCR + spdif_base_addr); /* turn off RX fifo, disable dma and autosync */ regval = __raw_readl(spdif_base_addr + SPDIF_REG_SCR); regval |= SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO | SCR_LOW_POWER; regval &= ~(SCR_DMA_RX_EN | SCR_RXFIFO_AUTOSYNC); __raw_writel(regval, spdif_base_addr + SPDIF_REG_SCR); clk_disable(plat_data->spdif_clk); return 0; } /* * MXC SPDIF IEC958 controller(mixer) functions * * Channel status get/put control * User bit value get/put control * Valid bit value get control * DPLL lock status get control * User bit sync mode selection control */ static int mxc_pb_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0; } static int mxc_pb_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue) { uvalue->value.iec958.status[0] = mxc_spdif_control.ch_status[0]; uvalue->value.iec958.status[1] = mxc_spdif_control.ch_status[1]; uvalue->value.iec958.status[2] = mxc_spdif_control.ch_status[2]; uvalue->value.iec958.status[3] = mxc_spdif_control.ch_status[3]; return 0; } static int mxc_pb_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue) { unsigned int ch_status; mxc_spdif_control.ch_status[0] = uvalue->value.iec958.status[0]; mxc_spdif_control.ch_status[1] = uvalue->value.iec958.status[1]; mxc_spdif_control.ch_status[2] = uvalue->value.iec958.status[2]; mxc_spdif_control.ch_status[3] = uvalue->value.iec958.status[3]; ch_status = ((mxc_spdif_control.ch_status[2] << 16) | (mxc_spdif_control.ch_status[1] << 8) | mxc_spdif_control.ch_status[0]); spdif_set_channel_status(ch_status, SPDIF_REG_STCSCH); ch_status = mxc_spdif_control.ch_status[3]; spdif_set_channel_status(ch_status, SPDIF_REG_STCSCL); pr_debug("STCSCH: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STCSCH)); pr_debug("STCSCL: 0x%08x\n", __raw_readl(spdif_base_addr + SPDIF_REG_STCSCL)); return 0; } static int mxc_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; uinfo->count = 1; return 0; } /* * Get channel status from SPDIF_RX_CCHAN register */ static int mxc_spdif_capture_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { unsigned int cstatus; if (!(__raw_readl(spdif_base_addr + SPDIF_REG_SIS) & INT_CNEW)) return -EAGAIN; cstatus = __raw_readl(spdif_base_addr + SPDIF_REG_SRCSLH); ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF; ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF; ucontrol->value.iec958.status[2] = cstatus & 0xFF; cstatus = __raw_readl(spdif_base_addr + SPDIF_REG_SRCSLL); ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF; ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF; ucontrol->value.iec958.status[5] = cstatus & 0xFF; /* clear intr */ __raw_writel(INT_CNEW, spdif_base_addr + SPDIF_REG_SIC); return 0; } /* * Get User bits (subcode) from chip value which readed out * in UChannel register. */ static int mxc_spdif_subcode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { unsigned long flags; int ret = 0; spin_lock_irqsave(&mxc_spdif_control.ctl_lock, flags); if (mxc_spdif_control.ready_buf) { memcpy(&ucontrol->value.iec958.subcode[0], &mxc_spdif_control.subcode[(mxc_spdif_control.ready_buf - 1) * SPDIF_UBITS_SIZE], SPDIF_UBITS_SIZE); } else { ret = -EAGAIN; } spin_unlock_irqrestore(&mxc_spdif_control.ctl_lock, flags); return ret; } /* * Q-subcode infomation. * the byte size is SPDIF_UBITS_SIZE/8 */ static int mxc_spdif_qinfo(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; uinfo->count = SPDIF_QSUB_SIZE; return 0; } /* * Get Q subcode from chip value which readed out * in QChannel register. */ static int mxc_spdif_qget(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { unsigned long flags; int ret = 0; spin_lock_irqsave(&mxc_spdif_control.ctl_lock, flags); if (mxc_spdif_control.ready_buf) { memcpy(&ucontrol->value.bytes.data[0], &mxc_spdif_control.qsub[(mxc_spdif_control.ready_buf - 1) * SPDIF_QSUB_SIZE], SPDIF_QSUB_SIZE); } else { ret = -EAGAIN; } spin_unlock_irqrestore(&mxc_spdif_control.ctl_lock, flags); return ret; } /* * Valid bit infomation. */ static int mxc_spdif_vbit_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0; } /* * Get valid good bit from interrupt status register. */ static int mxc_spdif_vbit_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { unsigned int int_val; int_val = __raw_readl(spdif_base_addr + SPDIF_REG_SIS); ucontrol->value.integer.value[0] = (int_val & INT_VAL_NOGOOD) != 0; __raw_writel(INT_VAL_NOGOOD, spdif_base_addr + SPDIF_REG_SIC); return 0; } /* * DPLL lock infomation. */ static int mxc_spdif_rxrate_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 16000; uinfo->value.integer.max = 96000; return 0; } /* * Get DPLL lock or not info from stable interrupt status register. * User application must use this control to get locked, * then can do next PCM operation */ static int mxc_spdif_rxrate_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; if (atomic_read(&spdif_priv->dpll_locked)) { ucontrol->value.integer.value[0] = spdif_get_rxclk_rate(plat_data->spdif_clk, SPDIF_DEFAULT_GAINSEL); } else { ucontrol->value.integer.value[0] = 0; } return 0; } /* * User bit sync mode info */ static int mxc_spdif_usync_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0; } /* * User bit sync mode: * 1 CD User channel subcode * 0 Non-CD data */ static int mxc_spdif_usync_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { unsigned int int_val; int_val = __raw_readl(spdif_base_addr + SPDIF_REG_SRCD); ucontrol->value.integer.value[0] = (int_val & SRCD_CD_USER) != 0; return 0; } /* * User bit sync mode: * 1 CD User channel subcode * 0 Non-CD data */ static int mxc_spdif_usync_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { unsigned int int_val; int_val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET; __raw_writel(int_val, spdif_base_addr + SPDIF_REG_SRCD); return 0; } /* * MXC SPDIF IEC958 controller defines */ static struct snd_kcontrol_new mxc_spdif_ctrls[] = { /* status cchanel controller */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = mxc_pb_spdif_info, .get = mxc_pb_spdif_get, .put = mxc_pb_spdif_put, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = mxc_spdif_info, .get = mxc_spdif_capture_get, }, /* user bits controller */ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "IEC958 Subcode Capture Default", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = mxc_spdif_info, .get = mxc_spdif_subcode_get, }, { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "IEC958 Q-subcode Capture Default", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = mxc_spdif_qinfo, .get = mxc_spdif_qget, }, /* valid bit error controller */ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "IEC958 V-Bit Errors", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = mxc_spdif_vbit_info, .get = mxc_spdif_vbit_get, }, /* DPLL lock info get controller */ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "RX Sample Rate", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = mxc_spdif_rxrate_info, .get = mxc_spdif_rxrate_get, }, /* User bit sync mode set/get controller */ { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "IEC958 USyncMode CDText", .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | SNDRV_CTL_ELEM_ACCESS_VOLATILE, .info = mxc_spdif_usync_info, .get = mxc_spdif_usync_get, .put = mxc_spdif_usync_put, }, }; static int mxc_spdif_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mxc_spdif_playback_startup(substream, dai); else ret = mxc_spdif_capture_startup(substream, dai); return ret; } static void mxc_spdif_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mxc_spdif_playback_shutdown(substream, dai); else ret = mxc_spdif_capture_shutdown(substream, dai); } static int mxc_spdif_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { int ret; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ret = mxc_spdif_playback_prepare(substream, dai); else ret = mxc_spdif_capture_prepare(substream, dai); return ret; } struct snd_soc_dai_ops mxc_spdif_codec_dai_ops = { .startup = mxc_spdif_startup, .prepare = mxc_spdif_prepare, .shutdown = mxc_spdif_shutdown, }; struct snd_soc_dai_driver mxc_spdif_codec_dai = { .name = "mxc-spdif", .ops = &mxc_spdif_codec_dai_ops, }; static int mxc_spdif_soc_probe(struct snd_soc_codec *codec) { snd_soc_add_controls(codec, mxc_spdif_ctrls, ARRAY_SIZE(mxc_spdif_ctrls)); return 0; } static int mxc_spdif_soc_remove(struct snd_soc_codec *codec) { /* all done in mxc_spdif_remove */ return 0; } #ifdef CONFIG_PM static int mxc_spdif_soc_suspend(struct snd_soc_codec *codec, pm_message_t state) { struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); struct mxc_spdif_platform_data *plat_data; if (codec == NULL) return -EINVAL; plat_data = spdif_priv->plat_data; clk_disable(plat_data->spdif_clk); clk_disable(plat_data->spdif_core_clk); return 0; } static int mxc_spdif_soc_resume(struct snd_soc_codec *codec) { struct mxc_spdif_priv *spdif_priv = snd_soc_codec_get_drvdata(codec); struct mxc_spdif_platform_data *plat_data; if (codec == NULL) return -EINVAL; plat_data = spdif_priv->plat_data; clk_enable(plat_data->spdif_core_clk); clk_enable(plat_data->spdif_clk); spdif_softreset(); return 0; } #else #define mxc_spdif_soc_suspend NULL #define mxc_spdif_soc_resume NULL #endif /* CONFIG_PM */ struct snd_soc_codec_driver soc_codec_dev_spdif = { .probe = mxc_spdif_soc_probe, .remove = mxc_spdif_soc_remove, .suspend = mxc_spdif_soc_suspend, .resume = mxc_spdif_soc_resume, }; static int __devinit mxc_spdif_probe(struct platform_device *pdev) { struct mxc_spdif_platform_data *plat_data = (struct mxc_spdif_platform_data *)pdev->dev.platform_data; struct resource *res; struct mxc_spdif_priv *spdif_priv; int err, irq; int ret = 0; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENOENT; dev_info(&pdev->dev, "MXC SPDIF Audio\n"); spdif_priv = kzalloc(sizeof(struct mxc_spdif_priv), GFP_KERNEL); if (spdif_priv == NULL) return -ENOMEM; if (plat_data->spdif_tx) { mxc_spdif_codec_dai.playback.stream_name = "Playback"; mxc_spdif_codec_dai.playback.channels_min = 2; mxc_spdif_codec_dai.playback.channels_max = 2; if (plat_data->spdif_clk_44100 >= 0) mxc_spdif_codec_dai.playback.rates |= SNDRV_PCM_RATE_44100; if (plat_data->spdif_clk_48000 >= 0) mxc_spdif_codec_dai.playback.rates |= SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000; mxc_spdif_codec_dai.playback.formats = MXC_SPDIF_FORMATS_PLAYBACK; } if (plat_data->spdif_rx) { mxc_spdif_codec_dai.capture.stream_name = "Capture"; mxc_spdif_codec_dai.capture.channels_min = 2; mxc_spdif_codec_dai.capture.channels_max = 2; if (plat_data->spdif_clk_44100 >= 0) mxc_spdif_codec_dai.capture.rates |= SNDRV_PCM_RATE_44100; if (plat_data->spdif_clk_48000 >= 0) mxc_spdif_codec_dai.capture.rates |= SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000; mxc_spdif_codec_dai.capture.formats = MXC_SPDIF_FORMATS_CAPTURE; } platform_set_drvdata(pdev, spdif_priv); spdif_priv->reg_phys_base = res->start; spdif_priv->reg_base = ioremap(res->start, res->end - res->start + 1); spdif_priv->plat_data = plat_data; spdif_base_addr = (unsigned long)spdif_priv->reg_base; /* initial spinlock for control data */ spin_lock_init(&mxc_spdif_control.ctl_lock); if (plat_data->spdif_tx) { /* init tx channel status default value */ mxc_spdif_control.ch_status[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015; mxc_spdif_control.ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID; mxc_spdif_control.ch_status[2] = 0x00; mxc_spdif_control.ch_status[3] = IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM; } plat_data->spdif_clk = clk_get(&pdev->dev, NULL); atomic_set(&spdif_priv->dpll_locked, 0); /* spdif_xtal_clk */ clk_enable(plat_data->spdif_core_clk); spdif_softreset(); /* disable all the interrupts */ spdif_intr_enable(0xffffff, 0); /* spdif interrupt register and disable */ irq = platform_get_irq(pdev, 0); if ((irq <= 0) || request_irq(irq, spdif_isr, 0, "spdif", spdif_priv)) { pr_err("MXC spdif: failed to request irq\n"); err = -EBUSY; goto card_err; } ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_spdif, &mxc_spdif_codec_dai, 1); if (ret) { dev_err(&pdev->dev, "failed to register codec\n"); goto card_err; } dumpregs(); return 0; card_err: clk_disable(plat_data->spdif_clk); clk_put(plat_data->spdif_clk); clk_disable(plat_data->spdif_core_clk); platform_set_drvdata(pdev, NULL); kfree(spdif_priv); return ret; } static int __devexit mxc_spdif_remove(struct platform_device *pdev) { struct mxc_spdif_priv *spdif_priv = platform_get_drvdata(pdev); struct mxc_spdif_platform_data *plat_data = spdif_priv->plat_data; snd_soc_unregister_codec(&pdev->dev); clk_disable(plat_data->spdif_clk); clk_put(plat_data->spdif_clk); clk_disable(plat_data->spdif_core_clk); platform_set_drvdata(pdev, NULL); kfree(spdif_priv); return 0; } struct platform_driver mxc_spdif_driver = { .driver = { .name = "mxc_spdif", .owner = THIS_MODULE, }, .probe = mxc_spdif_probe, .remove = __devexit_p(mxc_spdif_remove), }; static int __init mxc_spdif_init(void) { return platform_driver_register(&mxc_spdif_driver); } static void __exit mxc_spdif_exit(void) { return platform_driver_unregister(&mxc_spdif_driver); } module_init(mxc_spdif_init); module_exit(mxc_spdif_exit); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("MXC SPDIF transmitter"); MODULE_LICENSE("GPL");