mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-03 07:38:10 +00:00
Add support for XCVR on i.MX93 platform
Merge series from Chancel Liu <chancel.liu@nxp.com>: This patchset supports XCVR on i.MX93 platform. changes in v2: - remove unnecessary code which causes kernel test robot reporting error Chancel Liu (3): ASoC: dt-bindings: fsl,xcvr: Add compatible string for i.MX93 platform ASoC: fsl_xcvr: Add support for i.MX93 platform ASoC: fsl_xcvr: Add constraints of period size while using eDMA .../devicetree/bindings/sound/fsl,xcvr.yaml | 1 + sound/soc/fsl/fsl_xcvr.c | 155 ++++++++++++------ sound/soc/fsl/fsl_xcvr.h | 7 + 3 files changed, 115 insertions(+), 48 deletions(-) -- 2.25.1
This commit is contained in:
commit
8c187e2212
3 changed files with 115 additions and 48 deletions
|
@ -21,6 +21,7 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- fsl,imx8mp-xcvr
|
||||
- fsl,imx93-xcvr
|
||||
|
||||
reg:
|
||||
items:
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
struct fsl_xcvr_soc_data {
|
||||
const char *fw_name;
|
||||
bool spdif_only;
|
||||
bool use_edma;
|
||||
};
|
||||
|
||||
struct fsl_xcvr {
|
||||
|
@ -261,6 +263,9 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
|
|||
u32 i, div = 0, log2;
|
||||
int ret;
|
||||
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
|
@ -353,6 +358,7 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
|
|||
struct device *dev = &xcvr->pdev->dev;
|
||||
int ret;
|
||||
|
||||
freq = xcvr->soc_data->spdif_only ? freq / 10 : freq;
|
||||
clk_disable_unprepare(xcvr->phy_clk);
|
||||
ret = clk_set_rate(xcvr->phy_clk, freq);
|
||||
if (ret < 0) {
|
||||
|
@ -365,6 +371,8 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
return 0;
|
||||
/* Release AI interface from reset */
|
||||
ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
|
||||
FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
|
||||
|
@ -531,6 +539,16 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* EDMA controller needs period size to be a multiple of
|
||||
* tx/rx maxburst
|
||||
*/
|
||||
if (xcvr->soc_data->use_edma)
|
||||
snd_pcm_hw_constraint_step(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
tx ? xcvr->dma_prms_tx.maxburst :
|
||||
xcvr->dma_prms_rx.maxburst);
|
||||
|
||||
switch (xcvr->mode) {
|
||||
case FSL_XCVR_MODE_SPDIF:
|
||||
case FSL_XCVR_MODE_ARC:
|
||||
|
@ -547,10 +565,12 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
|
|||
|
||||
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);
|
||||
if (!xcvr->soc_data->spdif_only) {
|
||||
/* 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;
|
||||
}
|
||||
|
@ -567,12 +587,13 @@ static void fsl_xcvr_shutdown(struct snd_pcm_substream *substream,
|
|||
|
||||
/* 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));
|
||||
|
||||
if (!xcvr->soc_data->spdif_only) {
|
||||
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) {
|
||||
|
@ -673,7 +694,10 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
fallthrough;
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
break;
|
||||
else
|
||||
fallthrough;
|
||||
case FSL_XCVR_MODE_EARC:
|
||||
/* clear ISR_CMDC_TX_EN, W1C */
|
||||
ret = regmap_write(xcvr->regmap,
|
||||
|
@ -877,9 +901,13 @@ static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai)
|
|||
|
||||
snd_soc_dai_init_dma_data(dai, &xcvr->dma_prms_tx, &xcvr->dma_prms_rx);
|
||||
|
||||
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);
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
xcvr->mode = FSL_XCVR_MODE_SPDIF;
|
||||
else {
|
||||
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,
|
||||
|
@ -930,10 +958,11 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = {
|
|||
{ 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_CLK_CTRL, 0x0000018F },
|
||||
{ FSL_XCVR_RX_DPTH_CTRL, 0x00040CC1 },
|
||||
{ FSL_XCVR_RX_DPTH_CTRL_SET, 0x00040CC1 },
|
||||
{ FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00040CC1 },
|
||||
{ FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00040CC1 },
|
||||
{ FSL_XCVR_RX_DPTH_CNTR_CTRL, 0x00000000 },
|
||||
{ FSL_XCVR_RX_DPTH_CNTR_CTRL_SET, 0x00000000 },
|
||||
{ FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR, 0x00000000 },
|
||||
|
@ -966,6 +995,12 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = {
|
|||
|
||||
static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
|
||||
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
if ((reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) ||
|
||||
reg > FSL_XCVR_TX_DPTH_BCRR)
|
||||
return false;
|
||||
switch (reg) {
|
||||
case FSL_XCVR_VERSION:
|
||||
case FSL_XCVR_EXT_CTRL:
|
||||
|
@ -991,6 +1026,12 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
|
|||
case FSL_XCVR_RX_DPTH_CTRL_SET:
|
||||
case FSL_XCVR_RX_DPTH_CTRL_CLR:
|
||||
case FSL_XCVR_RX_DPTH_CTRL_TOG:
|
||||
case FSL_XCVR_RX_CS_DATA_0:
|
||||
case FSL_XCVR_RX_CS_DATA_1:
|
||||
case FSL_XCVR_RX_CS_DATA_2:
|
||||
case FSL_XCVR_RX_CS_DATA_3:
|
||||
case FSL_XCVR_RX_CS_DATA_4:
|
||||
case FSL_XCVR_RX_CS_DATA_5:
|
||||
case FSL_XCVR_RX_DPTH_CNTR_CTRL:
|
||||
case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
|
||||
case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
|
||||
|
@ -1027,6 +1068,11 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
|
|||
|
||||
static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
|
||||
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
if (reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA)
|
||||
return false;
|
||||
switch (reg) {
|
||||
case FSL_XCVR_EXT_CTRL:
|
||||
case FSL_XCVR_EXT_IER0:
|
||||
|
@ -1103,32 +1149,34 @@ static irqreturn_t irq0_isr(int irq, void *devid)
|
|||
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));
|
||||
if (!xcvr->soc_data->spdif_only) {
|
||||
/* 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;
|
||||
/* 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) {
|
||||
/* 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);
|
||||
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));
|
||||
}
|
||||
/* clear CS control register */
|
||||
memset_io(reg_ctrl, 0, sizeof(val));
|
||||
}
|
||||
}
|
||||
if (isr & FSL_XCVR_IRQ_NEW_UD) {
|
||||
|
@ -1168,8 +1216,14 @@ static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = {
|
|||
.fw_name = "imx/xcvr/xcvr-imx8mp.bin",
|
||||
};
|
||||
|
||||
static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = {
|
||||
.spdif_only = true,
|
||||
.use_edma = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id fsl_xcvr_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data },
|
||||
{ .compatible = "fsl,imx93-xcvr", .data = &fsl_xcvr_imx93_data},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids);
|
||||
|
@ -1229,7 +1283,7 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(xcvr->regmap);
|
||||
}
|
||||
|
||||
xcvr->reset = devm_reset_control_get_exclusive(dev, NULL);
|
||||
xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(xcvr->reset)) {
|
||||
dev_err(dev, "failed to get XCVR reset control\n");
|
||||
return PTR_ERR(xcvr->reset);
|
||||
|
@ -1306,12 +1360,14 @@ static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev)
|
|||
if (ret < 0)
|
||||
dev_err(dev, "Failed to clear IER0: %d\n", 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);
|
||||
if (!xcvr->soc_data->spdif_only) {
|
||||
/* 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);
|
||||
}
|
||||
|
||||
regcache_cache_only(xcvr->regmap, true);
|
||||
|
||||
|
@ -1367,6 +1423,9 @@ static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev)
|
|||
goto stop_spba_clk;
|
||||
}
|
||||
|
||||
if (xcvr->soc_data->spdif_only)
|
||||
return 0;
|
||||
|
||||
ret = reset_control_deassert(xcvr->reset);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to deassert M0+ reset.\n");
|
||||
|
|
|
@ -49,6 +49,13 @@
|
|||
#define FSL_XCVR_RX_DPTH_CTRL_CLR 0x188
|
||||
#define FSL_XCVR_RX_DPTH_CTRL_TOG 0x18c
|
||||
|
||||
#define FSL_XCVR_RX_CS_DATA_0 0x190
|
||||
#define FSL_XCVR_RX_CS_DATA_1 0x194
|
||||
#define FSL_XCVR_RX_CS_DATA_2 0x198
|
||||
#define FSL_XCVR_RX_CS_DATA_3 0x19C
|
||||
#define FSL_XCVR_RX_CS_DATA_4 0x1A0
|
||||
#define FSL_XCVR_RX_CS_DATA_5 0x1A4
|
||||
|
||||
#define FSL_XCVR_RX_DPTH_CNTR_CTRL 0x1C0
|
||||
#define FSL_XCVR_RX_DPTH_CNTR_CTRL_SET 0x1C4
|
||||
#define FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR 0x1C8
|
||||
|
|
Loading…
Reference in a new issue