summaryrefslogtreecommitdiff
path: root/sound/soc/samsung/i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/samsung/i2s.c')
-rw-r--r--sound/soc/samsung/i2s.c93
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");