From 28564486866fa889b78264360022c94836fa8072 Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Tue, 13 Oct 2020 15:17:32 +0300 Subject: ASoC: fsl_xcvr: Add XCVR ASoC CPU DAI driver XCVR (Audio Transceiver) is a on-chip functional module found on i.MX8MP. It support HDMI2.1 eARC, HDMI1.4 ARC and SPDIF. Signed-off-by: Viorel Suman Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/20201013121733.83684-2-viorel.suman@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 10 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/fsl_xcvr.c | 1359 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_xcvr.h | 266 +++++++++ 4 files changed, 1637 insertions(+) create mode 100644 sound/soc/fsl/fsl_xcvr.c create mode 100644 sound/soc/fsl/fsl_xcvr.h (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3f76ff71ea47..d04b64d32dc1 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -95,6 +95,16 @@ config SND_SOC_FSL_EASRC destination sample rate. It is a new design module compare with the old ASRC. +config SND_SOC_FSL_XCVR + tristate "NXP Audio Transceiver (XCVR) 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 Audio Transceiver (XCVR) support for NXP + iMX CPUs. XCVR is a digital module that supports HDMI2.1 eARC, + HDMI1.4 ARC and SPDIF. + config SND_SOC_FSL_UTILS tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index b835eebf8825..1d2231f9cc47 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -25,6 +25,7 @@ snd-soc-fsl-utils-objs := fsl_utils.o snd-soc-fsl-dma-objs := fsl_dma.o snd-soc-fsl-mqs-objs := fsl_mqs.o snd-soc-fsl-easrc-objs := fsl_easrc.o +snd-soc-fsl-xcvr-objs := fsl_xcvr.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o @@ -38,6 +39,7 @@ obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o +obj-$(CONFIG_SND_SOC_FSL_XCVR) += snd-soc-fsl-xcvr.o # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c new file mode 100644 index 000000000000..c055179e6d11 --- /dev/null +++ b/sound/soc/fsl/fsl_xcvr.c @@ -0,0 +1,1359 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2019 NXP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_xcvr.h" +#include "imx-pcm.h" + +#define FSL_XCVR_CAPDS_SIZE 256 + +struct fsl_xcvr_soc_data { + const char *fw_name; +}; + +struct fsl_xcvr { + const struct fsl_xcvr_soc_data *soc_data; + struct platform_device *pdev; + struct regmap *regmap; + struct clk *ipg_clk; + struct clk *pll_ipg_clk; + struct clk *phy_clk; + struct clk *spba_clk; + struct reset_control *reset; + u8 streams; + u32 mode; + u32 arc_mode; + void __iomem *ram_addr; + struct snd_dmaengine_dai_dma_data dma_prms_rx; + struct snd_dmaengine_dai_dma_data dma_prms_tx; + struct snd_aes_iec958 rx_iec958; + struct snd_aes_iec958 tx_iec958; + 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 = 54, .mfn = 1, .mfd = 6, .fout = 1300000000, }, /* 1.3 GHz */ + { .mfi = 32, .mfn = 96, .mfd = 125, .fout = 786432000, }, /* 8000 Hz */ + { .mfi = 30, .mfn = 66, .mfd = 625, .fout = 722534400, }, /* 11025 Hz */ + { .mfi = 29, .mfn = 1, .mfd = 6, .fout = 700000000, }, /* 700 MHz */ +}; + +/* + * HDMI2.1 spec defines 6- and 12-channels layout for one bit audio + * stream. Todo: to check how this case can be considered below + */ +static const u32 fsl_xcvr_earc_channels[] = { 1, 2, 8, 16, 32, }; +static const struct snd_pcm_hw_constraint_list fsl_xcvr_earc_channels_constr = { + .count = ARRAY_SIZE(fsl_xcvr_earc_channels), + .list = fsl_xcvr_earc_channels, +}; + +static const u32 fsl_xcvr_earc_rates[] = { + 32000, 44100, 48000, 64000, 88200, 96000, + 128000, 176400, 192000, 256000, 352800, 384000, + 512000, 705600, 768000, 1024000, 1411200, 1536000, +}; +static const struct snd_pcm_hw_constraint_list fsl_xcvr_earc_rates_constr = { + .count = ARRAY_SIZE(fsl_xcvr_earc_rates), + .list = fsl_xcvr_earc_rates, +}; + +static const u32 fsl_xcvr_spdif_channels[] = { 2, }; +static const struct snd_pcm_hw_constraint_list fsl_xcvr_spdif_channels_constr = { + .count = ARRAY_SIZE(fsl_xcvr_spdif_channels), + .list = fsl_xcvr_spdif_channels, +}; + +static const u32 fsl_xcvr_spdif_rates[] = { + 32000, 44100, 48000, 88200, 96000, 176400, 192000, +}; +static const struct snd_pcm_hw_constraint_list fsl_xcvr_spdif_rates_constr = { + .count = ARRAY_SIZE(fsl_xcvr_spdif_rates), + .list = fsl_xcvr_spdif_rates, +}; + +static int fsl_xcvr_arc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + + xcvr->arc_mode = snd_soc_enum_item_to_val(e, item[0]); + + return 0; +} + +static int fsl_xcvr_arc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + ucontrol->value.enumerated.item[0] = xcvr->arc_mode; + + return 0; +} + +static const u32 fsl_xcvr_phy_arc_cfg[] = { + FSL_XCVR_PHY_CTRL_ARC_MODE_SE_EN, FSL_XCVR_PHY_CTRL_ARC_MODE_CM_EN, +}; + +static const char * const fsl_xcvr_arc_mode[] = { "Single Ended", "Common", }; +static const struct soc_enum fsl_xcvr_arc_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fsl_xcvr_arc_mode), fsl_xcvr_arc_mode); +static struct snd_kcontrol_new fsl_xcvr_arc_mode_kctl = + SOC_ENUM_EXT("ARC Mode", fsl_xcvr_arc_mode_enum, + fsl_xcvr_arc_mode_get, fsl_xcvr_arc_mode_put); + +/* Capabilities data structure, bytes */ +static int fsl_xcvr_type_capds_bytes_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = FSL_XCVR_CAPDS_SIZE; + + return 0; +} + +static int fsl_xcvr_capds_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + memcpy(ucontrol->value.bytes.data, xcvr->cap_ds, FSL_XCVR_CAPDS_SIZE); + + return 0; +} + +static int fsl_xcvr_capds_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + memcpy(xcvr->cap_ds, ucontrol->value.bytes.data, FSL_XCVR_CAPDS_SIZE); + + return 0; +} + +static struct snd_kcontrol_new fsl_xcvr_earc_capds_kctl = { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Capabilities Data Structure", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = fsl_xcvr_type_capds_bytes_info, + .get = fsl_xcvr_capds_get, + .put = fsl_xcvr_capds_put, +}; + +static int fsl_xcvr_activate_ctl(struct snd_soc_dai *dai, const char *name, + bool active) +{ + struct snd_soc_card *card = dai->component->card; + struct snd_kcontrol *kctl; + bool enabled; + + kctl = snd_soc_card_get_kcontrol(card, name); + if (kctl == NULL) + return -ENOENT; + + enabled = ((kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_WRITE) != 0); + if (active == enabled) + return 0; /* nothing to do */ + + if (active) + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_WRITE; + else + kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE; + + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id); + + return 1; +} + +static int fsl_xcvr_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + struct snd_soc_card *card = dai->component->card; + struct snd_soc_pcm_runtime *rtd; + + xcvr->mode = snd_soc_enum_item_to_val(e, item[0]); + + fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_ARC)); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_EARC)); + /* Allow playback for SPDIF only */ + rtd = snd_soc_get_pcm_runtime(card, card->dai_link); + rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count = + (xcvr->mode == FSL_XCVR_MODE_SPDIF ? 1 : 0); + return 0; +} + +static int fsl_xcvr_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + ucontrol->value.enumerated.item[0] = xcvr->mode; + + return 0; +} + +static const char * const fsl_xcvr_mode[] = { "SPDIF", "ARC RX", "eARC", }; +static const struct soc_enum fsl_xcvr_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fsl_xcvr_mode), fsl_xcvr_mode); +static struct snd_kcontrol_new fsl_xcvr_mode_kctl = + SOC_ENUM_EXT("XCVR Mode", fsl_xcvr_mode_enum, + fsl_xcvr_mode_get, fsl_xcvr_mode_put); + +/** phy: true => phy, false => pll */ +static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy) +{ + struct device *dev = &xcvr->pdev->dev; + u32 val, idx, tidx; + int ret; + + 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_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx); + + ret = regmap_read_poll_timeout(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val, + (val & idx) != ((val & tidx) >> 1), + 10, 10000); + if (ret) + dev_err(dev, "AI timeout: failed to set %s reg 0x%02x=0x%08x\n", + phy ? "PHY" : "PLL", reg, data); + return ret; +} + +static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx) +{ + struct device *dev = &xcvr->pdev->dev; + u32 i, div = 0, log2; + int ret; + + 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); + + /* 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); + + /* 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); + if (tx) { /* TX is enabled for SPDIF only */ + /* PLL: POSTDIV: PDIV0 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 0), 0); + /* PLL: CTRL_SET: CLKMUX0_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM0_EN, 0); + } else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */ + /* PLL: POSTDIV: PDIV1 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 1), 0); + /* PLL: CTRL_SET: CLKMUX1_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM1_EN, 0); + } else { /* SPDIF / ARC RX */ + /* PLL: POSTDIV: PDIV2 */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV, + FSL_XCVR_PLL_PDIVx(log2, 2), 0); + /* PLL: CTRL_SET: CLKMUX2_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET, + FSL_XCVR_PLL_CTRL0_CM2_EN, 0); + } + + if (xcvr->mode == FSL_XCVR_MODE_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 if (!tx) { /* SPDIF / ARC RX mode */ + if (xcvr->mode == FSL_XCVR_MODE_SPDIF) + /* PHY: CTRL_SET: SPDIF_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, + FSL_XCVR_PHY_CTRL_SPDIF_EN, 1); + else /* PHY: CTRL_SET: ARC RX setup */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, + FSL_XCVR_PHY_CTRL_PHY_EN | + FSL_XCVR_PHY_CTRL_RX_CM_EN | + fsl_xcvr_phy_arc_cfg[xcvr->arc_mode], 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_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq) +{ + struct device *dev = &xcvr->pdev->dev; + int ret; + + clk_disable_unprepare(xcvr->phy_clk); + ret = clk_set_rate(xcvr->phy_clk, freq); + if (ret < 0) { + dev_err(dev, "Error while setting AUD PLL rate: %d\n", ret); + return ret; + } + ret = clk_prepare_enable(xcvr->phy_clk); + if (ret) { + dev_err(dev, "failed to start PHY clock: %d\n", ret); + return ret; + } + + /* 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; + } + + if (xcvr->mode == FSL_XCVR_MODE_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: TX_CLK_AUD_SS | SPDIF_EN */ + fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET, + FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS | + FSL_XCVR_PHY_CTRL_SPDIF_EN, 1); + } + + dev_dbg(dev, "PLL Fexp: %u\n", freq); + + return 0; +} + +#define FSL_XCVR_SPDIF_RX_FREQ 175000000 +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; + u32 r = substream->runtime->rate, ch = substream->runtime->channels; + u32 fout = 32 * r * ch * 10 * 2; + int ret = 0; + + switch (xcvr->mode) { + case FSL_XCVR_MODE_SPDIF: + case FSL_XCVR_MODE_ARC: + if (tx) { + ret = fsl_xcvr_en_aud_pll(xcvr, fout); + if (ret < 0) { + dev_err(dai->dev, "Failed to set TX freq %u: %d\n", + fout, ret); + return ret; + } + + ret = regmap_write(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET, + FSL_XCVR_TX_DPTH_CTRL_FRM_FMT); + if (ret < 0) { + dev_err(dai->dev, "Failed to set TX_DPTH: %d\n", ret); + return ret; + } + + /** + * set SPDIF MODE - this flag is used to gate + * SPDIF output, useless for SPDIF RX + */ + m_ctl |= FSL_XCVR_EXT_CTRL_SPDIF_MODE; + v_ctl |= FSL_XCVR_EXT_CTRL_SPDIF_MODE; + } else { + /** + * Clear RX FIFO, flip RX FIFO bits, + * disable eARC related HW mode detects + */ + ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET, + FSL_XCVR_RX_DPTH_CTRL_STORE_FMT | + FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO | + FSL_XCVR_RX_DPTH_CTRL_COMP | + FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL); + if (ret < 0) { + dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret); + return ret; + } + + ret = fsl_xcvr_en_phy_pll(xcvr, FSL_XCVR_SPDIF_RX_FREQ, tx); + if (ret < 0) { + dev_err(dai->dev, "Failed to set RX freq %u: %d\n", + FSL_XCVR_SPDIF_RX_FREQ, ret); + return ret; + } + } + break; + case FSL_XCVR_MODE_EARC: + if (!tx) { + /** Clear RX FIFO, flip RX FIFO bits */ + ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET, + FSL_XCVR_RX_DPTH_CTRL_STORE_FMT | + FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO); + if (ret < 0) { + dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret); + return ret; + } + + /** Enable eARC related HW mode detects */ + ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_CLR, + FSL_XCVR_RX_DPTH_CTRL_COMP | + FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL); + if (ret < 0) { + dev_err(dai->dev, "Failed to clr TX_DPTH: %d\n", ret); + return ret; + } + } + + /* clear CMDC RESET */ + m_ctl |= FSL_XCVR_EXT_CTRL_CMDC_RESET(tx); + /* set TX_RX_MODE */ + m_ctl |= FSL_XCVR_EXT_CTRL_TX_RX_MODE; + v_ctl |= (tx ? FSL_XCVR_EXT_CTRL_TX_RX_MODE : 0); + break; + } + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, FSL_XCVR_IRQ_EARC_ALL); + if (ret < 0) { + dev_err(dai->dev, "Error while setting IER0: %d\n", ret); + return ret; + } + + /* clear DPATH RESET */ + m_ctl |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx); + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, m_ctl, v_ctl); + if (ret < 0) { + dev_err(dai->dev, "Error while setting EXT_CTRL: %d\n", ret); + return ret; + } + + return 0; +} + +static int fsl_xcvr_constr(const struct snd_pcm_substream *substream, + const struct snd_pcm_hw_constraint_list *channels, + const struct snd_pcm_hw_constraint_list *rates) +{ + struct snd_pcm_runtime *rt = substream->runtime; + int ret; + + ret = snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + channels); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, + rates); + if (ret < 0) + return ret; + + return 0; +} + +static int fsl_xcvr_startup(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; + int ret = 0; + + if (xcvr->streams & BIT(substream->stream)) { + dev_err(dai->dev, "%sX busy\n", tx ? "T" : "R"); + return -EBUSY; + } + + switch (xcvr->mode) { + case FSL_XCVR_MODE_SPDIF: + case FSL_XCVR_MODE_ARC: + ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr, + &fsl_xcvr_spdif_rates_constr); + break; + case FSL_XCVR_MODE_EARC: + ret = fsl_xcvr_constr(substream, &fsl_xcvr_earc_channels_constr, + &fsl_xcvr_earc_rates_constr); + break; + } + if (ret < 0) + return ret; + + xcvr->streams |= BIT(substream->stream); + + /* Disable XCVR controls if there is stream started */ + fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, false); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, false); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, false); + + return 0; +} + +static void fsl_xcvr_shutdown(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 mask = 0, val = 0; + int ret; + + xcvr->streams &= ~BIT(substream->stream); + + /* Enable XCVR controls if there is no stream started */ + if (!xcvr->streams) { + fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, true); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_ARC)); + fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, + (xcvr->mode == FSL_XCVR_MODE_EARC)); + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0, + FSL_XCVR_IRQ_EARC_ALL, 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to set IER0: %d\n", ret); + return; + } + + /* clear SPDIF MODE */ + if (xcvr->mode == FSL_XCVR_MODE_SPDIF) + mask |= FSL_XCVR_EXT_CTRL_SPDIF_MODE; + } + + if (xcvr->mode == FSL_XCVR_MODE_EARC) { + /* set CMDC RESET */ + mask |= FSL_XCVR_EXT_CTRL_CMDC_RESET(tx); + val |= FSL_XCVR_EXT_CTRL_CMDC_RESET(tx); + } + + /* set DPATH RESET */ + mask |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx); + val |= FSL_XCVR_EXT_CTRL_DPTH_RESET(tx); + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, mask, val); + if (ret < 0) { + dev_err(dai->dev, "Err setting DPATH RESET: %d\n", ret); + return; + } +} + +static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (tx) { + switch (xcvr->mode) { + case FSL_XCVR_MODE_EARC: + /* set isr_cmdc_tx_en, w1c */ + 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); + return ret; + } + fallthrough; + case FSL_XCVR_MODE_SPDIF: + ret = regmap_write(xcvr->regmap, + FSL_XCVR_TX_DPTH_CTRL_SET, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + if (ret < 0) { + dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret); + return ret; + } + break; + } + } + + /* enable DMA RD/WR */ + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_DMA_DIS(tx), 0); + if (ret < 0) { + dev_err(dai->dev, "Failed to enable DMA: %d\n", ret); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + /* disable DMA RD/WR */ + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_DMA_DIS(tx), + FSL_XCVR_EXT_CTRL_DMA_DIS(tx)); + if (ret < 0) { + dev_err(dai->dev, "Failed to disable DMA: %d\n", ret); + return ret; + } + + if (tx) { + switch (xcvr->mode) { + case FSL_XCVR_MODE_SPDIF: + ret = regmap_write(xcvr->regmap, + FSL_XCVR_TX_DPTH_CTRL_CLR, + FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX); + if (ret < 0) { + dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret); + return ret; + } + fallthrough; + case FSL_XCVR_MODE_EARC: + /* clear ISR_CMDC_TX_EN, W1C */ + 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); + return ret; + } + break; + } + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) +{ + struct device *dev = &xcvr->pdev->dev; + const struct firmware *fw; + int ret = 0, rem, off, out, page = 0, size = FSL_XCVR_REG_OFFSET; + u32 mask, val; + + ret = request_firmware(&fw, xcvr->soc_data->fw_name, dev); + if (ret) { + dev_err(dev, "failed to request firmware.\n"); + return ret; + } + + rem = fw->size; + + /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ + if (rem > 16384) { + dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); + return -ENOMEM; + } + + for (page = 0; page < 10; page++) { + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_PAGE_MASK, + FSL_XCVR_EXT_CTRL_PAGE(page)); + if (ret < 0) { + dev_err(dev, "FW: failed to set page %d, err=%d\n", + page, ret); + goto err_firmware; + } + + off = page * size; + out = min(rem, size); + /* IPG clock is assumed to be running, otherwise it will hang */ + if (out > 0) { + /* write firmware into code memory */ + memcpy_toio(xcvr->ram_addr, fw->data + off, out); + rem -= out; + if (rem == 0) { + /* last part of firmware written */ + /* clean remaining part of code memory page */ + memset_io(xcvr->ram_addr + out, 0, size - out); + } + } else { + /* clean current page, including data memory */ + memset_io(xcvr->ram_addr, 0, size); + } + }; + +err_firmware: + release_firmware(fw); + if (ret < 0) + return ret; + + /* configure watermarks */ + mask = FSL_XCVR_EXT_CTRL_RX_FWM_MASK | FSL_XCVR_EXT_CTRL_TX_FWM_MASK; + val = FSL_XCVR_EXT_CTRL_RX_FWM(FSL_XCVR_FIFO_WMK_RX); + val |= FSL_XCVR_EXT_CTRL_TX_FWM(FSL_XCVR_FIFO_WMK_TX); + /* disable DMA RD/WR */ + mask |= FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS; + val |= FSL_XCVR_EXT_CTRL_DMA_RD_DIS | FSL_XCVR_EXT_CTRL_DMA_WR_DIS; + /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */ + mask |= FSL_XCVR_EXT_CTRL_PAGE_MASK; + val |= FSL_XCVR_EXT_CTRL_PAGE(8); + + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, mask, val); + if (ret < 0) { + dev_err(dev, "Failed to set watermarks: %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); + return 0; +} + +static int fsl_xcvr_type_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int fsl_xcvr_type_iec958_bytes_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof_field(struct snd_aes_iec958, status); + + return 0; +} + +static int fsl_xcvr_rx_cs_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + memcpy(ucontrol->value.iec958.status, xcvr->rx_iec958.status, 24); + + return 0; +} + +static int fsl_xcvr_tx_cs_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + memcpy(ucontrol->value.iec958.status, xcvr->tx_iec958.status, 24); + + return 0; +} + +static int fsl_xcvr_tx_cs_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + memcpy(xcvr->tx_iec958.status, ucontrol->value.iec958.status, 24); + + return 0; +} + +static struct snd_kcontrol_new fsl_xcvr_rx_ctls[] = { + /* Channel status controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = fsl_xcvr_type_iec958_info, + .get = fsl_xcvr_rx_cs_get, + }, + /* Capture channel status, bytes */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Capture Channel Status", + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = fsl_xcvr_type_iec958_bytes_info, + .get = fsl_xcvr_rx_cs_get, + }, +}; + +static struct snd_kcontrol_new fsl_xcvr_tx_ctls[] = { + /* Channel status controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = fsl_xcvr_type_iec958_info, + .get = fsl_xcvr_tx_cs_get, + .put = fsl_xcvr_tx_cs_put, + }, + /* Playback channel status, bytes */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Playback Channel Status", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = fsl_xcvr_type_iec958_bytes_info, + .get = fsl_xcvr_tx_cs_get, + .put = fsl_xcvr_tx_cs_put, + }, +}; + +static struct snd_soc_dai_ops fsl_xcvr_dai_ops = { + .prepare = fsl_xcvr_prepare, + .startup = fsl_xcvr_startup, + .shutdown = fsl_xcvr_shutdown, + .trigger = fsl_xcvr_trigger, +}; + +static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai) +{ + struct fsl_xcvr *xcvr = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &xcvr->dma_prms_tx, &xcvr->dma_prms_rx); + snd_soc_dai_set_drvdata(dai, xcvr); + + snd_soc_add_dai_controls(dai, &fsl_xcvr_mode_kctl, 1); + snd_soc_add_dai_controls(dai, &fsl_xcvr_arc_mode_kctl, 1); + snd_soc_add_dai_controls(dai, &fsl_xcvr_earc_capds_kctl, 1); + snd_soc_add_dai_controls(dai, fsl_xcvr_tx_ctls, + ARRAY_SIZE(fsl_xcvr_tx_ctls)); + snd_soc_add_dai_controls(dai, fsl_xcvr_rx_ctls, + ARRAY_SIZE(fsl_xcvr_rx_ctls)); + return 0; +} + +static struct snd_soc_dai_driver fsl_xcvr_dai = { + .probe = fsl_xcvr_dai_probe, + .ops = &fsl_xcvr_dai_ops, + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 32, + .rate_min = 32000, + .rate_max = 1536000, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, + .capture = { + .stream_name = "CPU-Capture", + .channels_min = 1, + .channels_max = 32, + .rate_min = 32000, + .rate_max = 1536000, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, + }, +}; + +static const struct snd_soc_component_driver fsl_xcvr_comp = { + .name = "fsl-xcvr-dai", +}; + +static const struct reg_default fsl_xcvr_reg_defaults[] = { + { FSL_XCVR_VERSION, 0x00000000 }, + { FSL_XCVR_EXT_CTRL, 0xF8204040 }, + { FSL_XCVR_EXT_STATUS, 0x00000000 }, + { FSL_XCVR_EXT_IER0, 0x00000000 }, + { FSL_XCVR_EXT_IER1, 0x00000000 }, + { FSL_XCVR_EXT_ISR, 0x00000000 }, + { FSL_XCVR_EXT_ISR_SET, 0x00000000 }, + { FSL_XCVR_EXT_ISR_CLR, 0x00000000 }, + { FSL_XCVR_EXT_ISR_TOG, 0x00000000 }, + { FSL_XCVR_IER, 0x00000000 }, + { FSL_XCVR_ISR, 0x00000000 }, + { FSL_XCVR_ISR_SET, 0x00000000 }, + { FSL_XCVR_ISR_CLR, 0x00000000 }, + { FSL_XCVR_ISR_TOG, 0x00000000 }, + { FSL_XCVR_RX_DPTH_CTRL, 0x00002C89 }, + { FSL_XCVR_RX_DPTH_CTRL_SET, 0x00002C89 }, + { FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00002C89 }, + { FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00002C89 }, + { FSL_XCVR_TX_DPTH_CTRL, 0x00000000 }, + { FSL_XCVR_TX_DPTH_CTRL_SET, 0x00000000 }, + { FSL_XCVR_TX_DPTH_CTRL_CLR, 0x00000000 }, + { FSL_XCVR_TX_DPTH_CTRL_TOG, 0x00000000 }, + { FSL_XCVR_TX_CS_DATA_0, 0x00000000 }, + { FSL_XCVR_TX_CS_DATA_1, 0x00000000 }, + { FSL_XCVR_TX_CS_DATA_2, 0x00000000 }, + { FSL_XCVR_TX_CS_DATA_3, 0x00000000 }, + { FSL_XCVR_TX_CS_DATA_4, 0x00000000 }, + { FSL_XCVR_TX_CS_DATA_5, 0x00000000 }, + { FSL_XCVR_DEBUG_REG_0, 0x00000000 }, + { FSL_XCVR_DEBUG_REG_1, 0x00000000 }, +}; + +static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_XCVR_VERSION: + case FSL_XCVR_EXT_CTRL: + case FSL_XCVR_EXT_STATUS: + case FSL_XCVR_EXT_IER0: + case FSL_XCVR_EXT_IER1: + case FSL_XCVR_EXT_ISR: + case FSL_XCVR_EXT_ISR_SET: + case FSL_XCVR_EXT_ISR_CLR: + case FSL_XCVR_EXT_ISR_TOG: + case FSL_XCVR_IER: + case FSL_XCVR_ISR: + case FSL_XCVR_ISR_SET: + case FSL_XCVR_ISR_CLR: + case FSL_XCVR_ISR_TOG: + case FSL_XCVR_PHY_AI_CTRL: + case FSL_XCVR_PHY_AI_CTRL_SET: + case FSL_XCVR_PHY_AI_CTRL_CLR: + case FSL_XCVR_PHY_AI_CTRL_TOG: + case FSL_XCVR_PHY_AI_RDATA: + case FSL_XCVR_CLK_CTRL: + case FSL_XCVR_RX_DPTH_CTRL: + case FSL_XCVR_RX_DPTH_CTRL_SET: + case FSL_XCVR_RX_DPTH_CTRL_CLR: + case FSL_XCVR_RX_DPTH_CTRL_TOG: + case FSL_XCVR_TX_DPTH_CTRL: + case FSL_XCVR_TX_DPTH_CTRL_SET: + case FSL_XCVR_TX_DPTH_CTRL_CLR: + case FSL_XCVR_TX_DPTH_CTRL_TOG: + case FSL_XCVR_TX_CS_DATA_0: + case FSL_XCVR_TX_CS_DATA_1: + case FSL_XCVR_TX_CS_DATA_2: + case FSL_XCVR_TX_CS_DATA_3: + case FSL_XCVR_TX_CS_DATA_4: + case FSL_XCVR_TX_CS_DATA_5: + case FSL_XCVR_DEBUG_REG_0: + case FSL_XCVR_DEBUG_REG_1: + return true; + default: + return false; + } +} + +static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_XCVR_EXT_CTRL: + case FSL_XCVR_EXT_IER0: + case FSL_XCVR_EXT_IER1: + case FSL_XCVR_EXT_ISR: + case FSL_XCVR_EXT_ISR_SET: + case FSL_XCVR_EXT_ISR_CLR: + case FSL_XCVR_EXT_ISR_TOG: + case FSL_XCVR_IER: + case FSL_XCVR_ISR_SET: + case FSL_XCVR_ISR_CLR: + case FSL_XCVR_ISR_TOG: + case FSL_XCVR_PHY_AI_CTRL: + case FSL_XCVR_PHY_AI_CTRL_SET: + case FSL_XCVR_PHY_AI_CTRL_CLR: + case FSL_XCVR_PHY_AI_CTRL_TOG: + case FSL_XCVR_PHY_AI_WDATA: + case FSL_XCVR_CLK_CTRL: + case FSL_XCVR_RX_DPTH_CTRL: + case FSL_XCVR_RX_DPTH_CTRL_SET: + case FSL_XCVR_RX_DPTH_CTRL_CLR: + case FSL_XCVR_RX_DPTH_CTRL_TOG: + case FSL_XCVR_TX_DPTH_CTRL_SET: + case FSL_XCVR_TX_DPTH_CTRL_CLR: + case FSL_XCVR_TX_DPTH_CTRL_TOG: + case FSL_XCVR_TX_CS_DATA_0: + case FSL_XCVR_TX_CS_DATA_1: + case FSL_XCVR_TX_CS_DATA_2: + case FSL_XCVR_TX_CS_DATA_3: + case FSL_XCVR_TX_CS_DATA_4: + case FSL_XCVR_TX_CS_DATA_5: + return true; + default: + return false; + } +} + +static bool fsl_xcvr_volatile_reg(struct device *dev, unsigned int reg) +{ + return fsl_xcvr_readable_reg(dev, reg); +} + +static const struct regmap_config fsl_xcvr_regmap_cfg = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = FSL_XCVR_MAX_REG, + .reg_defaults = fsl_xcvr_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_xcvr_reg_defaults), + .readable_reg = fsl_xcvr_readable_reg, + .volatile_reg = fsl_xcvr_volatile_reg, + .writeable_reg = fsl_xcvr_writeable_reg, + .cache_type = REGCACHE_FLAT, +}; + +static irqreturn_t irq0_isr(int irq, void *devid) +{ + struct fsl_xcvr *xcvr = (struct fsl_xcvr *)devid; + struct device *dev = &xcvr->pdev->dev; + struct regmap *regmap = xcvr->regmap; + void __iomem *reg_ctrl, *reg_buff; + u32 isr, isr_clr = 0, val, i; + + regmap_read(regmap, FSL_XCVR_EXT_ISR, &isr); + + if (isr & FSL_XCVR_IRQ_NEW_CS) { + dev_dbg(dev, "Received new CS block\n"); + isr_clr |= FSL_XCVR_IRQ_NEW_CS; + /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */ + regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_PAGE_MASK, + FSL_XCVR_EXT_CTRL_PAGE(8)); + + /* Find updated CS buffer */ + reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_0; + reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_0; + memcpy_fromio(&val, reg_ctrl, sizeof(val)); + if (!val) { + reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_1; + reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_1; + memcpy_fromio(&val, reg_ctrl, sizeof(val)); + } + + if (val) { + /* copy CS buffer */ + memcpy_fromio(&xcvr->rx_iec958.status, reg_buff, + sizeof(xcvr->rx_iec958.status)); + for (i = 0; i < 6; i++) { + val = *(u32 *)(xcvr->rx_iec958.status + i*4); + *(u32 *)(xcvr->rx_iec958.status + i*4) = + bitrev32(val); + } + /* clear CS control register */ + memset_io(reg_ctrl, 0, sizeof(val)); + } + } + if (isr & FSL_XCVR_IRQ_NEW_UD) { + dev_dbg(dev, "Received new UD block\n"); + isr_clr |= FSL_XCVR_IRQ_NEW_UD; + } + if (isr & FSL_XCVR_IRQ_MUTE) { + dev_dbg(dev, "HW mute bit detected\n"); + isr_clr |= FSL_XCVR_IRQ_MUTE; + } + if (isr & FSL_XCVR_IRQ_FIFO_UOFL_ERR) { + dev_dbg(dev, "RX/TX FIFO full/empty\n"); + isr_clr |= FSL_XCVR_IRQ_FIFO_UOFL_ERR; + } + if (isr & FSL_XCVR_IRQ_ARC_MODE) { + dev_dbg(dev, "CMDC SM falls out of eARC mode\n"); + isr_clr |= FSL_XCVR_IRQ_ARC_MODE; + } + if (isr & FSL_XCVR_IRQ_DMA_RD_REQ) { + dev_dbg(dev, "DMA read request\n"); + isr_clr |= FSL_XCVR_IRQ_DMA_RD_REQ; + } + if (isr & FSL_XCVR_IRQ_DMA_WR_REQ) { + dev_dbg(dev, "DMA write request\n"); + isr_clr |= FSL_XCVR_IRQ_DMA_WR_REQ; + } + + if (isr_clr) { + regmap_write(regmap, FSL_XCVR_EXT_ISR_CLR, isr_clr); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = { + .fw_name = "imx/xcvr/xcvr-imx8mp.bin", +}; + +static const struct of_device_id fsl_xcvr_dt_ids[] = { + { .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids); + +static int fsl_xcvr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *of_id; + struct fsl_xcvr *xcvr; + struct resource *ram_res, *regs_res, *rx_res, *tx_res; + void __iomem *regs; + int ret, irq; + + of_id = of_match_device(fsl_xcvr_dt_ids, dev); + if (!of_id) + return -EINVAL; + + xcvr = devm_kzalloc(dev, sizeof(*xcvr), GFP_KERNEL); + if (!xcvr) + return -ENOMEM; + + xcvr->pdev = pdev; + xcvr->soc_data = of_device_get_match_data(&pdev->dev); + + xcvr->ipg_clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(xcvr->ipg_clk)) { + dev_err(dev, "failed to get ipg clock\n"); + return PTR_ERR(xcvr->ipg_clk); + } + + xcvr->phy_clk = devm_clk_get(dev, "phy"); + if (IS_ERR(xcvr->phy_clk)) { + dev_err(dev, "failed to get phy clock\n"); + return PTR_ERR(xcvr->phy_clk); + } + + xcvr->spba_clk = devm_clk_get(dev, "spba"); + if (IS_ERR(xcvr->spba_clk)) { + dev_err(dev, "failed to get spba clock\n"); + return PTR_ERR(xcvr->spba_clk); + } + + xcvr->pll_ipg_clk = devm_clk_get(dev, "pll_ipg"); + if (IS_ERR(xcvr->pll_ipg_clk)) { + dev_err(dev, "failed to get pll_ipg clock\n"); + return PTR_ERR(xcvr->pll_ipg_clk); + } + + ram_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ram"); + xcvr->ram_addr = devm_ioremap_resource(dev, ram_res); + if (IS_ERR(xcvr->ram_addr)) + return PTR_ERR(xcvr->ram_addr); + + regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + regs = devm_ioremap_resource(dev, regs_res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + xcvr->regmap = devm_regmap_init_mmio_clk(dev, NULL, regs, + &fsl_xcvr_regmap_cfg); + if (IS_ERR(xcvr->regmap)) { + dev_err(dev, "failed to init XCVR regmap: %ld\n", + PTR_ERR(xcvr->regmap)); + return PTR_ERR(xcvr->regmap); + } + + xcvr->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(xcvr->reset)) { + dev_err(dev, "failed to get XCVR reset control\n"); + return PTR_ERR(xcvr->reset); + } + + /* get IRQs */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no irq[0]: %d\n", irq); + return irq; + } + + ret = devm_request_irq(dev, irq, irq0_isr, 0, pdev->name, xcvr); + if (ret) { + dev_err(dev, "failed to claim IRQ0: %i\n", ret); + return ret; + } + + rx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rxfifo"); + tx_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txfifo"); + xcvr->dma_prms_rx.chan_name = "rx"; + xcvr->dma_prms_tx.chan_name = "tx"; + xcvr->dma_prms_rx.addr = rx_res->start; + xcvr->dma_prms_tx.addr = tx_res->start; + xcvr->dma_prms_rx.maxburst = FSL_XCVR_MAXBURST_RX; + xcvr->dma_prms_tx.maxburst = FSL_XCVR_MAXBURST_TX; + + platform_set_drvdata(pdev, xcvr); + pm_runtime_enable(dev); + regcache_cache_only(xcvr->regmap, true); + + ret = devm_snd_soc_register_component(dev, &fsl_xcvr_comp, + &fsl_xcvr_dai, 1); + if (ret) { + dev_err(dev, "failed to register component %s\n", + fsl_xcvr_comp.name); + return ret; + } + + ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); + if (ret) + dev_err(dev, "failed to pcm register\n"); + + return ret; +} + +static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev) +{ + struct fsl_xcvr *xcvr = dev_get_drvdata(dev); + int ret; + + /* Assert M0+ reset */ + ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL, + FSL_XCVR_EXT_CTRL_CORE_RESET, + FSL_XCVR_EXT_CTRL_CORE_RESET); + if (ret < 0) + dev_err(dev, "Failed to assert M0+ core: %d\n", ret); + + ret = reset_control_assert(xcvr->reset); + if (ret < 0) + dev_err(dev, "Failed to assert M0+ reset: %d\n", ret); + + regcache_cache_only(xcvr->regmap, true); + + clk_disable_unprepare(xcvr->spba_clk); + clk_disable_unprepare(xcvr->phy_clk); + clk_disable_unprepare(xcvr->pll_ipg_clk); + clk_disable_unprepare(xcvr->ipg_clk); + + return 0; +} + +static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev) +{ + struct fsl_xcvr *xcvr = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(xcvr->ipg_clk); + if (ret) { + dev_err(dev, "failed to start IPG clock.\n"); + return ret; + } + + ret = clk_prepare_enable(xcvr->pll_ipg_clk); + if (ret) { + dev_err(dev, "failed to start PLL IPG clock.\n"); + goto stop_ipg_clk; + } + + ret = clk_prepare_enable(xcvr->phy_clk); + if (ret) { + dev_err(dev, "failed to start PHY clock: %d\n", ret); + goto stop_pll_ipg_clk; + } + + ret = clk_prepare_enable(xcvr->spba_clk); + if (ret) { + dev_err(dev, "failed to start SPBA clock.\n"); + goto stop_phy_clk; + } + + regcache_cache_only(xcvr->regmap, false); + regcache_mark_dirty(xcvr->regmap); + ret = regcache_sync(xcvr->regmap); + + if (ret) { + dev_err(dev, "failed to sync regcache.\n"); + goto stop_spba_clk; + } + + ret = reset_control_deassert(xcvr->reset); + if (ret) { + dev_err(dev, "failed to deassert M0+ reset.\n"); + goto stop_spba_clk; + } + + ret = fsl_xcvr_load_firmware(xcvr); + if (ret) { + dev_err(dev, "failed to load firmware.\n"); + goto stop_spba_clk; + } + + /* 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, "M0+ core release failed: %d\n", ret); + goto stop_spba_clk; + } + + /* Let M0+ core complete firmware initialization */ + msleep(50); + + return 0; + +stop_spba_clk: + clk_disable_unprepare(xcvr->spba_clk); +stop_phy_clk: + clk_disable_unprepare(xcvr->phy_clk); +stop_pll_ipg_clk: + clk_disable_unprepare(xcvr->pll_ipg_clk); +stop_ipg_clk: + clk_disable_unprepare(xcvr->ipg_clk); + + return ret; +} + +static const struct dev_pm_ops fsl_xcvr_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_xcvr_runtime_suspend, + fsl_xcvr_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver fsl_xcvr_driver = { + .probe = fsl_xcvr_probe, + .driver = { + .name = "fsl,imx8mp-audio-xcvr", + .pm = &fsl_xcvr_pm_ops, + .of_match_table = fsl_xcvr_dt_ids, + }, +}; +module_platform_driver(fsl_xcvr_driver); + +MODULE_AUTHOR("Viorel Suman "); +MODULE_DESCRIPTION("NXP Audio Transceiver (XCVR) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h new file mode 100644 index 000000000000..7f2853c60085 --- /dev/null +++ b/sound/soc/fsl/fsl_xcvr.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * NXP XCVR ALSA SoC Digital Audio Interface (DAI) driver + * + * Copyright 2019 NXP + */ + +#ifndef __FSL_XCVR_H +#define __FSL_XCVR_H + +#define FSL_XCVR_MODE_SPDIF 0 +#define FSL_XCVR_MODE_ARC 1 +#define FSL_XCVR_MODE_EARC 2 + +/* XCVR Registers */ +#define FSL_XCVR_REG_OFFSET 0x800 /* regs offset */ +#define FSL_XCVR_FIFO_SIZE 0x80 /* 128 */ +#define FSL_XCVR_FIFO_WMK_RX (FSL_XCVR_FIFO_SIZE >> 1) /* 64 */ +#define FSL_XCVR_FIFO_WMK_TX (FSL_XCVR_FIFO_SIZE >> 1) /* 64 */ +#define FSL_XCVR_MAXBURST_RX (FSL_XCVR_FIFO_WMK_RX >> 2) /* 16 */ +#define FSL_XCVR_MAXBURST_TX (FSL_XCVR_FIFO_WMK_TX >> 2) /* 16 */ + +#define FSL_XCVR_RX_FIFO_ADDR 0x0C00 +#define FSL_XCVR_TX_FIFO_ADDR 0x0E00 + +#define FSL_XCVR_VERSION 0x00 /* Version */ +#define FSL_XCVR_EXT_CTRL 0x10 /* Control */ +#define FSL_XCVR_EXT_STATUS 0x20 /* Status */ +#define FSL_XCVR_EXT_IER0 0x30 /* Interrupt en 0 */ +#define FSL_XCVR_EXT_IER1 0x40 /* Interrupt en 1 */ +#define FSL_XCVR_EXT_ISR 0x50 /* Interrupt status */ +#define FSL_XCVR_EXT_ISR_SET 0x54 /* Interrupt status */ +#define FSL_XCVR_EXT_ISR_CLR 0x58 /* Interrupt status */ +#define FSL_XCVR_EXT_ISR_TOG 0x5C /* Interrupt status */ +#define FSL_XCVR_IER 0x70 /* Interrupt en for M0+ */ +#define FSL_XCVR_ISR 0x80 /* Interrupt status */ +#define FSL_XCVR_ISR_SET 0x84 /* Interrupt status set */ +#define FSL_XCVR_ISR_CLR 0x88 /* Interrupt status clear */ +#define FSL_XCVR_ISR_TOG 0x8C /* Interrupt status toggle */ +#define FSL_XCVR_PHY_AI_CTRL 0x90 +#define FSL_XCVR_PHY_AI_CTRL_SET 0x94 +#define FSL_XCVR_PHY_AI_CTRL_CLR 0x98 +#define FSL_XCVR_PHY_AI_CTRL_TOG 0x9C +#define FSL_XCVR_PHY_AI_WDATA 0xA0 +#define FSL_XCVR_PHY_AI_RDATA 0xA4 +#define FSL_XCVR_CLK_CTRL 0xB0 +#define FSL_XCVR_RX_DPTH_CTRL 0x180 /* RX datapath ctrl reg */ +#define FSL_XCVR_RX_DPTH_CTRL_SET 0x184 +#define FSL_XCVR_RX_DPTH_CTRL_CLR 0x188 +#define FSL_XCVR_RX_DPTH_CTRL_TOG 0x18c + +#define FSL_XCVR_TX_DPTH_CTRL 0x220 /* TX datapath ctrl reg */ +#define FSL_XCVR_TX_DPTH_CTRL_SET 0x224 +#define FSL_XCVR_TX_DPTH_CTRL_CLR 0x228 +#define FSL_XCVR_TX_DPTH_CTRL_TOG 0x22C +#define FSL_XCVR_TX_CS_DATA_0 0x230 /* TX channel status bits regs */ +#define FSL_XCVR_TX_CS_DATA_1 0x234 +#define FSL_XCVR_TX_CS_DATA_2 0x238 +#define FSL_XCVR_TX_CS_DATA_3 0x23C +#define FSL_XCVR_TX_CS_DATA_4 0x240 +#define FSL_XCVR_TX_CS_DATA_5 0x244 +#define FSL_XCVR_DEBUG_REG_0 0x2E0 +#define FSL_XCVR_DEBUG_REG_1 0x2F0 + +#define FSL_XCVR_MAX_REG FSL_XCVR_DEBUG_REG_1 + +#define FSL_XCVR_EXT_CTRL_CORE_RESET BIT(31) + +#define FSL_XCVR_EXT_CTRL_RX_CMDC_RESET BIT(30) +#define FSL_XCVR_EXT_CTRL_TX_CMDC_RESET BIT(29) +#define FSL_XCVR_EXT_CTRL_CMDC_RESET(t) (t ? BIT(29) : BIT(30)) + +#define FSL_XCVR_EXT_CTRL_RX_DPTH_RESET BIT(28) +#define FSL_XCVR_EXT_CTRL_TX_DPTH_RESET BIT(27) +#define FSL_XCVR_EXT_CTRL_DPTH_RESET(t) (t ? BIT(27) : BIT(28)) + +#define FSL_XCVR_EXT_CTRL_TX_RX_MODE BIT(26) +#define FSL_XCVR_EXT_CTRL_DMA_RD_DIS BIT(25) +#define FSL_XCVR_EXT_CTRL_DMA_WR_DIS BIT(24) +#define FSL_XCVR_EXT_CTRL_DMA_DIS(t) (t ? BIT(24) : BIT(25)) +#define FSL_XCVR_EXT_CTRL_SPDIF_MODE BIT(23) +#define FSL_XCVR_EXT_CTRL_SLEEP_MODE BIT(21) + +#define FSL_XCVR_EXT_CTRL_TX_FWM_SHFT 0 +#define FSL_XCVR_EXT_CTRL_TX_FWM_MASK GENMASK(6, 0) +#define FSL_XCVR_EXT_CTRL_TX_FWM(i) (((i) << FSL_XCVR_EXT_CTRL_TX_FWM_SHFT) \ + & FSL_XCVR_EXT_CTRL_TX_FWM_MASK) +#define FSL_XCVR_EXT_CTRL_RX_FWM_SHFT 8 +#define FSL_XCVR_EXT_CTRL_RX_FWM_MASK GENMASK(14, 8) +#define FSL_XCVR_EXT_CTRL_RX_FWM(i) (((i) << FSL_XCVR_EXT_CTRL_RX_FWM_SHFT) \ + & FSL_XCVR_EXT_CTRL_RX_FWM_MASK) +#define FSL_XCVR_EXT_CTRL_PAGE_SHFT 16 +#define FSL_XCVR_EXT_CTRL_PAGE_MASK GENMASK(19, 16) +#define FSL_XCVR_EXT_CTRL_PAGE(i) (((i) << FSL_XCVR_EXT_CTRL_PAGE_SHFT) \ + & FSL_XCVR_EXT_CTRL_PAGE_MASK) + +#define FSL_XCVR_EXT_STUS_NT_FIFO_ENTR GENMASK(7, 0) +#define FSL_XCVR_EXT_STUS_NR_FIFO_ENTR GENMASK(15, 8) +#define FSL_XCVR_EXT_STUS_CM0_SLEEPING BIT(16) +#define FSL_XCVR_EXT_STUS_CM0_DEEP_SLP BIT(17) +#define FSL_XCVR_EXT_STUS_CM0_SLP_HACK BIT(18) +#define FSL_XCVR_EXT_STUS_RX_CMDC_RSTO BIT(23) +#define FSL_XCVR_EXT_STUS_TX_CMDC_RSTO BIT(24) +#define FSL_XCVR_EXT_STUS_RX_CMDC_COTO BIT(25) +#define FSL_XCVR_EXT_STUS_TX_CMDC_COTO BIT(26) +#define FSL_XCVR_EXT_STUS_HB_STATUS BIT(27) +#define FSL_XCVR_EXT_STUS_NEW_UD4_REC BIT(28) +#define FSL_XCVR_EXT_STUS_NEW_UD5_REC BIT(29) +#define FSL_XCVR_EXT_STUS_NEW_UD6_REC BIT(30) +#define FSL_XCVR_EXT_STUS_HPD_INPUT BIT(31) + +#define FSL_XCVR_IRQ_NEW_CS BIT(0) +#define FSL_XCVR_IRQ_NEW_UD BIT(1) +#define FSL_XCVR_IRQ_MUTE BIT(2) +#define FSL_XCVR_IRQ_CMDC_RESP_TO BIT(3) +#define FSL_XCVR_IRQ_ECC_ERR BIT(4) +#define FSL_XCVR_IRQ_PREAMBLE_MISMATCH BIT(5) +#define FSL_XCVR_IRQ_FIFO_UOFL_ERR BIT(6) +#define FSL_XCVR_IRQ_HOST_WAKEUP BIT(7) +#define FSL_XCVR_IRQ_HOST_OHPD BIT(8) +#define FSL_XCVR_IRQ_DMAC_NO_DATA_REC BIT(9) +#define FSL_XCVR_IRQ_DMAC_FMT_CHG_DET BIT(10) +#define FSL_XCVR_IRQ_HB_STATE_CHG BIT(11) +#define FSL_XCVR_IRQ_CMDC_STATUS_UPD BIT(12) +#define FSL_XCVR_IRQ_TEMP_UPD BIT(13) +#define FSL_XCVR_IRQ_DMA_RD_REQ BIT(14) +#define FSL_XCVR_IRQ_DMA_WR_REQ BIT(15) +#define FSL_XCVR_IRQ_DMAC_BME_BIT_ERR BIT(16) +#define FSL_XCVR_IRQ_PREAMBLE_MATCH BIT(17) +#define FSL_XCVR_IRQ_M_W_PRE_MISMATCH BIT(18) +#define FSL_XCVR_IRQ_B_PRE_MISMATCH BIT(19) +#define FSL_XCVR_IRQ_UNEXP_PRE_REC BIT(20) +#define FSL_XCVR_IRQ_ARC_MODE BIT(21) +#define FSL_XCVR_IRQ_CH_UD_OFLOW BIT(22) +#define FSL_XCVR_IRQ_EARC_ALL (FSL_XCVR_IRQ_NEW_CS | \ + FSL_XCVR_IRQ_NEW_UD | \ + FSL_XCVR_IRQ_MUTE | \ + FSL_XCVR_IRQ_FIFO_UOFL_ERR | \ + FSL_XCVR_IRQ_HOST_WAKEUP | \ + FSL_XCVR_IRQ_ARC_MODE) + +#define FSL_XCVR_ISR_CMDC_TX_EN BIT(3) +#define FSL_XCVR_ISR_HPD_TGL BIT(15) +#define FSL_XCVR_ISR_DMAC_SPARE_INT BIT(19) +#define FSL_XCVR_ISR_SET_SPDIF_RX_INT BIT(20) +#define FSL_XCVR_ISR_SET_SPDIF_TX_INT BIT(21) +#define FSL_XCVR_ISR_SET_SPDIF_MODE(t) (t ? BIT(21) : BIT(20)) +#define FSL_XCVR_ISR_SET_ARC_CM_INT BIT(22) +#define FSL_XCVR_ISR_SET_ARC_SE_INT BIT(23) + +#define FSL_XCVR_PHY_AI_ADDR_MASK GENMASK(7, 0) +#define FSL_XCVR_PHY_AI_RESETN BIT(15) +#define FSL_XCVR_PHY_AI_TOG_PLL BIT(24) +#define FSL_XCVR_PHY_AI_TOG_DONE_PLL BIT(25) +#define FSL_XCVR_PHY_AI_TOG_PHY BIT(26) +#define FSL_XCVR_PHY_AI_TOG_DONE_PHY BIT(27) +#define FSL_XCVR_PHY_AI_RW_MASK BIT(31) + +#define FSL_XCVR_RX_DPTH_CTRL_PAPB_FIFO_STATUS BIT(0) +#define FSL_XCVR_RX_DPTH_CTRL_DIS_PRE_ERR_CHK BIT(1) +#define FSL_XCVR_RX_DPTH_CTRL_DIS_NOD_REC_CHK BIT(2) +#define FSL_XCVR_RX_DPTH_CTRL_ECC_VUC_BIT_CHK BIT(3) +#define FSL_XCVR_RX_DPTH_CTRL_EN_CMP_PAR_CALC BIT(4) +#define FSL_XCVR_RX_DPTH_CTRL_RST_PKT_CNT_FIFO BIT(5) +#define FSL_XCVR_RX_DPTH_CTRL_STORE_FMT BIT(6) +#define FSL_XCVR_RX_DPTH_CTRL_EN_PAR_CALC BIT(7) +#define FSL_XCVR_RX_DPTH_CTRL_UDR BIT(8) +#define FSL_XCVR_RX_DPTH_CTRL_CSR BIT(9) +#define FSL_XCVR_RX_DPTH_CTRL_UDA BIT(10) +#define FSL_XCVR_RX_DPTH_CTRL_CSA BIT(11) +#define FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO BIT(12) +#define FSL_XCVR_RX_DPTH_CTRL_DIS_B_PRE_ERR_CHK BIT(13) +#define FSL_XCVR_RX_DPTH_CTRL_PABS BIT(19) +#define FSL_XCVR_RX_DPTH_CTRL_DTS_CDS BIT(20) +#define FSL_XCVR_RX_DPTH_CTRL_BLKC BIT(21) +#define FSL_XCVR_RX_DPTH_CTRL_MUTE_CTRL BIT(22) +#define FSL_XCVR_RX_DPTH_CTRL_MUTE_MODE BIT(23) +#define FSL_XCVR_RX_DPTH_CTRL_FMT_CHG_CTRL BIT(24) +#define FSL_XCVR_RX_DPTH_CTRL_FMT_CHG_MODE BIT(25) +#define FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL BIT(26) +#define FSL_XCVR_RX_DPTH_CTRL_LAYB_MODE BIT(27) +#define FSL_XCVR_RX_DPTH_CTRL_PRC BIT(28) +#define FSL_XCVR_RX_DPTH_CTRL_COMP BIT(29) +#define FSL_XCVR_RX_DPTH_CTRL_FSM GENMASK(31, 30) + +#define FSL_XCVR_TX_DPTH_CTRL_CS_ACK BIT(0) +#define FSL_XCVR_TX_DPTH_CTRL_UD_ACK BIT(1) +#define FSL_XCVR_TX_DPTH_CTRL_CS_MOD BIT(2) +#define FSL_XCVR_TX_DPTH_CTRL_UD_MOD BIT(3) +#define FSL_XCVR_TX_DPTH_CTRL_VLD_MOD BIT(4) +#define FSL_XCVR_TX_DPTH_CTRL_FRM_VLD BIT(5) +#define FSL_XCVR_TX_DPTH_CTRL_EN_PARITY BIT(6) +#define FSL_XCVR_TX_DPTH_CTRL_EN_PREAMBLE BIT(7) +#define FSL_XCVR_TX_DPTH_CTRL_EN_ECC_INTER BIT(8) +#define FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM BIT(10) +#define FSL_XCVR_TX_DPTH_CTRL_FRM_FMT BIT(11) +#define FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX BIT(14) +#define FSL_XCVR_TX_DPTH_CTRL_ADD_CYC_TX_OE_STR BIT(15) +#define FSL_XCVR_TX_DPTH_CTRL_ADD_CYC_TX_OE_END BIT(16) +#define FSL_XCVR_TX_DPTH_CTRL_CLK_RATIO BIT(29) +#define FSL_XCVR_TX_DPTH_CTRL_TM_NO_PRE_BME GENMASK(31, 30) + +#define FSL_XCVR_PHY_AI_CTRL_AI_RESETN BIT(15) + +#define FSL_XCVR_PLL_CTRL0 0x00 +#define FSL_XCVR_PLL_CTRL0_SET 0x04 +#define FSL_XCVR_PLL_CTRL0_CLR 0x08 +#define FSL_XCVR_PLL_NUM 0x20 +#define FSL_XCVR_PLL_DEN 0x30 +#define FSL_XCVR_PLL_PDIV 0x40 +#define FSL_XCVR_PLL_BANDGAP_SET 0x54 +#define FSL_XCVR_PHY_CTRL 0x00 +#define FSL_XCVR_PHY_CTRL_SET 0x04 +#define FSL_XCVR_PHY_CTRL_CLR 0x08 +#define FSL_XCVR_PHY_CTRL2 0x70 +#define FSL_XCVR_PHY_CTRL2_SET 0x74 +#define FSL_XCVR_PHY_CTRL2_CLR 0x78 + +#define FSL_XCVR_PLL_BANDGAP_EN_VBG BIT(0) +#define FSL_XCVR_PLL_CTRL0_HROFF BIT(13) +#define FSL_XCVR_PLL_CTRL0_PWP BIT(14) +#define FSL_XCVR_PLL_CTRL0_CM0_EN BIT(24) +#define FSL_XCVR_PLL_CTRL0_CM1_EN BIT(25) +#define FSL_XCVR_PLL_CTRL0_CM2_EN BIT(26) +#define FSL_XCVR_PLL_PDIVx(v, i) ((v & 0x7) << (4 * i)) + +#define FSL_XCVR_PHY_CTRL_PHY_EN BIT(0) +#define FSL_XCVR_PHY_CTRL_RX_CM_EN BIT(1) +#define FSL_XCVR_PHY_CTRL_TSDIFF_OE BIT(5) +#define FSL_XCVR_PHY_CTRL_SPDIF_EN BIT(8) +#define FSL_XCVR_PHY_CTRL_ARC_MODE_SE_EN BIT(9) +#define FSL_XCVR_PHY_CTRL_ARC_MODE_CM_EN BIT(10) +#define FSL_XCVR_PHY_CTRL_TX_CLK_MASK GENMASK(26, 25) +#define FSL_XCVR_PHY_CTRL_TX_CLK_HDMI_SS BIT(25) +#define FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS BIT(26) +#define FSL_XCVR_PHY_CTRL2_EARC_TXMS BIT(14) + +#define FSL_XCVR_CS_DATA_0_FS_MASK GENMASK(31, 24) +#define FSL_XCVR_CS_DATA_0_FS_32000 0x3000000 +#define FSL_XCVR_CS_DATA_0_FS_44100 0x0000000 +#define FSL_XCVR_CS_DATA_0_FS_48000 0x2000000 +#define FSL_XCVR_CS_DATA_0_FS_64000 0xB000000 +#define FSL_XCVR_CS_DATA_0_FS_88200 0x8000000 +#define FSL_XCVR_CS_DATA_0_FS_96000 0xA000000 +#define FSL_XCVR_CS_DATA_0_FS_176400 0xC000000 +#define FSL_XCVR_CS_DATA_0_FS_192000 0xE000000 + +#define FSL_XCVR_CS_DATA_0_CH_MASK 0x3A +#define FSL_XCVR_CS_DATA_0_CH_U2LPCM 0x00 +#define FSL_XCVR_CS_DATA_0_CH_UMLPCM 0x20 +#define FSL_XCVR_CS_DATA_0_CH_U1BAUD 0x30 + +#define FSL_XCVR_CS_DATA_1_CH_MASK 0xF000 +#define FSL_XCVR_CS_DATA_1_CH_2 0x0000 +#define FSL_XCVR_CS_DATA_1_CH_8 0x7000 +#define FSL_XCVR_CS_DATA_1_CH_16 0xB000 +#define FSL_XCVR_CS_DATA_1_CH_32 0x3000 + +/* Data memory structures */ +#define FSL_XCVR_RX_CS_CTRL_0 0x20 /* First RX CS control register */ +#define FSL_XCVR_RX_CS_CTRL_1 0x24 /* Second RX CS control register */ +#define FSL_XCVR_RX_CS_BUFF_0 0x80 /* First RX CS buffer */ +#define FSL_XCVR_RX_CS_BUFF_1 0xA0 /* Second RX CS buffer */ +#define FSL_XCVR_CAP_DATA_STR 0x300 /* Capabilities data structure */ + +#endif /* __FSL_XCVR_H */ -- cgit v1.2.3 From 516232e3609f485be04445b03723fbaed64a5321 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 15 Oct 2020 13:28:48 +0800 Subject: ASoC: fsl_spdif: Add support for i.MX8QM platform On i.MX8QM, there are separate interrupts for TX and RX. As the EDMA can't be configured to swing back to first FIFO after writing the second FIFO, so we need to force the burst size to be 2 on i.MX8QM. And EDMA don't support to shift the data from S24_LE to S16_LE, so the supported TX format is also different on i.MX8QM. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1602739728-4433-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index b0f643fefe1e..e494ad6ec7d7 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -49,10 +49,18 @@ static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb }; * @imx: for imx platform * @shared_root_clock: flag of sharing a clock source with others; * so the driver shouldn't set root clock rate + * @interrupts: interrupt number + * @tx_burst: tx maxburst size + * @rx_burst: rx maxburst size + * @tx_formats: tx supported data format */ struct fsl_spdif_soc_data { bool imx; bool shared_root_clock; + u32 interrupts; + u32 tx_burst; + u32 rx_burst; + u64 tx_formats; }; /* @@ -128,16 +136,38 @@ struct fsl_spdif_priv { static struct fsl_spdif_soc_data fsl_spdif_vf610 = { .imx = false, .shared_root_clock = false, + .interrupts = 1, + .tx_burst = FSL_SPDIF_TXFIFO_WML, + .rx_burst = FSL_SPDIF_RXFIFO_WML, + .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK, }; static struct fsl_spdif_soc_data fsl_spdif_imx35 = { .imx = true, .shared_root_clock = false, + .interrupts = 1, + .tx_burst = FSL_SPDIF_TXFIFO_WML, + .rx_burst = FSL_SPDIF_RXFIFO_WML, + .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK, }; static struct fsl_spdif_soc_data fsl_spdif_imx6sx = { .imx = true, .shared_root_clock = true, + .interrupts = 1, + .tx_burst = FSL_SPDIF_TXFIFO_WML, + .rx_burst = FSL_SPDIF_RXFIFO_WML, + .tx_formats = FSL_SPDIF_FORMATS_PLAYBACK, + +}; + +static struct fsl_spdif_soc_data fsl_spdif_imx8qm = { + .imx = true, + .shared_root_clock = true, + .interrupts = 2, + .tx_burst = 2, /* Applied for EDMA */ + .rx_burst = 2, /* Applied for EDMA */ + .tx_formats = SNDRV_PCM_FMTBIT_S24_LE, /* Applied for EDMA */ }; /* Check if clk is a root clock that does not share clock source with others */ @@ -1273,6 +1303,8 @@ static int fsl_spdif_probe(struct platform_device *pdev) /* Initialize this copy of the CPU DAI driver structure */ memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai)); spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev); + spdif_priv->cpu_dai_drv.playback.formats = + spdif_priv->soc->tx_formats; /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1287,15 +1319,19 @@ static int fsl_spdif_probe(struct platform_device *pdev) return PTR_ERR(spdif_priv->regmap); } - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + for (i = 0; i < spdif_priv->soc->interrupts; i++) { + irq = platform_get_irq(pdev, i); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", pdev->name); + return irq; + } - ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0, - dev_name(&pdev->dev), spdif_priv); - if (ret) { - dev_err(&pdev->dev, "could not claim irq %u\n", irq); - return ret; + ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0, + dev_name(&pdev->dev), spdif_priv); + if (ret) { + dev_err(&pdev->dev, "could not claim irq %u\n", irq); + return ret; + } } /* Get system clock for rx clock rate calculation */ @@ -1344,8 +1380,8 @@ static int fsl_spdif_probe(struct platform_device *pdev) spdif_priv->dpll_locked = false; - spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML; - spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML; + spdif_priv->dma_params_tx.maxburst = spdif_priv->soc->tx_burst; + spdif_priv->dma_params_rx.maxburst = spdif_priv->soc->rx_burst; spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL; spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL; @@ -1458,6 +1494,7 @@ static const struct of_device_id fsl_spdif_dt_ids[] = { { .compatible = "fsl,imx35-spdif", .data = &fsl_spdif_imx35, }, { .compatible = "fsl,vf610-spdif", .data = &fsl_spdif_vf610, }, { .compatible = "fsl,imx6sx-spdif", .data = &fsl_spdif_imx6sx, }, + { .compatible = "fsl,imx8qm-spdif", .data = &fsl_spdif_imx8qm, }, {} }; MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids); -- cgit v1.2.3 From 1bfa3eaa4511256ab14555ab2573e6e75194b1fa Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 13 Oct 2020 10:49:20 +0800 Subject: ASoC: fsl_spdif: Add support for higher sample rates Add 88200Hz and 176400Hz sample rates support for TX. Add 88200Hz, 176400Hz, 192000Hz sample rates support for RX. Signed-off-by: Viorel Suman Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Reviewed-by: Daniel Baluta Link: https://lore.kernel.org/r/1602557360-18795-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 16 +++++++++++++--- sound/soc/fsl/fsl_spdif.h | 9 ++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index e494ad6ec7d7..5fa178f3f497 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -459,10 +459,18 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream, rate = SPDIF_TXRATE_48000; csfs = IEC958_AES3_CON_FS_48000; break; + case 88200: + rate = SPDIF_TXRATE_88200; + csfs = IEC958_AES3_CON_FS_88200; + break; case 96000: rate = SPDIF_TXRATE_96000; csfs = IEC958_AES3_CON_FS_96000; break; + case 176400: + rate = SPDIF_TXRATE_176400; + csfs = IEC958_AES3_CON_FS_176400; + break; case 192000: rate = SPDIF_TXRATE_192000; csfs = IEC958_AES3_CON_FS_192000; @@ -857,7 +865,7 @@ static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol, uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 16000; - uinfo->value.integer.max = 96000; + uinfo->value.integer.max = 192000; return 0; } @@ -1175,7 +1183,8 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv, struct clk *clk, u64 savesub, enum spdif_txrate index, bool round) { - static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + static const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400, + 192000, }; bool is_sysclk = clk_is_match(clk, spdif_priv->sysclk); u64 rate_ideal, rate_actual, sub; u32 arate; @@ -1235,7 +1244,8 @@ out: static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, enum spdif_txrate index) { - static const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 }; + static const u32 rate[] = { 32000, 44100, 48000, 88200, 96000, 176400, + 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 e6c61e07bc1a..d5f1dfd58740 100644 --- a/sound/soc/fsl/fsl_spdif.h +++ b/sound/soc/fsl/fsl_spdif.h @@ -163,7 +163,9 @@ enum spdif_txrate { SPDIF_TXRATE_32000 = 0, SPDIF_TXRATE_44100, SPDIF_TXRATE_48000, + SPDIF_TXRATE_88200, SPDIF_TXRATE_96000, + SPDIF_TXRATE_176400, SPDIF_TXRATE_192000, }; #define SPDIF_TXRATE_MAX (SPDIF_TXRATE_192000 + 1) @@ -177,15 +179,20 @@ enum spdif_txrate { #define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | \ SNDRV_PCM_RATE_192000) #define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \ SNDRV_PCM_RATE_32000 | \ SNDRV_PCM_RATE_44100 | \ SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | \ SNDRV_PCM_RATE_64000 | \ - SNDRV_PCM_RATE_96000) + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | \ + SNDRV_PCM_RATE_192000) #define FSL_SPDIF_FORMATS_PLAYBACK (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_3LE | \ -- cgit v1.2.3 From 8a24c834c053ef1b0cdefbd9c5bcb487cbc5068f Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 2 Nov 2020 09:52:27 +0800 Subject: ASoC: fsl_aud2htx: Add aud2htx module driver The AUD2HTX is a digital module that provides a bridge between the Audio Subsystem and the HDMI RTX Subsystem. This module includes intermediate storage to queue SDMA transactions prior to being synchronized and passed to the HDMI RTX Subsystem over the Audio Link. The AUD2HTX contains a DMA request routed to the SDMA module. This DMA request is controlled based on the watermark level in the 32-entry sample buffer. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1604281947-26874-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 5 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/fsl_aud2htx.c | 311 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_aud2htx.h | 67 ++++++++++ 4 files changed, 385 insertions(+) create mode 100644 sound/soc/fsl/fsl_aud2htx.c create mode 100644 sound/soc/fsl/fsl_aud2htx.h (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index d04b64d32dc1..52a562215008 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -105,6 +105,11 @@ config SND_SOC_FSL_XCVR iMX CPUs. XCVR is a digital module that supports HDMI2.1 eARC, HDMI1.4 ARC and SPDIF. +config SND_SOC_FSL_AUD2HTX + tristate "AUDIO TO HDMI TX module support" + help + Say Y if you want to add AUDIO TO HDMI TX support for NXP. + config SND_SOC_FSL_UTILS tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 1d2231f9cc47..2181b7f9f677 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -26,6 +26,7 @@ snd-soc-fsl-dma-objs := fsl_dma.o snd-soc-fsl-mqs-objs := fsl_mqs.o snd-soc-fsl-easrc-objs := fsl_easrc.o snd-soc-fsl-xcvr-objs := fsl_xcvr.o +snd-soc-fsl-aud2htx-objs := fsl_aud2htx.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o @@ -40,6 +41,7 @@ obj-$(CONFIG_SND_SOC_FSL_MQS) += snd-soc-fsl-mqs.o obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o obj-$(CONFIG_SND_SOC_FSL_XCVR) += snd-soc-fsl-xcvr.o +obj-$(CONFIG_SND_SOC_FSL_AUD2HTX) += snd-soc-fsl-aud2htx.o # MPC5200 Platform Support obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c new file mode 100644 index 000000000000..124aeb70f24e --- /dev/null +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright 2020 NXP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsl_aud2htx.h" +#include "imx-pcm.h" + +static int fsl_aud2htx_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct fsl_aud2htx *aud2htx = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL, + AUD2HTX_CTRL_EN, AUD2HTX_CTRL_EN); + regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, + AUD2HTX_CTRE_DE, AUD2HTX_CTRE_DE); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, + AUD2HTX_CTRE_DE, 0); + regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL, + AUD2HTX_CTRL_EN, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct snd_soc_dai_ops fsl_aud2htx_dai_ops = { + .trigger = fsl_aud2htx_trigger, +}; + +static int fsl_aud2htx_dai_probe(struct snd_soc_dai *cpu_dai) +{ + struct fsl_aud2htx *aud2htx = dev_get_drvdata(cpu_dai->dev); + + /* DMA request when number of entries < WTMK_LOW */ + regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, + AUD2HTX_CTRE_DT_MASK, 0); + + /* Disable interrupts*/ + regmap_update_bits(aud2htx->regmap, AUD2HTX_IRQ_MASK, + AUD2HTX_WM_HIGH_IRQ_MASK | + AUD2HTX_WM_LOW_IRQ_MASK | + AUD2HTX_OVF_MASK, + AUD2HTX_WM_HIGH_IRQ_MASK | + AUD2HTX_WM_LOW_IRQ_MASK | + AUD2HTX_OVF_MASK); + + /* Configure watermark */ + regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, + AUD2HTX_CTRE_WL_MASK, + AUD2HTX_WTMK_LOW << AUD2HTX_CTRE_WL_SHIFT); + regmap_update_bits(aud2htx->regmap, AUD2HTX_CTRL_EXT, + AUD2HTX_CTRE_WH_MASK, + AUD2HTX_WTMK_HIGH << AUD2HTX_CTRE_WH_SHIFT); + + snd_soc_dai_init_dma_data(cpu_dai, &aud2htx->dma_params_tx, + &aud2htx->dma_params_rx); + + return 0; +} + +static struct snd_soc_dai_driver fsl_aud2htx_dai = { + .probe = fsl_aud2htx_dai_probe, + .playback = { + .stream_name = "CPU-Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = FSL_AUD2HTX_FORMATS, + }, + .ops = &fsl_aud2htx_dai_ops, +}; + +static const struct snd_soc_component_driver fsl_aud2htx_component = { + .name = "fsl-aud2htx", +}; + +static const struct reg_default fsl_aud2htx_reg_defaults[] = { + {AUD2HTX_CTRL, 0x00000000}, + {AUD2HTX_CTRL_EXT, 0x00000000}, + {AUD2HTX_WR, 0x00000000}, + {AUD2HTX_STATUS, 0x00000000}, + {AUD2HTX_IRQ_NOMASK, 0x00000000}, + {AUD2HTX_IRQ_MASKED, 0x00000000}, + {AUD2HTX_IRQ_MASK, 0x00000000}, +}; + +static bool fsl_aud2htx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AUD2HTX_CTRL: + case AUD2HTX_CTRL_EXT: + case AUD2HTX_STATUS: + case AUD2HTX_IRQ_NOMASK: + case AUD2HTX_IRQ_MASKED: + case AUD2HTX_IRQ_MASK: + return true; + default: + return false; + } +} + +static bool fsl_aud2htx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AUD2HTX_CTRL: + case AUD2HTX_CTRL_EXT: + case AUD2HTX_WR: + case AUD2HTX_IRQ_NOMASK: + case AUD2HTX_IRQ_MASKED: + case AUD2HTX_IRQ_MASK: + return true; + default: + return false; + } +} + +static bool fsl_aud2htx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AUD2HTX_STATUS: + case AUD2HTX_IRQ_NOMASK: + case AUD2HTX_IRQ_MASKED: + return true; + default: + return false; + } +} + +static const struct regmap_config fsl_aud2htx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + + .max_register = AUD2HTX_IRQ_MASK, + .reg_defaults = fsl_aud2htx_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(fsl_aud2htx_reg_defaults), + .readable_reg = fsl_aud2htx_readable_reg, + .volatile_reg = fsl_aud2htx_volatile_reg, + .writeable_reg = fsl_aud2htx_writeable_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct of_device_id fsl_aud2htx_dt_ids[] = { + { .compatible = "fsl,imx8mp-aud2htx",}, + {} +}; +MODULE_DEVICE_TABLE(of, fsl_aud2htx_dt_ids); + +static irqreturn_t fsl_aud2htx_isr(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static int fsl_aud2htx_probe(struct platform_device *pdev) +{ + struct fsl_aud2htx *aud2htx; + struct resource *res; + void __iomem *regs; + int ret, irq; + + aud2htx = devm_kzalloc(&pdev->dev, sizeof(*aud2htx), GFP_KERNEL); + if (!aud2htx) + return -ENOMEM; + + aud2htx->pdev = pdev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(regs)) { + dev_err(&pdev->dev, "failed ioremap\n"); + return PTR_ERR(regs); + } + + aud2htx->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &fsl_aud2htx_regmap_config); + if (IS_ERR(aud2htx->regmap)) { + dev_err(&pdev->dev, "failed to init regmap"); + return PTR_ERR(aud2htx->regmap); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq for node %s\n", + dev_name(&pdev->dev)); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0, + dev_name(&pdev->dev), aud2htx); + if (ret) { + dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret); + return ret; + } + + aud2htx->bus_clk = devm_clk_get(&pdev->dev, "bus"); + if (IS_ERR(aud2htx->bus_clk)) { + dev_err(&pdev->dev, "failed to get mem clock\n"); + return PTR_ERR(aud2htx->bus_clk); + } + + aud2htx->dma_params_tx.chan_name = "tx"; + aud2htx->dma_params_tx.maxburst = AUD2HTX_MAXBURST; + aud2htx->dma_params_tx.addr = res->start + AUD2HTX_WR; + + platform_set_drvdata(pdev, aud2htx); + pm_runtime_enable(&pdev->dev); + + regcache_cache_only(aud2htx->regmap, true); + + ret = devm_snd_soc_register_component(&pdev->dev, + &fsl_aud2htx_component, + &fsl_aud2htx_dai, 1); + if (ret) { + dev_err(&pdev->dev, "failed to register ASoC DAI\n"); + return ret; + } + + ret = imx_pcm_dma_init(pdev, IMX_DEFAULT_DMABUF_SIZE); + if (ret) + dev_err(&pdev->dev, "failed to init imx pcm dma: %d\n", ret); + + return ret; +} + +static int fsl_aud2htx_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static int fsl_aud2htx_runtime_suspend(struct device *dev) +{ + struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); + + regcache_cache_only(aud2htx->regmap, true); + clk_disable_unprepare(aud2htx->bus_clk); + + return 0; +} + +static int fsl_aud2htx_runtime_resume(struct device *dev) +{ + struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(aud2htx->bus_clk); + if (ret) + return ret; + + regcache_cache_only(aud2htx->regmap, false); + regcache_mark_dirty(aud2htx->regmap); + regcache_sync(aud2htx->regmap); + + return 0; +} + +static const struct dev_pm_ops fsl_aud2htx_pm_ops = { + SET_RUNTIME_PM_OPS(fsl_aud2htx_runtime_suspend, + fsl_aud2htx_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static struct platform_driver fsl_aud2htx_driver = { + .probe = fsl_aud2htx_probe, + .remove = fsl_aud2htx_remove, + .driver = { + .name = "fsl-aud2htx", + .pm = &fsl_aud2htx_pm_ops, + .of_match_table = fsl_aud2htx_dt_ids, + }, +}; +module_platform_driver(fsl_aud2htx_driver); + +MODULE_AUTHOR("Shengjiu Wang "); +MODULE_DESCRIPTION("NXP AUD2HTX driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/fsl/fsl_aud2htx.h b/sound/soc/fsl/fsl_aud2htx.h new file mode 100644 index 000000000000..ad70d6a7694c --- /dev/null +++ b/sound/soc/fsl/fsl_aud2htx.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2020 NXP + */ + +#ifndef _FSL_AUD2HTX_H +#define _FSL_AUD2HTX_H + +#define FSL_AUD2HTX_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* AUD2HTX Register Map */ +#define AUD2HTX_CTRL 0x0 /* AUD2HTX Control Register */ +#define AUD2HTX_CTRL_EXT 0x4 /* AUD2HTX Control Extended Register */ +#define AUD2HTX_WR 0x8 /* AUD2HTX Write Register */ +#define AUD2HTX_STATUS 0xC /* AUD2HTX Status Register */ +#define AUD2HTX_IRQ_NOMASK 0x10 /* AUD2HTX Nonmasked Interrupt Flags Register */ +#define AUD2HTX_IRQ_MASKED 0x14 /* AUD2HTX Masked Interrupt Flags Register */ +#define AUD2HTX_IRQ_MASK 0x18 /* AUD2HTX IRQ Masks Register */ + +/* AUD2HTX Control Register */ +#define AUD2HTX_CTRL_EN BIT(0) + +/* AUD2HTX Control Extended Register */ +#define AUD2HTX_CTRE_DE BIT(0) +#define AUD2HTX_CTRE_DT_SHIFT 0x1 +#define AUD2HTX_CTRE_DT_WIDTH 0x2 +#define AUD2HTX_CTRE_DT_MASK ((BIT(AUD2HTX_CTRE_DT_WIDTH) - 1) \ + << AUD2HTX_CTRE_DT_SHIFT) +#define AUD2HTX_CTRE_WL_SHIFT 16 +#define AUD2HTX_CTRE_WL_WIDTH 5 +#define AUD2HTX_CTRE_WL_MASK ((BIT(AUD2HTX_CTRE_WL_WIDTH) - 1) \ + << AUD2HTX_CTRE_WL_SHIFT) +#define AUD2HTX_CTRE_WH_SHIFT 24 +#define AUD2HTX_CTRE_WH_WIDTH 5 +#define AUD2HTX_CTRE_WH_MASK ((BIT(AUD2HTX_CTRE_WH_WIDTH) - 1) \ + << AUD2HTX_CTRE_WH_SHIFT) + +/* AUD2HTX IRQ Masks Register */ +#define AUD2HTX_WM_HIGH_IRQ_MASK BIT(2) +#define AUD2HTX_WM_LOW_IRQ_MASK BIT(1) +#define AUD2HTX_OVF_MASK BIT(0) + +#define AUD2HTX_FIFO_DEPTH 0x20 +#define AUD2HTX_WTMK_LOW 0x10 +#define AUD2HTX_WTMK_HIGH 0x10 +#define AUD2HTX_MAXBURST 0x10 + +/** + * fsl_aud2htx: AUD2HTX private data + * + * @pdev: platform device pointer + * @regmap: regmap handler + * @bus_clk: clock source to access register + * @dma_params_rx: DMA parameters for receive channel + * @dma_params_tx: DMA parameters for transmit channel + */ +struct fsl_aud2htx { + struct platform_device *pdev; + struct regmap *regmap; + struct clk *bus_clk; + + struct snd_dmaengine_dai_dma_data dma_params_rx; + struct snd_dmaengine_dai_dma_data dma_params_tx; +}; + +#endif /* _FSL_AUD2HTX_H */ -- cgit v1.2.3 From 1cc3245b2c7464b6d6ad210b0e333781676de519 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Sat, 7 Nov 2020 10:20:43 +0800 Subject: ASoC: fsl_aud2htx: Remove dev_err() usage after platform_get_irq() platform_get_irq() would print error message internally, so dev_err() after platform_get_irq() is not needed Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1604715643-29507-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_aud2htx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index 124aeb70f24e..4091ccc7c3e9 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -211,11 +211,8 @@ static int fsl_aud2htx_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no irq for node %s\n", - dev_name(&pdev->dev)); + if (irq < 0) return irq; - } ret = devm_request_irq(&pdev->dev, irq, fsl_aud2htx_isr, 0, dev_name(&pdev->dev), aud2htx); -- cgit v1.2.3 From 048751de568816de52dedf0fa967cceada7885f1 Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Mon, 2 Nov 2020 18:18:10 +0200 Subject: ASoC: fsl_xcvr: fix break condition The break condition copied by mistake as same as loop condition in the previous version, but must be the opposite. So fix it. Signed-off-by: Viorel Suman Link: https://lore.kernel.org/r/20201102161810.902464-1-viorel.suman@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index c055179e6d11..2a28810d0e29 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -247,7 +247,7 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy) regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx); ret = regmap_read_poll_timeout(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val, - (val & idx) != ((val & tidx) >> 1), + (val & idx) == ((val & tidx) >> 1), 10, 10000); if (ret) dev_err(dev, "AI timeout: failed to set %s reg 0x%02x=0x%08x\n", -- cgit v1.2.3 From 9c9fd07eb481cbfc89efa7820b3bb2b4a55eb303 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Nov 2020 17:39:32 -0300 Subject: ASoC: wm1133-ev1: Remove unused driver Since commit c93197b0041d ("ARM: imx: Remove i.MX31 board files"), the MACH_MX31ADS_WM1133_EV1 non-DT platform is no longer supported, so get rid of its machine audio driver too. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201110203937.25684-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 11 -- sound/soc/fsl/Makefile | 2 - sound/soc/fsl/wm1133-ev1.c | 289 --------------------------------------------- 3 files changed, 302 deletions(-) delete mode 100644 sound/soc/fsl/wm1133-ev1.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 52a562215008..3ad547193df8 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -239,17 +239,6 @@ config SND_SOC_IMX_SSI comment "SoC Audio support for Freescale i.MX boards:" -config SND_MXC_SOC_WM1133_EV1 - tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted" - depends on MACH_MX31ADS_WM1133_EV1 - select SND_SOC_WM8350 - select SND_SOC_IMX_PCM_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Enable support for audio on the i.MX31ADS with the WM1133-EV1 - PMIC board with WM8835x fitted. - config SND_SOC_MX27VIS_AIC32X4 tristate "SoC audio support for Visstrim M10 boards" depends on MACH_IMX27_VISSTRIM_M10 && I2C diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 2181b7f9f677..8d3e40415050 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -65,7 +65,6 @@ obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o -snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-imx-es8328-objs := imx-es8328.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-spdif-objs := imx-spdif.o @@ -75,7 +74,6 @@ snd-soc-imx-audmix-objs := imx-audmix.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o -obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c deleted file mode 100644 index 99611a037ada..000000000000 --- a/sound/soc/fsl/wm1133-ev1.c +++ /dev/null @@ -1,289 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// wm1133-ev1.c - Audio for WM1133-EV1 on i.MX31ADS -// -// Copyright (c) 2010 Wolfson Microelectronics plc -// Author: Mark Brown -// -// Based on an earlier driver for the same hardware by Liam Girdwood. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "imx-ssi.h" -#include "../codecs/wm8350.h" -#include "imx-audmux.h" - -/* There is a silicon mic on the board optionally connected via a solder pad - * SP1. Define this to enable it. - */ -#undef USE_SIMIC - -struct _wm8350_audio { - unsigned int channels; - snd_pcm_format_t format; - unsigned int rate; - unsigned int sysclk; - unsigned int bclkdiv; - unsigned int clkdiv; - unsigned int lr_rate; -}; - -/* in order of power consumption per rate (lowest first) */ -static const struct _wm8350_audio wm8350_audio[] = { - /* 16bit mono modes */ - {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000 >> 1, - WM8350_BCLK_DIV_48, WM8350_DACDIV_3, 16,}, - - /* 16 bit stereo modes */ - {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000, - WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000, - WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000, - WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600, - WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600, - WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - - /* 24bit stereo modes */ - {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, - {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, - {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, - {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, -}; - -static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - int i, found = 0; - snd_pcm_format_t format = params_format(params); - unsigned int rate = params_rate(params); - unsigned int channels = params_channels(params); - - /* find the correct audio parameters */ - for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) { - if (rate == wm8350_audio[i].rate && - format == wm8350_audio[i].format && - channels == wm8350_audio[i].channels) { - found = 1; - break; - } - } - if (!found) - return -EINVAL; - - /* codec FLL input is 14.75 MHz from MCLK */ - snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk); - - /* TODO: The SSI driver should figure this out for us */ - switch (channels) { - case 2: - snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 0); - break; - case 1: - snd_soc_dai_set_tdm_slot(cpu_dai, 0x1, 0x1, 1, 0); - break; - default: - return -EINVAL; - } - - /* set MCLK as the codec system clock for DAC and ADC */ - snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_MCLK, - wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN); - - /* set codec BCLK division for sample rate */ - snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV, - wm8350_audio[i].bclkdiv); - - /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */ - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_DACLR_CLKDIV, wm8350_audio[i].lr_rate); - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_ADCLR_CLKDIV, wm8350_audio[i].lr_rate); - - /* now configure DAC and ADC clocks */ - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv); - - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv); - - return 0; -} - -static const struct snd_soc_ops wm1133_ev1_ops = { - .hw_params = wm1133_ev1_hw_params, -}; - -static const struct snd_soc_dapm_widget wm1133_ev1_widgets[] = { -#ifdef USE_SIMIC - SND_SOC_DAPM_MIC("SiMIC", NULL), -#endif - SND_SOC_DAPM_MIC("Mic1 Jack", NULL), - SND_SOC_DAPM_MIC("Mic2 Jack", NULL), - SND_SOC_DAPM_LINE("Line In Jack", NULL), - SND_SOC_DAPM_LINE("Line Out Jack", NULL), - SND_SOC_DAPM_HP("Headphone Jack", NULL), -}; - -/* imx32ads soc_card audio map */ -static const struct snd_soc_dapm_route wm1133_ev1_map[] = { - -#ifdef USE_SIMIC - /* SiMIC --> IN1LN (with automatic bias) via SP1 */ - { "IN1LN", NULL, "Mic Bias" }, - { "Mic Bias", NULL, "SiMIC" }, -#endif - - /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */ - { "IN1LN", NULL, "Mic Bias" }, - { "IN1LP", NULL, "Mic1 Jack" }, - { "Mic Bias", NULL, "Mic1 Jack" }, - - /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */ - { "IN1RN", NULL, "Mic Bias" }, - { "IN1RP", NULL, "Mic2 Jack" }, - { "Mic Bias", NULL, "Mic2 Jack" }, - - /* Line in Jack --> AUX (L+R) */ - { "IN3R", NULL, "Line In Jack" }, - { "IN3L", NULL, "Line In Jack" }, - - /* Out1 --> Headphone Jack */ - { "Headphone Jack", NULL, "OUT1R" }, - { "Headphone Jack", NULL, "OUT1L" }, - - /* Out1 --> Line Out Jack */ - { "Line Out Jack", NULL, "OUT2R" }, - { "Line Out Jack", NULL, "OUT2L" }, -}; - -static struct snd_soc_jack hp_jack; - -static struct snd_soc_jack_pin hp_jack_pins[] = { - { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE }, -}; - -static struct snd_soc_jack mic_jack; - -static struct snd_soc_jack_pin mic_jack_pins[] = { - { .pin = "Mic1 Jack", .mask = SND_JACK_MICROPHONE }, - { .pin = "Mic2 Jack", .mask = SND_JACK_MICROPHONE }, -}; - -static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component; - - /* Headphone jack detection */ - snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE, - &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); - wm8350_hp_jack_detect(component, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE); - - /* Microphone jack detection */ - snd_soc_card_jack_new(rtd->card, "Microphone", - SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack, - mic_jack_pins, ARRAY_SIZE(mic_jack_pins)); - wm8350_mic_jack_detect(component, &mic_jack, SND_JACK_MICROPHONE, - SND_JACK_BTN_0); - - snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "Mic Bias"); - - return 0; -} - - -SND_SOC_DAILINK_DEFS(ev1, - DAILINK_COMP_ARRAY(COMP_CPU("imx-ssi.0")), - DAILINK_COMP_ARRAY(COMP_CODEC("wm8350-codec.0-0x1a", "wm8350-hifi")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("imx-ssi.0"))); - -static struct snd_soc_dai_link wm1133_ev1_dai = { - .name = "WM1133-EV1", - .stream_name = "Audio", - .init = wm1133_ev1_init, - .ops = &wm1133_ev1_ops, - .symmetric_rates = 1, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - SND_SOC_DAILINK_REG(ev1), -}; - -static struct snd_soc_card wm1133_ev1 = { - .name = "WM1133-EV1", - .owner = THIS_MODULE, - .dai_link = &wm1133_ev1_dai, - .num_links = 1, - - .dapm_widgets = wm1133_ev1_widgets, - .num_dapm_widgets = ARRAY_SIZE(wm1133_ev1_widgets), - .dapm_routes = wm1133_ev1_map, - .num_dapm_routes = ARRAY_SIZE(wm1133_ev1_map), -}; - -static struct platform_device *wm1133_ev1_snd_device; - -static int __init wm1133_ev1_audio_init(void) -{ - int ret; - unsigned int ptcr, pdcr; - - /* SSI0 mastered by port 5 */ - ptcr = IMX_AUDMUX_V2_PTCR_SYN | - IMX_AUDMUX_V2_PTCR_TFSDIR | - IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) | - IMX_AUDMUX_V2_PTCR_TCLKDIR | - IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); - pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); - imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr); - - ptcr = IMX_AUDMUX_V2_PTCR_SYN; - pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0); - imx_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr); - - wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1); - if (!wm1133_ev1_snd_device) - return -ENOMEM; - - platform_set_drvdata(wm1133_ev1_snd_device, &wm1133_ev1); - ret = platform_device_add(wm1133_ev1_snd_device); - - if (ret) - platform_device_put(wm1133_ev1_snd_device); - - return ret; -} -module_init(wm1133_ev1_audio_init); - -static void __exit wm1133_ev1_audio_exit(void) -{ - platform_device_unregister(wm1133_ev1_snd_device); -} -module_exit(wm1133_ev1_audio_exit); - -MODULE_AUTHOR("Mark Brown "); -MODULE_DESCRIPTION("Audio for WM1133-EV1 on i.MX31ADS"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 3fbb01fb583fd3ddd319f5a547bf10e322301a3f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Nov 2020 17:39:33 -0300 Subject: ASoC: mx27vis-aic32x4: Remove unused driver Since commit 879c0e5e0ac7 ("ARM: imx: Remove i.MX27 board files"), the MACH_IMX27_VISSTRIM_M10 non-DT platform is no longer supported, so get rid of its machine audio driver too. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201110203937.25684-2-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 11 --- sound/soc/fsl/Makefile | 2 - sound/soc/fsl/mx27vis-aic32x4.c | 214 ---------------------------------------- 3 files changed, 227 deletions(-) delete mode 100644 sound/soc/fsl/mx27vis-aic32x4.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 3ad547193df8..c18ec9310aa7 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -239,17 +239,6 @@ config SND_SOC_IMX_SSI comment "SoC Audio support for Freescale i.MX boards:" -config SND_SOC_MX27VIS_AIC32X4 - tristate "SoC audio support for Visstrim M10 boards" - depends on MACH_IMX27_VISSTRIM_M10 && I2C - select SND_SOC_TLV320AIC32X4 - select SND_SOC_IMX_PCM_DMA - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Say Y if you want to add support for SoC audio on Visstrim SM10 - board with TLV320AIC32X4 codec. - config SND_SOC_PHYCORE_AC97 tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" depends on MACH_PCM043 || MACH_PCA100 diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 8d3e40415050..5c6058f414be 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -64,7 +64,6 @@ obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-phycore-ac97-objs := phycore-ac97.o -snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o snd-soc-imx-es8328-objs := imx-es8328.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-spdif-objs := imx-spdif.o @@ -73,7 +72,6 @@ snd-soc-imx-audmix-objs := imx-audmix.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o -obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c deleted file mode 100644 index 8d3b1897370b..000000000000 --- a/sound/soc/fsl/mx27vis-aic32x4.c +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// mx27vis-aic32x4.c -// -// Copyright 2011 Vista Silicon S.L. -// -// Author: Javier Martin - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../codecs/tlv320aic32x4.h" -#include "imx-ssi.h" -#include "imx-audmux.h" - -#define MX27VIS_AMP_GAIN 0 -#define MX27VIS_AMP_MUTE 1 - -static int mx27vis_amp_gain; -static int mx27vis_amp_mute; -static int mx27vis_amp_gain0_gpio; -static int mx27vis_amp_gain1_gpio; -static int mx27vis_amp_mutel_gpio; -static int mx27vis_amp_muter_gpio; - -static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - int ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, - 25000000, SND_SOC_CLOCK_OUT); - if (ret) { - pr_err("%s: failed setting codec sysclk\n", __func__); - return ret; - } - - ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, - SND_SOC_CLOCK_IN); - if (ret) { - pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); - return ret; - } - - return 0; -} - -static const struct snd_soc_ops mx27vis_aic32x4_snd_ops = { - .hw_params = mx27vis_aic32x4_hw_params, -}; - -static int mx27vis_amp_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int value = ucontrol->value.integer.value[0]; - unsigned int reg = mc->reg; - int max = mc->max; - - if (value > max) - return -EINVAL; - - switch (reg) { - case MX27VIS_AMP_GAIN: - gpio_set_value(mx27vis_amp_gain0_gpio, value & 1); - gpio_set_value(mx27vis_amp_gain1_gpio, value >> 1); - mx27vis_amp_gain = value; - break; - case MX27VIS_AMP_MUTE: - gpio_set_value(mx27vis_amp_mutel_gpio, value & 1); - gpio_set_value(mx27vis_amp_muter_gpio, value >> 1); - mx27vis_amp_mute = value; - break; - } - return 0; -} - -static int mx27vis_amp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - - switch (reg) { - case MX27VIS_AMP_GAIN: - ucontrol->value.integer.value[0] = mx27vis_amp_gain; - break; - case MX27VIS_AMP_MUTE: - ucontrol->value.integer.value[0] = mx27vis_amp_mute; - break; - } - return 0; -} - -/* From 6dB to 24dB in steps of 6dB */ -static const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0); - -static const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = { - SOC_DAPM_PIN_SWITCH("External Mic"), - SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0, - mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv), - SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0, - mx27vis_amp_get, mx27vis_amp_set), -}; - -static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { - SND_SOC_DAPM_MIC("External Mic", NULL), -}; - -static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { - {"Mic Bias", NULL, "External Mic"}, - {"IN1_R", NULL, "Mic Bias"}, - {"IN2_R", NULL, "Mic Bias"}, - {"IN3_R", NULL, "Mic Bias"}, - {"IN1_L", NULL, "Mic Bias"}, - {"IN2_L", NULL, "Mic Bias"}, - {"IN3_L", NULL, "Mic Bias"}, -}; - -SND_SOC_DAILINK_DEFS(hifi, - DAILINK_COMP_ARRAY(COMP_CPU("imx-ssi.0")), - DAILINK_COMP_ARRAY(COMP_CODEC("tlv320aic32x4.0-0018", - "tlv320aic32x4-hifi")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("imx-ssi.0"))); - -static struct snd_soc_dai_link mx27vis_aic32x4_dai = { - .name = "tlv320aic32x4", - .stream_name = "TLV320AIC32X4", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &mx27vis_aic32x4_snd_ops, - SND_SOC_DAILINK_REG(hifi), -}; - -static struct snd_soc_card mx27vis_aic32x4 = { - .name = "visstrim_m10-audio", - .owner = THIS_MODULE, - .dai_link = &mx27vis_aic32x4_dai, - .num_links = 1, - .controls = mx27vis_aic32x4_controls, - .num_controls = ARRAY_SIZE(mx27vis_aic32x4_controls), - .dapm_widgets = aic32x4_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), - .dapm_routes = aic32x4_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), -}; - -static int mx27vis_aic32x4_probe(struct platform_device *pdev) -{ - struct snd_mx27vis_platform_data *pdata = pdev->dev.platform_data; - int ret; - - if (!pdata) { - dev_err(&pdev->dev, "No platform data supplied\n"); - return -EINVAL; - } - - mx27vis_amp_gain0_gpio = pdata->amp_gain0_gpio; - mx27vis_amp_gain1_gpio = pdata->amp_gain1_gpio; - mx27vis_amp_mutel_gpio = pdata->amp_mutel_gpio; - mx27vis_amp_muter_gpio = pdata->amp_muter_gpio; - - mx27vis_aic32x4.dev = &pdev->dev; - ret = devm_snd_soc_register_card(&pdev->dev, &mx27vis_aic32x4); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - return ret; - } - - /* Connect SSI0 as clock slave to SSI1 external pins */ - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_TFSDIR | - IMX_AUDMUX_V1_PCR_TCLKDIR | - IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) - ); - imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) - ); - - return ret; -} - -static struct platform_driver mx27vis_aic32x4_audio_driver = { - .driver = { - .name = "mx27vis", - }, - .probe = mx27vis_aic32x4_probe, -}; - -module_platform_driver(mx27vis_aic32x4_audio_driver); - -MODULE_AUTHOR("Javier Martin "); -MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:mx27vis"); -- cgit v1.2.3 From 440534a0ecfd20235a091fb2a98c2c3adf86834e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Nov 2020 17:39:34 -0300 Subject: ASoC: phycore-ac97: Remove unused driver Since commit e1324ece2af4 ("ARM: imx: Remove i.MX35 board files"), the MACH_PCM043 and MACH_PCA100 non-DT platform are no longer supported, so get rid of their machine audio driver too. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201110203937.25684-3-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 12 ----- sound/soc/fsl/Makefile | 2 - sound/soc/fsl/phycore-ac97.c | 121 ------------------------------------------- 3 files changed, 135 deletions(-) delete mode 100644 sound/soc/fsl/phycore-ac97.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index c18ec9310aa7..63be2bf5b509 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -239,18 +239,6 @@ config SND_SOC_IMX_SSI comment "SoC Audio support for Freescale i.MX boards:" -config SND_SOC_PHYCORE_AC97 - tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" - depends on MACH_PCM043 || MACH_PCA100 - select SND_SOC_AC97_BUS - select SND_SOC_WM9712 - select SND_SOC_IMX_PCM_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Say Y if you want to add support for SoC audio on Phytec phyCORE - and phyCARD boards in AC97 mode - config SND_SOC_EUKREA_TLV320 tristate "Eukrea TLV320" depends on ARCH_MXC && !ARM64 && I2C diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 5c6058f414be..12755058f877 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -63,7 +63,6 @@ obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o -snd-soc-phycore-ac97-objs := phycore-ac97.o snd-soc-imx-es8328-objs := imx-es8328.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-spdif-objs := imx-spdif.o @@ -71,7 +70,6 @@ snd-soc-imx-mc13783-objs := imx-mc13783.o snd-soc-imx-audmix-objs := imx-audmix.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o -obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c deleted file mode 100644 index e561f7ff1699..000000000000 --- a/sound/soc/fsl/phycore-ac97.c +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// phycore-ac97.c -- SoC audio for imx_phycore in AC97 mode -// -// Copyright 2009 Sascha Hauer, Pengutronix - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "imx-audmux.h" - -static struct snd_soc_card imx_phycore; - -static const struct snd_soc_ops imx_phycore_hifi_ops = { -}; - -SND_SOC_DAILINK_DEFS(hifi, - DAILINK_COMP_ARRAY(COMP_CPU("imx-ssi.0")), - DAILINK_COMP_ARRAY(COMP_CODEC("wm9712-codec", "wm9712-hifi")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("imx-ssi.0"))); - -static struct snd_soc_dai_link imx_phycore_dai_ac97[] = { - { - .name = "HiFi", - .stream_name = "HiFi", - .ops = &imx_phycore_hifi_ops, - SND_SOC_DAILINK_REG(hifi), - }, -}; - -static struct snd_soc_card imx_phycore = { - .name = "PhyCORE-ac97-audio", - .owner = THIS_MODULE, - .dai_link = imx_phycore_dai_ac97, - .num_links = ARRAY_SIZE(imx_phycore_dai_ac97), -}; - -static struct platform_device *imx_phycore_snd_ac97_device; -static struct platform_device *imx_phycore_snd_device; - -static int __init imx_phycore_init(void) -{ - int ret; - - if (machine_is_pca100()) { - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V1_PCR_TFCSEL(3) | - IMX_AUDMUX_V1_PCR_TCLKDIR | /* clock is output */ - IMX_AUDMUX_V1_PCR_RXDSEL(3)); - imx_audmux_v1_configure_port(3, - IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V1_PCR_TFCSEL(0) | - IMX_AUDMUX_V1_PCR_TFSDIR | - IMX_AUDMUX_V1_PCR_RXDSEL(0)); - } else if (machine_is_pcm043()) { - imx_audmux_v2_configure_port(3, - IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V2_PTCR_TFSEL(0) | - IMX_AUDMUX_V2_PTCR_TFSDIR, - IMX_AUDMUX_V2_PDCR_RXDSEL(0)); - imx_audmux_v2_configure_port(0, - IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V2_PTCR_TCSEL(3) | - IMX_AUDMUX_V2_PTCR_TCLKDIR, /* clock is output */ - IMX_AUDMUX_V2_PDCR_RXDSEL(3)); - } else { - /* return happy. We might run on a totally different machine */ - return 0; - } - - imx_phycore_snd_ac97_device = platform_device_alloc("soc-audio", -1); - if (!imx_phycore_snd_ac97_device) - return -ENOMEM; - - platform_set_drvdata(imx_phycore_snd_ac97_device, &imx_phycore); - ret = platform_device_add(imx_phycore_snd_ac97_device); - if (ret) - goto fail1; - - imx_phycore_snd_device = platform_device_alloc("wm9712-codec", -1); - if (!imx_phycore_snd_device) { - ret = -ENOMEM; - goto fail2; - } - ret = platform_device_add(imx_phycore_snd_device); - - if (ret) { - printk(KERN_ERR "ASoC: Platform device allocation failed\n"); - goto fail3; - } - - return 0; - -fail3: - platform_device_put(imx_phycore_snd_device); -fail2: - platform_device_del(imx_phycore_snd_ac97_device); -fail1: - platform_device_put(imx_phycore_snd_ac97_device); - return ret; -} - -static void __exit imx_phycore_exit(void) -{ - platform_device_unregister(imx_phycore_snd_device); - platform_device_unregister(imx_phycore_snd_ac97_device); -} - -late_initcall(imx_phycore_init); -module_exit(imx_phycore_exit); - -MODULE_AUTHOR("Sascha Hauer "); -MODULE_DESCRIPTION("PhyCORE ALSA SoC driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 83e7e2278680207f1650949db11ba0e1b6fbc3f5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Nov 2020 17:39:35 -0300 Subject: ASoC: imx-mc13783: Remove unused driver The imx-mc13783 was used on imx27-pdk and imx31-pdk non-DT platforms. Since 5.10-rc1, i.MX has been converted to a DT-only platform and all board files are gone. Remove the imx-mc13783 audio machine driver as there is no user at all. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201110203937.25684-4-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 8 --- sound/soc/fsl/Makefile | 2 - sound/soc/fsl/imx-mc13783.c | 156 -------------------------------------------- 3 files changed, 166 deletions(-) delete mode 100644 sound/soc/fsl/imx-mc13783.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 63be2bf5b509..1536c6d411fa 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -283,14 +283,6 @@ config SND_SOC_IMX_SPDIF Say Y if you want to add support for SoC audio on an i.MX board with a S/DPDIF. -config SND_SOC_IMX_MC13783 - tristate "SoC Audio support for I.MX boards with mc13783" - depends on MFD_MC13XXX && ARM - select SND_SOC_IMX_SSI - select SND_SOC_IMX_AUDMUX - select SND_SOC_MC13783 - select SND_SOC_IMX_PCM_DMA - config SND_SOC_FSL_ASOC_CARD tristate "Generic ASoC Sound Card with ASRC support" depends on OF && I2C diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 12755058f877..5637ebbe8ab2 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -66,12 +66,10 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o snd-soc-imx-es8328-objs := imx-es8328.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-spdif-objs := imx-spdif.o -snd-soc-imx-mc13783-objs := imx-mc13783.o snd-soc-imx-audmix-objs := imx-audmix.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o -obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c deleted file mode 100644 index d9dca7bbcae3..000000000000 --- a/sound/soc/fsl/imx-mc13783.c +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// imx-mc13783.c -- SoC audio for imx based boards with mc13783 codec -// -// Copyright 2012 Philippe Retornaz, -// -// Heavly based on phycore-mc13783: -// Copyright 2009 Sascha Hauer, Pengutronix - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../codecs/mc13783.h" -#include "imx-ssi.h" -#include "imx-audmux.h" - -#define FMT_SSI (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBM_CFM) - -static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int ret; - - ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 4, 16); - if (ret) - return ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, MC13783_CLK_CLIA, 26000000, 0); - if (ret) - return ret; - - return snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16); -} - -static const struct snd_soc_ops imx_mc13783_hifi_ops = { - .hw_params = imx_mc13783_hifi_hw_params, -}; - -SND_SOC_DAILINK_DEFS(hifi, - DAILINK_COMP_ARRAY(COMP_CPU("imx-ssi.0")), - DAILINK_COMP_ARRAY(COMP_CODEC("mc13783-codec", "mc13783-hifi")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("imx-ssi.0"))); - -static struct snd_soc_dai_link imx_mc13783_dai_mc13783[] = { - { - .name = "MC13783", - .stream_name = "Sound", - .ops = &imx_mc13783_hifi_ops, - .symmetric_rates = 1, - .dai_fmt = FMT_SSI, - SND_SOC_DAILINK_REG(hifi), - }, -}; - -static const struct snd_soc_dapm_widget imx_mc13783_widget[] = { - SND_SOC_DAPM_MIC("Mic", NULL), - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_SPK("Speaker", NULL), -}; - -static const struct snd_soc_dapm_route imx_mc13783_routes[] = { - {"Speaker", NULL, "LSP"}, - {"Headphone", NULL, "HSL"}, - {"Headphone", NULL, "HSR"}, - - {"MC1LIN", NULL, "MC1 Bias"}, - {"MC2IN", NULL, "MC2 Bias"}, - {"MC1 Bias", NULL, "Mic"}, - {"MC2 Bias", NULL, "Mic"}, -}; - -static struct snd_soc_card imx_mc13783 = { - .name = "imx_mc13783", - .owner = THIS_MODULE, - .dai_link = imx_mc13783_dai_mc13783, - .num_links = ARRAY_SIZE(imx_mc13783_dai_mc13783), - .dapm_widgets = imx_mc13783_widget, - .num_dapm_widgets = ARRAY_SIZE(imx_mc13783_widget), - .dapm_routes = imx_mc13783_routes, - .num_dapm_routes = ARRAY_SIZE(imx_mc13783_routes), -}; - -static int imx_mc13783_probe(struct platform_device *pdev) -{ - int ret; - - imx_mc13783.dev = &pdev->dev; - - ret = devm_snd_soc_register_card(&pdev->dev, &imx_mc13783); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - return ret; - } - - if (machine_is_mx31_3ds() || machine_is_mx31moboard()) { - imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4, - IMX_AUDMUX_V2_PTCR_SYN, - IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) | - IMX_AUDMUX_V2_PDCR_MODE(1) | - IMX_AUDMUX_V2_PDCR_INMMASK(0xfc)); - imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, - IMX_AUDMUX_V2_PTCR_SYN | - IMX_AUDMUX_V2_PTCR_TFSDIR | - IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) | - IMX_AUDMUX_V2_PTCR_TCLKDIR | - IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) | - IMX_AUDMUX_V2_PTCR_RFSDIR | - IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) | - IMX_AUDMUX_V2_PTCR_RCLKDIR | - IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4), - IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4)); - } else if (machine_is_mx27_3ds()) { - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_TFSDIR | - IMX_AUDMUX_V1_PCR_TCLKDIR | - IMX_AUDMUX_V1_PCR_RFSDIR | - IMX_AUDMUX_V1_PCR_RCLKDIR | - IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | - IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) - ); - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) - ); - } - - return ret; -} - -static struct platform_driver imx_mc13783_audio_driver = { - .driver = { - .name = "imx_mc13783", - }, - .probe = imx_mc13783_probe, -}; - -module_platform_driver(imx_mc13783_audio_driver); - -MODULE_AUTHOR("Sascha Hauer "); -MODULE_AUTHOR("Philippe Retornaz Date: Tue, 10 Nov 2020 17:39:36 -0300 Subject: ASoC: fsl: eukrea: Remove the SND_SOC_IMX_SSI selection SND_SOC_IMX_SSI was only used by i.MX non-DT platforms. SND_SOC_EUKREA_TLV320 already selects the SND_SOC_FSL_SSI symbol, which is enough. Remove the unneeded SND_SOC_IMX_SSI selection. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201110203937.25684-5-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 1536c6d411fa..31897fff6c71 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -244,7 +244,6 @@ config SND_SOC_EUKREA_TLV320 depends on ARCH_MXC && !ARM64 && I2C select SND_SOC_TLV320AIC23_I2C select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI select SND_SOC_FSL_SSI select SND_SOC_IMX_PCM_DMA help -- cgit v1.2.3 From c31da0b196f99c7d95c69bab96e709b72a30f509 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 10 Nov 2020 17:39:37 -0300 Subject: ASoC: imx-ssi: Remove unused driver The imx-ssi driver was only used by i.MX non-DT platforms. Since 5.10-rc1, i.MX has been converted to a DT-only platform and all board files are gone. Remove the imx-ssi audio driver as there are no more users at all. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201110203937.25684-6-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 6 +- sound/soc/fsl/Makefile | 2 - sound/soc/fsl/imx-ssi.c | 651 ------------------------------------------------ 3 files changed, 1 insertion(+), 658 deletions(-) delete mode 100644 sound/soc/fsl/imx-ssi.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 31897fff6c71..a299f61e529e 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -228,15 +228,11 @@ endif # SND_POWERPC_SOC config SND_SOC_IMX_PCM_FIQ tristate - default y if SND_SOC_IMX_SSI=y && (SND_SOC_FSL_SSI=m || SND_SOC_FSL_SPDIF=m) && (MXC_TZIC || MXC_AVIC) + default y if (SND_SOC_FSL_SSI=m || SND_SOC_FSL_SPDIF=m) && (MXC_TZIC || MXC_AVIC) select FIQ if SND_IMX_SOC -config SND_SOC_IMX_SSI - tristate - select SND_SOC_FSL_UTILS - comment "SoC Audio support for Freescale i.MX boards:" config SND_SOC_EUKREA_TLV320 diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 5637ebbe8ab2..0b20e038b65b 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -53,9 +53,7 @@ obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o # i.MX Platform Support -snd-soc-imx-ssi-objs := imx-ssi.o snd-soc-imx-audmux-objs := imx-audmux.o -obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c deleted file mode 100644 index f8488e8f5f5b..000000000000 --- a/sound/soc/fsl/imx-ssi.c +++ /dev/null @@ -1,651 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -// -// imx-ssi.c -- ALSA Soc Audio Layer -// -// Copyright 2009 Sascha Hauer -// -// This code is based on code copyrighted by Freescale, -// Liam Girdwood, Javier Martin and probably others. -// -// The i.MX SSI core has some nasty limitations in AC97 mode. While most -// sane processor vendors have a FIFO per AC97 slot, the i.MX has only -// one FIFO which combines all valid receive slots. We cannot even select -// which slots we want to receive. The WM9712 with which this driver -// was developed with always sends GPIO status data in slot 12 which -// we receive in our (PCM-) data stream. The only chance we have is to -// manually skip this data in the FIQ handler. With sampling rates different -// from 48000Hz not every frame has valid receive data, so the ratio -// between pcm data and GPIO status data changes. Our FIQ handler is not -// able to handle this, hence this driver only works with 48000Hz sampling -// rate. -// Reading and writing AC97 registers is another challenge. The core -// provides us status bits when the read register is updated with *another* -// value. When we read the same register two times (and the register still -// contains the same value) these status bits are not set. We work -// around this by not polling these bits but only wait a fixed delay. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "imx-ssi.h" -#include "fsl_utils.h" - -#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV) - -/* - * SSI Network Mode or TDM slots configuration. - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, - unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 sccr; - - sccr = readl(ssi->base + SSI_STCCR); - sccr &= ~SSI_STCCR_DC_MASK; - sccr |= SSI_STCCR_DC(slots - 1); - writel(sccr, ssi->base + SSI_STCCR); - - sccr = readl(ssi->base + SSI_SRCCR); - sccr &= ~SSI_STCCR_DC_MASK; - sccr |= SSI_STCCR_DC(slots - 1); - writel(sccr, ssi->base + SSI_SRCCR); - - writel(~tx_mask, ssi->base + SSI_STMSK); - writel(~rx_mask, ssi->base + SSI_SRMSK); - - return 0; -} - -/* - * SSI DAI format configuration. - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 strcr = 0, scr; - - scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); - - /* DAI mode */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - /* data on rising edge of bclk, frame low 1clk before data */ - strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | - SSI_STCR_TEFS; - scr |= SSI_SCR_NET; - if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) { - scr &= ~SSI_I2S_MODE_MASK; - scr |= SSI_SCR_I2S_MODE_SLAVE; - } - break; - case SND_SOC_DAIFMT_LEFT_J: - /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP; - break; - case SND_SOC_DAIFMT_DSP_B: - /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL; - break; - case SND_SOC_DAIFMT_DSP_A: - /* data on rising edge of bclk, frame high 1clk before data */ - strcr |= SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSL | - SSI_STCR_TEFS; - break; - } - - /* DAI clock inversion */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_IB_IF: - strcr ^= SSI_STCR_TSCKP | SSI_STCR_TFSI; - break; - case SND_SOC_DAIFMT_IB_NF: - strcr ^= SSI_STCR_TSCKP; - break; - case SND_SOC_DAIFMT_NB_IF: - strcr ^= SSI_STCR_TFSI; - break; - case SND_SOC_DAIFMT_NB_NF: - break; - } - - /* DAI clock master masks */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - break; - default: - /* Master mode not implemented, needs handling of clocks. */ - return -EINVAL; - } - - strcr |= SSI_STCR_TFEN0; - - if (ssi->flags & IMX_SSI_NET) - scr |= SSI_SCR_NET; - if (ssi->flags & IMX_SSI_SYN) - scr |= SSI_SCR_SYN; - - writel(strcr, ssi->base + SSI_STCR); - writel(strcr, ssi->base + SSI_SRCR); - writel(scr, ssi->base + SSI_SCR); - - return 0; -} - -/* - * SSI system clock configuration. - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 scr; - - scr = readl(ssi->base + SSI_SCR); - - switch (clk_id) { - case IMX_SSP_SYS_CLK: - if (dir == SND_SOC_CLOCK_OUT) - scr |= SSI_SCR_SYS_CLK_EN; - else - scr &= ~SSI_SCR_SYS_CLK_EN; - break; - default: - return -EINVAL; - } - - writel(scr, ssi->base + SSI_SCR); - - return 0; -} - -/* - * SSI Clock dividers - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 stccr, srccr; - - stccr = readl(ssi->base + SSI_STCCR); - srccr = readl(ssi->base + SSI_SRCCR); - - switch (div_id) { - case IMX_SSI_TX_DIV_2: - stccr &= ~SSI_STCCR_DIV2; - stccr |= div; - break; - case IMX_SSI_TX_DIV_PSR: - stccr &= ~SSI_STCCR_PSR; - stccr |= div; - break; - case IMX_SSI_TX_DIV_PM: - stccr &= ~0xff; - stccr |= SSI_STCCR_PM(div); - break; - case IMX_SSI_RX_DIV_2: - stccr &= ~SSI_STCCR_DIV2; - stccr |= div; - break; - case IMX_SSI_RX_DIV_PSR: - stccr &= ~SSI_STCCR_PSR; - stccr |= div; - break; - case IMX_SSI_RX_DIV_PM: - stccr &= ~0xff; - stccr |= SSI_STCCR_PM(div); - break; - default: - return -EINVAL; - } - - writel(stccr, ssi->base + SSI_STCCR); - writel(srccr, ssi->base + SSI_SRCCR); - - return 0; -} - -/* - * Should only be called when port is inactive (i.e. SSIEN = 0), - * although can be called multiple times by upper layers. - */ -static int imx_ssi_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 reg, sccr; - - /* Tx/Rx config */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - reg = SSI_STCCR; - else - reg = SSI_SRCCR; - - if (ssi->flags & IMX_SSI_SYN) - reg = SSI_STCCR; - - sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; - - /* DAI data (word) size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - sccr |= SSI_SRCCR_WL(16); - break; - case SNDRV_PCM_FORMAT_S20_3LE: - sccr |= SSI_SRCCR_WL(20); - break; - case SNDRV_PCM_FORMAT_S24_LE: - sccr |= SSI_SRCCR_WL(24); - break; - } - - writel(sccr, ssi->base + reg); - - return 0; -} - -static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai); - unsigned int sier_bits, sier; - unsigned int scr; - - scr = readl(ssi->base + SSI_SCR); - sier = readl(ssi->base + SSI_SIER); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (ssi->flags & IMX_SSI_DMA) - sier_bits = SSI_SIER_TDMAE; - else - sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN; - } else { - if (ssi->flags & IMX_SSI_DMA) - sier_bits = SSI_SIER_RDMAE; - else - sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN; - } - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - scr |= SSI_SCR_TE; - else - scr |= SSI_SCR_RE; - sier |= sier_bits; - - scr |= SSI_SCR_SSIEN; - - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - scr &= ~SSI_SCR_TE; - else - scr &= ~SSI_SCR_RE; - sier &= ~sier_bits; - - if (!(scr & (SSI_SCR_TE | SSI_SCR_RE))) - scr &= ~SSI_SCR_SSIEN; - - break; - default: - return -EINVAL; - } - - if (!(ssi->flags & IMX_SSI_USE_AC97)) - /* rx/tx are always enabled to access ac97 registers */ - writel(scr, ssi->base + SSI_SCR); - - writel(sier, ssi->base + SSI_SIER); - - return 0; -} - -static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { - .hw_params = imx_ssi_hw_params, - .set_fmt = imx_ssi_set_dai_fmt, - .set_clkdiv = imx_ssi_set_dai_clkdiv, - .set_sysclk = imx_ssi_set_dai_sysclk, - .set_tdm_slot = imx_ssi_set_dai_tdm_slot, - .trigger = imx_ssi_trigger, -}; - -static int imx_ssi_dai_probe(struct snd_soc_dai *dai) -{ - struct imx_ssi *ssi = dev_get_drvdata(dai->dev); - uint32_t val; - - snd_soc_dai_set_drvdata(dai, ssi); - - val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.maxburst) | - SSI_SFCSR_RFWM0(ssi->dma_params_rx.maxburst); - writel(val, ssi->base + SSI_SFCSR); - - /* Tx/Rx config */ - dai->playback_dma_data = &ssi->dma_params_tx; - dai->capture_dma_data = &ssi->dma_params_rx; - - return 0; -} - -static struct snd_soc_dai_driver imx_ssi_dai = { - .probe = imx_ssi_dai_probe, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &imx_ssi_pcm_dai_ops, -}; - -static struct snd_soc_dai_driver imx_ac97_dai = { - .probe = imx_ssi_dai_probe, - .playback = { - .stream_name = "AC97 Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "AC97 Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &imx_ssi_pcm_dai_ops, -}; - -static const struct snd_soc_component_driver imx_component = { - .name = DRV_NAME, -}; - -static void setup_channel_to_ac97(struct imx_ssi *imx_ssi) -{ - void __iomem *base = imx_ssi->base; - - writel(0x0, base + SSI_SCR); - writel(0x0, base + SSI_STCR); - writel(0x0, base + SSI_SRCR); - - writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR); - - writel(SSI_SFCSR_RFWM0(8) | - SSI_SFCSR_TFWM0(8) | - SSI_SFCSR_RFWM1(8) | - SSI_SFCSR_TFWM1(8), base + SSI_SFCSR); - - writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR); - writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR); - - writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR); - writel(SSI_SOR_WAIT(3), base + SSI_SOR); - - writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN | - SSI_SCR_TE | SSI_SCR_RE, - base + SSI_SCR); - - writel(SSI_SACNT_DEFAULT, base + SSI_SACNT); - writel(0xff, base + SSI_SACCDIS); - writel(0x300, base + SSI_SACCEN); -} - -static struct imx_ssi *ac97_ssi; - -static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - void __iomem *base = imx_ssi->base; - unsigned int lreg; - unsigned int lval; - - if (reg > 0x7f) - return; - - pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); - - lreg = reg << 12; - writel(lreg, base + SSI_SACADD); - - lval = val << 4; - writel(lval , base + SSI_SACDAT); - - writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT); - udelay(100); -} - -static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, - unsigned short reg) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - void __iomem *base = imx_ssi->base; - - unsigned short val = -1; - unsigned int lreg; - - lreg = (reg & 0x7f) << 12 ; - writel(lreg, base + SSI_SACADD); - writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT); - - udelay(100); - - val = (readl(base + SSI_SACDAT) >> 4) & 0xffff; - - pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); - - return val; -} - -static void imx_ssi_ac97_reset(struct snd_ac97 *ac97) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - - if (imx_ssi->ac97_reset) - imx_ssi->ac97_reset(ac97); - /* First read sometimes fails, do a dummy read */ - imx_ssi_ac97_read(ac97, 0); -} - -static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - - if (imx_ssi->ac97_warm_reset) - imx_ssi->ac97_warm_reset(ac97); - - /* First read sometimes fails, do a dummy read */ - imx_ssi_ac97_read(ac97, 0); -} - -static struct snd_ac97_bus_ops imx_ssi_ac97_ops = { - .read = imx_ssi_ac97_read, - .write = imx_ssi_ac97_write, - .reset = imx_ssi_ac97_reset, - .warm_reset = imx_ssi_ac97_warm_reset -}; - -static int imx_ssi_probe(struct platform_device *pdev) -{ - struct resource *res; - struct imx_ssi *ssi; - struct imx_ssi_platform_data *pdata = pdev->dev.platform_data; - int ret = 0; - struct snd_soc_dai_driver *dai; - - ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL); - if (!ssi) - return -ENOMEM; - dev_set_drvdata(&pdev->dev, ssi); - - if (pdata) { - ssi->ac97_reset = pdata->ac97_reset; - ssi->ac97_warm_reset = pdata->ac97_warm_reset; - ssi->flags = pdata->flags; - } - - ssi->irq = platform_get_irq(pdev, 0); - if (ssi->irq < 0) - return ssi->irq; - - ssi->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(ssi->clk)) { - ret = PTR_ERR(ssi->clk); - dev_err(&pdev->dev, "Cannot get the clock: %d\n", - ret); - goto failed_clk; - } - ret = clk_prepare_enable(ssi->clk); - if (ret) - goto failed_clk; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ssi->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(ssi->base)) { - ret = PTR_ERR(ssi->base); - goto failed_register; - } - - if (ssi->flags & IMX_SSI_USE_AC97) { - if (ac97_ssi) { - dev_err(&pdev->dev, "AC'97 SSI already registered\n"); - ret = -EBUSY; - goto failed_register; - } - ac97_ssi = ssi; - setup_channel_to_ac97(ssi); - dai = &imx_ac97_dai; - } else - dai = &imx_ssi_dai; - - writel(0x0, ssi->base + SSI_SIER); - - ssi->dma_params_rx.addr = res->start + SSI_SRX0; - ssi->dma_params_tx.addr = res->start + SSI_STX0; - - ssi->dma_params_tx.maxburst = 6; - ssi->dma_params_rx.maxburst = 4; - - ssi->dma_params_tx.filter_data = &ssi->filter_data_tx; - ssi->dma_params_rx.filter_data = &ssi->filter_data_rx; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); - if (res) { - imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start, - IMX_DMATYPE_SSI); - } - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); - if (res) { - imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start, - IMX_DMATYPE_SSI); - } - - platform_set_drvdata(pdev, ssi); - - ret = snd_soc_set_ac97_ops(&imx_ssi_ac97_ops); - if (ret != 0) { - dev_err(&pdev->dev, "Failed to set AC'97 ops: %d\n", ret); - goto failed_register; - } - - ret = snd_soc_register_component(&pdev->dev, &imx_component, - dai, 1); - if (ret) { - dev_err(&pdev->dev, "register DAI failed\n"); - goto failed_register; - } - - ssi->fiq_params.irq = ssi->irq; - ssi->fiq_params.base = ssi->base; - ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx; - ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx; - - ssi->fiq_init = imx_pcm_fiq_init(pdev, &ssi->fiq_params); - ssi->dma_init = imx_pcm_dma_init(pdev, IMX_SSI_DMABUF_SIZE); - - if (ssi->fiq_init && ssi->dma_init) { - ret = ssi->fiq_init; - goto failed_pcm; - } - - return 0; - -failed_pcm: - snd_soc_unregister_component(&pdev->dev); -failed_register: - clk_disable_unprepare(ssi->clk); -failed_clk: - snd_soc_set_ac97_ops(NULL); - - return ret; -} - -static int imx_ssi_remove(struct platform_device *pdev) -{ - struct imx_ssi *ssi = platform_get_drvdata(pdev); - - if (!ssi->fiq_init) - imx_pcm_fiq_exit(pdev); - - snd_soc_unregister_component(&pdev->dev); - - if (ssi->flags & IMX_SSI_USE_AC97) - ac97_ssi = NULL; - - clk_disable_unprepare(ssi->clk); - snd_soc_set_ac97_ops(NULL); - - return 0; -} - -static struct platform_driver imx_ssi_driver = { - .probe = imx_ssi_probe, - .remove = imx_ssi_remove, - - .driver = { - .name = "imx-ssi", - }, -}; - -module_platform_driver(imx_ssi_driver); - -/* Module information */ -MODULE_AUTHOR("Sascha Hauer, "); -MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-ssi"); -- cgit v1.2.3 From 674226db62ec758c4575bcdb933a2410f1a29bbf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 10 Nov 2020 15:51:20 +0100 Subject: ASoC: fsl: SND_SOC_FSL_AUD2HTX should depend on ARCH_MXC The Freescale/NXP AUDIO TO HDMI TX module is only present on NXP i.MX 8 Series SoCs. Hence add a dependency on ARCH_MXC, to prevent asking the user about this driver when configuring a kernel without i.MX 8 platform support. Fixes: 8a24c834c053ef1b ("ASoC: fsl_aud2htx: Add aud2htx module driver") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20201110145120.3280658-1-geert+renesas@glider.be Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index a299f61e529e..06a2d225d644 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -107,6 +107,7 @@ config SND_SOC_FSL_XCVR config SND_SOC_FSL_AUD2HTX tristate "AUDIO TO HDMI TX module support" + depends on CONFIG_ARCH_MXC || COMPILE_TEST help Say Y if you want to add AUDIO TO HDMI TX support for NXP. -- cgit v1.2.3 From 53233e40c142b1e0e1df9d9ac0ffc0945cfffbc9 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 19 Nov 2020 14:40:38 +0800 Subject: ASoC: fsl_sai: Correct the clock source for mclk0 On VF610, mclk0 = bus_clk; On i.MX6SX/6UL/6ULL/7D, mclk0 = mclk1; On i.MX7ULP, mclk0 = bus_clk; On i.MX8QM/8QXP, mclk0 = bus_clk; On i.MX8MQ/8MN/8MM/8MP, mclk0 = bus_clk; So add variable mclk0_is_mclk1 in fsl_sai_soc_data to distinguish these platforms. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1605768038-4582-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 20 ++++++++++++++++++-- sound/soc/fsl/fsl_sai.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 3e5c1eaccd5e..f3d3d20d35d7 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -359,7 +359,14 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) if (sai->is_slave_mode) return 0; - for (id = 0; id < FSL_SAI_MCLK_MAX; id++) { + /* + * There is no point in polling MCLK0 if it is identical to MCLK1. + * And given that MQS use case has to use MCLK1 though two clocks + * are the same, we simply skip MCLK0 and start to find from MCLK1. + */ + id = sai->soc_data->mclk0_is_mclk1 ? 1 : 0; + + for (; id < FSL_SAI_MCLK_MAX; id++) { clk_rate = clk_get_rate(sai->mclk_clk[id]); if (!clk_rate) continue; @@ -1040,7 +1047,6 @@ static int fsl_sai_probe(struct platform_device *pdev) sai->bus_clk = NULL; } - sai->mclk_clk[0] = sai->bus_clk; for (i = 1; i < FSL_SAI_MCLK_MAX; i++) { sprintf(tmp, "mclk%d", i); sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp); @@ -1051,6 +1057,11 @@ static int fsl_sai_probe(struct platform_device *pdev) } } + if (sai->soc_data->mclk0_is_mclk1) + sai->mclk_clk[0] = sai->mclk_clk[1]; + else + sai->mclk_clk[0] = sai->bus_clk; + irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -1165,6 +1176,7 @@ static const struct fsl_sai_soc_data fsl_sai_vf610_data = { .use_edma = false, .fifo_depth = 32, .reg_offset = 0, + .mclk0_is_mclk1 = false, }; static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = { @@ -1172,6 +1184,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx6sx_data = { .use_edma = false, .fifo_depth = 32, .reg_offset = 0, + .mclk0_is_mclk1 = true, }; static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = { @@ -1179,6 +1192,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx7ulp_data = { .use_edma = false, .fifo_depth = 16, .reg_offset = 8, + .mclk0_is_mclk1 = false, }; static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = { @@ -1186,6 +1200,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8mq_data = { .use_edma = false, .fifo_depth = 128, .reg_offset = 8, + .mclk0_is_mclk1 = false, }; static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = { @@ -1193,6 +1208,7 @@ static const struct fsl_sai_soc_data fsl_sai_imx8qm_data = { .use_edma = true, .fifo_depth = 64, .reg_offset = 0, + .mclk0_is_mclk1 = false, }; static const struct of_device_id fsl_sai_ids[] = { diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 4bbcd0dbe8f1..ff2619f1b214 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -219,6 +219,7 @@ struct fsl_sai_soc_data { bool use_imx_pcm; bool use_edma; + bool mclk0_is_mclk1; unsigned int fifo_depth; unsigned int reg_offset; }; -- cgit v1.2.3 From 6a8b8b582db13a18235f3b0400f103a0573c7859 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 23 Nov 2020 17:09:17 -0300 Subject: ASoC: imx-audmux: Remove unused .id_table Since 5.10-rc1 i.MX is a devicetree-only platform and the existing .id_table support in this driver was only useful for old non-devicetree platforms. Get rid of the .id_table since it is no longer used. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201123200917.16447-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audmux.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 25c18b9e348f..dfa05d40b276 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -170,22 +170,9 @@ static enum imx_audmux_type { IMX31_AUDMUX, } audmux_type; -static const struct platform_device_id imx_audmux_ids[] = { - { - .name = "imx21-audmux", - .driver_data = IMX21_AUDMUX, - }, { - .name = "imx31-audmux", - .driver_data = IMX31_AUDMUX, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, imx_audmux_ids); - static const struct of_device_id imx_audmux_dt_ids[] = { - { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, - { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, + { .compatible = "fsl,imx21-audmux", .data = (void *)IMX21_AUDMUX, }, + { .compatible = "fsl,imx31-audmux", .data = (void *)IMX31_AUDMUX, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); @@ -300,9 +287,6 @@ static int imx_audmux_parse_dt_defaults(struct platform_device *pdev, static int imx_audmux_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(imx_audmux_dt_ids, &pdev->dev); - audmux_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(audmux_base)) return PTR_ERR(audmux_base); @@ -314,9 +298,7 @@ static int imx_audmux_probe(struct platform_device *pdev) audmux_clk = NULL; } - if (of_id) - pdev->id_entry = of_id->data; - audmux_type = pdev->id_entry->driver_data; + audmux_type = (enum imx_audmux_type)of_device_get_match_data(&pdev->dev); switch (audmux_type) { case IMX31_AUDMUX: @@ -335,8 +317,7 @@ static int imx_audmux_probe(struct platform_device *pdev) if (!regcache) return -ENOMEM; - if (of_id) - imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); + imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); return 0; } @@ -386,7 +367,6 @@ static const struct dev_pm_ops imx_audmux_pm = { static struct platform_driver imx_audmux_driver = { .probe = imx_audmux_probe, .remove = imx_audmux_remove, - .id_table = imx_audmux_ids, .driver = { .name = DRIVER_NAME, .pm = &imx_audmux_pm, -- cgit v1.2.3 From 373c2cebf42772434c8dd0deffc3b3886ea8f1eb Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Tue, 24 Nov 2020 16:19:57 +0200 Subject: ASoC: fsl_xcvr: fix potential resource leak "fw" variable must be relased before return. Signed-off-by: Viorel Suman Link: https://lore.kernel.org/r/20201124141957.20481-1-viorel.suman@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_xcvr.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c index 2a28810d0e29..3d58c88ea603 100644 --- a/sound/soc/fsl/fsl_xcvr.c +++ b/sound/soc/fsl/fsl_xcvr.c @@ -706,6 +706,7 @@ static int fsl_xcvr_load_firmware(struct fsl_xcvr *xcvr) /* RAM is 20KiB = 16KiB code + 4KiB data => max 10 pages 2KiB each */ if (rem > 16384) { dev_err(dev, "FW size %d is bigger than 16KiB.\n", rem); + release_firmware(fw); return -ENOMEM; } -- cgit v1.2.3 From c61d1142cfd45f58b63bf9d2d59523f91096e873 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 26 Nov 2020 14:14:53 +0800 Subject: ASoC: fsl: Fix config name of CONFIG_ARCH_MXC CONFIG_ARCH_MXC should be ARCH_MXC Fixes: 674226db62ec ("ASoC: fsl: SND_SOC_FSL_AUD2HTX should depend on ARCH_MXC") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1606371293-29099-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 06a2d225d644..24710decd38a 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -107,7 +107,7 @@ config SND_SOC_FSL_XCVR config SND_SOC_FSL_AUD2HTX tristate "AUDIO TO HDMI TX module support" - depends on CONFIG_ARCH_MXC || COMPILE_TEST + depends on ARCH_MXC || COMPILE_TEST help Say Y if you want to add AUDIO TO HDMI TX support for NXP. -- cgit v1.2.3 From 5057d108d69a55f97f3e436aefbabb9c4064d9d6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 2 Dec 2020 22:34:39 -0300 Subject: ASoC: fsl_audmix: Remove unneeded data field The .data field is only used to pass the string name to platform_device_register_data(). Pass the string name directly to make the code simpler. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201203013439.10617-1-festevam@gmail.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index 7ad5925772e8..8dc44dec7956 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -455,7 +455,6 @@ static const struct regmap_config fsl_audmix_regmap_config = { static const struct of_device_id fsl_audmix_ids[] = { { .compatible = "fsl,imx8qm-audmix", - .data = "imx-audmix", }, { /* sentinel */ } }; @@ -465,17 +464,9 @@ static int fsl_audmix_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct fsl_audmix *priv; - const char *mdrv; - const struct of_device_id *of_id; void __iomem *regs; int ret; - of_id = of_match_device(fsl_audmix_ids, dev); - if (!of_id || !of_id->data) - return -EINVAL; - - mdrv = of_id->data; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -510,10 +501,10 @@ static int fsl_audmix_probe(struct platform_device *pdev) goto err_disable_pm; } - priv->pdev = platform_device_register_data(dev, mdrv, 0, NULL, 0); + priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0); if (IS_ERR(priv->pdev)) { ret = PTR_ERR(priv->pdev); - dev_err(dev, "failed to register platform %s: %d\n", mdrv, ret); + dev_err(dev, "failed to register platform: %d\n", ret); goto err_disable_pm; } -- cgit v1.2.3 From 77f1ff751037fcd39c8fc37b3c3796fb139fb388 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 30 Nov 2020 11:57:47 +0800 Subject: ASoC: fsl-asoc-card: Add support for si476x codec The si476x codec is used for FM radio function on i.MX6 auto board, it only supports recording function. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1606708668-28786-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index a2dd3b6b7fec..f62f81ceab0d 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -131,6 +131,13 @@ static const struct snd_soc_dapm_route audio_map_tx[] = { {"CPU-Playback", NULL, "ASRC-Playback"}, }; +static const struct snd_soc_dapm_route audio_map_rx[] = { + /* 1st half -- Normal DAPM routes */ + {"CPU-Capture", NULL, "Capture"}, + /* 2nd half -- ASRC DAPM routes */ + {"ASRC-Capture", NULL, "CPU-Capture"}, +}; + /* Add all possible widgets into here without being redundant */ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = { SND_SOC_DAPM_LINE("Line Out Jack", NULL), @@ -653,6 +660,11 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->cpu_priv.slot_width = 32; priv->card.dapm_routes = audio_map_tx; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx); + } else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) { + codec_dai_name = "si476x-codec"; + priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + priv->card.dapm_routes = audio_map_rx; + priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_rx); } else { dev_err(&pdev->dev, "unknown Device Tree compatible\n"); ret = -EINVAL; @@ -869,6 +881,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-wm8960", }, { .compatible = "fsl,imx-audio-mqs", }, { .compatible = "fsl,imx-audio-wm8524", }, + { .compatible = "fsl,imx-audio-si476x", }, {} }; MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids); -- cgit v1.2.3 From 7b153760513cee875515398f4a9ba329a8d426e2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 3 Dec 2020 23:28:47 +0100 Subject: ASoC: fsl_aud2htx: mark PM functions as __maybe_unused When CONFIG_PM is disabled, we get a warning for unused functions: sound/soc/fsl/fsl_aud2htx.c:261:12: error: unused function 'fsl_aud2htx_runtime_suspend' [-Werror,-Wunused-function] static int fsl_aud2htx_runtime_suspend(struct device *dev) sound/soc/fsl/fsl_aud2htx.c:271:12: error: unused function 'fsl_aud2htx_runtime_resume' [-Werror,-Wunused-function] static int fsl_aud2htx_runtime_resume(struct device *dev) Mark these as __maybe_unused to avoid the warning without adding an #ifdef. Fixes: 8a24c834c053 ("ASoC: fsl_aud2htx: Add aud2htx module driver") Signed-off-by: Arnd Bergmann Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/20201203222900.1042578-1-arnd@kernel.org Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_aud2htx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/fsl_aud2htx.c b/sound/soc/fsl/fsl_aud2htx.c index 4091ccc7c3e9..d70d5e75a30c 100644 --- a/sound/soc/fsl/fsl_aud2htx.c +++ b/sound/soc/fsl/fsl_aud2htx.c @@ -258,7 +258,7 @@ static int fsl_aud2htx_remove(struct platform_device *pdev) return 0; } -static int fsl_aud2htx_runtime_suspend(struct device *dev) +static int __maybe_unused fsl_aud2htx_runtime_suspend(struct device *dev) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); @@ -268,7 +268,7 @@ static int fsl_aud2htx_runtime_suspend(struct device *dev) return 0; } -static int fsl_aud2htx_runtime_resume(struct device *dev) +static int __maybe_unused fsl_aud2htx_runtime_resume(struct device *dev) { struct fsl_aud2htx *aud2htx = dev_get_drvdata(dev); int ret; -- cgit v1.2.3 From 6a5f850aa83a1d844d27e3e53ca2f247e55d438b Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Sun, 6 Dec 2020 18:41:59 +0800 Subject: ASoC: fsl: Add imx-hdmi machine driver The driver is initially designed for sound card using HDMI interface on i.MX platform. There is internal HDMI IP or external HDMI modules connect with SAI or AUD2HTX interface. It supports both transmitter and receiver devices. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1607251319-5821-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 12 +++ sound/soc/fsl/Makefile | 2 + sound/soc/fsl/imx-hdmi.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 sound/soc/fsl/imx-hdmi.c (limited to 'sound/soc/fsl') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 24710decd38a..84db0b7b9d59 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -305,6 +305,18 @@ config SND_SOC_IMX_AUDMIX Say Y if you want to add support for SoC audio on an i.MX board with an Audio Mixer. +config SND_SOC_IMX_HDMI + tristate "SoC Audio support for i.MX boards with HDMI port" + select SND_SOC_FSL_SAI + select SND_SOC_FSL_AUD2HTX + select SND_SOC_HDMI_CODEC + help + ALSA SoC Audio support with HDMI feature for Freescale SoCs that have + SAI/AUD2HTX and connect with internal HDMI IP or external module + SII902X. + Say Y if you want to add support for SoC audio on an i.MX board with + IMX HDMI. + endif # SND_IMX_SOC endmenu diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 0b20e038b65b..8c5fa8a859c0 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -65,9 +65,11 @@ snd-soc-imx-es8328-objs := imx-es8328.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-spdif-objs := imx-spdif.o snd-soc-imx-audmix-objs := imx-audmix.o +snd-soc-imx-hdmi-objs := imx-hdmi.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o +obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c new file mode 100644 index 000000000000..2c2a76a71940 --- /dev/null +++ b/sound/soc/fsl/imx-hdmi.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright 2017-2020 NXP + +#include +#include +#include +#include +#include +#include "fsl_sai.h" + +/** + * struct cpu_priv - CPU private data + * @sysclk_freq: SYSCLK rates for set_sysclk() + * @sysclk_dir: SYSCLK directions for set_sysclk() + * @sysclk_id: SYSCLK ids for set_sysclk() + * @slot_width: Slot width of each frame + * + * Note: [1] for tx and [0] for rx + */ +struct cpu_priv { + unsigned long sysclk_freq[2]; + u32 sysclk_dir[2]; + u32 sysclk_id[2]; + u32 slot_width; +}; + +struct imx_hdmi_data { + struct snd_soc_dai_link dai; + struct snd_soc_card card; + struct snd_soc_jack hdmi_jack; + struct snd_soc_jack_pin hdmi_jack_pin; + struct cpu_priv cpu_priv; + u32 dai_fmt; +}; + +static int imx_hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct imx_hdmi_data *data = snd_soc_card_get_drvdata(rtd->card); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + struct snd_soc_card *card = rtd->card; + struct device *dev = card->dev; + u32 slot_width = data->cpu_priv.slot_width; + int ret; + + /* MCLK always is (256 or 192) * rate. */ + ret = snd_soc_dai_set_sysclk(cpu_dai, data->cpu_priv.sysclk_id[tx], + 8 * slot_width * params_rate(params), + tx ? SND_SOC_CLOCK_OUT : SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set cpu sysclk: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0, 2, slot_width); + if (ret && ret != -ENOTSUPP) { + dev_err(dev, "failed to set cpu dai tdm slot: %d\n", ret); + return ret; + } + + return 0; +} + +static struct snd_soc_ops imx_hdmi_ops = { + .hw_params = imx_hdmi_hw_params, +}; + +static const struct snd_soc_dapm_widget imx_hdmi_widgets[] = { + SND_SOC_DAPM_LINE("HDMI Jack", NULL), +}; + +static int imx_hdmi_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + struct imx_hdmi_data *data = snd_soc_card_get_drvdata(card); + int ret; + + data->hdmi_jack_pin.pin = "HDMI Jack"; + data->hdmi_jack_pin.mask = SND_JACK_LINEOUT; + /* enable jack detection */ + ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT, + &data->hdmi_jack, &data->hdmi_jack_pin, 1); + if (ret) { + dev_err(card->dev, "Can't new HDMI Jack %d\n", ret); + return ret; + } + + ret = snd_soc_component_set_jack(component, &data->hdmi_jack, NULL); + if (ret && ret != -EOPNOTSUPP) { + dev_err(card->dev, "Can't set HDMI Jack %d\n", ret); + return ret; + } + + return 0; +}; + +static int imx_hdmi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + bool hdmi_out = of_property_read_bool(np, "hdmi-out"); + bool hdmi_in = of_property_read_bool(np, "hdmi-in"); + struct snd_soc_dai_link_component *dlc; + struct platform_device *cpu_pdev; + struct device_node *cpu_np; + struct imx_hdmi_data *data; + int ret; + + dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL); + if (!dlc) + return -ENOMEM; + + cpu_np = of_parse_phandle(np, "audio-cpu", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n"); + ret = -EINVAL; + goto fail; + } + + cpu_pdev = of_find_device_by_node(cpu_np); + if (!cpu_pdev) { + dev_err(&pdev->dev, "failed to find SAI platform device\n"); + ret = -EINVAL; + goto fail; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto fail; + } + + data->dai.cpus = &dlc[0]; + data->dai.num_cpus = 1; + data->dai.platforms = &dlc[1]; + data->dai.num_platforms = 1; + data->dai.codecs = &dlc[2]; + data->dai.num_codecs = 1; + + data->dai.name = "i.MX HDMI"; + data->dai.stream_name = "i.MX HDMI"; + data->dai.cpus->dai_name = dev_name(&cpu_pdev->dev); + data->dai.platforms->of_node = cpu_np; + data->dai.ops = &imx_hdmi_ops; + data->dai.playback_only = true; + data->dai.capture_only = false; + data->dai.init = imx_hdmi_init; + + if (of_node_name_eq(cpu_np, "sai")) { + data->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; + data->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; + } + + if (of_device_is_compatible(np, "fsl,imx-audio-sii902x")) { + data->dai_fmt = SND_SOC_DAIFMT_LEFT_J; + data->cpu_priv.slot_width = 24; + } else { + data->dai_fmt = SND_SOC_DAIFMT_I2S; + data->cpu_priv.slot_width = 32; + } + + if ((hdmi_out && hdmi_in) || (!hdmi_out && !hdmi_in)) { + dev_err(&pdev->dev, "Invalid HDMI DAI link\n"); + goto fail; + } + + if (hdmi_out) { + data->dai.playback_only = true; + data->dai.capture_only = false; + data->dai.codecs->dai_name = "i2s-hifi"; + data->dai.codecs->name = "hdmi-audio-codec.1"; + data->dai.dai_fmt = data->dai_fmt | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + } + + if (hdmi_in) { + data->dai.playback_only = false; + data->dai.capture_only = true; + data->dai.codecs->dai_name = "i2s-hifi"; + data->dai.codecs->name = "hdmi-audio-codec.2"; + data->dai.dai_fmt = data->dai_fmt | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + } + + data->card.dapm_widgets = imx_hdmi_widgets; + data->card.num_dapm_widgets = ARRAY_SIZE(imx_hdmi_widgets); + data->card.dev = &pdev->dev; + data->card.owner = THIS_MODULE; + ret = snd_soc_of_parse_card_name(&data->card, "model"); + if (ret) + goto fail; + + data->card.num_links = 1; + data->card.dai_link = &data->dai; + + snd_soc_card_set_drvdata(&data->card, data); + ret = devm_snd_soc_register_card(&pdev->dev, &data->card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + goto fail; + } + +fail: + if (cpu_np) + of_node_put(cpu_np); + + return ret; +} + +static const struct of_device_id imx_hdmi_dt_ids[] = { + { .compatible = "fsl,imx-audio-hdmi", }, + { .compatible = "fsl,imx-audio-sii902x", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids); + +static struct platform_driver imx_hdmi_driver = { + .driver = { + .name = "imx-hdmi", + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = imx_hdmi_dt_ids, + }, + .probe = imx_hdmi_probe, +}; +module_platform_driver(imx_hdmi_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale i.MX hdmi audio ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:imx-hdmi"); -- cgit v1.2.3