Add support for enabling I2S controller on FSD platform. FSD I2S controller is based on Exynos7 I2S controller, supporting 2CH playback/capture in I2S mode and 7.1CH playback/capture in TDM mode. Reported-by: kernel test robot <lkp@xxxxxxxxx> Signed-off-by: Padmanabhan Rajanbabu <p.rajanbabu@xxxxxxxxxxx> --- sound/soc/samsung/i2s-regs.h | 1 + sound/soc/samsung/i2s.c | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h index b4b5d6053503..138e95581979 100644 --- a/sound/soc/samsung/i2s-regs.h +++ b/sound/soc/samsung/i2s-regs.h @@ -132,6 +132,7 @@ #define EXYNOS7_MOD_RCLK_192FS 7 #define PSR_PSREN (1 << 15) +#define PSR_PSVAL(x) ((((x) - 1) << 8) & 0x3f00) #define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) #define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 9505200f3d11..6f96032090de 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -50,6 +50,10 @@ struct samsung_i2s_dai_data { u32 quirks; unsigned int pcm_rates; const struct samsung_i2s_variant_regs *i2s_variant_regs; + void (*fixup_early)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + void (*fixup_late)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); }; struct i2s_dai { @@ -111,6 +115,10 @@ struct samsung_i2s_priv { u32 suspend_i2spsr; const struct samsung_i2s_variant_regs *variant_regs; + void (*fixup_early)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); + void (*fixup_late)(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); u32 quirks; /* The clock provider's data */ @@ -940,6 +948,10 @@ static int i2s_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: pm_runtime_get_sync(dai->dev); + + if (priv->fixup_early) + priv->fixup_early(substream, dai); + spin_lock_irqsave(&priv->lock, flags); if (config_setup(i2s)) { @@ -947,6 +959,9 @@ static int i2s_trigger(struct snd_pcm_substream *substream, return -EINVAL; } + if (priv->fixup_late) + priv->fixup_late(substream, dai); + if (capture) i2s_rxctrl(i2s, 1); else @@ -1410,6 +1425,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (np) { priv->quirks = i2s_dai_data->quirks; + priv->fixup_early = i2s_dai_data->fixup_early; + priv->fixup_late = i2s_dai_data->fixup_late; } else { if (!i2s_pdata) { dev_err(&pdev->dev, "Missing platform data\n"); @@ -1563,6 +1580,31 @@ static int samsung_i2s_remove(struct platform_device *pdev) return 0; } +static void fsd_i2s_fixup_early(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); + struct i2s_dai *other = get_other_dai(i2s); + + if (!is_opened(other)) { + i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 0, SND_SOC_CLOCK_OUT); + i2s_set_sysclk(dai, SAMSUNG_I2S_OPCLK, 0, MOD_OPCLK_PCLK); + } +} + +static void fsd_i2s_fixup_late(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); + struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); + struct i2s_dai *other = get_other_dai(i2s); + + if (!is_opened(other)) + writel(PSR_PSVAL(2) | PSR_PSREN, priv->addr + I2SPSR); +} + static const struct samsung_i2s_variant_regs i2sv3_regs = { .bfs_off = 1, .rfs_off = 3, @@ -1652,6 +1694,14 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 __maybe_unused = { .i2s_variant_regs = &i2sv5_i2s1_regs, }; +static const struct samsung_i2s_dai_data fsd_dai_type __maybe_unused = { + .quirks = QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, + .pcm_rates = SNDRV_PCM_RATE_8000_192000, + .i2s_variant_regs = &i2sv7_regs, + .fixup_early = fsd_i2s_fixup_early, + .fixup_late = fsd_i2s_fixup_late, +}; + static const struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", @@ -1678,6 +1728,9 @@ static const struct of_device_id exynos_i2s_match[] = { }, { .compatible = "samsung,exynos7-i2s1", .data = &i2sv5_dai_type_i2s1, + }, { + .compatible = "tesla,fsd-i2s", + .data = &fsd_dai_type, }, {}, }; -- 2.17.1