[alsa-devel] [PATCH 0/5] Runtime PM support for Exynos I2S driver
Exynos I2S audio controller was missing proper runtime PM implementation and this patchset addresses this issue.
This patchset is a another step to add support for audio power domain on Exynos5 SoCs.
Audio power domain on Exynos5 SoCs contains following hardware modules: 1. clock controller 2. pin controller 3. PL330 DMA controller 4. I2S audio controller
The other patches related to enabling full support for audio power domain can be found here: 1. PL330 ADMA controller non-irqsafe runtime PM: https://www.spinics.net/lists/arm-kernel/msg550008.html 2. Runtime PM for clock controllers (Exynos Audio subsystem will be added in v4 soon): https://www.spinics.net/lists/arm-kernel/msg538122.html 3. Runtime PM support for pin controller: https://www.spinics.net/lists/linux-samsung-soc/msg56674.html
Patches are based on linux-next from 2016.12.24. Tested on Odroid U3 (Exynos4412), Odroid XU3 (Exynos5422) and TM2 (Exynos5433) boards.
Best regards Marek Szyprowski Samsung R&D Institute Poland
Patch summary:
Marek Szyprowski (5): ASoC: samsung: i2s: Remove virtual device for secondary DAI ASoC: samsung: i2s: Ensure proper runtime PM state of I2S device ASoC: samsung: i2s: Move saving and restoring regs to runtime pm operations ASoC: samsung: i2s: Let runtime PM operations to control op_clk too ASoC: samsung: i2s: Provide I2S device for registered clocks
sound/soc/samsung/i2s.c | 193 +++++++++++++++++++++++------------------------- 1 file changed, 92 insertions(+), 101 deletions(-)
For some unknown (maybe historical?) reasons support for secondary I2S DAI was implemented by adding additional virtual platform device, which was then probed again with the main I2S driver. This pattern is really hard to follow and provides no benefits, so lets remove this hack and register both DAIs during linear probe of Exynos I2S controller driver.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- sound/soc/samsung/i2s.c | 102 ++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 76 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index e00974bc5616..ce4e70ef5568 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -34,11 +34,6 @@
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
-enum samsung_dai_type { - TYPE_PRI, - TYPE_SEC, -}; - struct samsung_i2s_variant_regs { unsigned int bfs_off; unsigned int rfs_off; @@ -54,7 +49,6 @@ struct samsung_i2s_variant_regs { };
struct samsung_i2s_dai_data { - int dai_type; u32 quirks; const struct samsung_i2s_variant_regs *i2s_variant_regs; }; @@ -1066,7 +1060,6 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) { struct i2s_dai *i2s; - int ret;
i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL); if (i2s == NULL) @@ -1091,28 +1084,10 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) i2s->i2s_dai_drv.capture.channels_max = 2; i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES; i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; - dev_set_drvdata(&i2s->pdev->dev, i2s); - } else { /* Create a new platform_device for Secondary */ - i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1); - if (!i2s->pdev) - return NULL; - - i2s->pdev->dev.parent = &pdev->dev; - - platform_set_drvdata(i2s->pdev, i2s); - ret = platform_device_add(i2s->pdev); - if (ret < 0) - return NULL; } - return i2s; }
-static void i2s_free_sec_dai(struct i2s_dai *i2s) -{ - platform_device_del(i2s->pdev); -} - #ifdef CONFIG_PM static int i2s_runtime_suspend(struct device *dev) { @@ -1231,22 +1206,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) i2s_dai_data = (struct samsung_i2s_dai_data *) platform_get_device_id(pdev)->driver_data;
- /* Call during the secondary interface registration */ - if (i2s_dai_data->dai_type == TYPE_SEC) { - sec_dai = dev_get_drvdata(&pdev->dev); - if (!sec_dai) { - dev_err(&pdev->dev, "Unable to get drvdata\n"); - return -EFAULT; - } - ret = samsung_asoc_dma_platform_register(&pdev->dev, - sec_dai->filter, "tx-sec", NULL); - if (ret != 0) - return ret; - - return devm_snd_soc_register_component(&sec_dai->pdev->dev, - &samsung_i2s_component, - &sec_dai->i2s_dai_drv, 1); - }
pri_dai = i2s_alloc_dai(pdev, false); if (!pri_dai) { @@ -1318,6 +1277,12 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_disable_clk;
+ ret = devm_snd_soc_register_component(&pdev->dev, + &samsung_i2s_component, + &pri_dai->i2s_dai_drv, 1); + if (ret < 0) + goto err_disable_clk; + if (quirks & QUIRK_SEC_DAI) { sec_dai = i2s_alloc_dai(pdev, true); if (!sec_dai) { @@ -1342,6 +1307,17 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai->idma_playback.addr = idma_addr; sec_dai->pri_dai = pri_dai; pri_dai->sec_dai = sec_dai; + + ret = samsung_asoc_dma_platform_register(&pdev->dev, + sec_dai->filter, "tx-sec", NULL); + if (ret < 0) + goto err_disable_clk; + + ret = devm_snd_soc_register_component(&pdev->dev, + &samsung_i2s_component, + &sec_dai->i2s_dai_drv, 1); + if (ret < 0) + goto err_disable_clk; }
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { @@ -1350,11 +1326,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) goto err_disable_clk; }
- ret = devm_snd_soc_register_component(&pri_dai->pdev->dev, - &samsung_i2s_component, - &pri_dai->i2s_dai_drv, 1); - if (ret < 0) - goto err_free_dai; + dev_set_drvdata(&pdev->dev, pri_dai);
pm_runtime_enable(&pdev->dev); @@ -1364,9 +1336,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) return 0;
pm_runtime_disable(&pdev->dev); -err_free_dai: - if (sec_dai) - i2s_free_sec_dai(sec_dai); err_disable_clk: clk_disable_unprepare(pri_dai->clk); return ret; @@ -1374,25 +1343,18 @@ static int samsung_i2s_probe(struct platform_device *pdev)
static int samsung_i2s_remove(struct platform_device *pdev) { - struct i2s_dai *i2s, *other; + struct i2s_dai *pri_dai, *sec_dai;
- i2s = dev_get_drvdata(&pdev->dev); - other = get_other_dai(i2s); + pri_dai = dev_get_drvdata(&pdev->dev); + sec_dai = pri_dai->sec_dai;
- if (other) { - other->pri_dai = NULL; - other->sec_dai = NULL; - } else { - pm_runtime_disable(&pdev->dev); - } + pri_dai->sec_dai = NULL; + sec_dai->pri_dai = NULL;
- if (!is_secondary(i2s)) { - i2s_unregister_clock_provider(pdev); - clk_disable_unprepare(i2s->clk); - } + pm_runtime_disable(&pdev->dev);
- i2s->pri_dai = NULL; - i2s->sec_dai = NULL; + i2s_unregister_clock_provider(pdev); + clk_disable_unprepare(pri_dai->clk);
return 0; } @@ -1454,49 +1416,37 @@ static int samsung_i2s_remove(struct platform_device *pdev) };
static const struct samsung_i2s_dai_data i2sv3_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_NO_MUXPSR, .i2s_variant_regs = &i2sv3_regs, };
static const struct samsung_i2s_dai_data i2sv5_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_IDMA, .i2s_variant_regs = &i2sv3_regs, };
static const struct samsung_i2s_dai_data i2sv6_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, .i2s_variant_regs = &i2sv6_regs, };
static const struct samsung_i2s_dai_data i2sv7_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, .i2s_variant_regs = &i2sv7_regs, };
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, .i2s_variant_regs = &i2sv5_i2s1_regs, };
-static const struct samsung_i2s_dai_data samsung_dai_type_sec = { - .dai_type = TYPE_SEC, -}; - static const struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", .driver_data = (kernel_ulong_t)&i2sv3_dai_type, - }, { - .name = "samsung-i2s-sec", - .driver_data = (kernel_ulong_t)&samsung_dai_type_sec, }, {}, };
On Thu, Dec 29, 2016 at 12:34:03PM +0100, Marek Szyprowski wrote:
For some unknown (maybe historical?) reasons support for secondary I2S DAI was implemented by adding additional virtual platform device, which was then probed again with the main I2S driver. This pattern is really hard to follow and provides no benefits, so lets remove this hack and register both DAIs during linear probe of Exynos I2S controller driver.
It's historical, there used to be limitations that made it impossible to register multiple devices to one DAI IIRC.
The patch
ASoC: samsung: i2s: Remove virtual device for secondary DAI
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From be2c92eb64023e294d6bb9232578963670bb121b Mon Sep 17 00:00:00 2001
From: Marek Szyprowski m.szyprowski@samsung.com Date: Thu, 29 Dec 2016 12:34:03 +0100 Subject: [PATCH] ASoC: samsung: i2s: Remove virtual device for secondary DAI
For some unknown (maybe historical?) reasons support for secondary I2S DAI was implemented by adding additional virtual platform device, which was then probed again with the main I2S driver. This pattern is really hard to follow and provides no benefits, so lets remove this hack and register both DAIs during linear probe of Exynos I2S controller driver.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/samsung/i2s.c | 102 ++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 76 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index d55326289a4a..10b19a4afe86 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -34,11 +34,6 @@
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
-enum samsung_dai_type { - TYPE_PRI, - TYPE_SEC, -}; - struct samsung_i2s_variant_regs { unsigned int bfs_off; unsigned int rfs_off; @@ -54,7 +49,6 @@ struct samsung_i2s_variant_regs { };
struct samsung_i2s_dai_data { - int dai_type; u32 quirks; const struct samsung_i2s_variant_regs *i2s_variant_regs; }; @@ -1066,7 +1060,6 @@ static const struct snd_soc_component_driver samsung_i2s_component = { static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) { struct i2s_dai *i2s; - int ret;
i2s = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dai), GFP_KERNEL); if (i2s == NULL) @@ -1091,28 +1084,10 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) i2s->i2s_dai_drv.capture.channels_max = 2; i2s->i2s_dai_drv.capture.rates = SAMSUNG_I2S_RATES; i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; - dev_set_drvdata(&i2s->pdev->dev, i2s); - } else { /* Create a new platform_device for Secondary */ - i2s->pdev = platform_device_alloc("samsung-i2s-sec", -1); - if (!i2s->pdev) - return NULL; - - i2s->pdev->dev.parent = &pdev->dev; - - platform_set_drvdata(i2s->pdev, i2s); - ret = platform_device_add(i2s->pdev); - if (ret < 0) - return NULL; } - return i2s; }
-static void i2s_free_sec_dai(struct i2s_dai *i2s) -{ - platform_device_del(i2s->pdev); -} - #ifdef CONFIG_PM static int i2s_runtime_suspend(struct device *dev) { @@ -1230,22 +1205,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) i2s_dai_data = (struct samsung_i2s_dai_data *) platform_get_device_id(pdev)->driver_data;
- /* Call during the secondary interface registration */ - if (i2s_dai_data->dai_type == TYPE_SEC) { - sec_dai = dev_get_drvdata(&pdev->dev); - if (!sec_dai) { - dev_err(&pdev->dev, "Unable to get drvdata\n"); - return -EFAULT; - } - ret = samsung_asoc_dma_platform_register(&pdev->dev, - sec_dai->filter, "tx-sec", NULL); - if (ret != 0) - return ret; - - return devm_snd_soc_register_component(&sec_dai->pdev->dev, - &samsung_i2s_component, - &sec_dai->i2s_dai_drv, 1); - }
pri_dai = i2s_alloc_dai(pdev, false); if (!pri_dai) { @@ -1312,6 +1271,12 @@ static int samsung_i2s_probe(struct platform_device *pdev) if (ret < 0) goto err_disable_clk;
+ ret = devm_snd_soc_register_component(&pdev->dev, + &samsung_i2s_component, + &pri_dai->i2s_dai_drv, 1); + if (ret < 0) + goto err_disable_clk; + if (quirks & QUIRK_SEC_DAI) { sec_dai = i2s_alloc_dai(pdev, true); if (!sec_dai) { @@ -1336,6 +1301,17 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai->idma_playback.addr = idma_addr; sec_dai->pri_dai = pri_dai; pri_dai->sec_dai = sec_dai; + + ret = samsung_asoc_dma_platform_register(&pdev->dev, + sec_dai->filter, "tx-sec", NULL); + if (ret < 0) + goto err_disable_clk; + + ret = devm_snd_soc_register_component(&pdev->dev, + &samsung_i2s_component, + &sec_dai->i2s_dai_drv, 1); + if (ret < 0) + goto err_disable_clk; }
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { @@ -1344,11 +1320,7 @@ static int samsung_i2s_probe(struct platform_device *pdev) goto err_disable_clk; }
- ret = devm_snd_soc_register_component(&pri_dai->pdev->dev, - &samsung_i2s_component, - &pri_dai->i2s_dai_drv, 1); - if (ret < 0) - goto err_free_dai; + dev_set_drvdata(&pdev->dev, pri_dai);
pm_runtime_enable(&pdev->dev); @@ -1358,9 +1330,6 @@ static int samsung_i2s_probe(struct platform_device *pdev) return 0;
pm_runtime_disable(&pdev->dev); -err_free_dai: - if (sec_dai) - i2s_free_sec_dai(sec_dai); err_disable_clk: clk_disable_unprepare(pri_dai->clk); return ret; @@ -1368,25 +1337,18 @@ static int samsung_i2s_probe(struct platform_device *pdev)
static int samsung_i2s_remove(struct platform_device *pdev) { - struct i2s_dai *i2s, *other; + struct i2s_dai *pri_dai, *sec_dai;
- i2s = dev_get_drvdata(&pdev->dev); - other = get_other_dai(i2s); + pri_dai = dev_get_drvdata(&pdev->dev); + sec_dai = pri_dai->sec_dai;
- if (other) { - other->pri_dai = NULL; - other->sec_dai = NULL; - } else { - pm_runtime_disable(&pdev->dev); - } + pri_dai->sec_dai = NULL; + sec_dai->pri_dai = NULL;
- if (!is_secondary(i2s)) { - i2s_unregister_clock_provider(pdev); - clk_disable_unprepare(i2s->clk); - } + pm_runtime_disable(&pdev->dev);
- i2s->pri_dai = NULL; - i2s->sec_dai = NULL; + i2s_unregister_clock_provider(pdev); + clk_disable_unprepare(pri_dai->clk);
return 0; } @@ -1448,49 +1410,37 @@ static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = { };
static const struct samsung_i2s_dai_data i2sv3_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_NO_MUXPSR, .i2s_variant_regs = &i2sv3_regs, };
static const struct samsung_i2s_dai_data i2sv5_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_IDMA, .i2s_variant_regs = &i2sv3_regs, };
static const struct samsung_i2s_dai_data i2sv6_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA, .i2s_variant_regs = &i2sv6_regs, };
static const struct samsung_i2s_dai_data i2sv7_dai_type = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM, .i2s_variant_regs = &i2sv7_regs, };
static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = { - .dai_type = TYPE_PRI, .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR, .i2s_variant_regs = &i2sv5_i2s1_regs, };
-static const struct samsung_i2s_dai_data samsung_dai_type_sec = { - .dai_type = TYPE_SEC, -}; - static const struct platform_device_id samsung_i2s_driver_ids[] = { { .name = "samsung-i2s", .driver_data = (kernel_ulong_t)&i2sv3_dai_type, - }, { - .name = "samsung-i2s-sec", - .driver_data = (kernel_ulong_t)&samsung_dai_type_sec, }, {}, };
This patch adds calls to pm_runtime_get/put to ensure that any access to I2S registers is done with proper (active) runtime PM state of I2S device. Till now the driver enabled runtime PM, but didn't manage the state during driver operation. The driver worked fine only because the runtime PM callbacks managed device clock, which was enabled all the time because of the additional enable call in the driver's probe function.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- sound/soc/samsung/i2s.c | 54 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 8 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index ce4e70ef5568..2aa800b99938 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -477,6 +477,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; u32 mod, mask, val = 0; unsigned long flags; + int ret = 0; + + pm_runtime_get_sync(dai->dev);
spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); @@ -501,7 +504,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, && (mod & cdcon_mask))))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - return -EAGAIN; + ret = -EAGAIN; + goto err; }
if (dir == SND_SOC_CLOCK_IN) @@ -529,7 +533,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } else { i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); - return 0; + goto done; } }
@@ -540,8 +544,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, i2s->op_clk = clk_get(&i2s->pdev->dev, "i2s_opclk0");
- if (WARN_ON(IS_ERR(i2s->op_clk))) - return PTR_ERR(i2s->op_clk); + if (WARN_ON(IS_ERR(i2s->op_clk))) { + ret = PTR_ERR(i2s->op_clk); + goto err; + }
clk_prepare_enable(i2s->op_clk); i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); @@ -555,12 +561,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - return -EAGAIN; + ret = -EAGAIN; + goto err; } else { /* Call can't be on the active DAI */ i2s->op_clk = other->op_clk; i2s->rclk_srcrate = other->rclk_srcrate; - return 0; + goto done; }
if (clk_id == 1) @@ -568,7 +575,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); - return -EINVAL; + ret = -EINVAL; + goto err; }
spin_lock_irqsave(i2s->lock, flags); @@ -576,8 +584,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); +done: + pm_runtime_put(dai->dev);
return 0; +err: + pm_runtime_put(dai->dev); + return ret; }
static int i2s_set_fmt(struct snd_soc_dai *dai, @@ -646,6 +659,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; }
+ pm_runtime_get_sync(dai->dev); spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); /* @@ -655,6 +669,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, if (any_active(i2s) && ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { spin_unlock_irqrestore(i2s->lock, flags); + pm_runtime_put(dai->dev); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; @@ -664,6 +679,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, mod |= tmp; writel(mod, i2s->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); + pm_runtime_put(dai->dev);
return 0; } @@ -675,6 +691,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, u32 mod, mask = 0, val = 0; unsigned long flags;
+ WARN_ON(!pm_runtime_active(dai->dev)); + if (!is_secondary(i2s)) mask |= (MOD_DC2_EN | MOD_DC1_EN);
@@ -763,6 +781,8 @@ static int i2s_startup(struct snd_pcm_substream *substream, struct i2s_dai *other = get_other_dai(i2s); unsigned long flags;
+ pm_runtime_get_sync(dai->dev); + spin_lock_irqsave(&lock, flags);
i2s->mode |= DAI_OPENED; @@ -800,6 +820,8 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, i2s->bfs = 0;
spin_unlock_irqrestore(&lock, flags); + + pm_runtime_put(dai->dev); }
static int config_setup(struct i2s_dai *i2s) @@ -874,6 +896,7 @@ 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: + pm_runtime_get_sync(dai->dev); spin_lock_irqsave(i2s->lock, flags);
if (config_setup(i2s)) { @@ -902,6 +925,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream, }
spin_unlock_irqrestore(i2s->lock, flags); + pm_runtime_put(dai->dev); break; }
@@ -916,13 +940,16 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
switch (div_id) { case SAMSUNG_I2S_DIV_BCLK: + pm_runtime_get_sync(dai->dev); if ((any_active(i2s) && div && (get_bfs(i2s) != div)) || (other && other->bfs && (other->bfs != div))) { + pm_runtime_put(dai->dev); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } i2s->bfs = div; + pm_runtime_put(dai->dev); break; default: dev_err(&i2s->pdev->dev, @@ -941,6 +968,8 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai, 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)) @@ -984,6 +1013,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *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); @@ -1016,6 +1047,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (!is_opened(other)) i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 0, SND_SOC_CLOCK_IN); + pm_runtime_put(dai->dev);
return 0; } @@ -1025,6 +1057,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) struct i2s_dai *i2s = snd_soc_dai_get_drvdata(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); @@ -1033,6 +1067,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) } }
+ pm_runtime_put(dai->dev); + return 0; }
@@ -1328,7 +1364,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, pri_dai);
- + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev);
ret = i2s_register_clock_provider(pdev); @@ -1351,10 +1387,12 @@ static int samsung_i2s_remove(struct platform_device *pdev) pri_dai->sec_dai = NULL; sec_dai->pri_dai = NULL;
+ pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev);
i2s_unregister_clock_provider(pdev); clk_disable_unprepare(pri_dai->clk); + pm_runtime_put_noidle(&pdev->dev);
return 0; }
The patch
ASoC: samsung: i2s: Ensure proper runtime PM state of I2S device
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From dc938ddb56283a0b71d987e7ecd4be90390985d6 Mon Sep 17 00:00:00 2001
From: Marek Szyprowski m.szyprowski@samsung.com Date: Thu, 29 Dec 2016 12:34:04 +0100 Subject: [PATCH] ASoC: samsung: i2s: Ensure proper runtime PM state of I2S device
This patch adds calls to pm_runtime_get/put to ensure that any access to I2S registers is done with proper (active) runtime PM state of I2S device. Till now the driver enabled runtime PM, but didn't manage the state during driver operation. The driver worked fine only because the runtime PM callbacks managed device clock, which was enabled all the time because of the additional enable call in the driver's probe function.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/samsung/i2s.c | 54 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 8 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 10b19a4afe86..8d8965e7107c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -477,6 +477,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; u32 mod, mask, val = 0; unsigned long flags; + int ret = 0; + + pm_runtime_get_sync(dai->dev);
spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); @@ -501,7 +504,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, && (mod & cdcon_mask))))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - return -EAGAIN; + ret = -EAGAIN; + goto err; }
if (dir == SND_SOC_CLOCK_IN) @@ -529,7 +533,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, } else { i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); - return 0; + goto done; } }
@@ -540,8 +544,10 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, i2s->op_clk = clk_get(&i2s->pdev->dev, "i2s_opclk0");
- if (WARN_ON(IS_ERR(i2s->op_clk))) - return PTR_ERR(i2s->op_clk); + if (WARN_ON(IS_ERR(i2s->op_clk))) { + ret = PTR_ERR(i2s->op_clk); + goto err; + }
clk_prepare_enable(i2s->op_clk); i2s->rclk_srcrate = clk_get_rate(i2s->op_clk); @@ -555,12 +561,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, || (clk_id && !(mod & rsrc_mask))) { dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); - return -EAGAIN; + ret = -EAGAIN; + goto err; } else { /* Call can't be on the active DAI */ i2s->op_clk = other->op_clk; i2s->rclk_srcrate = other->rclk_srcrate; - return 0; + goto done; }
if (clk_id == 1) @@ -568,7 +575,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, break; default: dev_err(&i2s->pdev->dev, "We don't serve that!\n"); - return -EINVAL; + ret = -EINVAL; + goto err; }
spin_lock_irqsave(i2s->lock, flags); @@ -576,8 +584,13 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, mod = (mod & ~mask) | val; writel(mod, i2s->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); +done: + pm_runtime_put(dai->dev);
return 0; +err: + pm_runtime_put(dai->dev); + return ret; }
static int i2s_set_fmt(struct snd_soc_dai *dai, @@ -646,6 +659,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; }
+ pm_runtime_get_sync(dai->dev); spin_lock_irqsave(i2s->lock, flags); mod = readl(i2s->addr + I2SMOD); /* @@ -655,6 +669,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, if (any_active(i2s) && ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { spin_unlock_irqrestore(i2s->lock, flags); + pm_runtime_put(dai->dev); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; @@ -664,6 +679,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, mod |= tmp; writel(mod, i2s->addr + I2SMOD); spin_unlock_irqrestore(i2s->lock, flags); + pm_runtime_put(dai->dev);
return 0; } @@ -675,6 +691,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, u32 mod, mask = 0, val = 0; unsigned long flags;
+ WARN_ON(!pm_runtime_active(dai->dev)); + if (!is_secondary(i2s)) mask |= (MOD_DC2_EN | MOD_DC1_EN);
@@ -763,6 +781,8 @@ static int i2s_startup(struct snd_pcm_substream *substream, struct i2s_dai *other = get_other_dai(i2s); unsigned long flags;
+ pm_runtime_get_sync(dai->dev); + spin_lock_irqsave(&lock, flags);
i2s->mode |= DAI_OPENED; @@ -800,6 +820,8 @@ static void i2s_shutdown(struct snd_pcm_substream *substream, i2s->bfs = 0;
spin_unlock_irqrestore(&lock, flags); + + pm_runtime_put(dai->dev); }
static int config_setup(struct i2s_dai *i2s) @@ -874,6 +896,7 @@ 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: + pm_runtime_get_sync(dai->dev); spin_lock_irqsave(i2s->lock, flags);
if (config_setup(i2s)) { @@ -902,6 +925,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream, }
spin_unlock_irqrestore(i2s->lock, flags); + pm_runtime_put(dai->dev); break; }
@@ -916,13 +940,16 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
switch (div_id) { case SAMSUNG_I2S_DIV_BCLK: + pm_runtime_get_sync(dai->dev); if ((any_active(i2s) && div && (get_bfs(i2s) != div)) || (other && other->bfs && (other->bfs != div))) { + pm_runtime_put(dai->dev); dev_err(&i2s->pdev->dev, "%s:%d Other DAI busy\n", __func__, __LINE__); return -EAGAIN; } i2s->bfs = div; + pm_runtime_put(dai->dev); break; default: dev_err(&i2s->pdev->dev, @@ -941,6 +968,8 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 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)) @@ -984,6 +1013,8 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *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); @@ -1016,6 +1047,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) if (!is_opened(other)) i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 0, SND_SOC_CLOCK_IN); + pm_runtime_put(dai->dev);
return 0; } @@ -1025,6 +1057,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) struct i2s_dai *i2s = snd_soc_dai_get_drvdata(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); @@ -1033,6 +1067,8 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) } }
+ pm_runtime_put(dai->dev); + return 0; }
@@ -1322,7 +1358,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, pri_dai);
- + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev);
ret = i2s_register_clock_provider(pdev); @@ -1345,10 +1381,12 @@ static int samsung_i2s_remove(struct platform_device *pdev) pri_dai->sec_dai = NULL; sec_dai->pri_dai = NULL;
+ pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev);
i2s_unregister_clock_provider(pdev); clk_disable_unprepare(pri_dai->clk); + pm_runtime_put_noidle(&pdev->dev);
return 0; }
This patch moves saving and restoring I2S registers to runtime PM operations, what prepares the driver to operate with audio power domain. When support for audio power domain is enabled and the domain is being turned off, the I2S module will loose its context (registers), so runtime callbacks have to handle it. System sleep suspend/resume operation are implemented on top of runtime PM operations with generic pm_runtime_force_suspend/resume helpers.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- sound/soc/samsung/i2s.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 2aa800b99938..47a5650aaa66 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -983,24 +983,12 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai, #ifdef CONFIG_PM static int i2s_suspend(struct snd_soc_dai *dai) { - struct i2s_dai *i2s = to_info(dai); - - i2s->suspend_i2smod = readl(i2s->addr + I2SMOD); - i2s->suspend_i2scon = readl(i2s->addr + I2SCON); - i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR); - - return 0; + return pm_runtime_force_suspend(dai->dev); }
static int i2s_resume(struct snd_soc_dai *dai) { - struct i2s_dai *i2s = to_info(dai); - - writel(i2s->suspend_i2scon, i2s->addr + I2SCON); - writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); - writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); - - return 0; + return pm_runtime_force_resume(dai->dev); } #else #define i2s_suspend NULL @@ -1129,6 +1117,10 @@ static int i2s_runtime_suspend(struct device *dev) { struct i2s_dai *i2s = 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); + clk_disable_unprepare(i2s->clk);
return 0; @@ -1140,6 +1132,10 @@ static int i2s_runtime_resume(struct device *dev)
clk_prepare_enable(i2s->clk);
+ writel(i2s->suspend_i2scon, i2s->addr + I2SCON); + writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); + writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); + return 0; } #endif /* CONFIG_PM */ @@ -1516,6 +1512,8 @@ static int samsung_i2s_remove(struct platform_device *pdev) static const struct dev_pm_ops samsung_i2s_pm = { SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) };
static struct platform_driver samsung_i2s_driver = {
The patch
ASoC: samsung: i2s: Move saving and restoring regs to runtime pm operations
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From e7e52dfc68a2160570c7ec51415e391961160edb Mon Sep 17 00:00:00 2001
From: Marek Szyprowski m.szyprowski@samsung.com Date: Thu, 29 Dec 2016 12:34:05 +0100 Subject: [PATCH] ASoC: samsung: i2s: Move saving and restoring regs to runtime pm operations
This patch moves saving and restoring I2S registers to runtime PM operations, what prepares the driver to operate with audio power domain. When support for audio power domain is enabled and the domain is being turned off, the I2S module will loose its context (registers), so runtime callbacks have to handle it. System sleep suspend/resume operation are implemented on top of runtime PM operations with generic pm_runtime_force_suspend/resume helpers.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/samsung/i2s.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 8d8965e7107c..df3fae862665 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -983,24 +983,12 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) #ifdef CONFIG_PM static int i2s_suspend(struct snd_soc_dai *dai) { - struct i2s_dai *i2s = to_info(dai); - - i2s->suspend_i2smod = readl(i2s->addr + I2SMOD); - i2s->suspend_i2scon = readl(i2s->addr + I2SCON); - i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR); - - return 0; + return pm_runtime_force_suspend(dai->dev); }
static int i2s_resume(struct snd_soc_dai *dai) { - struct i2s_dai *i2s = to_info(dai); - - writel(i2s->suspend_i2scon, i2s->addr + I2SCON); - writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); - writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); - - return 0; + return pm_runtime_force_resume(dai->dev); } #else #define i2s_suspend NULL @@ -1129,6 +1117,10 @@ static int i2s_runtime_suspend(struct device *dev) { struct i2s_dai *i2s = 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); + clk_disable_unprepare(i2s->clk);
return 0; @@ -1140,6 +1132,10 @@ static int i2s_runtime_resume(struct device *dev)
clk_prepare_enable(i2s->clk);
+ writel(i2s->suspend_i2scon, i2s->addr + I2SCON); + writel(i2s->suspend_i2smod, i2s->addr + I2SMOD); + writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR); + return 0; } #endif /* CONFIG_PM */ @@ -1510,6 +1506,8 @@ MODULE_DEVICE_TABLE(of, exynos_i2s_match); static const struct dev_pm_ops samsung_i2s_pm = { SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) };
static struct platform_driver samsung_i2s_driver = {
This patch adds handling of parent operational clock to runtime PM callbacks. This way it is ensured that when I2S module is in runtime suspended state, all its parent clocks are disabled and unprepared.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- sound/soc/samsung/i2s.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 47a5650aaa66..07728b93f56e 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -546,6 +546,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
if (WARN_ON(IS_ERR(i2s->op_clk))) { ret = PTR_ERR(i2s->op_clk); + i2s->op_clk = NULL; goto err; }
@@ -1121,6 +1122,8 @@ static int i2s_runtime_suspend(struct device *dev) i2s->suspend_i2scon = readl(i2s->addr + I2SCON); i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
+ if (i2s->op_clk) + clk_disable_unprepare(i2s->op_clk); clk_disable_unprepare(i2s->clk);
return 0; @@ -1131,6 +1134,8 @@ static int i2s_runtime_resume(struct device *dev) struct i2s_dai *i2s = dev_get_drvdata(dev);
clk_prepare_enable(i2s->clk); + if (i2s->op_clk) + clk_prepare_enable(i2s->op_clk);
writel(i2s->suspend_i2scon, i2s->addr + I2SCON); writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
The patch
ASoC: samsung: i2s: Let runtime PM operations to control op_clk too
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From afa99da863e8e00efd8ce2f8840ed31d50abb889 Mon Sep 17 00:00:00 2001
From: Marek Szyprowski m.szyprowski@samsung.com Date: Thu, 29 Dec 2016 12:34:06 +0100 Subject: [PATCH] ASoC: samsung: i2s: Let runtime PM operations to control op_clk too
This patch adds handling of parent operational clock to runtime PM callbacks. This way it is ensured that when I2S module is in runtime suspended state, all its parent clocks are disabled and unprepared.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/samsung/i2s.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index df3fae862665..b2b9ee4a177a 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -546,6 +546,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
if (WARN_ON(IS_ERR(i2s->op_clk))) { ret = PTR_ERR(i2s->op_clk); + i2s->op_clk = NULL; goto err; }
@@ -1121,6 +1122,8 @@ static int i2s_runtime_suspend(struct device *dev) i2s->suspend_i2scon = readl(i2s->addr + I2SCON); i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
+ if (i2s->op_clk) + clk_disable_unprepare(i2s->op_clk); clk_disable_unprepare(i2s->clk);
return 0; @@ -1131,6 +1134,8 @@ static int i2s_runtime_resume(struct device *dev) struct i2s_dai *i2s = dev_get_drvdata(dev);
clk_prepare_enable(i2s->clk); + if (i2s->op_clk) + clk_prepare_enable(i2s->op_clk);
writel(i2s->suspend_i2scon, i2s->addr + I2SCON); writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
This patch adds pointer to I2S device to clk_register_* functions. This in the future allow clock framework to ensure proper runtime state of the I2S device during all operations on the clocks provided by I2S module.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com --- sound/soc/samsung/i2s.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 07728b93f56e..d24b825dc298 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1191,13 +1191,13 @@ static int i2s_register_clock_provider(struct platform_device *pdev) 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->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, "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->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, "i2s_presc", "i2s_rclksrc", CLK_SET_RATE_PARENT, i2s->addr + I2SPSR, 8, 6, 0, i2s->lock); @@ -1208,7 +1208,7 @@ static int i2s_register_clock_provider(struct platform_device *pdev) 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], + i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, clk_name[0], p_names[0], CLK_SET_RATE_PARENT, i2s->addr + I2SMOD, reg_info->cdclkcon_off, CLK_GATE_SET_TO_DISABLE, i2s->lock);
The patch
ASoC: samsung: i2s: Provide I2S device for registered clocks
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From 9b41da80e09128574f09bed8dc5a5fc6f72a8239 Mon Sep 17 00:00:00 2001
From: Marek Szyprowski m.szyprowski@samsung.com Date: Thu, 29 Dec 2016 12:34:07 +0100 Subject: [PATCH] ASoC: samsung: i2s: Provide I2S device for registered clocks
This patch adds pointer to I2S device to clk_register_* functions. This in the future allow clock framework to ensure proper runtime state of the I2S device during all operations on the clocks provided by I2S module.
Signed-off-by: Marek Szyprowski m.szyprowski@samsung.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/samsung/i2s.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index b2b9ee4a177a..2a5b92c672fb 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -1191,13 +1191,13 @@ static int i2s_register_clock_provider(struct platform_device *pdev) 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->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(dev, "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->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(dev, "i2s_presc", "i2s_rclksrc", CLK_SET_RATE_PARENT, i2s->addr + I2SPSR, 8, 6, 0, i2s->lock); @@ -1208,7 +1208,7 @@ static int i2s_register_clock_provider(struct platform_device *pdev) 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], + i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev, clk_name[0], p_names[0], CLK_SET_RATE_PARENT, i2s->addr + I2SMOD, reg_info->cdclkcon_off, CLK_GATE_SET_TO_DISABLE, i2s->lock);
participants (2)
-
Marek Szyprowski
-
Mark Brown