From f3a30baa2876f910cbfb15b9a7442bacd8437e9d Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 6 May 2014 16:42:25 +0800 Subject: ASoC: fsl_spdif: Improve coding style 1) Apply better indentations 2) Drop braces for single statement. 3) Use simpler ternary to reduce code. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 72 +++++++++++++++++++++++------------------------ sound/soc/fsl/fsl_spdif.h | 2 ++ 2 files changed, 37 insertions(+), 37 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index b912d45a2a4c..fe4d9e3da793 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -32,10 +32,13 @@ #define FSL_SPDIF_TXFIFO_WML 0x8 #define FSL_SPDIF_RXFIFO_WML 0x8 -#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC) -#define INTR_FOR_CAPTURE (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_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED) +#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC) +#define INTR_FOR_CAPTURE (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_RXFIFO_RESYNC |\ + INT_LOSS_LOCK | INT_DPLL_LOCKED) + +#define SIE_INTR_FOR(tx) (tx ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE) /* Index list for the values that has if (DPLL Locked) condition */ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb }; @@ -137,10 +140,9 @@ static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv) dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n"); - if (!spdif_priv->dpll_locked) { - /* DPLL unlocked seems no audio stream */ + /* Clear illegal symbol if DPLL unlocked since no audio stream */ + if (!spdif_priv->dpll_locked) regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0); - } } /* U/Q Channel receive register full */ @@ -335,8 +337,8 @@ static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv) u32 ch_status; ch_status = (bitrev8(ctrl->ch_status[0]) << 16) | - (bitrev8(ctrl->ch_status[1]) << 8) | - bitrev8(ctrl->ch_status[2]); + (bitrev8(ctrl->ch_status[1]) << 8) | + bitrev8(ctrl->ch_status[2]); regmap_write(regmap, REG_SPDIF_STCSCH, ch_status); dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status); @@ -433,13 +435,12 @@ clk_set_bypass: spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs); /* select clock source and divisor */ - stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df); - mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK; + stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | + STC_TXCLK_DF(txclk_df) | STC_SYSCLK_DF(sysclk_df); + mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | + STC_TXCLK_DF_MASK | STC_SYSCLK_DF_MASK; regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc); - regmap_update_bits(regmap, REG_SPDIF_STC, - STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df)); - dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n", spdif_priv->txrate[rate], sample_rate); @@ -553,7 +554,7 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream, return ret; } spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK, - IEC958_AES3_CON_CLOCK_1000PPM); + IEC958_AES3_CON_CLOCK_1000PPM); spdif_write_channel_status(spdif_priv); } else { /* Setup rx clock source */ @@ -569,9 +570,9 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct regmap *regmap = spdif_priv->regmap; - int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE; - u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + u32 intr = SIE_INTR_FOR(tx); + u32 dmaen = SCR_DMA_xX_EN(tx); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -662,9 +663,8 @@ static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol, u32 cstatus, val; regmap_read(regmap, REG_SPDIF_SIS, &val); - if (!(val & INT_CNEW)) { + if (!(val & INT_CNEW)) return -EAGAIN; - } regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus); ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF; @@ -693,15 +693,14 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol, struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; unsigned long flags; - int ret = 0; + int ret = -EAGAIN; spin_lock_irqsave(&ctrl->ctl_lock, flags); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE; memcpy(&ucontrol->value.iec958.subcode[0], &ctrl->subcode[idx], SPDIF_UBITS_SIZE); - } else { - ret = -EAGAIN; + ret = 0; } spin_unlock_irqrestore(&ctrl->ctl_lock, flags); @@ -726,15 +725,14 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol, struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control; unsigned long flags; - int ret = 0; + int ret = -EAGAIN; spin_lock_irqsave(&ctrl->ctl_lock, flags); if (ctrl->ready_buf) { int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE; memcpy(&ucontrol->value.bytes.data[0], &ctrl->qsub[idx], SPDIF_QSUB_SIZE); - } else { - ret = -EAGAIN; + ret = 0; } spin_unlock_irqrestore(&ctrl->ctl_lock, flags); @@ -799,10 +797,10 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv, regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf); clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf; - if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) { - /* Get bus clock from system */ + + /* Get bus clock from system */ + if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) busclk_freq = clk_get_rate(spdif_priv->sysclk); - } /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */ tmpval64 = (u64) busclk_freq * freqmeas; @@ -826,12 +824,12 @@ static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol, { struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai); - int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL); + int rate = 0; if (spdif_priv->dpll_locked) - ucontrol->value.integer.value[0] = rate; - else - ucontrol->value.integer.value[0] = 0; + rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL); + + ucontrol->value.integer.value[0] = rate; return 0; } @@ -1238,12 +1236,12 @@ static int fsl_spdif_probe(struct platform_device *pdev) spin_lock_init(&ctrl->ctl_lock); /* Init tx channel status default value */ - ctrl->ch_status[0] = - IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015; + ctrl->ch_status[0] = IEC958_AES0_CON_NOT_COPYRIGHT | + IEC958_AES0_CON_EMPHASIS_5015; ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID; ctrl->ch_status[2] = 0x00; - ctrl->ch_status[3] = - IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM; + ctrl->ch_status[3] = IEC958_AES3_CON_FS_44100 | + IEC958_AES3_CON_CLOCK_1000PPM; spdif_priv->dpll_locked = false; diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index 16fde4b927d3..b13e30ea1c9e 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -93,6 +93,8 @@ #define SCR_USRC_SEL_RECV (0x1 << SCR_USRC_SEL_OFFSET) #define SCR_USRC_SEL_CHIP (0x3 << SCR_USRC_SEL_OFFSET) +#define SCR_DMA_xX_EN(tx) (tx ? SCR_DMA_TX_EN : SCR_DMA_RX_EN) + /* SPDIF CDText control */ #define SRCD_CD_USER_OFFSET 1 #define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET) -- cgit v1.2.3 From c7dfeed10928f00f8c99ad3b58a4f4c5cfd7c581 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Mon, 16 Jun 2014 02:56:42 +0300 Subject: ASoC: fsl_spdif: Add support for output sample rates 96kHz and 192kHz Add support for the output sample rates 96kHz and 192kHz. Tested with a Cubox-i imx6 system and an Onkyo TX-SR607 receiver. Signed-off-by: Anssi Hannula Acked-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 14 +++++++++++--- sound/soc/fsl/fsl_spdif.h | 8 ++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index fe4d9e3da793..6e5e369d9693 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -99,7 +99,7 @@ struct fsl_spdif_priv { struct platform_device *pdev; struct regmap *regmap; bool dpll_locked; - u16 txrate[SPDIF_TXRATE_MAX]; + u32 txrate[SPDIF_TXRATE_MAX]; u8 txclk_df[SPDIF_TXRATE_MAX]; u8 sysclk_df[SPDIF_TXRATE_MAX]; u8 txclk_src[SPDIF_TXRATE_MAX]; @@ -392,6 +392,14 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, rate = SPDIF_TXRATE_48000; csfs = IEC958_AES3_CON_FS_48000; break; + case 96000: + rate = SPDIF_TXRATE_96000; + csfs = IEC958_AES3_CON_FS_96000; + break; + case 192000: + rate = SPDIF_TXRATE_192000; + csfs = IEC958_AES3_CON_FS_192000; + break; default: dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate); return -EINVAL; @@ -1044,7 +1052,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, enum spdif_txrate index, bool round) { - const u32 rate[] = { 32000, 44100, 48000 }; + const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; bool is_sysclk = clk == spdif_priv->sysclk; u64 rate_ideal, rate_actual, sub; u32 sysclk_dfmin, sysclk_dfmax; @@ -1103,7 +1111,7 @@ out: static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index) { - const u32 rate[] = { 32000, 44100, 48000 }; + const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; struct platform_device *pdev = spdif_priv->pdev; struct device *dev = &pdev->dev; u64 savesub = 100000, ret; diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h index b13e30ea1c9e..00bd3514c610 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -166,8 +166,10 @@ enum spdif_txrate { SPDIF_TXRATE_32000 = 0, SPDIF_TXRATE_44100, SPDIF_TXRATE_48000, + SPDIF_TXRATE_96000, + SPDIF_TXRATE_192000, }; -#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_48000 + 1) +#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_192000 + 1) #define SPDIF_CSTATUS_BYTE 6 @@ -177,7 +179,9 @@ enum spdif_txrate { #define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000) + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_192000) #define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_32000 | \ -- cgit v1.2.3 From ff40260f79dc0436604452bccd449bffd25ebafb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 Jun 2014 14:11:56 +0200 Subject: ASoC: fsl: refine DMA/FIQ dependencies Commit 31ee2bfd724ab ("ASoC: fsl: select SND_SOC_IMX_PCM_DMA where needed") started selecting SND_SOC_IMX_PCM_DMA and SND_SOC_IMX_PCM_FIQ for two drivers when building for i.MX. This has turned out too aggressive, as FIQ is only available for i.mx2 through i.mx5, but not i.mx6 or vybrid. Further, two more drivers have become user-selectable in the meantime, and they both depend on DMA for the imx platform as well. This changes the selection of FIQ to depend on the TZIC or AVIC interrupt controllers that actually export the imx specific FIQ interfaces, and adds the missing select statements for SAI and ESAI. Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 37933629cbed..52bbe9f80111 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -5,6 +5,7 @@ comment "Common SoC Audio options for Freescale CPUs:" config SND_SOC_FSL_SAI tristate "Synchronous Audio Interface (SAI) module support" select REGMAP_MMIO + select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y if you want to add Synchronous Audio Interface (SAI) @@ -15,7 +16,7 @@ config SND_SOC_FSL_SAI config SND_SOC_FSL_SSI tristate "Synchronous Serial Interface module support" select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n - select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC + select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC) select REGMAP_MMIO help Say Y if you want to add Synchronous Serial Interface (SSI) @@ -27,7 +28,7 @@ config SND_SOC_FSL_SPDIF tristate "Sony/Philips Digital Interface module support" select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n - select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC + select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC) help Say Y if you want to add Sony/Philips Digital Interface (SPDIF) support for the Freescale CPUs. @@ -37,6 +38,7 @@ config SND_SOC_FSL_SPDIF config SND_SOC_FSL_ESAI tristate "Enhanced Serial Audio Interface (ESAI) module support" select REGMAP_MMIO + select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_FSL_UTILS help Say Y if you want to add Enhanced Synchronous Audio Interface -- cgit v1.2.3 From 3117bb3109dc223e186302f5dc8ce9ed04adca90 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 29 Jul 2014 18:08:53 +0800 Subject: ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers The Asynchronous Sample Rate Converter (ASRC) converts the sampling rate of a signal associated with an input clock into a signal associated with a different output clock. The driver currently works as a Front End of DPCM with other Back Ends DAI links such as ESAI<->CS42888 and SSI<->WM8962 and SAI. It converts the original sample rate to a common rate supported by Back Ends for playback while converts the common rate of Back Ends to a desired rate for capture. It has 3 pairs to support three different substreams within totally 10 channels. Signed-off-by: Nicolin Chen Reviewed-by: Varka Bhadram Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 9 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/fsl_asrc.c | 992 +++++++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_asrc.h | 461 ++++++++++++++++++++ sound/soc/fsl/fsl_asrc_dma.c | 386 +++++++++++++++++ 5 files changed, 1850 insertions(+) create mode 100644 sound/soc/fsl/fsl_asrc.c create mode 100644 sound/soc/fsl/fsl_asrc.h create mode 100644 sound/soc/fsl/fsl_asrc_dma.c (limited to 'sound/soc') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 37933629cbed..56fd32b3a881 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -2,6 +2,15 @@ menu "SoC Audio for Freescale CPUs" comment "Common SoC Audio options for Freescale CPUs:" +config SND_SOC_FSL_ASRC + tristate "Asynchronous Sample Rate Converter (ASRC) module support" + select REGMAP_MMIO + help + Say Y if you want to add Asynchronous Sample Rate Converter (ASRC) + support for the Freescale CPUs. + This option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + config SND_SOC_FSL_SAI tristate "Synchronous Audio Interface (SAI) module support" select REGMAP_MMIO diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index db254e358c18..9ff59267eac9 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o # Freescale SSI/DMA/SAI/SPDIF Support +snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o snd-soc-fsl-sai-objs := fsl_sai.o snd-soc-fsl-ssi-y := fsl_ssi.o snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o @@ -18,6 +19,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o snd-soc-fsl-esai-objs := fsl_esai.o snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o +obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c new file mode 100644 index 000000000000..27a4a7084343 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc.c @@ -0,0 +1,992 @@ +/* + * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_asrc.h" + +#define IDEAL_RATIO_DECIMAL_DEPTH 26 + +#define pair_err(fmt, ...) \ + dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) + +#define pair_dbg(fmt, ...) \ + dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__) + +/* Sample rates are aligned with that defined in pcm.h file */ +static const u8 process_option[][8][2] = { + /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */ + {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */ + {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */ + {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */ + {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */ + {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */ + {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */ + {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */ + {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */ + {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */ + {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */ + {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */ + {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */ +}; + +/* Corresponding to process_option */ +static int supported_input_rate[] = { + 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, + 96000, 176400, 192000, +}; + +static int supported_asrc_rate[] = { + 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, +}; + +/** + * The following tables map the relationship between asrc_inclk/asrc_outclk in + * fsl_asrc.h and the registers of ASRCSR + */ +static unsigned char input_clk_map_imx35[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, +}; + +static unsigned char output_clk_map_imx35[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, +}; + +/* i.MX53 uses the same map for input and output */ +static unsigned char input_clk_map_imx53[] = { +/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ + 0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd, +}; + +static unsigned char output_clk_map_imx53[] = { +/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */ + 0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd, +}; + +static unsigned char *clk_map[2]; + +/** + * Request ASRC pair + * + * It assigns pair by the order of A->C->B because allocation of pair B, + * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A + * while pair A and pair C are comparatively independent. + */ +static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair) +{ + enum asrc_pair_index index = ASRC_INVALID_PAIR; + struct fsl_asrc *asrc_priv = pair->asrc_priv; + struct device *dev = &asrc_priv->pdev->dev; + unsigned long lock_flags; + int i, ret = 0; + + spin_lock_irqsave(&asrc_priv->lock, lock_flags); + + for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) { + if (asrc_priv->pair[i] != NULL) + continue; + + index = i; + + if (i != ASRC_PAIR_B) + break; + } + + if (index == ASRC_INVALID_PAIR) { + dev_err(dev, "all pairs are busy now\n"); + ret = -EBUSY; + } else if (asrc_priv->channel_avail < channels) { + dev_err(dev, "can't afford required channels: %d\n", channels); + ret = -EINVAL; + } else { + asrc_priv->channel_avail -= channels; + asrc_priv->pair[index] = pair; + pair->channels = channels; + pair->index = index; + } + + spin_unlock_irqrestore(&asrc_priv->lock, lock_flags); + + return ret; +} + +/** + * Release ASRC pair + * + * It clears the resource from asrc_priv and releases the occupied channels. + */ +static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc_priv = pair->asrc_priv; + enum asrc_pair_index index = pair->index; + unsigned long lock_flags; + + /* Make sure the pair is disabled */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_MASK(index), 0); + + spin_lock_irqsave(&asrc_priv->lock, lock_flags); + + asrc_priv->channel_avail += pair->channels; + asrc_priv->pair[index] = NULL; + pair->error = 0; + + spin_unlock_irqrestore(&asrc_priv->lock, lock_flags); +} + +/** + * Configure input and output thresholds + */ +static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out) +{ + struct fsl_asrc *asrc_priv = pair->asrc_priv; + enum asrc_pair_index index = pair->index; + + regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), + ASRMCRi_EXTTHRSHi_MASK | + ASRMCRi_INFIFO_THRESHOLD_MASK | + ASRMCRi_OUTFIFO_THRESHOLD_MASK, + ASRMCRi_EXTTHRSHi | + ASRMCRi_INFIFO_THRESHOLD(in) | + ASRMCRi_OUTFIFO_THRESHOLD(out)); +} + +/** + * Calculate the total divisor between asrck clock rate and sample rate + * + * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider + */ +static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div) +{ + u32 ps; + + /* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */ + for (ps = 0; div > 8; ps++) + div >>= 1; + + return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps; +} + +/** + * Calculate and set the ratio for Ideal Ratio mode only + * + * The ratio is a 32-bit fixed point value with 26 fractional bits. + */ +static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair, + int inrate, int outrate) +{ + struct fsl_asrc *asrc_priv = pair->asrc_priv; + enum asrc_pair_index index = pair->index; + unsigned long ratio; + int i; + + if (!outrate) { + pair_err("output rate should not be zero\n"); + return -EINVAL; + } + + /* Calculate the intergal part of the ratio */ + ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH; + + /* ... and then the 26 depth decimal part */ + inrate %= outrate; + + for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) { + inrate <<= 1; + + if (inrate < outrate) + continue; + + ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i); + inrate -= outrate; + + if (!inrate) + break; + } + + regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio); + regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24); + + return 0; +} + +/** + * Configure the assigned ASRC pair + * + * It configures those ASRC registers according to a configuration instance + * of struct asrc_config which includes in/output sample rate, width, channel + * and clock settings. + */ +static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) +{ + struct asrc_config *config = pair->config; + struct fsl_asrc *asrc_priv = pair->asrc_priv; + enum asrc_pair_index index = pair->index; + u32 inrate = config->input_sample_rate, indiv; + u32 outrate = config->output_sample_rate, outdiv; + bool ideal = config->inclk == INCLK_NONE; + u32 clk_index[2], div[2]; + int in, out, channels; + struct clk *clk; + + if (!config) { + pair_err("invalid pair config\n"); + return -EINVAL; + } + + /* Validate channels */ + if (config->channel_num < 1 || config->channel_num > 10) { + pair_err("does not support %d channels\n", config->channel_num); + return -EINVAL; + } + + /* Validate output width */ + if (config->output_word_width == ASRC_WIDTH_8_BIT) { + pair_err("does not support 8bit width output\n"); + return -EINVAL; + } + + /* Validate input and output sample rates */ + for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++) + if (inrate == supported_input_rate[in]) + break; + + if (in == ARRAY_SIZE(supported_input_rate)) { + pair_err("unsupported input sample rate: %dHz\n", inrate); + return -EINVAL; + } + + for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++) + if (outrate == supported_asrc_rate[out]) + break; + + if (out == ARRAY_SIZE(supported_asrc_rate)) { + pair_err("unsupported output sample rate: %dHz\n", outrate); + return -EINVAL; + } + + /* Validate input and output clock sources */ + clk_index[IN] = clk_map[IN][config->inclk]; + clk_index[OUT] = clk_map[OUT][config->outclk]; + + /* We only have output clock for ideal ratio mode */ + clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]]; + + div[IN] = clk_get_rate(clk) / inrate; + if (div[IN] == 0) { + pair_err("failed to support input sample rate %dHz by asrck_%x\n", + inrate, clk_index[ideal ? OUT : IN]); + return -EINVAL; + } + + clk = asrc_priv->asrck_clk[clk_index[OUT]]; + + /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */ + if (ideal) + div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE; + else + div[OUT] = clk_get_rate(clk) / outrate; + + if (div[OUT] == 0) { + pair_err("failed to support output sample rate %dHz by asrck_%x\n", + outrate, clk_index[OUT]); + return -EINVAL; + } + + /* Set the channel number */ + channels = config->channel_num; + + if (asrc_priv->channel_bits < 4) + channels /= 2; + + /* Update channels for current pair */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR, + ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits), + ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits)); + + /* Default setting: Automatic selection for processing mode */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index)); + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_USRi_MASK(index), 0); + + /* Set the input and output clock sources */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCSR, + ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index), + ASRCSR_AICS(index, clk_index[IN]) | + ASRCSR_AOCS(index, clk_index[OUT])); + + /* Calculate the input clock divisors */ + indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]); + outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]); + + /* Suppose indiv and outdiv includes prescaler, so add its MASK too */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index), + ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) | + ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index), + ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv)); + + /* Implement word_width configurations */ + regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index), + ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK, + ASRMCR1i_OW16(config->output_word_width) | + ASRMCR1i_IWD(config->input_word_width)); + + /* Enable BUFFER STALL */ + regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index), + ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi); + + /* Set default thresholds for input and output FIFO */ + fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD, + ASRC_INPUTFIFO_THRESHOLD); + + /* Configure the followings only for Ideal Ratio mode */ + if (!ideal) + return 0; + + /* Clear ASTSx bit to use Ideal Ratio mode */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ATSi_MASK(index), 0); + + /* Enable Ideal Ratio mode */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index), + ASRCTR_IDR(index) | ASRCTR_USR(index)); + + /* Apply configurations for pre- and post-processing */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCFG, + ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index), + ASRCFG_PREMOD(index, process_option[in][out][0]) | + ASRCFG_POSTMOD(index, process_option[in][out][1])); + + return fsl_asrc_set_ideal_ratio(pair, inrate, outrate); +} + +/** + * Start the assigned ASRC pair + * + * It enables the assigned pair and makes it stopped at the stall level. + */ +static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc_priv = pair->asrc_priv; + enum asrc_pair_index index = pair->index; + int reg, retry = 10, i; + + /* Enable the current pair */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index)); + + /* Wait for status of initialization */ + do { + udelay(5); + regmap_read(asrc_priv->regmap, REG_ASRCFG, ®); + reg &= ASRCFG_INIRQi_MASK(index); + } while (!reg && --retry); + + /* Make the input fifo to ASRC STALL level */ + regmap_read(asrc_priv->regmap, REG_ASRCNCR, ®); + for (i = 0; i < pair->channels * 4; i++) + regmap_write(asrc_priv->regmap, REG_ASRDI(index), 0); + + /* Enable overload interrupt */ + regmap_write(asrc_priv->regmap, REG_ASRIER, ASRIER_AOLIE); +} + +/** + * Stop the assigned ASRC pair + */ +static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair) +{ + struct fsl_asrc *asrc_priv = pair->asrc_priv; + enum asrc_pair_index index = pair->index; + + /* Stop the current pair */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_MASK(index), 0); +} + +/** + * Get DMA channel according to the pair and direction. + */ +struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir) +{ + struct fsl_asrc *asrc_priv = pair->asrc_priv; + enum asrc_pair_index index = pair->index; + char name[4]; + + sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a'); + + return dma_request_slave_channel(&asrc_priv->pdev->dev, name); +} +EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel); + +static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); + int width = snd_pcm_format_width(params_format(params)); + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + struct asrc_config config; + int word_width, ret; + + ret = fsl_asrc_request_pair(channels, pair); + if (ret) { + dev_err(dai->dev, "fail to request asrc pair\n"); + return ret; + } + + pair->config = &config; + + if (width == 16) + width = ASRC_WIDTH_16_BIT; + else + width = ASRC_WIDTH_24_BIT; + + if (asrc_priv->asrc_width == 16) + word_width = ASRC_WIDTH_16_BIT; + else + word_width = ASRC_WIDTH_24_BIT; + + config.pair = pair->index; + config.channel_num = channels; + config.inclk = INCLK_NONE; + config.outclk = OUTCLK_ASRCK1_CLK; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + config.input_word_width = width; + config.output_word_width = word_width; + config.input_sample_rate = rate; + config.output_sample_rate = asrc_priv->asrc_rate; + } else { + config.input_word_width = word_width; + config.output_word_width = width; + config.input_sample_rate = asrc_priv->asrc_rate; + config.output_sample_rate = rate; + } + + ret = fsl_asrc_config_pair(pair); + if (ret) { + dev_err(dai->dev, "fail to config asrc pair\n"); + return ret; + } + + return 0; +} + +static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + if (pair) + fsl_asrc_release_pair(pair); + + return 0; +} + +static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + fsl_asrc_start_pair(pair); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + fsl_asrc_stop_pair(pair); + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct snd_soc_dai_ops fsl_asrc_dai_ops = { + .hw_params = fsl_asrc_dai_hw_params, + .hw_free = fsl_asrc_dai_hw_free, + .trigger = fsl_asrc_dai_trigger, +}; + +static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) +{ + struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &asrc_priv->dma_params_tx, + &asrc_priv->dma_params_rx); + + return 0; +} + +#define FSL_ASRC_RATES SNDRV_PCM_RATE_8000_192000 +#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FORMAT_S20_3LE) + +static struct snd_soc_dai_driver fsl_asrc_dai = { + .probe = fsl_asrc_dai_probe, + .playback = { + .stream_name = "ASRC-Playback", + .channels_min = 1, + .channels_max = 10, + .rates = FSL_ASRC_RATES, + .formats = FSL_ASRC_FORMATS, + }, + .capture = { + .stream_name = "ASRC-Capture", + .channels_min = 1, + .channels_max = 10, + .rates = FSL_ASRC_RATES, + .formats = FSL_ASRC_FORMATS, + }, + .ops = &fsl_asrc_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_asrc_component = { + .name = "fsl-asrc-dai", +}; + +static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ASRCTR: + case REG_ASRIER: + case REG_ASRCNCR: + case REG_ASRCFG: + case REG_ASRCSR: + case REG_ASRCDR1: + case REG_ASRCDR2: + case REG_ASRSTR: + case REG_ASRPM1: + case REG_ASRPM2: + case REG_ASRPM3: + case REG_ASRPM4: + case REG_ASRPM5: + case REG_ASRTFR1: + case REG_ASRCCR: + case REG_ASRDOA: + case REG_ASRDOB: + case REG_ASRDOC: + case REG_ASRIDRHA: + case REG_ASRIDRLA: + case REG_ASRIDRHB: + case REG_ASRIDRLB: + case REG_ASRIDRHC: + case REG_ASRIDRLC: + case REG_ASR76K: + case REG_ASR56K: + case REG_ASRMCRA: + case REG_ASRFSTA: + case REG_ASRMCRB: + case REG_ASRFSTB: + case REG_ASRMCRC: + case REG_ASRFSTC: + case REG_ASRMCR1A: + case REG_ASRMCR1B: + case REG_ASRMCR1C: + return true; + default: + return false; + } +} + +static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ASRSTR: + case REG_ASRDIA: + case REG_ASRDIB: + case REG_ASRDIC: + case REG_ASRDOA: + case REG_ASRDOB: + case REG_ASRDOC: + case REG_ASRFSTA: + case REG_ASRFSTB: + case REG_ASRFSTC: + case REG_ASRCFG: + return true; + default: + return false; + } +} + +static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case REG_ASRCTR: + case REG_ASRIER: + case REG_ASRCNCR: + case REG_ASRCFG: + case REG_ASRCSR: + case REG_ASRCDR1: + case REG_ASRCDR2: + case REG_ASRSTR: + case REG_ASRPM1: + case REG_ASRPM2: + case REG_ASRPM3: + case REG_ASRPM4: + case REG_ASRPM5: + case REG_ASRTFR1: + case REG_ASRCCR: + case REG_ASRDIA: + case REG_ASRDIB: + case REG_ASRDIC: + case REG_ASRIDRHA: + case REG_ASRIDRLA: + case REG_ASRIDRHB: + case REG_ASRIDRLB: + case REG_ASRIDRHC: + case REG_ASRIDRLC: + case REG_ASR76K: + case REG_ASR56K: + case REG_ASRMCRA: + case REG_ASRMCRB: + case REG_ASRMCRC: + case REG_ASRMCR1A: + case REG_ASRMCR1B: + case REG_ASRMCR1C: + return true; + default: + return false; + } +} + +static struct regmap_config fsl_asrc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = REG_ASRMCR1C, + .readable_reg = fsl_asrc_readable_reg, + .volatile_reg = fsl_asrc_volatile_reg, + .writeable_reg = fsl_asrc_writeable_reg, + .cache_type = REGCACHE_RBTREE, +}; + +/** + * Initialize ASRC registers with a default configurations + */ +static int fsl_asrc_init(struct fsl_asrc *asrc_priv) +{ + /* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */ + regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN); + + /* Disable interrupt by default */ + regmap_write(asrc_priv->regmap, REG_ASRIER, 0x0); + + /* Apply recommended settings for parameters from Reference Manual */ + regmap_write(asrc_priv->regmap, REG_ASRPM1, 0x7fffff); + regmap_write(asrc_priv->regmap, REG_ASRPM2, 0x255555); + regmap_write(asrc_priv->regmap, REG_ASRPM3, 0xff7280); + regmap_write(asrc_priv->regmap, REG_ASRPM4, 0xff7280); + regmap_write(asrc_priv->regmap, REG_ASRPM5, 0xff7280); + + /* Base address for task queue FIFO. Set to 0x7C */ + regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1, + ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc)); + + /* Set the processing clock for 76KHz to 133M */ + regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6); + + /* Set the processing clock for 56KHz to 133M */ + return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947); +} + +/** + * Interrupt handler for ASRC + */ +static irqreturn_t fsl_asrc_isr(int irq, void *dev_id) +{ + struct fsl_asrc *asrc_priv = (struct fsl_asrc *)dev_id; + struct device *dev = &asrc_priv->pdev->dev; + enum asrc_pair_index index; + u32 status; + + regmap_read(asrc_priv->regmap, REG_ASRSTR, &status); + + /* Clean overload error */ + regmap_write(asrc_priv->regmap, REG_ASRSTR, ASRSTR_AOLE); + + /* + * We here use dev_dbg() for all exceptions because ASRC itself does + * not care if FIFO overflowed or underrun while a warning in the + * interrupt would result a ridged conversion. + */ + for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) { + if (!asrc_priv->pair[index]) + continue; + + if (status & ASRSTR_ATQOL) { + asrc_priv->pair[index]->error |= ASRC_TASK_Q_OVERLOAD; + dev_dbg(dev, "ASRC Task Queue FIFO overload\n"); + } + + if (status & ASRSTR_AOOL(index)) { + asrc_priv->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD; + pair_dbg("Output Task Overload\n"); + } + + if (status & ASRSTR_AIOL(index)) { + asrc_priv->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD; + pair_dbg("Input Task Overload\n"); + } + + if (status & ASRSTR_AODO(index)) { + asrc_priv->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW; + pair_dbg("Output Data Buffer has overflowed\n"); + } + + if (status & ASRSTR_AIDU(index)) { + asrc_priv->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN; + pair_dbg("Input Data Buffer has underflowed\n"); + } + } + + return IRQ_HANDLED; +} + +static int fsl_asrc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_asrc *asrc_priv; + struct resource *res; + void __iomem *regs; + int irq, ret, i; + char tmp[16]; + + asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL); + if (!asrc_priv) + return -ENOMEM; + + asrc_priv->pdev = pdev; + strcpy(asrc_priv->name, np->name); + + /* Get the addresses and IRQ */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + asrc_priv->paddr = res->start; + + /* Register regmap and let it prepare core clock */ + if (of_property_read_bool(np, "big-endian")) + fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG; + + asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs, + &fsl_asrc_regmap_config); + if (IS_ERR(asrc_priv->regmap)) { + dev_err(&pdev->dev, "failed to init regmap\n"); + return PTR_ERR(asrc_priv->regmap); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", np->full_name); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0, + asrc_priv->name, asrc_priv); + if (ret) { + dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret); + return ret; + } + + asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem"); + if (IS_ERR(asrc_priv->mem_clk)) { + dev_err(&pdev->dev, "failed to get mem clock\n"); + return PTR_ERR(asrc_priv->ipg_clk); + } + + asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(asrc_priv->ipg_clk)) { + dev_err(&pdev->dev, "failed to get ipg clock\n"); + return PTR_ERR(asrc_priv->ipg_clk); + } + + for (i = 0; i < ASRC_CLK_MAX_NUM; i++) { + sprintf(tmp, "asrck_%x", i); + asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp); + if (IS_ERR(asrc_priv->asrck_clk[i])) { + dev_err(&pdev->dev, "failed to get %s clock\n", tmp); + return PTR_ERR(asrc_priv->asrck_clk[i]); + } + } + + if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx35-asrc")) { + asrc_priv->channel_bits = 3; + clk_map[IN] = input_clk_map_imx35; + clk_map[OUT] = output_clk_map_imx35; + } else { + asrc_priv->channel_bits = 4; + clk_map[IN] = input_clk_map_imx53; + clk_map[OUT] = output_clk_map_imx53; + } + + ret = fsl_asrc_init(asrc_priv); + if (ret) { + dev_err(&pdev->dev, "failed to init asrc %d\n", ret); + return -EINVAL; + } + + asrc_priv->channel_avail = 10; + + ret = of_property_read_u32(np, "fsl,asrc-rate", + &asrc_priv->asrc_rate); + if (ret) { + dev_err(&pdev->dev, "failed to get output rate\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "fsl,asrc-width", + &asrc_priv->asrc_width); + if (ret) { + dev_err(&pdev->dev, "failed to get output width\n"); + return -EINVAL; + } + + if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) { + dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n"); + asrc_priv->asrc_width = 24; + } + + platform_set_drvdata(pdev, asrc_priv); + pm_runtime_enable(&pdev->dev); + spin_lock_init(&asrc_priv->lock); + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component, + &fsl_asrc_dai, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register ASoC DAI\n"); + return ret; + } + + ret = devm_snd_soc_register_platform(&pdev->dev, &fsl_asrc_platform); + if (ret) { + dev_err(&pdev->dev, "failed to register ASoC platform\n"); + return ret; + } + + dev_info(&pdev->dev, "driver registered\n"); + + return 0; +} + +#if CONFIG_PM_RUNTIME +static int fsl_asrc_runtime_resume(struct device *dev) +{ + struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); + int i; + + clk_prepare_enable(asrc_priv->mem_clk); + clk_prepare_enable(asrc_priv->ipg_clk); + for (i = 0; i < ASRC_CLK_MAX_NUM; i++) + clk_prepare_enable(asrc_priv->asrck_clk[i]); + + return 0; +} + +static int fsl_asrc_runtime_suspend(struct device *dev) +{ + struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ASRC_CLK_MAX_NUM; i++) + clk_disable_unprepare(asrc_priv->asrck_clk[i]); + clk_disable_unprepare(asrc_priv->ipg_clk); + clk_disable_unprepare(asrc_priv->mem_clk); + + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +#if CONFIG_PM_SLEEP +static int fsl_asrc_suspend(struct device *dev) +{ + struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); + + regcache_cache_only(asrc_priv->regmap, true); + regcache_mark_dirty(asrc_priv->regmap); + + return 0; +} + +static int fsl_asrc_resume(struct device *dev) +{ + struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); + u32 asrctr; + + /* Stop all pairs provisionally */ + regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr); + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_ALL_MASK, 0); + + /* Restore all registers */ + regcache_cache_only(asrc_priv->regmap, false); + regcache_sync(asrc_priv->regmap); + + /* Restart enabled pairs */ + regmap_update_bits(asrc_priv->regmap, REG_ASRCTR, + ASRCTR_ASRCEi_ALL_MASK, asrctr); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops fsl_asrc_pm = { + SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume) +}; + +static const struct of_device_id fsl_asrc_ids[] = { + { .compatible = "fsl,imx35-asrc", }, + { .compatible = "fsl,imx53-asrc", }, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_asrc_ids); + +static struct platform_driver fsl_asrc_driver = { + .probe = fsl_asrc_probe, + .driver = { + .name = "fsl-asrc", + .of_match_table = fsl_asrc_ids, + .pm = &fsl_asrc_pm, + }, +}; +module_platform_driver(fsl_asrc_driver); + +MODULE_DESCRIPTION("Freescale ASRC ASoC driver"); +MODULE_AUTHOR("Nicolin Chen "); +MODULE_ALIAS("platform:fsl-asrc"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h new file mode 100644 index 000000000000..a3f211f53c23 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc.h @@ -0,0 +1,461 @@ +/* + * fsl_asrc.h - Freescale ASRC ALSA SoC header file + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _FSL_ASRC_H +#define _FSL_ASRC_H + +#define IN 0 +#define OUT 1 + +#define ASRC_DMA_BUFFER_NUM 2 +#define ASRC_INPUTFIFO_THRESHOLD 32 +#define ASRC_OUTPUTFIFO_THRESHOLD 32 +#define ASRC_FIFO_THRESHOLD_MIN 0 +#define ASRC_FIFO_THRESHOLD_MAX 63 +#define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4) +#define ASRC_MAX_BUFFER_SIZE (1024 * 48) +#define ASRC_OUTPUT_LAST_SAMPLE 8 + +#define IDEAL_RATIO_RATE 1000000 + +#define REG_ASRCTR 0x00 +#define REG_ASRIER 0x04 +#define REG_ASRCNCR 0x0C +#define REG_ASRCFG 0x10 +#define REG_ASRCSR 0x14 + +#define REG_ASRCDR1 0x18 +#define REG_ASRCDR2 0x1C +#define REG_ASRCDR(i) ((i < 2) ? REG_ASRCDR1 : REG_ASRCDR2) + +#define REG_ASRSTR 0x20 +#define REG_ASRRA 0x24 +#define REG_ASRRB 0x28 +#define REG_ASRRC 0x2C +#define REG_ASRPM1 0x40 +#define REG_ASRPM2 0x44 +#define REG_ASRPM3 0x48 +#define REG_ASRPM4 0x4C +#define REG_ASRPM5 0x50 +#define REG_ASRTFR1 0x54 +#define REG_ASRCCR 0x5C + +#define REG_ASRDIA 0x60 +#define REG_ASRDOA 0x64 +#define REG_ASRDIB 0x68 +#define REG_ASRDOB 0x6C +#define REG_ASRDIC 0x70 +#define REG_ASRDOC 0x74 +#define REG_ASRDI(i) (REG_ASRDIA + (i << 3)) +#define REG_ASRDO(i) (REG_ASRDOA + (i << 3)) +#define REG_ASRDx(x, i) (x == IN ? REG_ASRDI(i) : REG_ASRDO(i)) + +#define REG_ASRIDRHA 0x80 +#define REG_ASRIDRLA 0x84 +#define REG_ASRIDRHB 0x88 +#define REG_ASRIDRLB 0x8C +#define REG_ASRIDRHC 0x90 +#define REG_ASRIDRLC 0x94 +#define REG_ASRIDRH(i) (REG_ASRIDRHA + (i << 3)) +#define REG_ASRIDRL(i) (REG_ASRIDRLA + (i << 3)) + +#define REG_ASR76K 0x98 +#define REG_ASR56K 0x9C + +#define REG_ASRMCRA 0xA0 +#define REG_ASRFSTA 0xA4 +#define REG_ASRMCRB 0xA8 +#define REG_ASRFSTB 0xAC +#define REG_ASRMCRC 0xB0 +#define REG_ASRFSTC 0xB4 +#define REG_ASRMCR(i) (REG_ASRMCRA + (i << 3)) +#define REG_ASRFST(i) (REG_ASRFSTA + (i << 3)) + +#define REG_ASRMCR1A 0xC0 +#define REG_ASRMCR1B 0xC4 +#define REG_ASRMCR1C 0xC8 +#define REG_ASRMCR1(i) (REG_ASRMCR1A + (i << 2)) + + +/* REG0 0x00 REG_ASRCTR */ +#define ASRCTR_ATSi_SHIFT(i) (20 + i) +#define ASRCTR_ATSi_MASK(i) (1 << ASRCTR_ATSi_SHIFT(i)) +#define ASRCTR_ATS(i) (1 << ASRCTR_ATSi_SHIFT(i)) +#define ASRCTR_USRi_SHIFT(i) (14 + (i << 1)) +#define ASRCTR_USRi_MASK(i) (1 << ASRCTR_USRi_SHIFT(i)) +#define ASRCTR_USR(i) (1 << ASRCTR_USRi_SHIFT(i)) +#define ASRCTR_IDRi_SHIFT(i) (13 + (i << 1)) +#define ASRCTR_IDRi_MASK(i) (1 << ASRCTR_IDRi_SHIFT(i)) +#define ASRCTR_IDR(i) (1 << ASRCTR_IDRi_SHIFT(i)) +#define ASRCTR_SRST_SHIFT 4 +#define ASRCTR_SRST_MASK (1 << ASRCTR_SRST_SHIFT) +#define ASRCTR_SRST (1 << ASRCTR_SRST_SHIFT) +#define ASRCTR_ASRCEi_SHIFT(i) (1 + i) +#define ASRCTR_ASRCEi_MASK(i) (1 << ASRCTR_ASRCEi_SHIFT(i)) +#define ASRCTR_ASRCE(i) (1 << ASRCTR_ASRCEi_SHIFT(i)) +#define ASRCTR_ASRCEi_ALL_MASK (0x7 << ASRCTR_ASRCEi_SHIFT(0)) +#define ASRCTR_ASRCEN_SHIFT 0 +#define ASRCTR_ASRCEN_MASK (1 << ASRCTR_ASRCEN_SHIFT) +#define ASRCTR_ASRCEN (1 << ASRCTR_ASRCEN_SHIFT) + +/* REG1 0x04 REG_ASRIER */ +#define ASRIER_AFPWE_SHIFT 7 +#define ASRIER_AFPWE_MASK (1 << ASRIER_AFPWE_SHIFT) +#define ASRIER_AFPWE (1 << ASRIER_AFPWE_SHIFT) +#define ASRIER_AOLIE_SHIFT 6 +#define ASRIER_AOLIE_MASK (1 << ASRIER_AOLIE_SHIFT) +#define ASRIER_AOLIE (1 << ASRIER_AOLIE_SHIFT) +#define ASRIER_ADOEi_SHIFT(i) (3 + i) +#define ASRIER_ADOEi_MASK(i) (1 << ASRIER_ADOEi_SHIFT(i)) +#define ASRIER_ADOE(i) (1 << ASRIER_ADOEi_SHIFT(i)) +#define ASRIER_ADIEi_SHIFT(i) (0 + i) +#define ASRIER_ADIEi_MASK(i) (1 << ASRIER_ADIEi_SHIFT(i)) +#define ASRIER_ADIE(i) (1 << ASRIER_ADIEi_SHIFT(i)) + +/* REG2 0x0C REG_ASRCNCR */ +#define ASRCNCR_ANCi_SHIFT(i, b) (b * i) +#define ASRCNCR_ANCi_MASK(i, b) (((1 << b) - 1) << ASRCNCR_ANCi_SHIFT(i, b)) +#define ASRCNCR_ANCi(i, v, b) ((v << ASRCNCR_ANCi_SHIFT(i, b)) & ASRCNCR_ANCi_MASK(i, b)) + +/* REG3 0x10 REG_ASRCFG */ +#define ASRCFG_INIRQi_SHIFT(i) (21 + i) +#define ASRCFG_INIRQi_MASK(i) (1 << ASRCFG_INIRQi_SHIFT(i)) +#define ASRCFG_INIRQi (1 << ASRCFG_INIRQi_SHIFT(i)) +#define ASRCFG_NDPRi_SHIFT(i) (18 + i) +#define ASRCFG_NDPRi_MASK(i) (1 << ASRCFG_NDPRi_SHIFT(i)) +#define ASRCFG_NDPRi (1 << ASRCFG_NDPRi_SHIFT(i)) +#define ASRCFG_POSTMODi_SHIFT(i) (8 + (i << 2)) +#define ASRCFG_POSTMODi_WIDTH 2 +#define ASRCFG_POSTMODi_MASK(i) (((1 << ASRCFG_POSTMODi_WIDTH) - 1) << ASRCFG_POSTMODi_SHIFT(i)) +#define ASRCFG_POSTMOD(i, v) ((v) << ASRCFG_POSTMODi_SHIFT(i)) +#define ASRCFG_POSTMODi_UP(i) (0 << ASRCFG_POSTMODi_SHIFT(i)) +#define ASRCFG_POSTMODi_DCON(i) (1 << ASRCFG_POSTMODi_SHIFT(i)) +#define ASRCFG_POSTMODi_DOWN(i) (2 << ASRCFG_POSTMODi_SHIFT(i)) +#define ASRCFG_PREMODi_SHIFT(i) (6 + (i << 2)) +#define ASRCFG_PREMODi_WIDTH 2 +#define ASRCFG_PREMODi_MASK(i) (((1 << ASRCFG_PREMODi_WIDTH) - 1) << ASRCFG_PREMODi_SHIFT(i)) +#define ASRCFG_PREMOD(i, v) ((v) << ASRCFG_PREMODi_SHIFT(i)) +#define ASRCFG_PREMODi_UP(i) (0 << ASRCFG_PREMODi_SHIFT(i)) +#define ASRCFG_PREMODi_DCON(i) (1 << ASRCFG_PREMODi_SHIFT(i)) +#define ASRCFG_PREMODi_DOWN(i) (2 << ASRCFG_PREMODi_SHIFT(i)) +#define ASRCFG_PREMODi_BYPASS(i) (3 << ASRCFG_PREMODi_SHIFT(i)) + +/* REG4 0x14 REG_ASRCSR */ +#define ASRCSR_AxCSi_WIDTH 4 +#define ASRCSR_AxCSi_MASK ((1 << ASRCSR_AxCSi_WIDTH) - 1) +#define ASRCSR_AOCSi_SHIFT(i) (12 + (i << 2)) +#define ASRCSR_AOCSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AOCSi_SHIFT(i)) +#define ASRCSR_AOCS(i, v) ((v) << ASRCSR_AOCSi_SHIFT(i)) +#define ASRCSR_AICSi_SHIFT(i) (i << 2) +#define ASRCSR_AICSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AICSi_SHIFT(i)) +#define ASRCSR_AICS(i, v) ((v) << ASRCSR_AICSi_SHIFT(i)) + +/* REG5&6 0x18 & 0x1C REG_ASRCDR1 & ASRCDR2 */ +#define ASRCDRi_AxCPi_WIDTH 3 +#define ASRCDRi_AICPi_SHIFT(i) (0 + (i % 2) * 6) +#define ASRCDRi_AICPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICPi_SHIFT(i)) +#define ASRCDRi_AICP(i, v) ((v) << ASRCDRi_AICPi_SHIFT(i)) +#define ASRCDRi_AICDi_SHIFT(i) (3 + (i % 2) * 6) +#define ASRCDRi_AICDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICDi_SHIFT(i)) +#define ASRCDRi_AICD(i, v) ((v) << ASRCDRi_AICDi_SHIFT(i)) +#define ASRCDRi_AOCPi_SHIFT(i) ((i < 2) ? 12 + i * 6 : 6) +#define ASRCDRi_AOCPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCPi_SHIFT(i)) +#define ASRCDRi_AOCP(i, v) ((v) << ASRCDRi_AOCPi_SHIFT(i)) +#define ASRCDRi_AOCDi_SHIFT(i) ((i < 2) ? 15 + i * 6 : 9) +#define ASRCDRi_AOCDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCDi_SHIFT(i)) +#define ASRCDRi_AOCD(i, v) ((v) << ASRCDRi_AOCDi_SHIFT(i)) + +/* REG7 0x20 REG_ASRSTR */ +#define ASRSTR_DSLCNT_SHIFT 21 +#define ASRSTR_DSLCNT_MASK (1 << ASRSTR_DSLCNT_SHIFT) +#define ASRSTR_DSLCNT (1 << ASRSTR_DSLCNT_SHIFT) +#define ASRSTR_ATQOL_SHIFT 20 +#define ASRSTR_ATQOL_MASK (1 << ASRSTR_ATQOL_SHIFT) +#define ASRSTR_ATQOL (1 << ASRSTR_ATQOL_SHIFT) +#define ASRSTR_AOOLi_SHIFT(i) (17 + i) +#define ASRSTR_AOOLi_MASK(i) (1 << ASRSTR_AOOLi_SHIFT(i)) +#define ASRSTR_AOOL(i) (1 << ASRSTR_AOOLi_SHIFT(i)) +#define ASRSTR_AIOLi_SHIFT(i) (14 + i) +#define ASRSTR_AIOLi_MASK(i) (1 << ASRSTR_AIOLi_SHIFT(i)) +#define ASRSTR_AIOL(i) (1 << ASRSTR_AIOLi_SHIFT(i)) +#define ASRSTR_AODOi_SHIFT(i) (11 + i) +#define ASRSTR_AODOi_MASK(i) (1 << ASRSTR_AODOi_SHIFT(i)) +#define ASRSTR_AODO(i) (1 << ASRSTR_AODOi_SHIFT(i)) +#define ASRSTR_AIDUi_SHIFT(i) (8 + i) +#define ASRSTR_AIDUi_MASK(i) (1 << ASRSTR_AIDUi_SHIFT(i)) +#define ASRSTR_AIDU(i) (1 << ASRSTR_AIDUi_SHIFT(i)) +#define ASRSTR_FPWT_SHIFT 7 +#define ASRSTR_FPWT_MASK (1 << ASRSTR_FPWT_SHIFT) +#define ASRSTR_FPWT (1 << ASRSTR_FPWT_SHIFT) +#define ASRSTR_AOLE_SHIFT 6 +#define ASRSTR_AOLE_MASK (1 << ASRSTR_AOLE_SHIFT) +#define ASRSTR_AOLE (1 << ASRSTR_AOLE_SHIFT) +#define ASRSTR_AODEi_SHIFT(i) (3 + i) +#define ASRSTR_AODFi_MASK(i) (1 << ASRSTR_AODEi_SHIFT(i)) +#define ASRSTR_AODF(i) (1 << ASRSTR_AODEi_SHIFT(i)) +#define ASRSTR_AIDEi_SHIFT(i) (0 + i) +#define ASRSTR_AIDEi_MASK(i) (1 << ASRSTR_AIDEi_SHIFT(i)) +#define ASRSTR_AIDE(i) (1 << ASRSTR_AIDEi_SHIFT(i)) + +/* REG10 0x54 REG_ASRTFR1 */ +#define ASRTFR1_TF_BASE_WIDTH 7 +#define ASRTFR1_TF_BASE_SHIFT 6 +#define ASRTFR1_TF_BASE_MASK (((1 << ASRTFR1_TF_BASE_WIDTH) - 1) << ASRTFR1_TF_BASE_SHIFT) +#define ASRTFR1_TF_BASE(i) ((i) << ASRTFR1_TF_BASE_SHIFT) + +/* + * REG22 0xA0 REG_ASRMCRA + * REG24 0xA8 REG_ASRMCRB + * REG26 0xB0 REG_ASRMCRC + */ +#define ASRMCRi_ZEROBUFi_SHIFT 23 +#define ASRMCRi_ZEROBUFi_MASK (1 << ASRMCRi_ZEROBUFi_SHIFT) +#define ASRMCRi_ZEROBUFi (1 << ASRMCRi_ZEROBUFi_SHIFT) +#define ASRMCRi_EXTTHRSHi_SHIFT 22 +#define ASRMCRi_EXTTHRSHi_MASK (1 << ASRMCRi_EXTTHRSHi_SHIFT) +#define ASRMCRi_EXTTHRSHi (1 << ASRMCRi_EXTTHRSHi_SHIFT) +#define ASRMCRi_BUFSTALLi_SHIFT 21 +#define ASRMCRi_BUFSTALLi_MASK (1 << ASRMCRi_BUFSTALLi_SHIFT) +#define ASRMCRi_BUFSTALLi (1 << ASRMCRi_BUFSTALLi_SHIFT) +#define ASRMCRi_BYPASSPOLYi_SHIFT 20 +#define ASRMCRi_BYPASSPOLYi_MASK (1 << ASRMCRi_BYPASSPOLYi_SHIFT) +#define ASRMCRi_BYPASSPOLYi (1 << ASRMCRi_BYPASSPOLYi_SHIFT) +#define ASRMCRi_OUTFIFO_THRESHOLD_WIDTH 6 +#define ASRMCRi_OUTFIFO_THRESHOLD_SHIFT 12 +#define ASRMCRi_OUTFIFO_THRESHOLD_MASK (((1 << ASRMCRi_OUTFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT) +#define ASRMCRi_OUTFIFO_THRESHOLD(v) (((v) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT) & ASRMCRi_OUTFIFO_THRESHOLD_MASK) +#define ASRMCRi_RSYNIFi_SHIFT 11 +#define ASRMCRi_RSYNIFi_MASK (1 << ASRMCRi_RSYNIFi_SHIFT) +#define ASRMCRi_RSYNIFi (1 << ASRMCRi_RSYNIFi_SHIFT) +#define ASRMCRi_RSYNOFi_SHIFT 10 +#define ASRMCRi_RSYNOFi_MASK (1 << ASRMCRi_RSYNOFi_SHIFT) +#define ASRMCRi_RSYNOFi (1 << ASRMCRi_RSYNOFi_SHIFT) +#define ASRMCRi_INFIFO_THRESHOLD_WIDTH 6 +#define ASRMCRi_INFIFO_THRESHOLD_SHIFT 0 +#define ASRMCRi_INFIFO_THRESHOLD_MASK (((1 << ASRMCRi_INFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_INFIFO_THRESHOLD_SHIFT) +#define ASRMCRi_INFIFO_THRESHOLD(v) (((v) << ASRMCRi_INFIFO_THRESHOLD_SHIFT) & ASRMCRi_INFIFO_THRESHOLD_MASK) + +/* + * REG23 0xA4 REG_ASRFSTA + * REG25 0xAC REG_ASRFSTB + * REG27 0xB4 REG_ASRFSTC + */ +#define ASRFSTi_OAFi_SHIFT 23 +#define ASRFSTi_OAFi_MASK (1 << ASRFSTi_OAFi_SHIFT) +#define ASRFSTi_OAFi (1 << ASRFSTi_OAFi_SHIFT) +#define ASRFSTi_OUTPUT_FIFO_WIDTH 7 +#define ASRFSTi_OUTPUT_FIFO_SHIFT 12 +#define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT) +#define ASRFSTi_IAEi_SHIFT 11 +#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_OAFi_SHIFT) +#define ASRFSTi_IAEi (1 << ASRFSTi_OAFi_SHIFT) +#define ASRFSTi_INPUT_FIFO_WIDTH 7 +#define ASRFSTi_INPUT_FIFO_SHIFT 0 +#define ASRFSTi_INPUT_FIFO_MASK ((1 << ASRFSTi_INPUT_FIFO_WIDTH) - 1) + +/* REG28 0xC0 & 0xC4 & 0xC8 REG_ASRMCR1i */ +#define ASRMCR1i_IWD_WIDTH 3 +#define ASRMCR1i_IWD_SHIFT 9 +#define ASRMCR1i_IWD_MASK (((1 << ASRMCR1i_IWD_WIDTH) - 1) << ASRMCR1i_IWD_SHIFT) +#define ASRMCR1i_IWD(v) ((v) << ASRMCR1i_IWD_SHIFT) +#define ASRMCR1i_IMSB_SHIFT 8 +#define ASRMCR1i_IMSB_MASK (1 << ASRMCR1i_IMSB_SHIFT) +#define ASRMCR1i_IMSB_MSB (1 << ASRMCR1i_IMSB_SHIFT) +#define ASRMCR1i_IMSB_LSB (0 << ASRMCR1i_IMSB_SHIFT) +#define ASRMCR1i_OMSB_SHIFT 2 +#define ASRMCR1i_OMSB_MASK (1 << ASRMCR1i_OMSB_SHIFT) +#define ASRMCR1i_OMSB_MSB (1 << ASRMCR1i_OMSB_SHIFT) +#define ASRMCR1i_OMSB_LSB (0 << ASRMCR1i_OMSB_SHIFT) +#define ASRMCR1i_OSGN_SHIFT 1 +#define ASRMCR1i_OSGN_MASK (1 << ASRMCR1i_OSGN_SHIFT) +#define ASRMCR1i_OSGN (1 << ASRMCR1i_OSGN_SHIFT) +#define ASRMCR1i_OW16_SHIFT 0 +#define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT) +#define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT) + + +enum asrc_pair_index { + ASRC_INVALID_PAIR = -1, + ASRC_PAIR_A = 0, + ASRC_PAIR_B = 1, + ASRC_PAIR_C = 2, +}; + +#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1) + +enum asrc_inclk { + INCLK_NONE = 0x03, + INCLK_ESAI_RX = 0x00, + INCLK_SSI1_RX = 0x01, + INCLK_SSI2_RX = 0x02, + INCLK_SSI3_RX = 0x07, + INCLK_SPDIF_RX = 0x04, + INCLK_MLB_CLK = 0x05, + INCLK_PAD = 0x06, + INCLK_ESAI_TX = 0x08, + INCLK_SSI1_TX = 0x09, + INCLK_SSI2_TX = 0x0a, + INCLK_SSI3_TX = 0x0b, + INCLK_SPDIF_TX = 0x0c, + INCLK_ASRCK1_CLK = 0x0f, +}; + +enum asrc_outclk { + OUTCLK_NONE = 0x03, + OUTCLK_ESAI_TX = 0x00, + OUTCLK_SSI1_TX = 0x01, + OUTCLK_SSI2_TX = 0x02, + OUTCLK_SSI3_TX = 0x07, + OUTCLK_SPDIF_TX = 0x04, + OUTCLK_MLB_CLK = 0x05, + OUTCLK_PAD = 0x06, + OUTCLK_ESAI_RX = 0x08, + OUTCLK_SSI1_RX = 0x09, + OUTCLK_SSI2_RX = 0x0a, + OUTCLK_SSI3_RX = 0x0b, + OUTCLK_SPDIF_RX = 0x0c, + OUTCLK_ASRCK1_CLK = 0x0f, +}; + +#define ASRC_CLK_MAX_NUM 16 + +enum asrc_word_width { + ASRC_WIDTH_24_BIT = 0, + ASRC_WIDTH_16_BIT = 1, + ASRC_WIDTH_8_BIT = 2, +}; + +struct asrc_config { + enum asrc_pair_index pair; + unsigned int channel_num; + unsigned int buffer_num; + unsigned int dma_buffer_size; + unsigned int input_sample_rate; + unsigned int output_sample_rate; + enum asrc_word_width input_word_width; + enum asrc_word_width output_word_width; + enum asrc_inclk inclk; + enum asrc_outclk outclk; +}; + +struct asrc_req { + unsigned int chn_num; + enum asrc_pair_index index; +}; + +struct asrc_querybuf { + unsigned int buffer_index; + unsigned int input_length; + unsigned int output_length; + unsigned long input_offset; + unsigned long output_offset; +}; + +struct asrc_convert_buffer { + void *input_buffer_vaddr; + void *output_buffer_vaddr; + unsigned int input_buffer_length; + unsigned int output_buffer_length; +}; + +struct asrc_status_flags { + enum asrc_pair_index index; + unsigned int overload_error; +}; + +enum asrc_error_status { + ASRC_TASK_Q_OVERLOAD = 0x01, + ASRC_OUTPUT_TASK_OVERLOAD = 0x02, + ASRC_INPUT_TASK_OVERLOAD = 0x04, + ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08, + ASRC_INPUT_BUFFER_UNDERRUN = 0x10, +}; + +struct dma_block { + dma_addr_t dma_paddr; + void *dma_vaddr; + unsigned int length; +}; + +/** + * fsl_asrc_pair: ASRC Pair private data + * + * @asrc_priv: pointer to its parent module + * @config: configuration profile + * @error: error record + * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C) + * @channels: occupied channel number + * @desc: input and output dma descriptors + * @dma_chan: inputer and output DMA channels + * @dma_data: private dma data + * @pos: hardware pointer position + * @private: pair private area + */ +struct fsl_asrc_pair { + struct fsl_asrc *asrc_priv; + struct asrc_config *config; + unsigned int error; + + enum asrc_pair_index index; + unsigned int channels; + + struct dma_async_tx_descriptor *desc[2]; + struct dma_chan *dma_chan[2]; + struct imx_dma_data dma_data; + unsigned int pos; + + void *private; +}; + +/** + * fsl_asrc_pair: ASRC private data + * + * @dma_params_rx: DMA parameters for receive channel + * @dma_params_tx: DMA parameters for transmit channel + * @pdev: platform device pointer + * @regmap: regmap handler + * @paddr: physical address to the base address of registers + * @mem_clk: clock source to access register + * @ipg_clk: clock source to drive peripheral + * @asrck_clk: clock sources to driver ASRC internal logic + * @lock: spin lock for resource protection + * @pair: pair pointers + * @channel_bits: width of ASRCNCR register for each pair + * @channel_avail: non-occupied channel numbers + * @asrc_rate: default sample rate for ASoC Back-Ends + * @asrc_width: default sample width for ASoC Back-Ends + * @name: driver name + */ +struct fsl_asrc { + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; + struct platform_device *pdev; + struct regmap *regmap; + unsigned long paddr; + struct clk *mem_clk; + struct clk *ipg_clk; + struct clk *asrck_clk[ASRC_CLK_MAX_NUM]; + spinlock_t lock; + + struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM]; + unsigned int channel_bits; + unsigned int channel_avail; + + int asrc_rate; + int asrc_width; + + char name[32]; +}; + +extern struct snd_soc_platform_driver fsl_asrc_platform; +struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir); +#endif /* _FSL_ASRC_H */ diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c new file mode 100644 index 000000000000..5b1e73e97817 --- /dev/null +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -0,0 +1,386 @@ +/* + * Freescale ASRC ALSA SoC Platform (DMA) driver + * + * Copyright (C) 2014 Freescale Semiconductor, Inc. + * + * Author: Nicolin Chen + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "fsl_asrc.h" + +#define FSL_ASRC_DMABUF_SIZE (256 * 1024) + +static struct snd_pcm_hardware snd_imx_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .buffer_bytes_max = FSL_ASRC_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 65535, /* Limited by SDMA engine */ + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +static bool filter(struct dma_chan *chan, void *param) +{ + if (!imx_dma_is_general_purpose(chan)) + return false; + + chan->private = param; + + return true; +} + +static void fsl_asrc_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + pair->pos += snd_pcm_lib_period_bytes(substream); + if (pair->pos >= snd_pcm_lib_buffer_bytes(substream)) + pair->pos = 0; + + snd_pcm_period_elapsed(substream); +} + +static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream) +{ + u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct device *dev = rtd->platform->dev; + unsigned long flags = DMA_CTRL_ACK; + + /* Prepare and submit Front-End DMA channel */ + if (!substream->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + + pair->pos = 0; + pair->desc[!dir] = dmaengine_prep_dma_cyclic( + pair->dma_chan[!dir], runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), + dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags); + if (!pair->desc[!dir]) { + dev_err(dev, "failed to prepare slave DMA for Front-End\n"); + return -ENOMEM; + } + + pair->desc[!dir]->callback = fsl_asrc_dma_complete; + pair->desc[!dir]->callback_param = substream; + + dmaengine_submit(pair->desc[!dir]); + + /* Prepare and submit Back-End DMA channel */ + pair->desc[dir] = dmaengine_prep_dma_cyclic( + pair->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0); + if (!pair->desc[dir]) { + dev_err(dev, "failed to prepare slave DMA for Back-End\n"); + return -ENOMEM; + } + + dmaengine_submit(pair->desc[dir]); + + return 0; +} + +static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = fsl_asrc_dma_prepare_and_submit(substream); + if (ret) + return ret; + dma_async_issue_pending(pair->dma_chan[IN]); + dma_async_issue_pending(pair->dma_chan[OUT]); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dmaengine_terminate_all(pair->dma_chan[OUT]); + dmaengine_terminate_all(pair->dma_chan[IN]); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL; + struct snd_dmaengine_dai_dma_data *dma_params_be = NULL; + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct fsl_asrc *asrc_priv = pair->asrc_priv; + struct dma_slave_config config_fe, config_be; + enum asrc_pair_index index = pair->index; + struct device *dev = rtd->platform->dev; + int stream = substream->stream; + struct imx_dma_data *tmp_data; + struct snd_soc_dpcm *dpcm; + struct dma_chan *tmp_chan; + struct device *dev_be; + u8 dir = tx ? OUT : IN; + dma_cap_mask_t mask; + int ret; + + /* Fetch the Back-End dma_data from DPCM */ + list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *substream_be; + struct snd_soc_dai *dai = be->cpu_dai; + + if (dpcm->fe != rtd) + continue; + + substream_be = snd_soc_dpcm_get_substream(be, stream); + dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be); + dev_be = dai->dev; + break; + } + + if (!dma_params_be) { + dev_err(dev, "failed to get the substream of Back-End\n"); + return -EINVAL; + } + + /* Override dma_data of the Front-End and config its dmaengine */ + dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index); + dma_params_fe->maxburst = dma_params_be->maxburst; + + pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir); + if (!pair->dma_chan[!dir]) { + dev_err(dev, "failed to request DMA channel\n"); + return -EINVAL; + } + + memset(&config_fe, 0, sizeof(config_fe)); + ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe); + if (ret) { + dev_err(dev, "failed to prepare DMA config for Front-End\n"); + return ret; + } + + ret = dmaengine_slave_config(pair->dma_chan[!dir], &config_fe); + if (ret) { + dev_err(dev, "failed to config DMA channel for Front-End\n"); + return ret; + } + + /* Request and config DMA channel for Back-End */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_CYCLIC, mask); + + /* Get DMA request of Back-End */ + tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx"); + tmp_data = tmp_chan->private; + pair->dma_data.dma_request = tmp_data->dma_request; + dma_release_channel(tmp_chan); + + /* Get DMA request of Front-End */ + tmp_chan = fsl_asrc_get_dma_channel(pair, dir); + tmp_data = tmp_chan->private; + pair->dma_data.dma_request2 = tmp_data->dma_request; + pair->dma_data.peripheral_type = tmp_data->peripheral_type; + pair->dma_data.priority = tmp_data->priority; + dma_release_channel(tmp_chan); + + pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data); + if (!pair->dma_chan[dir]) { + dev_err(dev, "failed to request DMA channel for Back-End\n"); + return -EINVAL; + } + + if (asrc_priv->asrc_width == 16) + buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + else + buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + + config_be.direction = DMA_DEV_TO_DEV; + config_be.src_addr_width = buswidth; + config_be.src_maxburst = dma_params_be->maxburst; + config_be.dst_addr_width = buswidth; + config_be.dst_maxburst = dma_params_be->maxburst; + + if (tx) { + config_be.src_addr = asrc_priv->paddr + REG_ASRDO(index); + config_be.dst_addr = dma_params_be->addr; + } else { + config_be.dst_addr = asrc_priv->paddr + REG_ASRDI(index); + config_be.src_addr = dma_params_be->addr; + } + + ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be); + if (ret) { + dev_err(dev, "failed to config DMA channel for Back-End\n"); + return ret; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + snd_pcm_set_runtime_buffer(substream, NULL); + + if (pair->dma_chan[IN]) + dma_release_channel(pair->dma_chan[IN]); + + if (pair->dma_chan[OUT]) + dma_release_channel(pair->dma_chan[OUT]); + + pair->dma_chan[IN] = NULL; + pair->dma_chan[OUT] = NULL; + + return 0; +} + +static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct device *dev = rtd->platform->dev; + struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); + struct fsl_asrc_pair *pair; + + pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL); + if (!pair) { + dev_err(dev, "failed to allocate pair\n"); + return -ENOMEM; + } + + pair->asrc_priv = asrc_priv; + + runtime->private_data = pair; + + snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); + + return 0; +} + +static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + struct fsl_asrc *asrc_priv = pair->asrc_priv; + + if (pair && asrc_priv->pair[pair->index] == pair) + asrc_priv->pair[pair->index] = NULL; + + kfree(pair); + + return 0; +} + +static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct fsl_asrc_pair *pair = runtime->private_data; + + return bytes_to_frames(substream->runtime, pair->pos); +} + +static struct snd_pcm_ops fsl_asrc_dma_pcm_ops = { + .ioctl = snd_pcm_lib_ioctl, + .hw_params = fsl_asrc_dma_hw_params, + .hw_free = fsl_asrc_dma_hw_free, + .trigger = fsl_asrc_dma_trigger, + .open = fsl_asrc_dma_startup, + .close = fsl_asrc_dma_shutdown, + .pointer = fsl_asrc_dma_pcm_pointer, +}; + +static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm_substream *substream; + struct snd_pcm *pcm = rtd->pcm; + int ret, i; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(card->dev, "failed to set DMA mask\n"); + return ret; + } + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { + substream = pcm->streams[i].substream; + if (!substream) + continue; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev, + FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer); + if (ret) { + dev_err(card->dev, "failed to allocate DMA buffer\n"); + goto err; + } + } + + return 0; + +err: + if (--i == 0 && pcm->streams[i].substream) + snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer); + + return ret; +} + +static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + int i; + + for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) { + substream = pcm->streams[i].substream; + if (!substream) + continue; + + snd_dma_free_pages(&substream->dma_buffer); + substream->dma_buffer.area = NULL; + substream->dma_buffer.addr = 0; + } +} + +struct snd_soc_platform_driver fsl_asrc_platform = { + .ops = &fsl_asrc_dma_pcm_ops, + .pcm_new = fsl_asrc_dma_pcm_new, + .pcm_free = fsl_asrc_dma_pcm_free, +}; +EXPORT_SYMBOL_GPL(fsl_asrc_platform); -- cgit v1.2.3 From d3dacda9390b936a1c341d868f548944cc1c70de Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 29 Jul 2014 16:10:16 -0300 Subject: ASoC: fsl_asrc: Use 'ifdef' for config options Fix the following build errors that were observed by building with make ARCH=microblaze allyesconfig: >> sound/soc/fsl/fsl_asrc.c:906:5: warning: "CONFIG_PM_RUNTIME" is not defined [-Wundef] #if CONFIG_PM_RUNTIME ^ >> sound/soc/fsl/fsl_asrc.c:934:5: warning: "CONFIG_PM_SLEEP" is not defined [-Wundef] #if CONFIG_PM_SLEEP ^ >> sound/soc/fsl/fsl_asrc.c:906:5: warning: "CONFIG_PM_RUNTIME" is not defined [-Wundef] #if CONFIG_PM_RUNTIME ^ >> sound/soc/fsl/fsl_asrc.c:934:5: warning: "CONFIG_PM_SLEEP" is not defined [-Wundef] #if CONFIG_PM_SLEEP Reported-by: kbuild test robot Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 27a4a7084343..b9a288877267 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -903,7 +903,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) return 0; } -#if CONFIG_PM_RUNTIME +#ifdef CONFIG_PM_RUNTIME static int fsl_asrc_runtime_resume(struct device *dev) { struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); @@ -931,7 +931,7 @@ static int fsl_asrc_runtime_suspend(struct device *dev) } #endif /* CONFIG_PM_RUNTIME */ -#if CONFIG_PM_SLEEP +#ifdef CONFIG_PM_SLEEP static int fsl_asrc_suspend(struct device *dev) { struct fsl_asrc *asrc_priv = dev_get_drvdata(dev); -- cgit v1.2.3 From bdb9eb49671566afb9ca2025752f57d0e1a6b2a3 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 30 Jul 2014 11:27:06 -0300 Subject: ASoC: fsl: fsl_asrc: Select SND_SOC_GENERIC_DMAENGINE_PCM Building a kernel with SND_SOC_GENERIC_DMAENGINE_PCM=n leads to the following error: ERROR: "snd_dmaengine_pcm_prepare_slave_config" [sound/soc/fsl/snd-soc-fsl-asrc.ko] undefined! Let SND_SOC_FSL_ASRC select SND_SOC_GENERIC_DMAENGINE_PCM in order to fix such error. Reported-by: kbuild test robot Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 56fd32b3a881..0ec1b7a42910 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -5,6 +5,7 @@ comment "Common SoC Audio options for Freescale CPUs:" config SND_SOC_FSL_ASRC tristate "Asynchronous Sample Rate Converter (ASRC) module support" select REGMAP_MMIO + select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y if you want to add Asynchronous Sample Rate Converter (ASRC) support for the Freescale CPUs. -- cgit v1.2.3 From d526416c4fb23a48ed2547138c43e96fa3901124 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Thu, 31 Jul 2014 12:07:40 +0800 Subject: ASoC: fsl_asrc: Fix sparse warnings in FSL_ASRC_FORMATS due to typo reproduce: make C=1 CF=-D__CHECK_ENDIAN__ sparse warnings: (new ones prefixed by >>) >> sound/soc/fsl/fsl_asrc.c:563:28: sparse: restricted snd_pcm_format_t degrades to integer >> sound/soc/fsl/fsl_asrc.c:570:28: sparse: restricted snd_pcm_format_t degrades to integer vim +563 sound/soc/fsl/fsl_asrc.c 557 .probe = fsl_asrc_dai_probe, 558 .playback = { 559 .stream_name = "ASRC-Playback", 560 .channels_min = 1, 561 .channels_max = 10, 562 .rates = FSL_ASRC_RATES, > 563 .formats = FSL_ASRC_FORMATS, 564 }, 565 .capture = { 566 .stream_name = "ASRC-Capture", 567 .channels_min = 1, 568 .channels_max = 10, 569 .rates = FSL_ASRC_RATES, > 570 .formats = FSL_ASRC_FORMATS, 571 }, 572 .ops = &fsl_asrc_dai_ops, 573 }; Reported-by: kbuild test robot Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index b9a288877267..910525b5e5fc 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -551,7 +551,7 @@ static int fsl_asrc_dai_probe(struct snd_soc_dai *dai) #define FSL_ASRC_RATES SNDRV_PCM_RATE_8000_192000 #define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FORMAT_S20_3LE) + SNDRV_PCM_FMTBIT_S20_3LE) static struct snd_soc_dai_driver fsl_asrc_dai = { .probe = fsl_asrc_dai_probe, -- cgit v1.2.3 From d387dd08e444b22f844475780fe12a1ad1c6fffd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 31 Jul 2014 12:32:09 +0300 Subject: ASoC: fsl_asrc: fix an error code in fsl_asrc_probe() There is a cut and paste bug so it returns success instead of the error code. Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 910525b5e5fc..424b23c38d9a 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -826,7 +826,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem"); if (IS_ERR(asrc_priv->mem_clk)) { dev_err(&pdev->dev, "failed to get mem clock\n"); - return PTR_ERR(asrc_priv->ipg_clk); + return PTR_ERR(asrc_priv->mem_clk); } asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); -- cgit v1.2.3 From 74ccb27c35c799a14933c282c4e3c864886fc429 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 30 Jul 2014 11:10:26 +0800 Subject: ASoC: fsl_esai: Add stream names for DPCM usage DPCM needs extra dapm routes in the machine driver to route audio between Front-End and Back-End. In order to differ the stream names in the route map from CODECs, we here add specific stream names to ESAI driver so that we can implement ASRC via DPCM to it. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index d719caf26dc2..72d154e7dd03 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -624,12 +624,14 @@ static int fsl_esai_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver fsl_esai_dai = { .probe = fsl_esai_dai_probe, .playback = { + .stream_name = "CPU-Playback", .channels_min = 1, .channels_max = 12, .rates = FSL_ESAI_RATES, .formats = FSL_ESAI_FORMATS, }, .capture = { + .stream_name = "CPU-Capture", .channels_min = 1, .channels_max = 8, .rates = FSL_ESAI_RATES, -- cgit v1.2.3 From 20d5b76fb2c7070c70fc91b666f5395e5d16e197 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 30 Jul 2014 11:10:27 +0800 Subject: ASoC: fsl_sai: Add stream names for DPCM usage DPCM needs extra dapm routes in the machine driver to route audio between Front-End and Back-End. In order to differ the stream names in the route map from CODECs, we here add specific stream names to SAI driver so that we can implement ASRC via DPCM to it. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index c5a0e8af8226..575515d804b4 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -446,12 +446,14 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) static struct snd_soc_dai_driver fsl_sai_dai = { .probe = fsl_sai_dai_probe, .playback = { + .stream_name = "CPU-Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, .formats = FSL_SAI_FORMATS, }, .capture = { + .stream_name = "CPU-Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_96000, -- cgit v1.2.3 From 756409320bcb366aa5954b4162612aa4be7e37a4 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 30 Jul 2014 11:10:28 +0800 Subject: ASoC: fsl_spdif: Add stream names for DPCM usage DPCM needs extra dapm routes in the machine driver to route audio between Front-End and Back-End. In order to differ the stream names in the route map from CODECs, we here add specific stream names to SPDIF driver so that we can implement ASRC via DPCM to it. Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index b912d45a2a4c..5fbcd9993bd0 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -969,12 +969,14 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai) static struct snd_soc_dai_driver fsl_spdif_dai = { .probe = &fsl_spdif_dai_probe, .playback = { + .stream_name = "CPU-Playback", .channels_min = 2, .channels_max = 2, .rates = FSL_SPDIF_RATES_PLAYBACK, .formats = FSL_SPDIF_FORMATS_PLAYBACK, }, .capture = { + .stream_name = "CPU-Capture", .channels_min = 2, .channels_max = 2, .rates = FSL_SPDIF_RATES_CAPTURE, -- cgit v1.2.3 From e365500459095276d19a920d5be1a65d0ef9999c Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Wed, 30 Jul 2014 11:10:29 +0800 Subject: ASoC: fsl_ssi: Add stream names for DPCM usage DPCM needs extra dapm routes in the machine driver to route audio between Front-End and Back-End. In order to differ the stream names in the route map from CODECs, we here add specific stream names to SSI driver so that we can implement ASRC via DPCM to it. Signed-off-by: Nicolin Chen Acked-by: Timur Tabi Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 9bfef55d77d1..00660acdb805 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1032,12 +1032,14 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = { static struct snd_soc_dai_driver fsl_ssi_dai_template = { .probe = fsl_ssi_dai_probe, .playback = { + .stream_name = "CPU-Playback", .channels_min = 1, .channels_max = 2, .rates = FSLSSI_I2S_RATES, .formats = FSLSSI_I2S_FORMATS, }, .capture = { + .stream_name = "CPU-Capture", .channels_min = 1, .channels_max = 2, .rates = FSLSSI_I2S_RATES, -- cgit v1.2.3 From 6ccf62c7bea561cca7ffbd50839f883327080800 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 4 Aug 2014 12:19:48 +0800 Subject: ASoC: fsl_sarc_dma: Check pair before using it The patch 3117bb3109dc: "ASoC: fsl_asrc: Add ASRC ASoC CPU DAI and platform drivers" from Jul 29, 2014, leads to the following Smatch complaint: sound/soc/fsl/fsl_asrc_dma.c:304 fsl_asrc_dma_shutdown() warn: variable dereferenced before check 'pair' (see line 302) sound/soc/fsl/fsl_asrc_dma.c 301 struct fsl_asrc_pair *pair = runtime->private_data; 302 struct fsl_asrc *asrc_priv = pair->asrc_priv; ^^^^^^^^^^^^^^^ Dereference. 303 304 if (pair && asrc_priv->pair[pair->index] == pair) ^^^^ Check. 305 asrc_priv->pair[pair->index] = NULL; 306 So we just let the driver check pair before using it. Reported-by: Dan Carpenter Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc_dma.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 5b1e73e97817..ffc000bc1f15 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -299,9 +299,14 @@ static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct fsl_asrc_pair *pair = runtime->private_data; - struct fsl_asrc *asrc_priv = pair->asrc_priv; + struct fsl_asrc *asrc_priv; + + if (!pair) + return 0; + + asrc_priv = pair->asrc_priv; - if (pair && asrc_priv->pair[pair->index] == pair) + if (asrc_priv->pair[pair->index] == pair) asrc_priv->pair[pair->index] = NULL; kfree(pair); -- cgit v1.2.3 From 4e13eb722153a5ad66edd80bc26c3028d96a7b93 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 4 Aug 2014 12:19:49 +0800 Subject: ASoC: fsl_asrc: Don't access members of config before checking it sound/soc/fsl/fsl_asrc.c:250 fsl_asrc_config_pair() warn: variable dereferenced before check 'config' (see line 243) git remote add next git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git git remote update next git checkout 3117bb3109dc223e186302f5dc8ce9ed04adca90 vim +/config +250 sound/soc/fsl/fsl_asrc.c 237 */ 238 static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) 239 { 240 struct asrc_config *config = pair->config; 241 struct fsl_asrc *asrc_priv = pair->asrc_priv; 242 enum asrc_pair_index index = pair->index; @243 u32 inrate = config->input_sample_rate, indiv; 244 u32 outrate = config->output_sample_rate, outdiv; 245 bool ideal = config->inclk == INCLK_NONE; 246 u32 clk_index[2], div[2]; 247 int in, out, channels; 248 struct clk *clk; 249 @250 if (!config) { 251 pair_err("invalid pair config\n"); 252 return -EINVAL; 253 } Reported-by: Dan Carpenter Signed-off-by: Nicolin Chen Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 424b23c38d9a..822110420b71 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -240,12 +240,11 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) struct asrc_config *config = pair->config; struct fsl_asrc *asrc_priv = pair->asrc_priv; enum asrc_pair_index index = pair->index; - u32 inrate = config->input_sample_rate, indiv; - u32 outrate = config->output_sample_rate, outdiv; - bool ideal = config->inclk == INCLK_NONE; + u32 inrate, outrate, indiv, outdiv; u32 clk_index[2], div[2]; int in, out, channels; struct clk *clk; + bool ideal; if (!config) { pair_err("invalid pair config\n"); @@ -264,6 +263,10 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair) return -EINVAL; } + inrate = config->input_sample_rate; + outrate = config->output_sample_rate; + ideal = config->inclk == INCLK_NONE; + /* Validate input and output sample rates */ for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++) if (inrate == supported_input_rate[in]) -- cgit v1.2.3 From e5f89768e9bc1f441d18e2299518a2907e5017c9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 1 Aug 2014 17:55:55 +0100 Subject: ASoC: imx-audmux: Use uintptr_t for port numbers Since we pass the port number through file private data for debugfs we cast it to and from a pointer so use uintptr_t in order to ensure that the types are compatible, avoiding warnings on 64 bit platforms where pointers are 64 bit and unsigned integers 32 bit. Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audmux.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 267717aa96c1..46f9beb6b273 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -67,7 +67,7 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf, { ssize_t ret; char *buf; - int port = (int)file->private_data; + uintptr_t port = (uintptr_t)file->private_data; u32 pdcr, ptcr; if (audmux_clk) { @@ -147,7 +147,7 @@ static const struct file_operations audmux_debugfs_fops = { static void audmux_debugfs_init(void) { - int i; + uintptr_t i; char buf[20]; audmux_debugfs_root = debugfs_create_dir("audmux", NULL); @@ -157,10 +157,10 @@ static void audmux_debugfs_init(void) } for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) { - snprintf(buf, sizeof(buf), "ssi%d", i); + snprintf(buf, sizeof(buf), "ssi%lu", i); if (!debugfs_create_file(buf, 0444, audmux_debugfs_root, (void *)i, &audmux_debugfs_fops)) - pr_warning("Failed to create AUDMUX port %d debugfs file\n", + pr_warning("Failed to create AUDMUX port %lu debugfs file\n", i); } } -- cgit v1.2.3