diff options
Diffstat (limited to 'sound/soc/samsung/i2s.c')
| -rw-r--r-- | sound/soc/samsung/i2s.c | 93 |
1 files changed, 71 insertions, 22 deletions
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 309badc97290..e9964f0e010a 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -13,8 +13,6 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <sound/soc.h> @@ -50,6 +48,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 +113,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 */ @@ -671,11 +677,11 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: tmp |= mod_slave; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_BP_FP: /* * Set default source clock in Master mode, only when the * CLK_I2S_RCLK_SRC clock is not exposed so we ensure any @@ -931,8 +937,8 @@ static int i2s_trigger(struct snd_pcm_substream *substream, { struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE); - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0)); + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct i2s_dai *i2s = to_info(snd_soc_rtd_to_cpu(rtd, 0)); unsigned long flags; switch (cmd) { @@ -940,6 +946,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 +957,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 @@ -1105,6 +1118,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) } static const struct snd_soc_dai_ops samsung_i2s_dai_ops = { + .probe = samsung_i2s_dai_probe, + .remove = samsung_i2s_dai_remove, .trigger = i2s_trigger, .hw_params = i2s_hw_params, .set_fmt = i2s_set_fmt, @@ -1143,6 +1158,8 @@ static const struct snd_soc_component_driver samsung_i2s_component = { .suspend = i2s_suspend, .resume = i2s_resume, + + .legacy_dai_naming = 1, }; #define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ @@ -1171,9 +1188,6 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv, for (i = 0; i < num_dais; i++) { dai_drv = &priv->dai_drv[i]; - dai_drv->probe = samsung_i2s_dai_probe; - dai_drv->remove = samsung_i2s_dai_remove; - dai_drv->symmetric_rate = 1; dai_drv->ops = &samsung_i2s_dai_ops; @@ -1202,7 +1216,6 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv, return 0; } -#ifdef CONFIG_PM static int i2s_runtime_suspend(struct device *dev) { struct samsung_i2s_priv *priv = dev_get_drvdata(dev); @@ -1240,7 +1253,6 @@ static int i2s_runtime_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ static void i2s_unregister_clocks(struct samsung_i2s_priv *priv) { @@ -1272,7 +1284,7 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) int ret, i; /* Register the clock provider only if it's expected in the DTB */ - if (!of_find_property(dev->of_node, "#clock-cells", NULL)) + if (!of_property_present(dev->of_node, "#clock-cells")) return 0; /* Get the RCLKSRC mux clock parent clock names */ @@ -1349,6 +1361,10 @@ static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) return -ENOMEM; pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL); + if (!pdev_sec->driver_override) { + platform_device_put(pdev_sec); + return -ENOMEM; + } ret = platform_device_add(pdev_sec); if (ret < 0) { @@ -1404,6 +1420,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"); @@ -1537,13 +1555,13 @@ err_disable_clk: return ret; } -static int samsung_i2s_remove(struct platform_device *pdev) +static void samsung_i2s_remove(struct platform_device *pdev) { struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev); /* The secondary device has no driver data assigned */ if (!priv) - return 0; + return; pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1553,8 +1571,31 @@ static int samsung_i2s_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk); pm_runtime_put_noidle(&pdev->dev); +} - return 0; +static void fsd_i2s_fixup_early(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct i2s_dai *i2s = to_info(snd_soc_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 = snd_soc_substream_to_rtd(substream); + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); + struct i2s_dai *i2s = to_info(snd_soc_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 = { @@ -1646,6 +1687,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", @@ -1672,6 +1721,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, }, {}, }; @@ -1679,10 +1731,8 @@ MODULE_DEVICE_TABLE(of, exynos_i2s_match); #endif static const struct dev_pm_ops samsung_i2s_pm = { - SET_RUNTIME_PM_OPS(i2s_runtime_suspend, - i2s_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver samsung_i2s_driver = { @@ -1692,7 +1742,7 @@ static struct platform_driver samsung_i2s_driver = { .driver = { .name = "samsung-i2s", .of_match_table = of_match_ptr(exynos_i2s_match), - .pm = &samsung_i2s_pm, + .pm = pm_ptr(&samsung_i2s_pm), }, }; @@ -1701,5 +1751,4 @@ module_platform_driver(samsung_i2s_driver); /* Module information */ MODULE_AUTHOR("Jaswinder Singh, <jassisinghbrar@gmail.com>"); MODULE_DESCRIPTION("Samsung I2S Interface"); -MODULE_ALIAS("platform:samsung-i2s"); MODULE_LICENSE("GPL"); |
