summaryrefslogtreecommitdiffstats
path: root/sound/soc/samsung
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/samsung')
-rw-r--r--sound/soc/samsung/dma.h3
-rw-r--r--sound/soc/samsung/dmaengine.c16
-rw-r--r--sound/soc/samsung/i2s.c725
-rw-r--r--sound/soc/samsung/odroid.c206
-rw-r--r--sound/soc/samsung/pcm.c2
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c2
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c2
-rw-r--r--sound/soc/samsung/spdif.c2
8 files changed, 609 insertions, 349 deletions
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index 7ae580d677c8..0ae15d01a3f6 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -17,5 +17,6 @@
* otherwise actual DMA channel names must be passed to this function.
*/
int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
- const char *tx, const char *rx);
+ const char *tx, const char *rx,
+ struct device *dma_dev);
#endif /* _SAMSUNG_DMA_H */
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
index 9104c98deeb7..302871974cb3 100644
--- a/sound/soc/samsung/dmaengine.c
+++ b/sound/soc/samsung/dmaengine.c
@@ -25,9 +25,9 @@
#include "dma.h"
int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
- const char *tx, const char *rx)
+ const char *tx, const char *rx,
+ struct device *dma_dev)
{
- unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT;
struct snd_dmaengine_pcm_config *pcm_conf;
pcm_conf = devm_kzalloc(dev, sizeof(*pcm_conf), GFP_KERNEL);
@@ -36,15 +36,13 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter,
pcm_conf->prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config;
pcm_conf->compat_filter_fn = filter;
+ pcm_conf->dma_dev = dma_dev;
- if (dev->of_node) {
- pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
- pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
- } else {
- flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME;
- }
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
- return devm_snd_dmaengine_pcm_register(dev, pcm_conf, flags);
+ return devm_snd_dmaengine_pcm_register(dev, pcm_conf,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
}
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index ce00fe2f6aae..4231001226f4 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1,14 +1,9 @@
-/* sound/soc/samsung/i2s.c
- *
- * ALSA SoC Audio Layer - Samsung I2S Controller driver
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd.
- * Jaswinder Singh <jassisinghbrar@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Audio Layer - Samsung I2S Controller driver
+//
+// Copyright (c) 2010 Samsung Electronics Co. Ltd.
+// Jaswinder Singh <jassisinghbrar@gmail.com>
#include <dt-bindings/sound/samsung-i2s.h>
#include <linux/delay.h>
@@ -34,6 +29,9 @@
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
+#define SAMSUNG_I2S_ID_PRIMARY 1
+#define SAMSUNG_I2S_ID_SECONDARY 2
+
struct samsung_i2s_variant_regs {
unsigned int bfs_off;
unsigned int rfs_off;
@@ -57,64 +55,88 @@ struct samsung_i2s_dai_data {
struct i2s_dai {
/* Platform device for this DAI */
struct platform_device *pdev;
- /* Memory mapped SFR region */
- void __iomem *addr;
- /* Rate of RCLK source clock */
- unsigned long rclk_srcrate;
- /* Frame Clock */
+
+ /* Frame clock */
unsigned frmclk;
/*
- * Specifically requested RCLK,BCLK by MACHINE Driver.
+ * Specifically requested RCLK, BCLK by machine driver.
* 0 indicates CPU driver is free to choose any value.
*/
unsigned rfs, bfs;
- /* I2S Controller's core clock */
- struct clk *clk;
- /* Clock for generating I2S signals */
- struct clk *op_clk;
/* Pointer to the Primary_Fifo if this is Sec_Fifo, NULL otherwise */
struct i2s_dai *pri_dai;
/* Pointer to the Secondary_Fifo if it has one, NULL otherwise */
struct i2s_dai *sec_dai;
-#define DAI_OPENED (1 << 0) /* Dai is opened */
-#define DAI_MANAGER (1 << 1) /* Dai is the manager */
+
+#define DAI_OPENED (1 << 0) /* DAI is opened */
+#define DAI_MANAGER (1 << 1) /* DAI is the manager */
unsigned mode;
+
/* Driver for this DAI */
- struct snd_soc_dai_driver i2s_dai_drv;
+ struct snd_soc_dai_driver *drv;
+
/* DMA parameters */
struct snd_dmaengine_dai_dma_data dma_playback;
struct snd_dmaengine_dai_dma_data dma_capture;
struct snd_dmaengine_dai_dma_data idma_playback;
dma_filter_fn filter;
- u32 quirks;
- u32 suspend_i2smod;
- u32 suspend_i2scon;
- u32 suspend_i2spsr;
- const struct samsung_i2s_variant_regs *variant_regs;
+
+ struct samsung_i2s_priv *priv;
+};
+
+struct samsung_i2s_priv {
+ struct platform_device *pdev;
+ struct platform_device *pdev_sec;
+
+ /* Memory mapped SFR region */
+ void __iomem *addr;
/* Spinlock protecting access to the device's registers */
- spinlock_t spinlock;
- spinlock_t *lock;
+ spinlock_t lock;
+
+ /* Lock for cross interface checks */
+ spinlock_t pcm_lock;
+
+ /* CPU DAIs and their corresponding drivers */
+ struct i2s_dai *dai;
+ struct snd_soc_dai_driver *dai_drv;
+ int num_dais;
+
+ /* The I2S controller's core clock */
+ struct clk *clk;
+
+ /* Clock for generating I2S signals */
+ struct clk *op_clk;
+
+ /* Rate of RCLK source clock */
+ unsigned long rclk_srcrate;
+
+ /* Cache of selected I2S registers for system suspend */
+ u32 suspend_i2smod;
+ u32 suspend_i2scon;
+ u32 suspend_i2spsr;
+
+ const struct samsung_i2s_variant_regs *variant_regs;
+ u32 quirks;
- /* Below fields are only valid if this is the primary FIFO */
+ /* The clock provider's data */
struct clk *clk_table[3];
struct clk_onecell_data clk_data;
};
-/* Lock for cross i/f checks */
-static DEFINE_SPINLOCK(lock);
-
-/* If this is the 'overlay' stereo DAI */
+/* Returns true if this is the 'overlay' stereo DAI */
static inline bool is_secondary(struct i2s_dai *i2s)
{
- return i2s->pri_dai ? true : false;
+ return i2s->drv->id == SAMSUNG_I2S_ID_SECONDARY;
}
/* If operating in SoC-Slave mode */
static inline bool is_slave(struct i2s_dai *i2s)
{
- u32 mod = readl(i2s->addr + I2SMOD);
- return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false;
+ struct samsung_i2s_priv *priv = i2s->priv;
+
+ u32 mod = readl(priv->addr + I2SMOD);
+ return (mod & (1 << priv->variant_regs->mss_off)) ? true : false;
}
/* If this interface of the controller is transmitting data */
@@ -125,7 +147,7 @@ static inline bool tx_active(struct i2s_dai *i2s)
if (!i2s)
return false;
- active = readl(i2s->addr + I2SCON);
+ active = readl(i2s->priv->addr + I2SCON);
if (is_secondary(i2s))
active &= CON_TXSDMA_ACTIVE;
@@ -163,7 +185,7 @@ static inline bool rx_active(struct i2s_dai *i2s)
if (!i2s)
return false;
- active = readl(i2s->addr + I2SCON) & CON_RXDMA_ACTIVE;
+ active = readl(i2s->priv->addr + I2SCON) & CON_RXDMA_ACTIVE;
return active ? true : false;
}
@@ -202,7 +224,9 @@ static inline bool any_active(struct i2s_dai *i2s)
static inline struct i2s_dai *to_info(struct snd_soc_dai *dai)
{
- return snd_soc_dai_get_drvdata(dai);
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ return &priv->dai[dai->id - 1];
}
static inline bool is_opened(struct i2s_dai *i2s)
@@ -224,9 +248,11 @@ static inline bool is_manager(struct i2s_dai *i2s)
/* Read RCLK of I2S (in multiples of LRCLK) */
static inline unsigned get_rfs(struct i2s_dai *i2s)
{
+ struct samsung_i2s_priv *priv = i2s->priv;
u32 rfs;
- rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off;
- rfs &= i2s->variant_regs->rfs_mask;
+
+ rfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->rfs_off;
+ rfs &= priv->variant_regs->rfs_mask;
switch (rfs) {
case 7: return 192;
@@ -243,10 +269,11 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
/* Write RCLK of I2S (in multiples of LRCLK) */
static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
{
- u32 mod = readl(i2s->addr + I2SMOD);
- int rfs_shift = i2s->variant_regs->rfs_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ u32 mod = readl(priv->addr + I2SMOD);
+ int rfs_shift = priv->variant_regs->rfs_off;
- mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift);
+ mod &= ~(priv->variant_regs->rfs_mask << rfs_shift);
switch (rfs) {
case 192:
@@ -275,15 +302,17 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
break;
}
- writel(mod, i2s->addr + I2SMOD);
+ writel(mod, priv->addr + I2SMOD);
}
-/* Read Bit-Clock of I2S (in multiples of LRCLK) */
+/* Read bit-clock of I2S (in multiples of LRCLK) */
static inline unsigned get_bfs(struct i2s_dai *i2s)
{
+ struct samsung_i2s_priv *priv = i2s->priv;
u32 bfs;
- bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off;
- bfs &= i2s->variant_regs->bfs_mask;
+
+ bfs = readl(priv->addr + I2SMOD) >> priv->variant_regs->bfs_off;
+ bfs &= priv->variant_regs->bfs_mask;
switch (bfs) {
case 8: return 256;
@@ -298,12 +327,13 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
}
}
-/* Write Bit-Clock of I2S (in multiples of LRCLK) */
+/* Write bit-clock of I2S (in multiples of LRCLK) */
static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
{
- u32 mod = readl(i2s->addr + I2SMOD);
- int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
- int bfs_shift = i2s->variant_regs->bfs_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ u32 mod = readl(priv->addr + I2SMOD);
+ int tdm = priv->quirks & QUIRK_SUPPORTS_TDM;
+ int bfs_shift = priv->variant_regs->bfs_off;
/* Non-TDM I2S controllers do not support BCLK > 48 * FS */
if (!tdm && bfs > 48) {
@@ -311,7 +341,7 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
return;
}
- mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift);
+ mod &= ~(priv->variant_regs->bfs_mask << bfs_shift);
switch (bfs) {
case 48:
@@ -346,13 +376,13 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
return;
}
- writel(mod, i2s->addr + I2SMOD);
+ writel(mod, priv->addr + I2SMOD);
}
-/* Sample-Size */
+/* Sample size */
static inline int get_blc(struct i2s_dai *i2s)
{
- int blc = readl(i2s->addr + I2SMOD);
+ int blc = readl(i2s->priv->addr + I2SMOD);
blc = (blc >> 13) & 0x3;
@@ -363,11 +393,12 @@ static inline int get_blc(struct i2s_dai *i2s)
}
}
-/* TX Channel Control */
+/* TX channel control */
static void i2s_txctrl(struct i2s_dai *i2s, int on)
{
- void __iomem *addr = i2s->addr;
- int txr_off = i2s->variant_regs->txr_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ void __iomem *addr = priv->addr;
+ int txr_off = priv->variant_regs->txr_off;
u32 con = readl(addr + I2SCON);
u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
@@ -416,8 +447,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
/* RX Channel Control */
static void i2s_rxctrl(struct i2s_dai *i2s, int on)
{
- void __iomem *addr = i2s->addr;
- int txr_off = i2s->variant_regs->txr_off;
+ struct samsung_i2s_priv *priv = i2s->priv;
+ void __iomem *addr = priv->addr;
+ int txr_off = priv->variant_regs->txr_off;
u32 con = readl(addr + I2SCON);
u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
@@ -453,9 +485,9 @@ static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush)
return;
if (is_secondary(i2s))
- fic = i2s->addr + I2SFICS;
+ fic = i2s->priv->addr + I2SFICS;
else
- fic = i2s->addr + I2SFIC;
+ fic = i2s->priv->addr + I2SFIC;
/* Flush the FIFO */
writel(readl(fic) | flush, fic);
@@ -468,12 +500,13 @@ static inline void i2s_fifo(struct i2s_dai *i2s, u32 flush)
writel(readl(fic) & ~flush, fic);
}
-static int i2s_set_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int rfs, int dir)
+static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs,
+ int dir)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
- const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
+ const struct samsung_i2s_variant_regs *i2s_regs = priv->variant_regs;
unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
u32 mod, mask, val = 0;
@@ -482,9 +515,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
pm_runtime_get_sync(dai->dev);
- spin_lock_irqsave(i2s->lock, flags);
- mod = readl(i2s->addr + I2SMOD);
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
+ spin_unlock_irqrestore(&priv->lock, flags);
switch (clk_id) {
case SAMSUNG_I2S_OPCLK:
@@ -519,51 +552,46 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */
mask = 1 << i2s_regs->rclksrc_off;
- if ((i2s->quirks & QUIRK_NO_MUXPSR)
+ if ((priv->quirks & QUIRK_NO_MUXPSR)
|| (clk_id == SAMSUNG_I2S_RCLKSRC_0))
clk_id = 0;
else
clk_id = 1;
if (!any_active(i2s)) {
- if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
+ if (priv->op_clk && !IS_ERR(priv->op_clk)) {
if ((clk_id && !(mod & rsrc_mask)) ||
(!clk_id && (mod & rsrc_mask))) {
- clk_disable_unprepare(i2s->op_clk);
- clk_put(i2s->op_clk);
+ clk_disable_unprepare(priv->op_clk);
+ clk_put(priv->op_clk);
} else {
- i2s->rclk_srcrate =
- clk_get_rate(i2s->op_clk);
+ priv->rclk_srcrate =
+ clk_get_rate(priv->op_clk);
goto done;
}
}
if (clk_id)
- i2s->op_clk = clk_get(&i2s->pdev->dev,
+ priv->op_clk = clk_get(&i2s->pdev->dev,
"i2s_opclk1");
else
- i2s->op_clk = clk_get(&i2s->pdev->dev,
+ priv->op_clk = clk_get(&i2s->pdev->dev,
"i2s_opclk0");
- if (WARN_ON(IS_ERR(i2s->op_clk))) {
- ret = PTR_ERR(i2s->op_clk);
- i2s->op_clk = NULL;
+ if (WARN_ON(IS_ERR(priv->op_clk))) {
+ ret = PTR_ERR(priv->op_clk);
+ priv->op_clk = NULL;
goto err;
}
- ret = clk_prepare_enable(i2s->op_clk);
+ ret = clk_prepare_enable(priv->op_clk);
if (ret) {
- clk_put(i2s->op_clk);
- i2s->op_clk = NULL;
+ clk_put(priv->op_clk);
+ priv->op_clk = NULL;
goto err;
}
- i2s->rclk_srcrate = clk_get_rate(i2s->op_clk);
+ priv->rclk_srcrate = clk_get_rate(priv->op_clk);
- /* Over-ride the other's */
- if (other) {
- other->op_clk = i2s->op_clk;
- other->rclk_srcrate = i2s->rclk_srcrate;
- }
} else if ((!clk_id && (mod & rsrc_mask))
|| (clk_id && !(mod & rsrc_mask))) {
dev_err(&i2s->pdev->dev,
@@ -572,8 +600,6 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
goto err;
} else {
/* Call can't be on the active DAI */
- i2s->op_clk = other->op_clk;
- i2s->rclk_srcrate = other->rclk_srcrate;
goto done;
}
@@ -586,11 +612,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
goto err;
}
- spin_lock_irqsave(i2s->lock, flags);
- mod = readl(i2s->addr + I2SMOD);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
mod = (mod & ~mask) | val;
- writel(mod, i2s->addr + I2SMOD);
- spin_unlock_irqrestore(i2s->lock, flags);
+ writel(mod, priv->addr + I2SMOD);
+ spin_unlock_irqrestore(&priv->lock, flags);
done:
pm_runtime_put(dai->dev);
@@ -600,17 +626,17 @@ err:
return ret;
}
-static int i2s_set_fmt(struct snd_soc_dai *dai,
- unsigned int fmt)
+static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
u32 mod, tmp = 0;
unsigned long flags;
- lrp_shift = i2s->variant_regs->lrp_off;
- sdf_shift = i2s->variant_regs->sdf_off;
- mod_slave = 1 << i2s->variant_regs->mss_off;
+ lrp_shift = priv->variant_regs->lrp_off;
+ sdf_shift = priv->variant_regs->sdf_off;
+ mod_slave = 1 << priv->variant_regs->mss_off;
sdf_mask = MOD_SDF_MASK << sdf_shift;
lrp_rlow = MOD_LR_RLOW << lrp_shift;
@@ -661,7 +687,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
* CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
* clock configuration assigned in DT is not overwritten.
*/
- if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL)
+ if (priv->rclk_srcrate == 0 && priv->clk_data.clks == NULL)
i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN);
break;
@@ -671,15 +697,15 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
}
pm_runtime_get_sync(dai->dev);
- spin_lock_irqsave(i2s->lock, flags);
- mod = readl(i2s->addr + I2SMOD);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
/*
* Don't change the I2S mode if any controller is active on this
* channel.
*/
if (any_active(i2s) &&
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
pm_runtime_put(dai->dev);
dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__);
@@ -688,8 +714,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
mod &= ~(sdf_mask | lrp_rlow | mod_slave);
mod |= tmp;
- writel(mod, i2s->addr + I2SMOD);
- spin_unlock_irqrestore(i2s->lock, flags);
+ writel(mod, priv->addr + I2SMOD);
+ spin_unlock_irqrestore(&priv->lock, flags);
pm_runtime_put(dai->dev);
return 0;
@@ -698,6 +724,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
static int i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
u32 mod, mask = 0, val = 0;
struct clk *rclksrc;
@@ -711,7 +738,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_channels(params)) {
case 6:
val |= MOD_DC2_EN;
- /* fall through */
+ /* Fall through */
case 4:
val |= MOD_DC1_EN;
break;
@@ -773,34 +800,35 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- spin_lock_irqsave(i2s->lock, flags);
- mod = readl(i2s->addr + I2SMOD);
+ spin_lock_irqsave(&priv->lock, flags);
+ mod = readl(priv->addr + I2SMOD);
mod = (mod & ~mask) | val;
- writel(mod, i2s->addr + I2SMOD);
- spin_unlock_irqrestore(i2s->lock, flags);
+ writel(mod, priv->addr + I2SMOD);
+ spin_unlock_irqrestore(&priv->lock, flags);
snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
i2s->frmclk = params_rate(params);
- rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
+ rclksrc = priv->clk_table[CLK_I2S_RCLK_SRC];
if (rclksrc && !IS_ERR(rclksrc))
- i2s->rclk_srcrate = clk_get_rate(rclksrc);
+ priv->rclk_srcrate = clk_get_rate(rclksrc);
return 0;
}
-/* We set constraints on the substream acc to the version of I2S */
+/* We set constraints on the substream according to the version of I2S */
static int i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
pm_runtime_get_sync(dai->dev);
- spin_lock_irqsave(&lock, flags);
+ spin_lock_irqsave(&priv->pcm_lock, flags);
i2s->mode |= DAI_OPENED;
@@ -809,10 +837,10 @@ static int i2s_startup(struct snd_pcm_substream *substream,
else
i2s->mode |= DAI_MANAGER;
- if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR))
- writel(CON_RSTCLR, i2s->addr + I2SCON);
+ if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR))
+ writel(CON_RSTCLR, i2s->priv->addr + I2SCON);
- spin_unlock_irqrestore(&lock, flags);
+ spin_unlock_irqrestore(&priv->pcm_lock, flags);
return 0;
}
@@ -820,11 +848,12 @@ static int i2s_startup(struct snd_pcm_substream *substream,
static void i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
- spin_lock_irqsave(&lock, flags);
+ spin_lock_irqsave(&priv->pcm_lock, flags);
i2s->mode &= ~DAI_OPENED;
i2s->mode &= ~DAI_MANAGER;
@@ -836,13 +865,14 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
i2s->rfs = 0;
i2s->bfs = 0;
- spin_unlock_irqrestore(&lock, flags);
+ spin_unlock_irqrestore(&priv->pcm_lock, flags);
pm_runtime_put(dai->dev);
}
static int config_setup(struct i2s_dai *i2s)
{
+ struct samsung_i2s_priv *priv = i2s->priv;
struct i2s_dai *other = get_other_dai(i2s);
unsigned rfs, bfs, blc;
u32 psr;
@@ -890,12 +920,12 @@ static int config_setup(struct i2s_dai *i2s)
if (is_slave(i2s))
return 0;
- if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
- psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
- writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
+ if (!(priv->quirks & QUIRK_NO_MUXPSR)) {
+ psr = priv->rclk_srcrate / i2s->frmclk / rfs;
+ writel(((psr - 1) << 8) | PSR_PSREN, priv->addr + I2SPSR);
dev_dbg(&i2s->pdev->dev,
"RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n",
- i2s->rclk_srcrate, psr, rfs, bfs);
+ priv->rclk_srcrate, psr, rfs, bfs);
}
return 0;
@@ -904,6 +934,7 @@ static int config_setup(struct i2s_dai *i2s)
static int i2s_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
+ 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 = substream->private_data;
struct i2s_dai *i2s = to_info(rtd->cpu_dai);
@@ -914,10 +945,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);
- spin_lock_irqsave(i2s->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
if (config_setup(i2s)) {
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
return -EINVAL;
}
@@ -926,12 +957,12 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
else
i2s_txctrl(i2s, 1);
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- spin_lock_irqsave(i2s->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
if (capture) {
i2s_rxctrl(i2s, 0);
@@ -941,7 +972,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
i2s_fifo(i2s, FIC_TXFLUSH);
}
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
pm_runtime_put(dai->dev);
break;
}
@@ -980,19 +1011,19 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
static snd_pcm_sframes_t
i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
- u32 reg = readl(i2s->addr + I2SFIC);
+ u32 reg = readl(priv->addr + I2SFIC);
snd_pcm_sframes_t delay;
- const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
WARN_ON(!pm_runtime_active(dai->dev));
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
delay = FIC_RXCOUNT(reg);
else if (is_secondary(i2s))
- delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
+ delay = FICS_TXCOUNT(readl(priv->addr + I2SFICS));
else
- delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f;
+ delay = (reg >> priv->variant_regs->ftx0cnt_off) & 0x7f;
return delay;
}
@@ -1014,39 +1045,39 @@ static int i2s_resume(struct snd_soc_dai *dai)
static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
{
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags;
pm_runtime_get_sync(dai->dev);
- if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
- snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback,
- NULL);
+ if (is_secondary(i2s)) {
+ /* If this is probe on the secondary DAI */
+ snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, NULL);
} else {
snd_soc_dai_init_dma_data(dai, &i2s->dma_playback,
- &i2s->dma_capture);
+ &i2s->dma_capture);
- if (i2s->quirks & QUIRK_NEED_RSTCLR)
- writel(CON_RSTCLR, i2s->addr + I2SCON);
+ if (priv->quirks & QUIRK_NEED_RSTCLR)
+ writel(CON_RSTCLR, priv->addr + I2SCON);
- if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
- idma_reg_addr_init(i2s->addr,
- i2s->sec_dai->idma_playback.addr);
+ if (priv->quirks & QUIRK_SUPPORTS_IDMA)
+ idma_reg_addr_init(priv->addr,
+ other->idma_playback.addr);
}
/* Reset any constraint on RFS and BFS */
i2s->rfs = 0;
i2s->bfs = 0;
- i2s->rclk_srcrate = 0;
- spin_lock_irqsave(i2s->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
i2s_txctrl(i2s, 0);
i2s_rxctrl(i2s, 0);
i2s_fifo(i2s, FIC_TXFLUSH);
i2s_fifo(other, FIC_TXFLUSH);
i2s_fifo(i2s, FIC_RXFLUSH);
- spin_unlock_irqrestore(i2s->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
/* Gate CDCLK by default */
if (!is_opened(other))
@@ -1059,16 +1090,17 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
{
- struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct i2s_dai *i2s = to_info(dai);
unsigned long flags;
pm_runtime_get_sync(dai->dev);
if (!is_secondary(i2s)) {
- if (i2s->quirks & QUIRK_NEED_RSTCLR) {
- spin_lock_irqsave(i2s->lock, flags);
- writel(0, i2s->addr + I2SCON);
- spin_unlock_irqrestore(i2s->lock, flags);
+ if (priv->quirks & QUIRK_NEED_RSTCLR) {
+ spin_lock_irqsave(&priv->lock, flags);
+ writel(0, priv->addr + I2SCON);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
}
@@ -1088,118 +1120,157 @@ static const struct snd_soc_dai_ops samsung_i2s_dai_ops = {
.delay = i2s_delay,
};
+static const struct snd_soc_dapm_widget samsung_i2s_widgets[] = {
+ /* Backend DAI */
+ SND_SOC_DAPM_AIF_OUT("Mixer DAI TX", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("Mixer DAI RX", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ /* Playback Mixer */
+ SND_SOC_DAPM_MIXER("Playback Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route samsung_i2s_dapm_routes[] = {
+ { "Playback Mixer", NULL, "Primary" },
+ { "Playback Mixer", NULL, "Secondary" },
+
+ { "Mixer DAI TX", NULL, "Playback Mixer" },
+ { "Playback Mixer", NULL, "Mixer DAI RX" },
+};
+
static const struct snd_soc_component_driver samsung_i2s_component = {
- .name = "samsung-i2s",
+ .name = "samsung-i2s",
+
+ .dapm_widgets = samsung_i2s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(samsung_i2s_widgets),
+
+ .dapm_routes = samsung_i2s_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(samsung_i2s_dapm_routes),
};
-#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE)
+#define SAMSUNG_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
-static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev,
- const struct samsung_i2s_dai_data *i2s_dai_data,
- bool sec)
+static int i2s_alloc_dais(struct samsung_i2s_priv *priv,
+ const struct samsung_i2s_dai_data *i2s_dai_data,
+ int num_dais)
{
- struct i2s_dai *i2s;
-
- i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL);
- if (i2s == NULL)
- return NULL;
-
- i2s->pdev = pdev;
- i2s->pri_dai = NULL;
- i2s->sec_dai = NULL;
- i2s->i2s_dai_drv.id = 1;
- i2s->i2s_dai_drv.symmetric_rates = 1;
- i2s->i2s_dai_drv.probe = samsung_i2s_dai_probe;
- i2s->i2s_dai_drv.remove = samsung_i2s_dai_remove;
- i2s->i2s_dai_drv.ops = &samsung_i2s_dai_ops;
- i2s->i2s_dai_drv.suspend = i2s_suspend;
- i2s->i2s_dai_drv.resume = i2s_resume;
- i2s->i2s_dai_drv.playback.channels_min = 1;
- i2s->i2s_dai_drv.playback.channels_max = 2;
- i2s->i2s_dai_drv.playback.rates = i2s_dai_data->pcm_rates;
- i2s->i2s_dai_drv.playback.formats = SAMSUNG_I2S_FMTS;
-
- if (!sec) {
- i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI;
- i2s->i2s_dai_drv.capture.channels_min = 1;
- i2s->i2s_dai_drv.capture.channels_max = 2;
- i2s->i2s_dai_drv.capture.rates = i2s_dai_data->pcm_rates;
- i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
- } else {
- i2s->i2s_dai_drv.name = SAMSUNG_I2S_DAI_SEC;
+ static const char *dai_names[] = { "samsung-i2s", "samsung-i2s-sec" };
+ static const char *stream_names[] = { "Primary", "Secondary" };
+ struct snd_soc_dai_driver *dai_drv;
+ struct i2s_dai *dai;
+ int i;
+
+ priv->dai = devm_kcalloc(&priv->pdev->dev, num_dais,
+ sizeof(*dai), GFP_KERNEL);
+ if (!priv->dai)
+ return -ENOMEM;
+
+ priv->dai_drv = devm_kcalloc(&priv->pdev->dev, num_dais,
+ sizeof(*dai_drv), GFP_KERNEL);
+ if (!priv->dai_drv)
+ return -ENOMEM;
+
+ 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->suspend = i2s_suspend;
+ dai_drv->resume = i2s_resume;
+
+ dai_drv->symmetric_rates = 1;
+ dai_drv->ops = &samsung_i2s_dai_ops;
+
+ dai_drv->playback.channels_min = 1;
+ dai_drv->playback.channels_max = 2;
+ dai_drv->playback.rates = i2s_dai_data->pcm_rates;
+ dai_drv->playback.formats = SAMSUNG_I2S_FMTS;
+ dai_drv->playback.stream_name = stream_names[i];
+
+ dai_drv->id = i + 1;
+ dai_drv->name = dai_names[i];
+
+ priv->dai[i].drv = &priv->dai_drv[i];
+ priv->dai[i].pdev = priv->pdev;
}
- return i2s;
+
+ /* Initialize capture only for the primary DAI */
+ dai_drv = &priv->dai_drv[SAMSUNG_I2S_ID_PRIMARY - 1];
+
+ dai_drv->capture.channels_min = 1;
+ dai_drv->capture.channels_max = 2;
+ dai_drv->capture.rates = i2s_dai_data->pcm_rates;
+ dai_drv->capture.formats = SAMSUNG_I2S_FMTS;
+
+ return 0;
}
#ifdef CONFIG_PM
static int i2s_runtime_suspend(struct device *dev)
{
- struct i2s_dai *i2s = dev_get_drvdata(dev);
+ struct samsung_i2s_priv *priv = dev_get_drvdata(dev);
- i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
- i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
- i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
+ priv->suspend_i2smod = readl(priv->addr + I2SMOD);
+ priv->suspend_i2scon = readl(priv->addr + I2SCON);
+ priv->suspend_i2spsr = readl(priv->addr + I2SPSR);
- if (i2s->op_clk)
- clk_disable_unprepare(i2s->op_clk);
- clk_disable_unprepare(i2s->clk);
+ if (priv->op_clk)
+ clk_disable_unprepare(priv->op_clk);
+ clk_disable_unprepare(priv->clk);
return 0;
}
static int i2s_runtime_resume(struct device *dev)
{
- struct i2s_dai *i2s = dev_get_drvdata(dev);
+ struct samsung_i2s_priv *priv = dev_get_drvdata(dev);
int ret;
- ret = clk_prepare_enable(i2s->clk);
+ ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
- if (i2s->op_clk) {
- ret = clk_prepare_enable(i2s->op_clk);
+ if (priv->op_clk) {
+ ret = clk_prepare_enable(priv->op_clk);
if (ret) {
- clk_disable_unprepare(i2s->clk);
+ clk_disable_unprepare(priv->clk);
return ret;
}
}
- writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
- writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
- writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
+ writel(priv->suspend_i2scon, priv->addr + I2SCON);
+ writel(priv->suspend_i2smod, priv->addr + I2SMOD);
+ writel(priv->suspend_i2spsr, priv->addr + I2SPSR);
return 0;
}
#endif /* CONFIG_PM */
-static void i2s_unregister_clocks(struct i2s_dai *i2s)
+static void i2s_unregister_clocks(struct samsung_i2s_priv *priv)
{
int i;
- for (i = 0; i < i2s->clk_data.clk_num; i++) {
- if (!IS_ERR(i2s->clk_table[i]))
- clk_unregister(i2s->clk_table[i]);
+ for (i = 0; i < priv->clk_data.clk_num; i++) {
+ if (!IS_ERR(priv->clk_table[i]))
+ clk_unregister(priv->clk_table[i]);
}
}
-static void i2s_unregister_clock_provider(struct platform_device *pdev)
+static void i2s_unregister_clock_provider(struct samsung_i2s_priv *priv)
{
- struct i2s_dai *i2s = dev_get_drvdata(&pdev->dev);
-
- of_clk_del_provider(pdev->dev.of_node);
- i2s_unregister_clocks(i2s);
+ of_clk_del_provider(priv->pdev->dev.of_node);
+ i2s_unregister_clocks(priv);
}
-static int i2s_register_clock_provider(struct platform_device *pdev)
+
+static int i2s_register_clock_provider(struct samsung_i2s_priv *priv)
{
+
const char * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" };
const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" };
const char *p_names[2] = { NULL };
- struct device *dev = &pdev->dev;
- struct i2s_dai *i2s = dev_get_drvdata(dev);
- const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs;
+ struct device *dev = &priv->pdev->dev;
+ const struct samsung_i2s_variant_regs *reg_info = priv->variant_regs;
const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)];
struct clk *rclksrc;
int ret, i;
@@ -1224,110 +1295,170 @@ static int i2s_register_clock_provider(struct platform_device *pdev)
return -ENOMEM;
}
- if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
+ if (!(priv->quirks & QUIRK_NO_MUXPSR)) {
/* Activate the prescaler */
- u32 val = readl(i2s->addr + I2SPSR);
- writel(val | PSR_PSREN, i2s->addr + I2SPSR);
+ u32 val = readl(priv->addr + I2SPSR);
+ writel(val | PSR_PSREN, priv->addr + I2SPSR);
- i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,
+ priv->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev,
i2s_clk_name[CLK_I2S_RCLK_SRC], p_names,
ARRAY_SIZE(p_names),
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
- i2s->addr + I2SMOD, reg_info->rclksrc_off,
- 1, 0, i2s->lock);
+ priv->addr + I2SMOD, reg_info->rclksrc_off,
+ 1, 0, &priv->lock);
- i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,
+ priv->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev,
i2s_clk_name[CLK_I2S_RCLK_PSR],
i2s_clk_name[CLK_I2S_RCLK_SRC],
CLK_SET_RATE_PARENT,
- i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
+ priv->addr + I2SPSR, 8, 6, 0, &priv->lock);
p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR];
- i2s->clk_data.clk_num = 2;
+ priv->clk_data.clk_num = 2;
}
- i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev,
+ priv->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev,
i2s_clk_name[CLK_I2S_CDCLK], p_names[0],
CLK_SET_RATE_PARENT,
- i2s->addr + I2SMOD, reg_info->cdclkcon_off,
- CLK_GATE_SET_TO_DISABLE, i2s->lock);
+ priv->addr + I2SMOD, reg_info->cdclkcon_off,
+ CLK_GATE_SET_TO_DISABLE, &priv->lock);
- i2s->clk_data.clk_num += 1;
- i2s->clk_data.clks = i2s->clk_table;
+ priv->clk_data.clk_num += 1;
+ priv->clk_data.clks = priv->clk_table;
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
- &i2s->clk_data);
+ &priv->clk_data);
if (ret < 0) {
dev_err(dev, "failed to add clock provider: %d\n", ret);
- i2s_unregister_clocks(i2s);
+ i2s_unregister_clocks(priv);
}
return ret;
}
+/* Create platform device for the secondary PCM */
+static int i2s_create_secondary_device(struct samsung_i2s_priv *priv)
+{
+ struct platform_device *pdev_sec;
+ const char *devname;
+ int ret;
+
+ devname = devm_kasprintf(&priv->pdev->dev, GFP_KERNEL, "%s-sec",
+ dev_name(&priv->pdev->dev));
+ if (!devname)
+ return -ENOMEM;
+
+ pdev_sec = platform_device_alloc(devname, -1);
+ if (!pdev_sec)
+ return -ENOMEM;
+
+ pdev_sec->driver_override = kstrdup("samsung-i2s", GFP_KERNEL);
+
+ ret = platform_device_add(pdev_sec);
+ if (ret < 0) {
+ platform_device_put(pdev_sec);
+ return ret;
+ }
+
+ ret = device_attach(&pdev_sec->dev);
+ if (ret <= 0) {
+ platform_device_unregister(priv->pdev_sec);
+ dev_info(&pdev_sec->dev, "device_attach() failed\n");
+ return ret;
+ }
+
+ priv->pdev_sec = pdev_sec;
+
+ return 0;
+}
+
+static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv)
+{
+ platform_device_unregister(priv->pdev_sec);
+ priv->pdev_sec = NULL;
+}
+
static int samsung_i2s_probe(struct platform_device *pdev)
{
struct i2s_dai *pri_dai, *sec_dai = NULL;
struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
- struct resource *res;
- u32 regs_base, quirks = 0, idma_addr = 0;
+ u32 regs_base, idma_addr = 0;
struct device_node *np = pdev->dev.of_node;
const struct samsung_i2s_dai_data *i2s_dai_data;
- int ret;
+ const struct platform_device_id *id;
+ struct samsung_i2s_priv *priv;
+ struct resource *res;
+ int num_dais, ret;
- if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node)
+ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
i2s_dai_data = of_device_get_match_data(&pdev->dev);
- else
- i2s_dai_data = (struct samsung_i2s_dai_data *)
- platform_get_device_id(pdev)->driver_data;
+ } else {
+ id = platform_get_device_id(pdev);
- pri_dai = i2s_alloc_dai(pdev, i2s_dai_data, false);
- if (!pri_dai) {
- dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
- return -ENOMEM;
+ /* Nothing to do if it is the secondary device probe */
+ if (!id)
+ return 0;
+
+ i2s_dai_data = (struct samsung_i2s_dai_data *)id->driver_data;
}
- spin_lock_init(&pri_dai->spinlock);
- pri_dai->lock = &pri_dai->spinlock;
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- if (!np) {
- if (i2s_pdata == NULL) {
- dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
+ if (np) {
+ priv->quirks = i2s_dai_data->quirks;
+ } else {
+ if (!i2s_pdata) {
+ dev_err(&pdev->dev, "Missing platform data\n");
return -EINVAL;
}
+ priv->quirks = i2s_pdata->type.quirks;
+ }
+ num_dais = (priv->quirks & QUIRK_SEC_DAI) ? 2 : 1;
+ priv->pdev = pdev;
+ priv->variant_regs = i2s_dai_data->i2s_variant_regs;
+
+ ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais);
+ if (ret < 0)
+ return ret;
+
+ pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1];
+
+ spin_lock_init(&priv->lock);
+ spin_lock_init(&priv->pcm_lock);
+
+ if (!np) {
pri_dai->dma_playback.filter_data = i2s_pdata->dma_playback;
pri_dai->dma_capture.filter_data = i2s_pdata->dma_capture;
pri_dai->filter = i2s_pdata->dma_filter;
- quirks = i2s_pdata->type.quirks;
idma_addr = i2s_pdata->type.idma_addr;
} else {
- quirks = i2s_dai_data->quirks;
if (of_property_read_u32(np, "samsung,idma-addr",
&idma_addr)) {
- if (quirks & QUIRK_SUPPORTS_IDMA) {
+ if (priv->quirks & QUIRK_SUPPORTS_IDMA) {
dev_info(&pdev->dev, "idma address is not"\
"specified");
}
}
}
- quirks &= ~(QUIRK_SEC_DAI | QUIRK_SUPPORTS_IDMA);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- pri_dai->addr = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pri_dai->addr))
- return PTR_ERR(pri_dai->addr);
+ priv->addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->addr))
+ return PTR_ERR(priv->addr);
regs_base = res->start;
- pri_dai->clk = devm_clk_get(&pdev->dev, "iis");
- if (IS_ERR(pri_dai->clk)) {
+ priv->clk = devm_clk_get(&pdev->dev, "iis");
+ if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "Failed to get iis clock\n");
- return PTR_ERR(pri_dai->clk);
+ return PTR_ERR(priv->clk);
}
- ret = clk_prepare_enable(pri_dai->clk);
+ ret = clk_prepare_enable(priv->clk);
if (ret != 0) {
dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
@@ -1338,33 +1469,19 @@ static int samsung_i2s_probe(struct platform_device *pdev)
pri_dai->dma_capture.chan_name = "rx";
pri_dai->dma_playback.addr_width = 4;
pri_dai->dma_capture.addr_width = 4;
- pri_dai->quirks = quirks;
- pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
+ pri_dai->priv = priv;
- if (quirks & QUIRK_PRI_6CHAN)
- pri_dai->i2s_dai_drv.playback.channels_max = 6;
+ if (priv->quirks & QUIRK_PRI_6CHAN)
+ pri_dai->drv->playback.channels_max = 6;
ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
- NULL, NULL);
- if (ret < 0)
- goto err_disable_clk;
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &samsung_i2s_component,
- &pri_dai->i2s_dai_drv, 1);
+ "tx", "rx", NULL);
if (ret < 0)
goto err_disable_clk;
- if (quirks & QUIRK_SEC_DAI) {
- sec_dai = i2s_alloc_dai(pdev, i2s_dai_data, true);
- if (!sec_dai) {
- dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
- ret = -ENOMEM;
- goto err_disable_clk;
- }
+ if (priv->quirks & QUIRK_SEC_DAI) {
+ sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1];
- sec_dai->lock = &pri_dai->spinlock;
- sec_dai->variant_regs = pri_dai->variant_regs;
sec_dai->dma_playback.addr = regs_base + I2STXDS;
sec_dai->dma_playback.chan_name = "tx-sec";
@@ -1374,62 +1491,72 @@ static int samsung_i2s_probe(struct platform_device *pdev)
}
sec_dai->dma_playback.addr_width = 4;
- sec_dai->addr = pri_dai->addr;
- sec_dai->clk = pri_dai->clk;
- sec_dai->quirks = quirks;
sec_dai->idma_playback.addr = idma_addr;
sec_dai->pri_dai = pri_dai;
+ sec_dai->priv = priv;
pri_dai->sec_dai = sec_dai;
- ret = samsung_asoc_dma_platform_register(&pdev->dev,
- sec_dai->filter, "tx-sec", NULL);
+ ret = i2s_create_secondary_device(priv);
if (ret < 0)
goto err_disable_clk;
- ret = devm_snd_soc_register_component(&pdev->dev,
- &samsung_i2s_component,
- &sec_dai->i2s_dai_drv, 1);
+ ret = samsung_asoc_dma_platform_register(&priv->pdev_sec->dev,
+ sec_dai->filter, "tx-sec", NULL,
+ &pdev->dev);
if (ret < 0)
- goto err_disable_clk;
+ goto err_del_sec;
+
}
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to configure gpio\n");
ret = -EINVAL;
- goto err_disable_clk;
+ goto err_del_sec;
}
- dev_set_drvdata(&pdev->dev, pri_dai);
+ dev_set_drvdata(&pdev->dev, priv);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &samsung_i2s_component,
+ priv->dai_drv, num_dais);
+ if (ret < 0)
+ goto err_del_sec;
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
- ret = i2s_register_clock_provider(pdev);
+ ret = i2s_register_clock_provider(priv);
if (ret < 0)
goto err_disable_pm;
- pri_dai->op_clk = clk_get_parent(pri_dai->clk_table[CLK_I2S_RCLK_SRC]);
+ priv->op_clk = clk_get_parent(priv->clk_table[CLK_I2S_RCLK_SRC]);
return 0;
err_disable_pm:
pm_runtime_disable(&pdev->dev);
+err_del_sec:
+ i2s_delete_secondary_device(priv);
err_disable_clk:
- clk_disable_unprepare(pri_dai->clk);
+ clk_disable_unprepare(priv->clk);
return ret;
}
static int samsung_i2s_remove(struct platform_device *pdev)
{
- struct i2s_dai *pri_dai;
+ struct samsung_i2s_priv *priv = dev_get_drvdata(&pdev->dev);
- pri_dai = dev_get_drvdata(&pdev->dev);
+ /* The secondary device has no driver data assigned */
+ if (!priv)
+ return 0;
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
- i2s_unregister_clock_provider(pdev);
- clk_disable_unprepare(pri_dai->clk);
+ i2s_unregister_clock_provider(priv);
+ i2s_delete_secondary_device(priv);
+ clk_disable_unprepare(priv->clk);
+
pm_runtime_put_noidle(&pdev->dev);
return 0;
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
index e7b371b07230..694512f980fd 100644
--- a/sound/soc/samsung/odroid.c
+++ b/sound/soc/samsung/odroid.c
@@ -7,6 +7,7 @@
*/
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/module.h>
@@ -17,26 +18,52 @@
struct odroid_priv {
struct snd_soc_card card;
- struct snd_soc_dai_link dai_link;
-
struct clk *clk_i2s_bus;
struct clk *sclk_i2s;
+
+ /* Spinlock protecting fields below */
+ spinlock_t lock;
+ unsigned int be_sample_rate;
+ bool be_active;
};
-static int odroid_card_startup(struct snd_pcm_substream *substream)
+static int odroid_card_fe_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+
return 0;
}
-static int odroid_card_hw_params(struct snd_pcm_substream *substream,
+static int odroid_card_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->be_active && priv->be_sample_rate != params_rate(params))
+ ret = -EINVAL;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static const struct snd_soc_ops odroid_card_fe_ops = {
+ .startup = odroid_card_fe_startup,
+ .hw_params = odroid_card_fe_hw_params,
+};
+
+static int odroid_card_be_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
unsigned int pll_freq, rclk_freq, rfs;
+ unsigned long flags;
int ret;
switch (params_rate(params)) {
@@ -83,22 +110,97 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->be_sample_rate = params_rate(params);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+static int odroid_card_be_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ priv->be_active = true;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ priv->be_active = false;
+ break;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
return 0;
}
-static const struct snd_soc_ops odroid_card_ops = {
- .startup = odroid_card_startup,
- .hw_params = odroid_card_hw_params,
+static const struct snd_soc_ops odroid_card_be_ops = {
+ .hw_params = odroid_card_be_hw_params,
+ .trigger = odroid_card_be_trigger,
+};
+
+/* DAPM routes for backward compatibility with old DTS */
+static const struct snd_soc_dapm_route odroid_dapm_routes[] = {
+ { "I2S Playback", NULL, "Mixer DAI TX" },
+ { "HiFi Playback", NULL, "Mixer DAI TX" },
+};
+
+static struct snd_soc_dai_link odroid_card_dais[] = {
+ {
+ /* Primary FE <-> BE link */
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .ops = &odroid_card_fe_ops,
+ .name = "Primary",
+ .stream_name = "Primary",
+ .platform_name = "3830000.i2s",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ }, {
+ /* BE <-> CODECs link */
+ .name = "I2S Mixer",
+ .cpu_name = "snd-soc-dummy",
+ .cpu_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "snd-soc-dummy",
+ .ops = &odroid_card_be_ops,
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ }, {
+ /* Secondary FE <-> BE link */
+ .playback_only = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .ops = &odroid_card_fe_ops,
+ .name = "Secondary",
+ .stream_name = "Secondary",
+ .platform_name = "3830000.i2s-sec",
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ }
};
static int odroid_audio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct device_node *cpu_dai = NULL;
struct device_node *cpu, *codec;
struct odroid_priv *priv;
- struct snd_soc_dai_link *link;
struct snd_soc_card *card;
- int ret;
+ struct snd_soc_dai_link *link, *codec_link;
+ int num_pcms, ret, i;
+ struct of_phandle_args args = {};
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -110,6 +212,7 @@ static int odroid_audio_probe(struct platform_device *pdev)
card->owner = THIS_MODULE;
card->fully_routed = true;
+ spin_lock_init(&priv->lock);
snd_soc_card_set_drvdata(card, priv);
ret = snd_soc_of_parse_card_name(card, "model");
@@ -130,45 +233,78 @@ static int odroid_audio_probe(struct platform_device *pdev)
return ret;
}
- link = &priv->dai_link;
-
- link->ops = &odroid_card_ops;
- link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS;
-
- card->dai_link = &priv->dai_link;
- card->num_links = 1;
+ card->dai_link = odroid_card_dais;
+ card->num_links = ARRAY_SIZE(odroid_card_dais);
cpu = of_get_child_by_name(dev->of_node, "cpu");
codec = of_get_child_by_name(dev->of_node, "codec");
+ link = card->dai_link;
+ codec_link = &card->dai_link[1];
- link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
- if (!link->cpu_of_node) {
- dev_err(dev, "Failed parsing cpu/sound-dai property\n");
- return -EINVAL;
+ /*
+ * For backwards compatibility create the secondary CPU DAI link only
+ * if there are 2 CPU DAI entries in the cpu sound-dai property in DT.
+ * Also add required DAPM routes not available in old DTS.
+ */
+ num_pcms = of_count_phandle_with_args(cpu, "sound-dai",
+ "#sound-dai-cells");
+ if (num_pcms == 1) {
+ card->dapm_routes = odroid_dapm_routes;
+ card->num_dapm_routes = ARRAY_SIZE(odroid_dapm_routes);
+ card->num_links--;
}
- ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
- if (ret < 0)
- goto err_put_codec_n;
+ for (i = 0; i < num_pcms; i++, link += 2) {
+ ret = of_parse_phandle_with_args(cpu, "sound-dai",
+ "#sound-dai-cells", i, &args);
+ if (ret < 0)
+ break;
+
+ if (!args.np) {
+ dev_err(dev, "sound-dai property parse error: %d\n", ret);
+ ret = -EINVAL;
+ break;
+ }
- link->platform_of_node = link->cpu_of_node;
+ ret = snd_soc_get_dai_name(&args, &link->cpu_dai_name);
+ of_node_put(args.np);
- link->name = "Primary";
- link->stream_name = link->name;
+ if (ret < 0)
+ break;
+ }
+ if (ret == 0) {
+ cpu_dai = of_parse_phandle(cpu, "sound-dai", 0);
+ if (!cpu_dai)
+ ret = -EINVAL;
+ }
+ of_node_put(cpu);
+ of_node_put(codec);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link);
+ if (ret < 0)
+ goto err_put_cpu_dai;
+
+ /* Set capture capability only for boards with the MAX98090 CODEC */
+ if (codec_link->num_codecs > 1) {
+ card->dai_link[0].dpcm_capture = 1;
+ card->dai_link[1].dpcm_capture = 1;
+ }
- priv->sclk_i2s = of_clk_get_by_name(link->cpu_of_node, "i2s_opclk1");
+ priv->sclk_i2s = of_clk_get_by_name(cpu_dai, "i2s_opclk1");
if (IS_ERR(priv->sclk_i2s)) {
ret = PTR_ERR(priv->sclk_i2s);
- goto err_put_i2s_n;
+ goto err_put_cpu_dai;
}
- priv->clk_i2s_bus = of_clk_get_by_name(link->cpu_of_node, "iis");
+ priv->clk_i2s_bus = of_clk_get_by_name(cpu_dai, "iis");
if (IS_ERR(priv->clk_i2s_bus)) {
ret = PTR_ERR(priv->clk_i2s_bus);
goto err_put_sclk;
}
+ of_node_put(cpu_dai);
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0) {
@@ -182,10 +318,9 @@ err_put_clk_i2s:
clk_put(priv->clk_i2s_bus);
err_put_sclk:
clk_put(priv->sclk_i2s);
-err_put_i2s_n:
- of_node_put(link->cpu_of_node);
-err_put_codec_n:
- snd_soc_of_put_dai_link_codecs(link);
+err_put_cpu_dai:
+ of_node_put(cpu_dai);
+ snd_soc_of_put_dai_link_codecs(codec_link);
return ret;
}
@@ -193,8 +328,7 @@ static int odroid_audio_remove(struct platform_device *pdev)
{
struct odroid_priv *priv = platform_get_drvdata(pdev);
- of_node_put(priv->dai_link.cpu_of_node);
- snd_soc_of_put_dai_link_codecs(&priv->dai_link);
+ snd_soc_of_put_dai_link_codecs(&priv->card.dai_link[1]);
clk_put(priv->sclk_i2s);
clk_put(priv->clk_i2s_bus);
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index 37f95eee1558..3c7baa561084 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -553,7 +553,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret) {
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
goto err_dis_pclk;
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index cc0840fff5aa..c08638b0e458 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -177,7 +177,7 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
ret = samsung_asoc_dma_platform_register(&pdev->dev,
pdata->dma_filter,
- NULL, NULL);
+ "tx", "rx", NULL);
if (ret) {
pr_err("failed to register the DMA: %d\n", ret);
return ret;
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index 8d58d02183bf..a8026b640c95 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -446,7 +446,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL,
- NULL, NULL);
+ "tx", "rx", NULL);
if (ret) {
dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret);
return ret;
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index cb59911e65c0..5e4afb330416 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -430,7 +430,7 @@ static int spdif_probe(struct platform_device *pdev)
spdif->dma_playback = &spdif_stereo_out;
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
- NULL, NULL);
+ NULL, NULL, NULL);
if (ret) {
dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
goto err4;