From 3871cab89d7127abd4233f69298f5f0cd7dfc993 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:35 +0100 Subject: [PATCH 01/16] ASoC: samsung: i2s: Move SFR pointer to common driver data structure The SFR region is common for both DAIs so move related data structure field from struct i2s_dai to the common driver data structure. Change-Id: I2080a75f0a49f431ee37fa4488ed13ae8fd26b12 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 106 +++++++++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 01e9512..7cfcef6 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -60,8 +60,7 @@ struct samsung_i2s_dai_data { struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; - /* Memory mapped SFR region */ - void __iomem *addr; + /* Frame Clock */ unsigned frmclk; /* @@ -100,6 +99,9 @@ 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; @@ -143,7 +145,9 @@ static inline bool is_secondary(struct i2s_dai *i2s) /* If operating in SoC-Slave mode */ static inline bool is_slave(struct i2s_dai *i2s) { - u32 mod = readl(i2s->addr + I2SMOD); + struct samsung_i2s_priv *priv = i2s->priv; + + u32 mod = readl(priv->addr + I2SMOD); return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false; } @@ -155,7 +159,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; @@ -193,7 +197,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; } @@ -256,8 +260,10 @@ 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 = readl(priv->addr + I2SMOD) >> i2s->variant_regs->rfs_off; rfs &= i2s->variant_regs->rfs_mask; switch (rfs) { @@ -275,7 +281,8 @@ 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); + struct samsung_i2s_priv *priv = i2s->priv; + u32 mod = readl(priv->addr + I2SMOD); int rfs_shift = i2s->variant_regs->rfs_off; mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift); @@ -307,14 +314,16 @@ 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) */ 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 = readl(priv->addr + I2SMOD) >> i2s->variant_regs->bfs_off; bfs &= i2s->variant_regs->bfs_mask; switch (bfs) { @@ -333,7 +342,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) /* 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); + struct samsung_i2s_priv *priv = i2s->priv; + u32 mod = readl(priv->addr + I2SMOD); int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; int bfs_shift = i2s->variant_regs->bfs_off; @@ -378,13 +388,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 */ 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; @@ -398,7 +408,8 @@ static inline int get_blc(struct i2s_dai *i2s) /* TX Channel Control */ static void i2s_txctrl(struct i2s_dai *i2s, int on) { - void __iomem *addr = i2s->addr; + struct samsung_i2s_priv *priv = i2s->priv; + void __iomem *addr = priv->addr; int txr_off = i2s->variant_regs->txr_off; u32 con = readl(addr + I2SCON); u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); @@ -448,7 +459,8 @@ 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; + struct samsung_i2s_priv *priv = i2s->priv; + void __iomem *addr = priv->addr; int txr_off = i2s->variant_regs->txr_off; u32 con = readl(addr + I2SCON); u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); @@ -485,9 +497,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); @@ -516,7 +528,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, pm_runtime_get_sync(dai->dev); spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); + mod = readl(priv->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); switch (clk_id) { @@ -613,9 +625,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, } spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); + mod = readl(priv->addr + I2SMOD); mod = (mod & ~mask) | val; - writel(mod, i2s->addr + I2SMOD); + writel(mod, priv->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); done: pm_runtime_put(dai->dev); @@ -698,7 +710,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) pm_runtime_get_sync(dai->dev); spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); + mod = readl(priv->addr + I2SMOD); /* * Don't change the I2S mode if any controller is active on this * channel. @@ -714,7 +726,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod |= tmp; - writel(mod, i2s->addr + I2SMOD); + writel(mod, priv->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); pm_runtime_put(dai->dev); @@ -800,9 +812,9 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, } spin_lock_irqsave(i2s->lock, flags); - mod = readl(i2s->addr + I2SMOD); + mod = readl(priv->addr + I2SMOD); mod = (mod & ~mask) | val; - writel(mod, i2s->addr + I2SMOD); + writel(mod, priv->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); @@ -836,7 +848,7 @@ static int i2s_startup(struct snd_pcm_substream *substream, i2s->mode |= DAI_MANAGER; if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR)) - writel(CON_RSTCLR, i2s->addr + I2SCON); + writel(CON_RSTCLR, i2s->priv->addr + I2SCON); spin_unlock_irqrestore(&lock, flags); @@ -919,7 +931,7 @@ static int config_setup(struct i2s_dai *i2s) if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { psr = priv->rclk_srcrate / i2s->frmclk / rfs; - writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR); + writel(((psr - 1) << 8) | PSR_PSREN, priv->addr + I2SPSR); dev_dbg(&i2s->pdev->dev, "RCLK_SRC=%luHz PSR=%u, RCLK=%dfs, BCLK=%dfs\n", priv->rclk_srcrate, psr, rfs, bfs); @@ -1007,8 +1019,9 @@ 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; @@ -1017,7 +1030,7 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 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; @@ -1041,6 +1054,7 @@ 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; @@ -1055,10 +1069,10 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) &i2s->dma_capture); if (i2s->quirks & QUIRK_NEED_RSTCLR) - writel(CON_RSTCLR, i2s->addr + I2SCON); + writel(CON_RSTCLR, priv->addr + I2SCON); if (i2s->quirks & QUIRK_SUPPORTS_IDMA) - idma_reg_addr_init(i2s->addr, + idma_reg_addr_init(priv->addr, i2s->sec_dai->idma_playback.addr); } @@ -1085,6 +1099,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) { + struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = to_info(dai); unsigned long flags; @@ -1093,7 +1108,7 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) if (!is_secondary(i2s)) { if (i2s->quirks & QUIRK_NEED_RSTCLR) { spin_lock_irqsave(i2s->lock, flags); - writel(0, i2s->addr + I2SCON); + writel(0, priv->addr + I2SCON); spin_unlock_irqrestore(i2s->lock, flags); } } @@ -1204,11 +1219,10 @@ static int i2s_alloc_dais(struct samsung_i2s_priv *priv, static int i2s_runtime_suspend(struct device *dev) { struct samsung_i2s_priv *priv = dev_get_drvdata(dev); - struct i2s_dai *i2s = samsung_i2s_get_pri_dai(dev); - priv->suspend_i2smod = readl(i2s->addr + I2SMOD); - priv->suspend_i2scon = readl(i2s->addr + I2SCON); - priv->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 (priv->op_clk) clk_disable_unprepare(priv->op_clk); @@ -1220,7 +1234,6 @@ static int i2s_runtime_suspend(struct device *dev) static int i2s_runtime_resume(struct device *dev) { struct samsung_i2s_priv *priv = dev_get_drvdata(dev); - struct i2s_dai *i2s = samsung_i2s_get_pri_dai(dev); int ret; ret = clk_prepare_enable(priv->clk); @@ -1235,9 +1248,9 @@ static int i2s_runtime_resume(struct device *dev) } } - writel(priv->suspend_i2scon, i2s->addr + I2SCON); - writel(priv->suspend_i2smod, i2s->addr + I2SMOD); - writel(priv->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; } @@ -1295,21 +1308,21 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) if (!(i2s->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); 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, + priv->addr + I2SMOD, reg_info->rclksrc_off, 1, 0, i2s->lock); 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, i2s->lock); p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR]; priv->clk_data.clk_num = 2; @@ -1318,7 +1331,7 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) 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, + priv->addr + I2SMOD, reg_info->cdclkcon_off, CLK_GATE_SET_TO_DISABLE, i2s->lock); priv->clk_data.clk_num += 1; @@ -1422,9 +1435,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) } 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; @@ -1471,7 +1484,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) } sec_dai->dma_playback.addr_width = 4; - sec_dai->addr = pri_dai->addr; sec_dai->quirks = quirks; sec_dai->idma_playback.addr = idma_addr; sec_dai->pri_dai = pri_dai; -- 2.7.4 From 0287faee08f15bdb184269714ef40e6073e14f4f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:36 +0100 Subject: [PATCH 02/16] ASoC: samsung: i2s: Drop spinlock pointer from i2s_dai data structure As we now have the 'priv' pointer in most of the places we can use priv->lock directly, dropping extra indirection in the SFR region spinlock access. Change-Id: Ic137ee120aa1871a9d2142eaee9f9292e2c0f25f Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 51 +++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 7cfcef6..a1265f9 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -87,8 +87,6 @@ struct i2s_dai { u32 quirks; const struct samsung_i2s_variant_regs *variant_regs; - spinlock_t *lock; - struct samsung_i2s_priv *priv; }; @@ -103,7 +101,7 @@ struct samsung_i2s_priv { void __iomem *addr; /* Spinlock protecting access to the device's registers */ - spinlock_t spinlock; + spinlock_t lock; /* CPU DAIs and their corresponding drivers */ struct i2s_dai *dai; @@ -527,9 +525,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, pm_runtime_get_sync(dai->dev); - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); mod = readl(priv->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); switch (clk_id) { case SAMSUNG_I2S_OPCLK: @@ -624,11 +622,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, goto err; } - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); mod = readl(priv->addr + I2SMOD); mod = (mod & ~mask) | val; writel(mod, priv->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); done: pm_runtime_put(dai->dev); @@ -709,7 +707,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } pm_runtime_get_sync(dai->dev); - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); mod = readl(priv->addr + I2SMOD); /* * Don't change the I2S mode if any controller is active on this @@ -717,7 +715,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) */ 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__); @@ -727,7 +725,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod |= tmp; writel(mod, priv->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); pm_runtime_put(dai->dev); return 0; @@ -811,11 +809,11 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); mod = readl(priv->addr + I2SMOD); mod = (mod & ~mask) | val; writel(mod, priv->addr + I2SMOD); - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); @@ -943,6 +941,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); @@ -953,10 +952,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; } @@ -965,12 +964,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); @@ -980,7 +979,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; } @@ -1080,13 +1079,13 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) i2s->rfs = 0; i2s->bfs = 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)) @@ -1107,9 +1106,9 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) if (!is_secondary(i2s)) { if (i2s->quirks & QUIRK_NEED_RSTCLR) { - spin_lock_irqsave(i2s->lock, flags); + spin_lock_irqsave(&priv->lock, flags); writel(0, priv->addr + I2SCON); - spin_unlock_irqrestore(i2s->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } } @@ -1316,13 +1315,13 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) ARRAY_SIZE(p_names), CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, priv->addr + I2SMOD, reg_info->rclksrc_off, - 1, 0, i2s->lock); + 1, 0, &priv->lock); 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, - priv->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]; priv->clk_data.clk_num = 2; @@ -1332,7 +1331,7 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) i2s_clk_name[CLK_I2S_CDCLK], p_names[0], CLK_SET_RATE_PARENT, priv->addr + I2SMOD, reg_info->cdclkcon_off, - CLK_GATE_SET_TO_DISABLE, i2s->lock); + CLK_GATE_SET_TO_DISABLE, &priv->lock); priv->clk_data.clk_num += 1; priv->clk_data.clks = priv->clk_table; @@ -1410,8 +1409,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1]; - spin_lock_init(&priv->spinlock); - pri_dai->lock = &priv->spinlock; + spin_lock_init(&priv->lock); if (!np) { if (i2s_pdata == NULL) { @@ -1473,7 +1471,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (quirks & QUIRK_SEC_DAI) { sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1]; - sec_dai->lock = &priv->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"; -- 2.7.4 From 17fffe1efeed25f4b0ffc914b920ca0771c3583f Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:37 +0100 Subject: [PATCH 03/16] ASoC: samsung: i2s: Move IP variant data to common driver data structure The IP variant data is another thing common for both DAIs, move it to the driver's common data structure. Change-Id: Id83d14a38d6c5d0d4d25dfd0ca8332e88315d271 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index a1265f9..547ec2d 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -85,7 +85,6 @@ struct i2s_dai { struct snd_dmaengine_dai_dma_data idma_playback; dma_filter_fn filter; u32 quirks; - const struct samsung_i2s_variant_regs *variant_regs; struct samsung_i2s_priv *priv; }; @@ -122,6 +121,8 @@ struct samsung_i2s_priv { u32 suspend_i2scon; u32 suspend_i2spsr; + const struct samsung_i2s_variant_regs *variant_regs; + /* The clock provider's data */ struct clk *clk_table[3]; struct clk_onecell_data clk_data; @@ -146,7 +147,7 @@ static inline bool is_slave(struct i2s_dai *i2s) struct samsung_i2s_priv *priv = i2s->priv; u32 mod = readl(priv->addr + I2SMOD); - return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false; + return (mod & (1 << priv->variant_regs->mss_off)) ? true : false; } /* If this interface of the controller is transmitting data */ @@ -261,8 +262,8 @@ static inline unsigned get_rfs(struct i2s_dai *i2s) struct samsung_i2s_priv *priv = i2s->priv; u32 rfs; - rfs = readl(priv->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; @@ -281,9 +282,9 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) { struct samsung_i2s_priv *priv = i2s->priv; u32 mod = readl(priv->addr + I2SMOD); - int rfs_shift = i2s->variant_regs->rfs_off; + 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: @@ -321,8 +322,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s) struct samsung_i2s_priv *priv = i2s->priv; u32 bfs; - bfs = readl(priv->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; @@ -343,7 +344,7 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) struct samsung_i2s_priv *priv = i2s->priv; u32 mod = readl(priv->addr + I2SMOD); int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; - int bfs_shift = i2s->variant_regs->bfs_off; + int bfs_shift = priv->variant_regs->bfs_off; /* Non-TDM I2S controllers do not support BCLK > 48 * FS */ if (!tdm && bfs > 48) { @@ -351,7 +352,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: @@ -408,7 +409,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on) { struct samsung_i2s_priv *priv = i2s->priv; void __iomem *addr = priv->addr; - int txr_off = i2s->variant_regs->txr_off; + int txr_off = priv->variant_regs->txr_off; u32 con = readl(addr + I2SCON); u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); @@ -459,7 +460,7 @@ static void i2s_rxctrl(struct i2s_dai *i2s, int on) { struct samsung_i2s_priv *priv = i2s->priv; void __iomem *addr = priv->addr; - int txr_off = i2s->variant_regs->txr_off; + int txr_off = priv->variant_regs->txr_off; u32 con = readl(addr + I2SCON); u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off); @@ -516,7 +517,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, 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; @@ -644,9 +645,9 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) 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; @@ -1022,7 +1023,6 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) struct i2s_dai *i2s = to_info(dai); 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)); @@ -1031,7 +1031,7 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) else if (is_secondary(i2s)) 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; } @@ -1280,7 +1280,7 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) const char *p_names[2] = { NULL }; struct device *dev = &priv->pdev->dev; struct i2s_dai *i2s = samsung_i2s_get_pri_dai(dev); - const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs; + 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; @@ -1399,6 +1399,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->variant_regs = i2s_dai_data->i2s_variant_regs; + quirks = np ? i2s_dai_data->quirks : i2s_pdata->type.quirks; num_dais = (quirks & QUIRK_SEC_DAI) ? 2 : 1; priv->pdev = pdev; @@ -1457,7 +1459,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) 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) @@ -1471,7 +1472,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (quirks & QUIRK_SEC_DAI) { sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1]; - sec_dai->variant_regs = pri_dai->variant_regs; sec_dai->dma_playback.addr = regs_base + I2STXDS; sec_dai->dma_playback.chan_name = "tx-sec"; -- 2.7.4 From a6f9076f9e74a07393677954ee966cbde14dd1ad Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:38 +0100 Subject: [PATCH 04/16] ASoC: samsung: i2s: Move quirks data to common driver data structure The quirk flags are common for the primary and the secondary DAI so move respective field from struct i2s_dai to common driver data structure. Change-Id: I27506c48b3faa8c344535c6749b534c6c11901e7 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 547ec2d..1be3547 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -84,7 +84,6 @@ struct i2s_dai { struct snd_dmaengine_dai_dma_data dma_capture; struct snd_dmaengine_dai_dma_data idma_playback; dma_filter_fn filter; - u32 quirks; struct samsung_i2s_priv *priv; }; @@ -122,19 +121,13 @@ struct samsung_i2s_priv { u32 suspend_i2spsr; const struct samsung_i2s_variant_regs *variant_regs; + u32 quirks; /* The clock provider's data */ struct clk *clk_table[3]; struct clk_onecell_data clk_data; }; -struct i2s_dai *samsung_i2s_get_pri_dai(struct device *dev) -{ - struct samsung_i2s_priv *priv = dev_get_drvdata(dev); - - return &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1]; -} - /* Returns true if this is the 'overlay' stereo DAI */ static inline bool is_secondary(struct i2s_dai *i2s) { @@ -343,7 +336,7 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) { struct samsung_i2s_priv *priv = i2s->priv; u32 mod = readl(priv->addr + I2SMOD); - int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM; + 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 */ @@ -563,7 +556,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, 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 @@ -831,6 +824,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, 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; @@ -846,7 +840,7 @@ static int i2s_startup(struct snd_pcm_substream *substream, else i2s->mode |= DAI_MANAGER; - if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR)) + if (!any_active(i2s) && (priv->quirks & QUIRK_NEED_RSTCLR)) writel(CON_RSTCLR, i2s->priv->addr + I2SCON); spin_unlock_irqrestore(&lock, flags); @@ -928,7 +922,7 @@ static int config_setup(struct i2s_dai *i2s) if (is_slave(i2s)) return 0; - if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + 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, @@ -1067,10 +1061,10 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); - if (i2s->quirks & QUIRK_NEED_RSTCLR) + if (priv->quirks & QUIRK_NEED_RSTCLR) writel(CON_RSTCLR, priv->addr + I2SCON); - if (i2s->quirks & QUIRK_SUPPORTS_IDMA) + if (priv->quirks & QUIRK_SUPPORTS_IDMA) idma_reg_addr_init(priv->addr, i2s->sec_dai->idma_playback.addr); } @@ -1105,7 +1099,7 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) pm_runtime_get_sync(dai->dev); if (!is_secondary(i2s)) { - if (i2s->quirks & QUIRK_NEED_RSTCLR) { + if (priv->quirks & QUIRK_NEED_RSTCLR) { spin_lock_irqsave(&priv->lock, flags); writel(0, priv->addr + I2SCON); spin_unlock_irqrestore(&priv->lock, flags); @@ -1279,7 +1273,6 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" }; const char *p_names[2] = { NULL }; struct device *dev = &priv->pdev->dev; - struct i2s_dai *i2s = samsung_i2s_get_pri_dai(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; @@ -1305,7 +1298,7 @@ static int i2s_register_clock_provider(struct samsung_i2s_priv *priv) return -ENOMEM; } - if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + if (!(priv->quirks & QUIRK_NO_MUXPSR)) { /* Activate the prescaler */ u32 val = readl(priv->addr + I2SPSR); writel(val | PSR_PSREN, priv->addr + I2SPSR); @@ -1399,11 +1392,11 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->variant_regs = i2s_dai_data->i2s_variant_regs; - quirks = np ? i2s_dai_data->quirks : i2s_pdata->type.quirks; num_dais = (quirks & QUIRK_SEC_DAI) ? 2 : 1; priv->pdev = pdev; + priv->variant_regs = i2s_dai_data->i2s_variant_regs; + priv->quirks = quirks; ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais); if (ret < 0) @@ -1458,7 +1451,6 @@ 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->priv = priv; if (quirks & QUIRK_PRI_6CHAN) @@ -1481,7 +1473,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) } sec_dai->dma_playback.addr_width = 4; - sec_dai->quirks = quirks; sec_dai->idma_playback.addr = idma_addr; sec_dai->pri_dai = pri_dai; sec_dai->priv = priv; -- 2.7.4 From cad14895b2f1684cd8ac27944e802fa21faac3ff Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:40 +0100 Subject: [PATCH 05/16] ASoC: samsung: odroid: Add support for secondary CPU DAI This patch adds DPCM links in order to support the secondary I2S interface. For the secondary PCM interface to be actually available one more entry should be added to the sound-dai property in sound/cpu node in DT. The changes in driver are done in a way so we are backwards compatible with existing DTS/DTB, i.e. if the cpu sound-dai property contains only one entry only one PCM will be registered. Change-Id: I7bc789cf1d025b168fd3411f24f4015016025bb2 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/odroid.c | 131 ++++++++++++++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 36 deletions(-) diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index e7b371b..18bb3bf 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -17,21 +18,24 @@ struct odroid_priv { struct snd_soc_card card; - struct snd_soc_dai_link dai_link; - struct clk *clk_i2s_bus; struct clk *sclk_i2s; }; -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 const struct snd_soc_ops odroid_card_fe_ops = { + .startup = odroid_card_fe_startup, +}; + +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; @@ -86,19 +90,55 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream, 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, +}; + +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 = "samsung-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, *codec; + struct device_node *cpu, *cpu_dai, *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) @@ -130,45 +170,67 @@ 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. + */ + num_pcms = of_count_phandle_with_args(cpu, "sound-dai", + "#sound-dai-cells"); + if (num_pcms == 1) + card->num_links--; + + 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) + return ret; + + if (!args.np) { + dev_err(dev, "sound-dai property parse error: %d\n", ret); + return -EINVAL; + } + + ret = snd_soc_get_dai_name(&args, &link->cpu_dai_name); + of_node_put(args.np); + + if (ret < 0) + return ret; } - ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); + cpu_dai = of_parse_phandle(cpu, "sound-dai", 0); + of_node_put(cpu); + of_node_put(codec); + + ret = snd_soc_of_get_dai_link_codecs(dev, codec, codec_link); if (ret < 0) goto err_put_codec_n; - link->platform_of_node = link->cpu_of_node; - - link->name = "Primary"; - link->stream_name = link->name; - + /* 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_codec_n; } - 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 +244,8 @@ 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); + snd_soc_of_put_dai_link_codecs(codec_link); return ret; } @@ -193,8 +253,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); -- 2.7.4 From 24a3d132294163f3476c25c17d92b5d730323379 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:39 +0100 Subject: [PATCH 06/16] ASoC: samsung: i2s: Get rid of a static spinlock This patch makes the spinlock serializing access to the primary/secondary PCM a per I2S controller lock, rather than a global one. There is no need to have a global lock across multiple I2S controllers in the SoC. Change-Id: I6b56296ce45e219ed5f37af85fd352ff51af617a Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 1be3547..766a049 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -88,9 +88,6 @@ struct i2s_dai { struct samsung_i2s_priv *priv; }; -/* Lock for cross i/f checks */ -static DEFINE_SPINLOCK(lock); - struct samsung_i2s_priv { struct platform_device *pdev; struct platform_device *pdev_sec; @@ -101,6 +98,9 @@ struct samsung_i2s_priv { /* Spinlock protecting access to the device's registers */ spinlock_t lock; + /* Lock for cross i/f checks */ + spinlock_t pcm_lock; + /* CPU DAIs and their corresponding drivers */ struct i2s_dai *dai; struct snd_soc_dai_driver *dai_drv; @@ -831,7 +831,7 @@ static int i2s_startup(struct snd_pcm_substream *substream, pm_runtime_get_sync(dai->dev); - spin_lock_irqsave(&lock, flags); + spin_lock_irqsave(&priv->pcm_lock, flags); i2s->mode |= DAI_OPENED; @@ -843,7 +843,7 @@ static int i2s_startup(struct snd_pcm_substream *substream, 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; } @@ -851,11 +851,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; @@ -867,7 +868,7 @@ 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); } @@ -1405,6 +1406,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai = &priv->dai[SAMSUNG_I2S_ID_PRIMARY - 1]; spin_lock_init(&priv->lock); + spin_lock_init(&priv->pcm_lock); if (!np) { if (i2s_pdata == NULL) { -- 2.7.4 From 8b17eb5cd2d23bd7de57188142fbb5cfb0bbf09b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:41 +0100 Subject: [PATCH 07/16] ASoC: samsung: Specify DMA channel names through custom DMA config This is a part of conversion of Samsung platforms to use the custom DMA config for specifying DMA channel names, in addition to passing custom DMA device for the secondary CPU DAI's "PCM" component for some variants of the I2S controller. We also don't set the SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME any more as setting it wouldn't allow to specify DMA channels through the custom DMA config. Change-Id: Ia7e4da88746788c6fd56e1781160328bdaa224b8 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/dmaengine.c | 12 ++++-------- sound/soc/samsung/i2s.c | 2 +- sound/soc/samsung/s3c2412-i2s.c | 2 +- sound/soc/samsung/s3c24xx-i2s.c | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c index 84601fa..3028719 100644 --- a/sound/soc/samsung/dmaengine.c +++ b/sound/soc/samsung/dmaengine.c @@ -28,7 +28,6 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, 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); @@ -39,14 +38,11 @@ int samsung_asoc_dma_platform_register(struct device *dev, dma_filter_fn filter, 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 766a049..c446e07 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1459,7 +1459,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->drv->playback.channels_max = 6; ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, - NULL, NULL, NULL); + "tx", "rx", NULL); if (ret < 0) goto err_disable_clk; diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index 67dfa27..c08638b 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, 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 ba0f2b9..a8026b6 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, NULL); + "tx", "rx", NULL); if (ret) { dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret); return ret; -- 2.7.4 From 95425ac00de835df5b9f734ff342e6eecba9447b Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:44 +0100 Subject: [PATCH 08/16] ASoC: samsung: i2s: Simplify pri_dai, sec_dai pointers usage If the probe call is on the primary DAI we can use 'other' in place of i2s->sec_dai, if the probe call is on the secondary DAI we can use 'i2s' in place of other->sec_dai. While at it fix one whitespace issue. Change-Id: Idbe9190d87f628f4d8fab77dbdbe433c8cd7825d Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index c446e07..79965fc 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1056,18 +1056,17 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) 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); + 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 (priv->quirks & QUIRK_NEED_RSTCLR) writel(CON_RSTCLR, priv->addr + I2SCON); if (priv->quirks & QUIRK_SUPPORTS_IDMA) idma_reg_addr_init(priv->addr, - i2s->sec_dai->idma_playback.addr); + other->idma_playback.addr); } /* Reset any constraint on RFS and BFS */ -- 2.7.4 From b1d7acb5f6056bf7d1773dff0e5242ac11190288 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:45 +0100 Subject: [PATCH 09/16] ASoC: samsung: i2s: Change indentation in SAMSUNG_I2S_FMTS definition Change indentation so this macro definition spans 2 rows and looks more consistent with surrounding code. Change-Id: I747acdce454a660c637d29ab34450081e02084d2 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 79965fc..846e506 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1149,9 +1149,8 @@ static const struct snd_soc_component_driver samsung_i2s_component = { .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 int i2s_alloc_dais(struct samsung_i2s_priv *priv, const struct samsung_i2s_dai_data *i2s_dai_data, -- 2.7.4 From beb985932da87029eeeed309caebba9fb73735be Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 3 Aug 2018 11:33:57 -0500 Subject: [PATCH 10/16] ASoC: samsung: i2s: Mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Change-Id: I030b163708ab40f17cf500cff7a8f326b890b7f5 Addresses-Coverity-ID: 1381093 ("Missing break in switch") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 846e506..07f815a 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -742,6 +742,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, switch (params_channels(params)) { case 6: val |= MOD_DC2_EN; + /* fall through */ case 4: val |= MOD_DC1_EN; break; -- 2.7.4 From 0e3c6872eff35018e90a522cff8298f36d92ebd2 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:46 +0100 Subject: [PATCH 11/16] ASoC: samsung: i2s: Comments clean up Spelling error fixes, upper/lower case letter changes. Change-Id: Id1060cbb859c6da4480a0dd6f676a6614b64da8c Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 07f815a..84cfa2c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1,5 +1,4 @@ -/* sound/soc/samsung/i2s.c - * +/* * ALSA SoC Audio Layer - Samsung I2S Controller driver * * Copyright (c) 2010 Samsung Electronics Co. Ltd. @@ -61,10 +60,10 @@ struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; - /* 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; @@ -72,8 +71,9 @@ struct i2s_dai { 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 */ @@ -98,7 +98,7 @@ struct samsung_i2s_priv { /* Spinlock protecting access to the device's registers */ spinlock_t lock; - /* Lock for cross i/f checks */ + /* Lock for cross interface checks */ spinlock_t pcm_lock; /* CPU DAIs and their corresponding drivers */ @@ -309,7 +309,7 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs) 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; @@ -331,7 +331,7 @@ 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) { struct samsung_i2s_priv *priv = i2s->priv; @@ -383,7 +383,7 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs) writel(mod, priv->addr + I2SMOD); } -/* Sample-Size */ +/* Sample size */ static inline int get_blc(struct i2s_dai *i2s) { int blc = readl(i2s->priv->addr + I2SMOD); @@ -397,7 +397,7 @@ 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) { struct samsung_i2s_priv *priv = i2s->priv; @@ -742,7 +742,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; @@ -821,7 +821,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, 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) { @@ -1056,7 +1056,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) pm_runtime_get_sync(dai->dev); - if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ + 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, -- 2.7.4 From c7b52e5da4517c05d97eee3a7ad120d8580d218d Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 10:37:47 +0100 Subject: [PATCH 12/16] ASoC: samsung: i2s: Convert to SPDX License Indentifier Replace GPL v2.0 license statements with SPDX license identifier. Change-Id: Iaf0ae489d7d55dc949ba70e956afe3642cdfaeb4 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 84cfa2c..03fff1c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1,13 +1,9 @@ -/* - * ALSA SoC Audio Layer - Samsung I2S Controller driver - * - * Copyright (c) 2010 Samsung Electronics Co. Ltd. - * Jaswinder Singh - * - * 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 #include #include -- 2.7.4 From 890838a2f4d0a07299e4622af9a21d21e8c66538 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 16:58:40 +0100 Subject: [PATCH 13/16] ASoC: samsung: i2s: Prevent potential NULL platform data dereference When np is NULL i2s_pdata could also be NULL but i2s_pdata is now being dereferenced without proper check. Fix this and shorten the error message so we don't exceed 80 characters limit. Change-Id: I5d5b14ff953d4a2617500f50e81eb459d56a6e4f Reported-by: Dan Carpenter Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 03fff1c..02472f5 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1369,7 +1369,7 @@ 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 num_dais, ret; @@ -1389,11 +1389,19 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - quirks = np ? i2s_dai_data->quirks : i2s_pdata->type.quirks; - num_dais = (quirks & QUIRK_SEC_DAI) ? 2 : 1; + 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; - priv->quirks = quirks; ret = i2s_alloc_dais(priv, i2s_dai_data, num_dais); if (ret < 0) @@ -1405,11 +1413,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) spin_lock_init(&priv->pcm_lock); if (!np) { - if (i2s_pdata == NULL) { - dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); - return -EINVAL; - } - 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; @@ -1418,7 +1421,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) } else { 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"); } @@ -1451,7 +1454,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) pri_dai->dma_capture.addr_width = 4; pri_dai->priv = priv; - if (quirks & QUIRK_PRI_6CHAN) + if (priv->quirks & QUIRK_PRI_6CHAN) pri_dai->drv->playback.channels_max = 6; ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter, @@ -1459,7 +1462,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_disable_clk; - if (quirks & QUIRK_SEC_DAI) { + if (priv->quirks & QUIRK_SEC_DAI) { sec_dai = &priv->dai[SAMSUNG_I2S_ID_SECONDARY - 1]; sec_dai->dma_playback.addr = regs_base + I2STXDS; -- 2.7.4 From 88f9017acba5b213d082eda016e84f1c53cf85f8 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 14 Feb 2019 17:00:11 +0100 Subject: [PATCH 14/16] ASoC: samsung: odroid: Ensure proper sample rate on pri/sec PCM Currently when playing sound with different sample rates actual sample rate will be determined by audio stream which starts first on either primary or secondary PCM. The audio root clock will be configured appropriately only for the first stream. As the hardware is limited to same sample rate on both interfaces we need to disallow streams with different sample rates. It is done by this patch by returning error in FE hw_params if there is already active stream running with different sample rate. Change-Id: I643d02d38acf20a7711ba5930e6bceceeaafc087 Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- sound/soc/samsung/odroid.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index 18bb3bf..941e8c3 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -20,6 +20,11 @@ struct odroid_priv { struct snd_soc_card card; 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_fe_startup(struct snd_pcm_substream *substream) @@ -31,8 +36,25 @@ static int odroid_card_fe_startup(struct snd_pcm_substream *substream) return 0; } +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, @@ -41,6 +63,7 @@ static int odroid_card_be_hw_params(struct snd_pcm_substream *substream, 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)) { @@ -87,11 +110,43 @@ static int odroid_card_be_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_be_ops = { .hw_params = odroid_card_be_hw_params, + .trigger = odroid_card_be_trigger, }; static struct snd_soc_dai_link odroid_card_dais[] = { @@ -150,6 +205,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"); -- 2.7.4 From 769d3be463ba1fe1cf7ef8b59e8e969e00cd7efc Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 15 Feb 2019 13:04:22 +0100 Subject: [PATCH 15/16] ASoC: samsung: odroid: Add missing DAPM routes With old DTS there will be missing DAPM routes linking BE with CODECs. Add those routes in the card driver so sound works properly on Odroid XU3/4 also without DTS updates enabling the secondary PCM. Change-Id: I0a2a8e59859cdaf46da0563e30278dc9a8fe2376 Signed-off-by: Sylwester Nawrocki Signed-off-by: Mark Brown --- sound/soc/samsung/odroid.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c index 941e8c3..5b2bcd1 100644 --- a/sound/soc/samsung/odroid.c +++ b/sound/soc/samsung/odroid.c @@ -149,6 +149,12 @@ static const struct snd_soc_ops odroid_card_be_ops = { .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 */ @@ -237,11 +243,15 @@ static int odroid_audio_probe(struct platform_device *pdev) /* * 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) + if (num_pcms == 1) { + card->dapm_routes = odroid_dapm_routes; + card->num_dapm_routes = ARRAY_SIZE(odroid_dapm_routes); card->num_links--; + } for (i = 0; i < num_pcms; i++, link += 2) { ret = of_parse_phandle_with_args(cpu, "sound-dai", -- 2.7.4 From d34667c7f4ab779cece08f75c44b0a832c04be0c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 19 Feb 2019 16:19:40 +0100 Subject: [PATCH 16/16] ASoC: samsung: i2s: Fix secondary platform device unregistration This fixes unregistration of the secondary platform device so all resources are properly released. Additionally the removal sequence is corrected so it is in reverse order comparing to probe sequence. The test against NULL priv->pdev_sec is removed as it is not necessary. Change-Id: If325e11b4109fbc61fbb2df3ac012f2c1e9f0cb5 Signed-off-by: Sylwester Nawrocki Acked-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- sound/soc/samsung/i2s.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 02472f5..cd92bb6 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1359,11 +1359,10 @@ static int i2s_create_secondary_device(struct samsung_i2s_priv *priv) static void i2s_delete_secondary_device(struct samsung_i2s_priv *priv) { - if (priv->pdev_sec) { - platform_device_del(priv->pdev_sec); - priv->pdev_sec = NULL; - } + 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; @@ -1487,14 +1486,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) 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, priv); @@ -1503,7 +1502,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) &samsung_i2s_component, priv->dai_drv, num_dais); if (ret < 0) - goto err_disable_clk; + goto err_del_sec; pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -1518,9 +1517,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) err_disable_pm: pm_runtime_disable(&pdev->dev); +err_del_sec: + i2s_delete_secondary_device(priv); err_disable_clk: clk_disable_unprepare(priv->clk); - i2s_delete_secondary_device(priv); return ret; } @@ -1536,9 +1536,10 @@ static int samsung_i2s_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); i2s_unregister_clock_provider(priv); + i2s_delete_secondary_device(priv); clk_disable_unprepare(priv->clk); + pm_runtime_put_noidle(&pdev->dev); - i2s_delete_secondary_device(priv); return 0; } -- 2.7.4