diff options
Diffstat (limited to 'sound/soc/samsung/i2s.c')
-rw-r--r-- | sound/soc/samsung/i2s.c | 362 |
1 files changed, 216 insertions, 146 deletions
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b5a80c528d8..b92ab40d2be 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -10,9 +10,11 @@ * published by the Free Software Foundation. */ +#include <dt-bindings/sound/samsung-i2s.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> @@ -59,10 +61,8 @@ struct samsung_i2s_dai_data { struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; - /* IOREMAP'd SFRs */ + /* Memory mapped SFR region */ void __iomem *addr; - /* Physical base address of SFRs */ - u32 base; /* Rate of RCLK source clock */ unsigned long rclk_srcrate; /* Frame Clock */ @@ -83,8 +83,6 @@ struct i2s_dai { #define DAI_OPENED (1 << 0) /* Dai is opened */ #define DAI_MANAGER (1 << 1) /* Dai is the manager */ unsigned mode; - /* CDCLK pin direction: 0 - input, 1 - output */ - unsigned int cdclk_out:1; /* Driver for this DAI */ struct snd_soc_dai_driver i2s_dai_drv; /* DMA parameters */ @@ -95,8 +93,15 @@ struct i2s_dai { u32 suspend_i2smod; u32 suspend_i2scon; u32 suspend_i2spsr; - unsigned long gpios[7]; /* i2s gpio line numbers */ const struct samsung_i2s_variant_regs *variant_regs; + + /* Spinlock protecting access to the device's registers */ + spinlock_t spinlock; + spinlock_t *lock; + + /* Below fields are only valid if this is the primary FIFO */ + struct clk *clk_table[3]; + struct clk_onecell_data clk_data; }; /* Lock for cross i/f checks */ @@ -133,10 +138,16 @@ static inline bool tx_active(struct i2s_dai *i2s) return active ? true : false; } +/* Return pointer to the other DAI */ +static inline struct i2s_dai *get_other_dai(struct i2s_dai *i2s) +{ + return i2s->pri_dai ? : i2s->sec_dai; +} + /* If the other interface of the controller is transmitting data */ static inline bool other_tx_active(struct i2s_dai *i2s) { - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); return tx_active(other); } @@ -163,7 +174,7 @@ static inline bool rx_active(struct i2s_dai *i2s) /* If the other interface of the controller is receiving data */ static inline bool other_rx_active(struct i2s_dai *i2s) { - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); return rx_active(other); } @@ -464,18 +475,23 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int rfs, int dir) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; - u32 mod = readl(i2s->addr + I2SMOD); + struct i2s_dai *other = get_other_dai(i2s); const struct samsung_i2s_variant_regs *i2s_regs = i2s->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; + + spin_lock(i2s->lock); + mod = readl(i2s->addr + I2SMOD); + spin_unlock(i2s->lock); switch (clk_id) { case SAMSUNG_I2S_OPCLK: - mod &= ~MOD_OPCLK_MASK; - mod |= dir; + mask = MOD_OPCLK_MASK; + val = dir; break; case SAMSUNG_I2S_CDCLK: + mask = 1 << i2s_regs->cdclkcon_off; /* Shouldn't matter in GATING(CLOCK_IN) mode */ if (dir == SND_SOC_CLOCK_IN) rfs = 0; @@ -492,15 +508,15 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } if (dir == SND_SOC_CLOCK_IN) - mod |= 1 << i2s_regs->cdclkcon_off; - else - mod &= ~(1 << i2s_regs->cdclkcon_off); + val = 1 << i2s_regs->cdclkcon_off; i2s->rfs = rfs; break; case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ + mask = 1 << i2s_regs->rclksrc_off; + if ((i2s->quirks & QUIRK_NO_MUXPSR) || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) clk_id = 0; @@ -550,18 +566,19 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, return 0; } - if (clk_id == 0) - mod &= ~(1 << i2s_regs->rclksrc_off); - else - mod |= 1 << i2s_regs->rclksrc_off; - + if (clk_id == 1) + val = 1 << i2s_regs->rclksrc_off; break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); return -EINVAL; } + spin_lock(i2s->lock); + mod = readl(i2s->addr + I2SMOD); + mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); + spin_unlock(i2s->lock); return 0; } @@ -570,9 +587,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct i2s_dai *i2s = to_info(dai); - u32 mod = readl(i2s->addr + I2SMOD); int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; - u32 tmp = 0; + u32 mod, tmp = 0; lrp_shift = i2s->variant_regs->lrp_off; sdf_shift = i2s->variant_regs->sdf_off; @@ -632,12 +648,15 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } + spin_lock(i2s->lock); + mod = readl(i2s->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(i2s->lock); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; @@ -646,6 +665,7 @@ 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(i2s->lock); return 0; } @@ -654,16 +674,16 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - u32 mod = readl(i2s->addr + I2SMOD); + u32 mod, mask = 0, val = 0; if (!is_secondary(i2s)) - mod &= ~(MOD_DC2_EN | MOD_DC1_EN); + mask |= (MOD_DC2_EN | MOD_DC1_EN); switch (params_channels(params)) { case 6: - mod |= MOD_DC2_EN; + val |= MOD_DC2_EN; case 4: - mod |= MOD_DC1_EN; + val |= MOD_DC1_EN; break; case 2: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -685,44 +705,49 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, } if (is_secondary(i2s)) - mod &= ~MOD_BLCS_MASK; + mask |= MOD_BLCS_MASK; else - mod &= ~MOD_BLCP_MASK; + mask |= MOD_BLCP_MASK; if (is_manager(i2s)) - mod &= ~MOD_BLC_MASK; + mask |= MOD_BLC_MASK; switch (params_width(params)) { case 8: if (is_secondary(i2s)) - mod |= MOD_BLCS_8BIT; + val |= MOD_BLCS_8BIT; else - mod |= MOD_BLCP_8BIT; + val |= MOD_BLCP_8BIT; if (is_manager(i2s)) - mod |= MOD_BLC_8BIT; + val |= MOD_BLC_8BIT; break; case 16: if (is_secondary(i2s)) - mod |= MOD_BLCS_16BIT; + val |= MOD_BLCS_16BIT; else - mod |= MOD_BLCP_16BIT; + val |= MOD_BLCP_16BIT; if (is_manager(i2s)) - mod |= MOD_BLC_16BIT; + val |= MOD_BLC_16BIT; break; case 24: if (is_secondary(i2s)) - mod |= MOD_BLCS_24BIT; + val |= MOD_BLCS_24BIT; else - mod |= MOD_BLCP_24BIT; + val |= MOD_BLCP_24BIT; if (is_manager(i2s)) - mod |= MOD_BLC_24BIT; + val |= MOD_BLC_24BIT; break; default: dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", params_format(params)); return -EINVAL; } + + spin_lock(i2s->lock); + mod = readl(i2s->addr + I2SMOD); + mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); + spin_unlock(i2s->lock); samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); @@ -736,7 +761,7 @@ static int i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; spin_lock_irqsave(&lock, flags); @@ -753,9 +778,6 @@ static int i2s_startup(struct snd_pcm_substream *substream, spin_unlock_irqrestore(&lock, flags); - if (!is_opened(other) && i2s->cdclk_out) - i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, - 0, SND_SOC_CLOCK_OUT); return 0; } @@ -763,38 +785,27 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); unsigned long flags; - const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; spin_lock_irqsave(&lock, flags); i2s->mode &= ~DAI_OPENED; i2s->mode &= ~DAI_MANAGER; - if (is_opened(other)) { + if (is_opened(other)) other->mode |= DAI_MANAGER; - } else { - u32 mod = readl(i2s->addr + I2SMOD); - i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off)); - if (other) - other->cdclk_out = i2s->cdclk_out; - } + /* Reset any constraint on RFS and BFS */ i2s->rfs = 0; i2s->bfs = 0; spin_unlock_irqrestore(&lock, flags); - - /* Gate CDCLK by default */ - if (!is_opened(other)) - i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, - 0, SND_SOC_CLOCK_IN); } static int config_setup(struct i2s_dai *i2s) { - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); unsigned rfs, bfs, blc; u32 psr; @@ -864,10 +875,10 @@ static int i2s_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - local_irq_save(flags); + spin_lock_irqsave(i2s->lock, flags); if (config_setup(i2s)) { - local_irq_restore(flags); + spin_unlock_irqrestore(i2s->lock, flags); return -EINVAL; } @@ -876,12 +887,12 @@ static int i2s_trigger(struct snd_pcm_substream *substream, else i2s_txctrl(i2s, 1); - local_irq_restore(flags); + spin_unlock_irqrestore(i2s->lock, flags); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - local_irq_save(flags); + spin_lock_irqsave(i2s->lock, flags); if (capture) { i2s_rxctrl(i2s, 0); @@ -891,7 +902,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream, i2s_fifo(i2s, FIC_TXFLUSH); } - local_irq_restore(flags); + spin_unlock_irqrestore(i2s->lock, flags); break; } @@ -902,7 +913,7 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; + struct i2s_dai *other = get_other_dai(i2s); switch (div_id) { case SAMSUNG_I2S_DIV_BCLK: @@ -971,58 +982,36 @@ static int i2s_resume(struct snd_soc_dai *dai) static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) { struct i2s_dai *i2s = to_info(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; - int ret; + struct i2s_dai *other = get_other_dai(i2s); + unsigned long flags; - if (other && other->clk) { /* If this is probe on secondary */ + if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, NULL); - goto probe_exit; - } - - i2s->addr = ioremap(i2s->base, 0x100); - if (i2s->addr == NULL) { - dev_err(&i2s->pdev->dev, "cannot ioremap registers\n"); - return -ENXIO; - } - - i2s->clk = clk_get(&i2s->pdev->dev, "iis"); - if (IS_ERR(i2s->clk)) { - dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n"); - iounmap(i2s->addr); - return PTR_ERR(i2s->clk); - } - - ret = clk_prepare_enable(i2s->clk); - if (ret != 0) { - dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret); - return ret; - } - - samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); - - if (other) { - other->addr = i2s->addr; - other->clk = i2s->clk; - } + } else { + samsung_asoc_init_dma_data(dai, &i2s->dma_playback, + &i2s->dma_capture); - if (i2s->quirks & QUIRK_NEED_RSTCLR) - writel(CON_RSTCLR, i2s->addr + I2SCON); + if (i2s->quirks & QUIRK_NEED_RSTCLR) + writel(CON_RSTCLR, i2s->addr + I2SCON); - if (i2s->quirks & QUIRK_SUPPORTS_IDMA) - idma_reg_addr_init(i2s->addr, + if (i2s->quirks & QUIRK_SUPPORTS_IDMA) + idma_reg_addr_init(i2s->addr, i2s->sec_dai->idma_playback.dma_addr); + } -probe_exit: /* Reset any constraint on RFS and BFS */ i2s->rfs = 0; i2s->bfs = 0; i2s->rclk_srcrate = 0; + + spin_lock_irqsave(i2s->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); /* Gate CDCLK by default */ if (!is_opened(other)) @@ -1035,21 +1024,15 @@ probe_exit: static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) { struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); - struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; - - if (!other || !other->clk) { - if (i2s->quirks & QUIRK_NEED_RSTCLR) + if (!is_secondary(i2s)) { + if (i2s->quirks & QUIRK_NEED_RSTCLR) { + spin_lock(i2s->lock); writel(0, i2s->addr + I2SCON); - - clk_disable_unprepare(i2s->clk); - clk_put(i2s->clk); - - iounmap(i2s->addr); + spin_unlock(i2s->lock); + } } - i2s->clk = NULL; - return 0; } @@ -1124,15 +1107,14 @@ static const struct of_device_id exynos_i2s_match[]; static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data( struct platform_device *pdev) { -#ifdef CONFIG_OF - if (pdev->dev.of_node) { + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(exynos_i2s_match, pdev->dev.of_node); - return match->data; - } else -#endif + return match ? match->data : NULL; + } else { return (struct samsung_i2s_dai_data *) platform_get_device_id(pdev)->driver_data; + } } #ifdef CONFIG_PM @@ -1155,6 +1137,87 @@ static int i2s_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ +static void i2s_unregister_clocks(struct i2s_dai *i2s) +{ + 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]); + } +} + +static void i2s_unregister_clock_provider(struct platform_device *pdev) +{ + struct i2s_dai *i2s = dev_get_drvdata(&pdev->dev); + + of_clk_del_provider(pdev->dev.of_node); + i2s_unregister_clocks(i2s); +} + +static int i2s_register_clock_provider(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct i2s_dai *i2s = dev_get_drvdata(dev); + const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" }; + const char *p_names[2] = { NULL }; + const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs; + struct clk *rclksrc; + int ret, i; + + /* Register the clock provider only if it's expected in the DTB */ + if (!of_find_property(dev->of_node, "#clock-cells", NULL)) + return 0; + + /* Get the RCLKSRC mux clock parent clock names */ + for (i = 0; i < ARRAY_SIZE(p_names); i++) { + rclksrc = clk_get(dev, clk_name[i]); + if (IS_ERR(rclksrc)) + continue; + p_names[i] = __clk_get_name(rclksrc); + clk_put(rclksrc); + } + + if (!(i2s->quirks & QUIRK_NO_MUXPSR)) { + /* Activate the prescaler */ + u32 val = readl(i2s->addr + I2SPSR); + writel(val | PSR_PSREN, i2s->addr + I2SPSR); + + i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL, + "i2s_rclksrc", 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); + + i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL, + "i2s_presc", "i2s_rclksrc", + CLK_SET_RATE_PARENT, + i2s->addr + I2SPSR, 8, 6, 0, i2s->lock); + + p_names[0] = "i2s_presc"; + i2s->clk_data.clk_num = 2; + } + of_property_read_string_index(dev->of_node, + "clock-output-names", 0, &clk_name[0]); + + i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0], + p_names[0], CLK_SET_RATE_PARENT, + i2s->addr + I2SMOD, reg_info->cdclkcon_off, + CLK_GATE_SET_TO_DISABLE, i2s->lock); + + i2s->clk_data.clk_num += 1; + i2s->clk_data.clks = i2s->clk_table; + + ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, + &i2s->clk_data); + if (ret < 0) { + dev_err(dev, "failed to add clock provider: %d\n", ret); + i2s_unregister_clocks(i2s); + } + + return ret; +} + static int samsung_i2s_probe(struct platform_device *pdev) { struct i2s_dai *pri_dai, *sec_dai = NULL; @@ -1164,7 +1227,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) u32 regs_base, quirks = 0, idma_addr = 0; struct device_node *np = pdev->dev.of_node; const struct samsung_i2s_dai_data *i2s_dai_data; - int ret = 0; + int ret; /* Call during Seconday interface registration */ i2s_dai_data = samsung_i2s_get_driver_data(pdev); @@ -1175,11 +1238,13 @@ static int samsung_i2s_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to get drvdata\n"); return -EFAULT; } - devm_snd_soc_register_component(&sec_dai->pdev->dev, + ret = devm_snd_soc_register_component(&sec_dai->pdev->dev, &samsung_i2s_component, &sec_dai->i2s_dai_drv, 1); - samsung_asoc_dma_platform_register(&pdev->dev); - return 0; + if (ret != 0) + return ret; + + return samsung_asoc_dma_platform_register(&pdev->dev); } pri_dai = i2s_alloc_dai(pdev, false); @@ -1188,6 +1253,9 @@ static int samsung_i2s_probe(struct platform_device *pdev) return -ENOMEM; } + spin_lock_init(&pri_dai->spinlock); + pri_dai->lock = &pri_dai->spinlock; + if (!np) { res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!res) { @@ -1229,25 +1297,29 @@ static int samsung_i2s_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S SFR address\n"); - return -ENXIO; - } + pri_dai->addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pri_dai->addr)) + return PTR_ERR(pri_dai->addr); - if (!request_mem_region(res->start, resource_size(res), - "samsung-i2s")) { - dev_err(&pdev->dev, "Unable to request SFR region\n"); - return -EBUSY; - } regs_base = res->start; + pri_dai->clk = devm_clk_get(&pdev->dev, "iis"); + if (IS_ERR(pri_dai->clk)) { + dev_err(&pdev->dev, "Failed to get iis clock\n"); + return PTR_ERR(pri_dai->clk); + } + + ret = clk_prepare_enable(pri_dai->clk); + if (ret != 0) { + dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); + return ret; + } pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_playback.ch_name = "tx"; pri_dai->dma_capture.ch_name = "rx"; pri_dai->dma_playback.dma_size = 4; pri_dai->dma_capture.dma_size = 4; - pri_dai->base = regs_base; pri_dai->quirks = quirks; pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; @@ -1258,10 +1330,10 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai = i2s_alloc_dai(pdev, true); if (!sec_dai) { dev_err(&pdev->dev, "Unable to alloc I2S_sec\n"); - ret = -ENOMEM; - goto err; + return -ENOMEM; } + sec_dai->lock = &pri_dai->spinlock; sec_dai->variant_regs = pri_dai->variant_regs; sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.ch_name = "tx-sec"; @@ -1273,7 +1345,8 @@ static int samsung_i2s_probe(struct platform_device *pdev) } sec_dai->dma_playback.dma_size = 4; - sec_dai->base = regs_base; + sec_dai->addr = pri_dai->addr; + sec_dai->clk = pri_dai->clk; sec_dai->quirks = quirks; sec_dai->idma_playback.dma_addr = idma_addr; sec_dai->pri_dai = pri_dai; @@ -1282,8 +1355,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) 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; + return -EINVAL; } devm_snd_soc_register_component(&pri_dai->pdev->dev, @@ -1292,32 +1364,30 @@ static int samsung_i2s_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - samsung_asoc_dma_platform_register(&pdev->dev); - - return 0; -err: - if (res) - release_mem_region(regs_base, resource_size(res)); + ret = samsung_asoc_dma_platform_register(&pdev->dev); + if (ret != 0) + return ret; - return ret; + return i2s_register_clock_provider(pdev); } static int samsung_i2s_remove(struct platform_device *pdev) { struct i2s_dai *i2s, *other; - struct resource *res; i2s = dev_get_drvdata(&pdev->dev); - other = i2s->pri_dai ? : i2s->sec_dai; + other = get_other_dai(i2s); if (other) { other->pri_dai = NULL; other->sec_dai = NULL; } else { pm_runtime_disable(&pdev->dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); + } + + if (!is_secondary(i2s)) { + i2s_unregister_clock_provider(pdev); + clk_disable_unprepare(i2s->clk); } i2s->pri_dai = NULL; |