summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_xcvr.c
diff options
context:
space:
mode:
authorViorel Suman <viorel.suman@nxp.com>2020-04-16 14:43:32 +0300
committerViorel Suman <viorel.suman@nxp.com>2020-04-17 15:49:23 +0300
commitb164260078426fece986dc4020b8a69d9a2f6023 (patch)
tree1e852e680bb444dddcf330821f02665f13a46b46 /sound/soc/fsl/fsl_xcvr.c
parent7882f7f1391b2d6dcc9fca9a8248e3944b7ed008 (diff)
MLK-23567-2 ASoC: fsl_xcvr: enable SPDIF TX
Enable XCVR SPDIF TX mode. Signed-off-by: Viorel Suman <viorel.suman@nxp.com> Reviewed-by: Shengjiu Wang <shengjiu.wang@nxp.com>
Diffstat (limited to 'sound/soc/fsl/fsl_xcvr.c')
-rw-r--r--sound/soc/fsl/fsl_xcvr.c320
1 files changed, 183 insertions, 137 deletions
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index 21ab428a2c7f..72b4d6b23b78 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -11,6 +11,8 @@
#include <linux/regmap.h>
#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_iec958.h>
+#include <sound/pcm_params.h>
#include "fsl_xcvr.h"
#include "imx-pcm.h"
@@ -36,6 +38,16 @@ struct fsl_xcvr {
u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
};
+static const struct fsl_xcvr_pll_conf {
+ u8 mfi; /* min=0x18, max=0x38 */
+ u32 mfn; /* signed int, 2's compl., min=0x3FFF0000, max=0x00010000 */
+ u32 mfd; /* unsigned int */
+ u32 fout; /* Fout = Fref*(MFI + MFN/MFD), Fref is 24MHz */
+} fsl_xcvr_pll_cfg[] = {
+ { .mfi = 32, .mfn = 96, .mfd = 125, .fout = 786432000, },
+ { .mfi = 30, .mfn = 66, .mfd = 625, .fout = 722534400, },
+};
+
static const u32 fsl_xcvr_earc_channels[] = { 1, 2, 8, 16, 32, }; /* one bit 6, 12 ? */
static const struct snd_pcm_hw_constraint_list fsl_xcvr_earc_channels_constr = {
.count = ARRAY_SIZE(fsl_xcvr_earc_channels),
@@ -65,37 +77,91 @@ static const char * const fsl_xcvr_arc_mode[] = {
static const struct soc_enum fsl_xcvr_arc_mode_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fsl_xcvr_arc_mode), fsl_xcvr_arc_mode);
-/*
- * pll_phy: pll 0; phy 1;
-*/
-static int fsl_xcvr_phy_write(struct fsl_xcvr *xcvr, int reg, int data, int pll_phy)
+/** phy: true => phy, false => pll */
+static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy)
{
- int val;
- int idx0, idx1;
+ u32 val, idx, tidx;
- idx0 = pll_phy > 0 ? 26 : 24;
- idx1 = pll_phy > 0 ? 24 : 26;
+ idx = BIT(phy ? 26 : 24);
+ tidx = BIT(phy ? 27 : 25);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_WDATA, data);
- regmap_read(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, &val);
-
- val = ((~(val & BIT(idx0))) & BIT(idx0)) | (val & BIT(idx1)) | reg | BIT(15);
-
- regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val);
+ regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx);
do {
regmap_read(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, &val);
- } while ((val & BIT(idx0)) != ((val & BIT(idx0 + 1)) >> 1));
+ } while ((val & idx) != ((val & tidx) >> 1));
return 0;
}
+static int fsl_xcvr_en_phy(struct fsl_xcvr *xcvr, bool earc, u32 freq)
+{
+ struct device *dev = &xcvr->pdev->dev;
+ u32 i, div = 0, log2;
+
+ for (i = 0; i < ARRAY_SIZE(fsl_xcvr_pll_cfg); i++) {
+ if (fsl_xcvr_pll_cfg[i].fout % freq == 0) {
+ div = fsl_xcvr_pll_cfg[i].fout / freq;
+ break;
+ }
+ }
+
+ if (!div || i >= ARRAY_SIZE(fsl_xcvr_pll_cfg))
+ return -EINVAL;
+
+ log2 = ilog2(div);
+
+ /* PLL: CTRL0: DIV_INTEGER */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0);
+ /* PLL: NUMERATOR: MFN */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0);
+ /* PLL: DENOMINATOR: MFD */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0);
+ /* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
+ FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0);
+ udelay(25);
+ /* PLL: CTRL0: Clear Hold Ring Off */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR,
+ FSL_XCVR_PLL_CTRL0_HROFF, 0);
+ udelay(100);
+ /* PLL: POSTDIV: PDIV0 */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, log2, 0);
+ /* PLL: CTRL_SET: CLKMUX0_EN */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
+ FSL_XCVR_PLL_CTRL0_CM0_EN, 0);
+
+ if (earc) { /* eARC mode */
+ /* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
+ FSL_XCVR_PHY_CTRL_TSDIFF_OE |
+ FSL_XCVR_PHY_CTRL_PHY_EN, 1);
+ /* PHY: CTRL2_SET: EARC_TX_MODE */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL2_SET,
+ FSL_XCVR_PHY_CTRL2_EARC_TXMS, 1);
+
+ } else { /* SPDIF mode */
+ /* PHY: CTRL_SET: SPDIF_EN */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
+ FSL_XCVR_PHY_CTRL_SPDIF_EN, 1);
+ }
+
+ dev_dbg(dev, "PLL Fexp: %u, Fout: %u, mfi: %u, mfn: %u, mfd: %d, div: %u, pdiv0: %u\n",
+ freq, fsl_xcvr_pll_cfg[i].fout, fsl_xcvr_pll_cfg[i].mfi,
+ fsl_xcvr_pll_cfg[i].mfn, fsl_xcvr_pll_cfg[i].mfd, div, log2);
+ return 0;
+}
+
static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u32 m_ctl = 0, v_ctl = 0, m_isr = 0, v_isr = 0;
+ u32 m_ctl = 0, v_ctl = 0, m_isr = 0, v_isr = 0, val;
+ u32 mode = xcvr->mode & FSL_XCVR_AMODE_MASK;
int ret = 0;
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
@@ -106,7 +172,7 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
}
/* configure EXT_CTRL */
- switch (xcvr->mode & FSL_XCVR_AMODE_MASK) {
+ switch (mode) {
case FSL_XCVR_AMODE_SPDIF:
/* set SPDIF MODE */
m_ctl |= FSL_XCVR_EXT_CTRL_SPDIF_MODE;
@@ -146,6 +212,24 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
return ret;
}
+ if (tx && mode == FSL_XCVR_AMODE_SPDIF) {
+ val = FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM;
+ val |= FSL_XCVR_TX_DPTH_CTRL_FRM_FMT;
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET, val);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set TX_DPTH: %d\n", ret);
+ return ret;
+ }
+ } else {
+ /* Release M0+ reset */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
+ if (ret < 0) {
+ dev_err(dai->dev, "M0+ core release failed: %d\n", ret);
+ return ret;
+ }
+ }
+
return 0;
}
@@ -202,45 +286,6 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
xcvr->streams |= BIT(substream->stream);
- switch (xcvr->mode & FSL_XCVR_AMODE_MASK) {
- case FSL_XCVR_AMODE_SPDIF:
- if (tx) {
- fsl_xcvr_phy_write(xcvr, 0x54, 0x1, 0);
- fsl_xcvr_phy_write(xcvr, 0x4, 0x1, 1);
- fsl_xcvr_phy_write(xcvr, 0x0, 0x28, 0);
- fsl_xcvr_phy_write(xcvr, 0x20, 0x60, 0);
- fsl_xcvr_phy_write(xcvr, 0x30, 0x64, 0);
- fsl_xcvr_phy_write(xcvr, 0x4, 0x1006000, 0);
- udelay(25);
- fsl_xcvr_phy_write(xcvr, 0x8, 0x2000, 0);
- udelay(100);
- fsl_xcvr_phy_write(xcvr, 0x40, 0x5, 0);
-
- fsl_xcvr_phy_write(xcvr, 0x4, 0x20, 1);
- fsl_xcvr_phy_write(xcvr, 0x74, 0x4000, 1);
- }
- break;
- case FSL_XCVR_AMODE_EARC:
- if (tx) {
- fsl_xcvr_phy_write(xcvr, 0x54, 0x1, 0);
- fsl_xcvr_phy_write(xcvr, 0x4, 0x1, 1);
- fsl_xcvr_phy_write(xcvr, 0x0, 0x28, 0);
- fsl_xcvr_phy_write(xcvr, 0x20, 0x60, 0);
- fsl_xcvr_phy_write(xcvr, 0x30, 0x64, 0);
- fsl_xcvr_phy_write(xcvr, 0x4, 0x1006000, 0);
- udelay(25);
- fsl_xcvr_phy_write(xcvr, 0x8, 0x2000, 0);
- udelay(100);
- fsl_xcvr_phy_write(xcvr, 0x40, 0x1, 0);
-
- fsl_xcvr_phy_write(xcvr, 0x4, 0x20, 1);
- fsl_xcvr_phy_write(xcvr, 0x74, 0x4000, 1);
- }
- break;
- default:
- break;
- }
-
return 0;
}
@@ -289,53 +334,65 @@ static int fsl_xcvr_hw_params(struct snd_pcm_substream *substream,
{
struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u32 mask, val, r = params_rate(params), ch = params_channels(params);
- int ret;
+ u32 r = params_rate(params), ch = params_channels(params);
+ snd_pcm_format_t f = params_format(params);
+ u32 reg, val, bclk = 32 * r * ch;
+ u32 mode = xcvr->mode & FSL_XCVR_AMODE_MASK;
+ int i, ret;
- if (tx) {
- mask = FSL_XCVR_CS_DATA_0_FS_MASK;
- mask |= FSL_XCVR_CS_DATA_0_CH_MASK;
-
- switch (r) {
- case 32000: val = FSL_XCVR_CS_DATA_0_FS_32000; break;
- case 44100: val = FSL_XCVR_CS_DATA_0_FS_44100; break;
- case 48000: val = FSL_XCVR_CS_DATA_0_FS_48000; break;
- case 64000: val = FSL_XCVR_CS_DATA_0_FS_64000; break;
- case 88200: val = FSL_XCVR_CS_DATA_0_FS_88200; break;
- case 96000: val = FSL_XCVR_CS_DATA_0_FS_96000; break;
- case 176400: val = FSL_XCVR_CS_DATA_0_FS_176400; break;
- case 192000: val = FSL_XCVR_CS_DATA_0_FS_192000; break;
- default:
- dev_err(dai->dev, "Unsupported rate: %u\n", r);
- return -EINVAL;
- }
- val |= FSL_XCVR_CS_DATA_0_CH_UMLPCM;
+ if (!tx)
+ return 0;
+
+ ret = snd_pcm_create_iec958_consumer_hw_params(params,
+ xcvr->tx_iec958.status, ARRAY_SIZE(xcvr->tx_iec958.status));
+ if (ret < 0) {
+ dev_err(dai->dev, "Creating IEC958 CS failed %d\n", ret);
+ return ret;
+ }
- /* Update TX CS data bits 0 */
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_CS_DATA_0,
- mask, val);
+ for (i = 0; i < 6; i++) {
+ val = (xcvr->tx_iec958.status[4 * i + 0] << 24) |
+ (xcvr->tx_iec958.status[4 * i + 1] << 16) |
+ (xcvr->tx_iec958.status[4 * i + 2] << 8) |
+ (xcvr->tx_iec958.status[4 * i + 3] << 0);
+ /* Update TX CS data bits */
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_TX_CS_DATA_0 + 4 * i,
+ val);
if (ret < 0) {
- dev_err(dai->dev, "Failed to set TX CS 0: %d\n", ret);
+ dev_err(dai->dev, "Failed to set TXCS%d: %d\n", i, ret);
return ret;
}
+ }
- switch (ch) {
- case 2: val = FSL_XCVR_CS_DATA_1_CH_2; break;
- case 8: val = FSL_XCVR_CS_DATA_1_CH_8; break;
- case 16: val = FSL_XCVR_CS_DATA_1_CH_16; break;
- case 32: val = FSL_XCVR_CS_DATA_1_CH_32; break;
- default:
- dev_err(dai->dev, "Unsupported channels: %u\n", ch);
- return -EINVAL;
- }
+ switch (mode) {
+ case FSL_XCVR_AMODE_EARC:
+ bclk *= 10;
+ break;
+ case FSL_XCVR_AMODE_SPDIF:
+ bclk *= 2;
+ break;
+ }
- /* Update TX CS data bits 1 */
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_CS_DATA_1,
- FSL_XCVR_CS_DATA_1_CH_MASK, val);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to set TX CS 1: %d\n", ret);
- return ret;
- }
+ ret = fsl_xcvr_en_phy(xcvr, (mode == FSL_XCVR_AMODE_EARC), bclk);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to set TX freq %u: %d\n", bclk, ret);
+ return ret;
+ }
+
+ /* enable/disable IEC958 encoding as function of input format */
+ val = FSL_XCVR_TX_DPTH_CTRL_CS_MOD;
+ val |= FSL_XCVR_TX_DPTH_CTRL_EN_PREAMBLE;
+ val |= FSL_XCVR_TX_DPTH_CTRL_EN_PARITY;
+
+ if (f == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
+ reg = FSL_XCVR_TX_DPTH_CTRL_CLR; /* disable IEC958 encoding */
+ else
+ reg = FSL_XCVR_TX_DPTH_CTRL_SET; /* enable IEC958 encoding */
+
+ ret = regmap_write(xcvr->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to en/dis IEC958 encode: %d\n", ret);
+ return ret;
}
return 0;
@@ -356,10 +413,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
switch (xcvr->mode & FSL_XCVR_AMODE_MASK) {
case FSL_XCVR_AMODE_EARC:
/* set isr_cmdc_tx_en, w1c */
- ret = regmap_update_bits(xcvr->regmap,
- FSL_XCVR_ISR_SET,
- FSL_XCVR_ISR_CMDC_TX_EN,
- FSL_XCVR_ISR_CMDC_TX_EN);
+ ret = regmap_write(xcvr->regmap,
+ FSL_XCVR_ISR_SET,
+ FSL_XCVR_ISR_CMDC_TX_EN);
if (ret < 0) {
dev_err(dai->dev,
"err updating isr %d\n", ret);
@@ -368,9 +424,8 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
/* fall through */
case FSL_XCVR_AMODE_SPDIF:
- ret = regmap_update_bits(xcvr->regmap,
+ ret = regmap_write(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL_SET,
- FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to set TX_DPTH_CTRL_STRT_DATA_TX: %d\n", ret);
@@ -378,26 +433,6 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
}
break;
}
- } else {
- /* Clear RX FIFO */
- ret = regmap_update_bits(xcvr->regmap,
- FSL_XCVR_RX_DPTH_CTRL_SET,
- FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO,
- FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to clear RX FIFO: %d\n", ret);
- return ret;
- }
-
- /* Flip RX FIFO bits */
- ret = regmap_update_bits(xcvr->regmap,
- FSL_XCVR_RX_DPTH_CTRL_SET,
- FSL_XCVR_RX_DPTH_CTRL_STORE_FMT,
- FSL_XCVR_RX_DPTH_CTRL_STORE_FMT);
- if (ret < 0) {
- dev_err(dai->dev, "Failed to set reverse bit in RX FIFO: %d\n", ret);
- return ret;
- }
}
/* enable DMA RD/WR */
@@ -423,9 +458,8 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
if (tx) {
switch (xcvr->mode & FSL_XCVR_AMODE_MASK) {
case FSL_XCVR_AMODE_SPDIF:
- ret = regmap_update_bits(xcvr->regmap,
+ ret = regmap_write(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL_CLR,
- FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to clr TX_DPTH_CTRL_STRT_DATA_TX: %d\n", ret);
@@ -434,10 +468,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
/* fall through ...*/
case FSL_XCVR_AMODE_EARC:
/* clear ISR_CMDC_TX_EN, W1C */
- ret = regmap_update_bits(xcvr->regmap,
- FSL_XCVR_ISR_CLR,
- FSL_XCVR_ISR_CMDC_TX_EN,
- FSL_XCVR_ISR_CMDC_TX_EN);
+ ret = regmap_write(xcvr->regmap,
+ FSL_XCVR_ISR_CLR,
+ FSL_XCVR_ISR_CMDC_TX_EN);
if (ret < 0) {
dev_err(dai->dev,
"Err updating ISR %d\n", ret);
@@ -521,9 +554,29 @@ err_firmware:
return ret;
}
+ /* Flip RX FIFO bits */
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET,
+ FSL_XCVR_RX_DPTH_CTRL_STORE_FMT);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set reverse bit in RX FIFO: %d\n", ret);
+ return ret;
+ }
+
/* Store Capabilities Data Structure into Data RAM */
memcpy_toio(xcvr->ram_addr + FSL_XCVR_CAP_DATA_STR, xcvr->cap_ds,
FSL_XCVR_CAPDS_SIZE);
+
+ /* Release AI interface from reset */
+ ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
+ FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
+ if (ret < 0) {
+ dev_err(dev, "Error while setting IER0: %d\n", ret);
+ return ret;
+ }
+
+ /* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */
+ fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET,
+ FSL_XCVR_PLL_BANDGAP_EN_VBG, 0);
return 0;
}
@@ -716,7 +769,8 @@ static const struct snd_soc_pcm_stream playback = {
.rate_min = 32000,
.rate_max = 1536000,
.rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_XCVR_FORMATS,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
};
static const struct snd_soc_pcm_stream capture = {
@@ -726,7 +780,7 @@ static const struct snd_soc_pcm_stream capture = {
.rate_min = 32000,
.rate_max = 1536000,
.rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_XCVR_FORMATS,
+ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
};
static struct snd_soc_dai_driver fsl_xcvr_dai = {
@@ -1198,14 +1252,6 @@ static int fsl_xcvr_runtime_resume(struct device *dev)
return ret;
}
- /* Release M0+ reset */
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
- FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
- if (ret < 0) {
- dev_err(dev, "Failed to release M0+ core: %d\n", ret);
- return ret;
- }
-
return 0;
}
#endif /* CONFIG_PM*/