aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Warren <swarren@nvidia.com>2012-04-09 11:52:22 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-04-09 12:42:48 -0400
commit82ef0ae46b8614f052cc3ee856c5624eff614063 (patch)
tree6501a803485bd0be0926717373c0555bfbee4b23
parent1eecb8280b038019f2f914abc01b28caf5d0a168 (diff)
ASoC: tegra: add runtime PM support
To the Tegra I2S and SPDIF drivers Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--sound/soc/tegra/tegra20_i2s.c54
-rw-r--r--sound/soc/tegra/tegra20_spdif.c50
2 files changed, 94 insertions, 10 deletions
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index 9427f36e6a2..b598ebdefde 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -35,6 +35,7 @@
35#include <linux/module.h> 35#include <linux/module.h>
36#include <linux/of.h> 36#include <linux/of.h>
37#include <linux/platform_device.h> 37#include <linux/platform_device.h>
38#include <linux/pm_runtime.h>
38#include <linux/seq_file.h> 39#include <linux/seq_file.h>
39#include <linux/slab.h> 40#include <linux/slab.h>
40#include <sound/core.h> 41#include <sound/core.h>
@@ -56,6 +57,29 @@ static inline u32 tegra20_i2s_read(struct tegra20_i2s *i2s, u32 reg)
56 return __raw_readl(i2s->regs + reg); 57 return __raw_readl(i2s->regs + reg);
57} 58}
58 59
60static int tegra20_i2s_runtime_suspend(struct device *dev)
61{
62 struct tegra20_i2s *i2s = dev_get_drvdata(dev);
63
64 clk_disable(i2s->clk_i2s);
65
66 return 0;
67}
68
69static int tegra20_i2s_runtime_resume(struct device *dev)
70{
71 struct tegra20_i2s *i2s = dev_get_drvdata(dev);
72 int ret;
73
74 ret = clk_enable(i2s->clk_i2s);
75 if (ret) {
76 dev_err(dev, "clk_enable failed: %d\n", ret);
77 return ret;
78 }
79
80 return 0;
81}
82
59#ifdef CONFIG_DEBUG_FS 83#ifdef CONFIG_DEBUG_FS
60static int tegra20_i2s_show(struct seq_file *s, void *unused) 84static int tegra20_i2s_show(struct seq_file *s, void *unused)
61{ 85{
@@ -219,16 +243,12 @@ static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream,
219 if (i2sclock % (2 * srate)) 243 if (i2sclock % (2 * srate))
220 reg |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE; 244 reg |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE;
221 245
222 clk_enable(i2s->clk_i2s);
223
224 tegra20_i2s_write(i2s, TEGRA20_I2S_TIMING, reg); 246 tegra20_i2s_write(i2s, TEGRA20_I2S_TIMING, reg);
225 247
226 tegra20_i2s_write(i2s, TEGRA20_I2S_FIFO_SCR, 248 tegra20_i2s_write(i2s, TEGRA20_I2S_FIFO_SCR,
227 TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | 249 TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
228 TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); 250 TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
229 251
230 clk_disable(i2s->clk_i2s);
231
232 return 0; 252 return 0;
233} 253}
234 254
@@ -265,7 +285,6 @@ static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
265 case SNDRV_PCM_TRIGGER_START: 285 case SNDRV_PCM_TRIGGER_START:
266 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 286 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
267 case SNDRV_PCM_TRIGGER_RESUME: 287 case SNDRV_PCM_TRIGGER_RESUME:
268 clk_enable(i2s->clk_i2s);
269 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 288 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
270 tegra20_i2s_start_playback(i2s); 289 tegra20_i2s_start_playback(i2s);
271 else 290 else
@@ -278,7 +297,6 @@ static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
278 tegra20_i2s_stop_playback(i2s); 297 tegra20_i2s_stop_playback(i2s);
279 else 298 else
280 tegra20_i2s_stop_capture(i2s); 299 tegra20_i2s_stop_capture(i2s);
281 clk_disable(i2s->clk_i2s);
282 break; 300 break;
283 default: 301 default:
284 return -EINVAL; 302 return -EINVAL;
@@ -395,11 +413,18 @@ static __devinit int tegra20_i2s_platform_probe(struct platform_device *pdev)
395 413
396 i2s->reg_ctrl = TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED; 414 i2s->reg_ctrl = TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED;
397 415
416 pm_runtime_enable(&pdev->dev);
417 if (!pm_runtime_enabled(&pdev->dev)) {
418 ret = tegra20_i2s_runtime_resume(&pdev->dev);
419 if (ret)
420 goto err_pm_disable;
421 }
422
398 ret = snd_soc_register_dai(&pdev->dev, &i2s->dai); 423 ret = snd_soc_register_dai(&pdev->dev, &i2s->dai);
399 if (ret) { 424 if (ret) {
400 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 425 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
401 ret = -ENOMEM; 426 ret = -ENOMEM;
402 goto err_clk_put; 427 goto err_suspend;
403 } 428 }
404 429
405 ret = tegra_pcm_platform_register(&pdev->dev); 430 ret = tegra_pcm_platform_register(&pdev->dev);
@@ -414,6 +439,11 @@ static __devinit int tegra20_i2s_platform_probe(struct platform_device *pdev)
414 439
415err_unregister_dai: 440err_unregister_dai:
416 snd_soc_unregister_dai(&pdev->dev); 441 snd_soc_unregister_dai(&pdev->dev);
442err_suspend:
443 if (!pm_runtime_status_suspended(&pdev->dev))
444 tegra20_i2s_runtime_suspend(&pdev->dev);
445err_pm_disable:
446 pm_runtime_disable(&pdev->dev);
417err_clk_put: 447err_clk_put:
418 clk_put(i2s->clk_i2s); 448 clk_put(i2s->clk_i2s);
419err: 449err:
@@ -424,6 +454,10 @@ static int __devexit tegra20_i2s_platform_remove(struct platform_device *pdev)
424{ 454{
425 struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev); 455 struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev);
426 456
457 pm_runtime_disable(&pdev->dev);
458 if (!pm_runtime_status_suspended(&pdev->dev))
459 tegra20_i2s_runtime_suspend(&pdev->dev);
460
427 tegra_pcm_platform_unregister(&pdev->dev); 461 tegra_pcm_platform_unregister(&pdev->dev);
428 snd_soc_unregister_dai(&pdev->dev); 462 snd_soc_unregister_dai(&pdev->dev);
429 463
@@ -439,11 +473,17 @@ static const struct of_device_id tegra20_i2s_of_match[] __devinitconst = {
439 {}, 473 {},
440}; 474};
441 475
476static const struct dev_pm_ops tegra20_i2s_pm_ops __devinitconst = {
477 SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend,
478 tegra20_i2s_runtime_resume, NULL)
479};
480
442static struct platform_driver tegra20_i2s_driver = { 481static struct platform_driver tegra20_i2s_driver = {
443 .driver = { 482 .driver = {
444 .name = DRV_NAME, 483 .name = DRV_NAME,
445 .owner = THIS_MODULE, 484 .owner = THIS_MODULE,
446 .of_match_table = tegra20_i2s_of_match, 485 .of_match_table = tegra20_i2s_of_match,
486 .pm = &tegra20_i2s_pm_ops,
447 }, 487 },
448 .probe = tegra20_i2s_platform_probe, 488 .probe = tegra20_i2s_platform_probe,
449 .remove = __devexit_p(tegra20_i2s_platform_remove), 489 .remove = __devexit_p(tegra20_i2s_platform_remove),
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index ef5d49ed565..9efd71e13a0 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -26,6 +26,7 @@
26#include <linux/io.h> 26#include <linux/io.h>
27#include <linux/module.h> 27#include <linux/module.h>
28#include <linux/platform_device.h> 28#include <linux/platform_device.h>
29#include <linux/pm_runtime.h>
29#include <linux/seq_file.h> 30#include <linux/seq_file.h>
30#include <linux/slab.h> 31#include <linux/slab.h>
31#include <sound/core.h> 32#include <sound/core.h>
@@ -48,6 +49,29 @@ static inline u32 tegra20_spdif_read(struct tegra20_spdif *spdif, u32 reg)
48 return __raw_readl(spdif->regs + reg); 49 return __raw_readl(spdif->regs + reg);
49} 50}
50 51
52static int tegra20_spdif_runtime_suspend(struct device *dev)
53{
54 struct tegra20_spdif *spdif = dev_get_drvdata(dev);
55
56 clk_disable(spdif->clk_spdif_out);
57
58 return 0;
59}
60
61static int tegra20_spdif_runtime_resume(struct device *dev)
62{
63 struct tegra20_spdif *spdif = dev_get_drvdata(dev);
64 int ret;
65
66 ret = clk_enable(spdif->clk_spdif_out);
67 if (ret) {
68 dev_err(dev, "clk_enable failed: %d\n", ret);
69 return ret;
70 }
71
72 return 0;
73}
74
51#ifdef CONFIG_DEBUG_FS 75#ifdef CONFIG_DEBUG_FS
52static int tegra20_spdif_show(struct seq_file *s, void *unused) 76static int tegra20_spdif_show(struct seq_file *s, void *unused)
53{ 77{
@@ -195,14 +219,12 @@ static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
195 case SNDRV_PCM_TRIGGER_START: 219 case SNDRV_PCM_TRIGGER_START:
196 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 220 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
197 case SNDRV_PCM_TRIGGER_RESUME: 221 case SNDRV_PCM_TRIGGER_RESUME:
198 clk_enable(spdif->clk_spdif_out);
199 tegra20_spdif_start_playback(spdif); 222 tegra20_spdif_start_playback(spdif);
200 break; 223 break;
201 case SNDRV_PCM_TRIGGER_STOP: 224 case SNDRV_PCM_TRIGGER_STOP:
202 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 225 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
203 case SNDRV_PCM_TRIGGER_SUSPEND: 226 case SNDRV_PCM_TRIGGER_SUSPEND:
204 tegra20_spdif_stop_playback(spdif); 227 tegra20_spdif_stop_playback(spdif);
205 clk_disable(spdif->clk_spdif_out);
206 break; 228 break;
207 default: 229 default:
208 return -EINVAL; 230 return -EINVAL;
@@ -295,11 +317,18 @@ static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev)
295 spdif->playback_dma_data.width = 32; 317 spdif->playback_dma_data.width = 32;
296 spdif->playback_dma_data.req_sel = dmareq->start; 318 spdif->playback_dma_data.req_sel = dmareq->start;
297 319
320 pm_runtime_enable(&pdev->dev);
321 if (!pm_runtime_enabled(&pdev->dev)) {
322 ret = tegra20_spdif_runtime_resume(&pdev->dev);
323 if (ret)
324 goto err_pm_disable;
325 }
326
298 ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai); 327 ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai);
299 if (ret) { 328 if (ret) {
300 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 329 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
301 ret = -ENOMEM; 330 ret = -ENOMEM;
302 goto err_clk_put; 331 goto err_suspend;
303 } 332 }
304 333
305 ret = tegra_pcm_platform_register(&pdev->dev); 334 ret = tegra_pcm_platform_register(&pdev->dev);
@@ -314,6 +343,11 @@ static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev)
314 343
315err_unregister_dai: 344err_unregister_dai:
316 snd_soc_unregister_dai(&pdev->dev); 345 snd_soc_unregister_dai(&pdev->dev);
346err_suspend:
347 if (!pm_runtime_status_suspended(&pdev->dev))
348 tegra20_spdif_runtime_suspend(&pdev->dev);
349err_pm_disable:
350 pm_runtime_disable(&pdev->dev);
317err_clk_put: 351err_clk_put:
318 clk_put(spdif->clk_spdif_out); 352 clk_put(spdif->clk_spdif_out);
319err: 353err:
@@ -324,6 +358,10 @@ static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev)
324{ 358{
325 struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev); 359 struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev);
326 360
361 pm_runtime_disable(&pdev->dev);
362 if (!pm_runtime_status_suspended(&pdev->dev))
363 tegra20_spdif_runtime_suspend(&pdev->dev);
364
327 tegra_pcm_platform_unregister(&pdev->dev); 365 tegra_pcm_platform_unregister(&pdev->dev);
328 snd_soc_unregister_dai(&pdev->dev); 366 snd_soc_unregister_dai(&pdev->dev);
329 367
@@ -334,10 +372,16 @@ static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev)
334 return 0; 372 return 0;
335} 373}
336 374
375static const struct dev_pm_ops tegra20_spdif_pm_ops __devinitconst = {
376 SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend,
377 tegra20_spdif_runtime_resume, NULL)
378};
379
337static struct platform_driver tegra20_spdif_driver = { 380static struct platform_driver tegra20_spdif_driver = {
338 .driver = { 381 .driver = {
339 .name = DRV_NAME, 382 .name = DRV_NAME,
340 .owner = THIS_MODULE, 383 .owner = THIS_MODULE,
384 .pm = &tegra20_spdif_pm_ops,
341 }, 385 },
342 .probe = tegra20_spdif_platform_probe, 386 .probe = tegra20_spdif_platform_probe,
343 .remove = __devexit_p(tegra20_spdif_platform_remove), 387 .remove = __devexit_p(tegra20_spdif_platform_remove),