diff options
author | Ravindra Lokhande <rlokhande@nvidia.com> | 2015-08-19 08:22:56 -0400 |
---|---|---|
committer | Sameer Pujar <spujar@nvidia.com> | 2017-08-07 05:26:21 -0400 |
commit | f967a99dc42cbd40024830913b1e84e6c9ac2682 (patch) | |
tree | 63755e165652e79f5c8e49a629396cd265fc0284 /sound/pci/hda | |
parent | 3de71a6bfe89a217a78d2e04e447b5d4208e0a87 (diff) |
ALSA: hda - Add power management support
Add pm runtime suspend/resume function to tegra hda driver.
Bug 200127452
Change-Id: I6146cbcbd2b2f71a0f17f6678f4113147ad9b480
Signed-off-by: Ravindra Lokhande <rlokhande@nvidia.com>
Reviewed-on: http://git-master/r/785946
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/hda_tegra.c | 88 |
1 files changed, 84 insertions, 4 deletions
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index e9bd8a4f6..7483705df 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/of_device.h> | 31 | #include <linux/of_device.h> |
32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/time.h> | 33 | #include <linux/time.h> |
34 | #include <linux/pm_runtime.h> | ||
34 | #include <linux/tegra-powergate.h> | 35 | #include <linux/tegra-powergate.h> |
35 | #include <linux/tegra_pm_domains.h> | 36 | #include <linux/tegra_pm_domains.h> |
36 | 37 | ||
@@ -40,6 +41,14 @@ | |||
40 | #include "hda_codec.h" | 41 | #include "hda_codec.h" |
41 | #include "hda_controller.h" | 42 | #include "hda_controller.h" |
42 | 43 | ||
44 | |||
45 | static struct of_device_id tegra_disb_pd[] = { | ||
46 | { .compatible = "nvidia,tegra210-disb-pd", }, | ||
47 | { .compatible = "nvidia,tegra132-disb-pd", }, | ||
48 | { .compatible = "nvidia,tegra124-disb-pd", }, | ||
49 | {}, | ||
50 | }; | ||
51 | |||
43 | /* Defines for Nvidia Tegra HDA support */ | 52 | /* Defines for Nvidia Tegra HDA support */ |
44 | #define HDA_BAR0 0x8000 | 53 | #define HDA_BAR0 0x8000 |
45 | 54 | ||
@@ -74,6 +83,7 @@ struct hda_tegra { | |||
74 | struct clk *hda_clk; | 83 | struct clk *hda_clk; |
75 | struct clk *hda2codec_2x_clk; | 84 | struct clk *hda2codec_2x_clk; |
76 | struct clk *hda2hdmi_clk; | 85 | struct clk *hda2hdmi_clk; |
86 | int partition_id; | ||
77 | void __iomem *regs; | 87 | void __iomem *regs; |
78 | struct work_struct probe_work; | 88 | struct work_struct probe_work; |
79 | }; | 89 | }; |
@@ -216,7 +226,7 @@ static int hda_tegra_enable_clocks(struct hda_tegra *data) | |||
216 | { | 226 | { |
217 | int rc; | 227 | int rc; |
218 | 228 | ||
219 | tegra_unpowergate_partition(TEGRA_POWERGATE_DISB); | 229 | tegra_unpowergate_partition(data->partition_id); |
220 | 230 | ||
221 | rc = clk_prepare_enable(data->hda_clk); | 231 | rc = clk_prepare_enable(data->hda_clk); |
222 | if (rc) | 232 | if (rc) |
@@ -234,7 +244,7 @@ disable_codec_2x: | |||
234 | clk_disable_unprepare(data->hda2codec_2x_clk); | 244 | clk_disable_unprepare(data->hda2codec_2x_clk); |
235 | disable_hda: | 245 | disable_hda: |
236 | clk_disable_unprepare(data->hda_clk); | 246 | clk_disable_unprepare(data->hda_clk); |
237 | tegra_powergate_partition(TEGRA_POWERGATE_DISB); | 247 | tegra_powergate_partition(data->partition_id); |
238 | return rc; | 248 | return rc; |
239 | } | 249 | } |
240 | 250 | ||
@@ -245,7 +255,7 @@ static void hda_tegra_disable_clocks(struct hda_tegra *data) | |||
245 | clk_disable_unprepare(data->hda2codec_2x_clk); | 255 | clk_disable_unprepare(data->hda2codec_2x_clk); |
246 | clk_disable_unprepare(data->hda_clk); | 256 | clk_disable_unprepare(data->hda_clk); |
247 | 257 | ||
248 | tegra_powergate_partition(TEGRA_POWERGATE_DISB); | 258 | tegra_powergate_partition(data->partition_id); |
249 | } | 259 | } |
250 | 260 | ||
251 | /* | 261 | /* |
@@ -257,6 +267,8 @@ static int hda_tegra_suspend(struct device *dev) | |||
257 | struct azx *chip = card->private_data; | 267 | struct azx *chip = card->private_data; |
258 | struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); | 268 | struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); |
259 | 269 | ||
270 | hda_tegra_enable_clocks(hda); | ||
271 | |||
260 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); | 272 | snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); |
261 | 273 | ||
262 | azx_stop_chip(chip); | 274 | azx_stop_chip(chip); |
@@ -274,18 +286,63 @@ static int hda_tegra_resume(struct device *dev) | |||
274 | 286 | ||
275 | hda_tegra_enable_clocks(hda); | 287 | hda_tegra_enable_clocks(hda); |
276 | 288 | ||
289 | if (hda->dev) { | ||
290 | pm_runtime_disable(hda->dev); | ||
291 | pm_runtime_set_active(hda->dev); | ||
292 | pm_runtime_get_noresume(hda->dev); | ||
293 | pm_runtime_enable(hda->dev); | ||
294 | } | ||
277 | hda_tegra_init(hda); | 295 | hda_tegra_init(hda); |
278 | 296 | ||
279 | azx_init_chip(chip, 1); | 297 | azx_init_chip(chip, 1); |
280 | 298 | ||
281 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); | 299 | snd_power_change_state(card, SNDRV_CTL_POWER_D0); |
282 | 300 | ||
301 | pm_runtime_put(hda->dev); | ||
302 | |||
283 | return 0; | 303 | return 0; |
284 | } | 304 | } |
285 | #endif /* CONFIG_PM_SLEEP */ | 305 | #endif /* CONFIG_PM_SLEEP */ |
286 | 306 | ||
307 | #ifdef CONFIG_PM_RUNTIME | ||
308 | static int hda_tegra_runtime_suspend(struct device *dev) | ||
309 | { | ||
310 | struct snd_card *card = dev_get_drvdata(dev); | ||
311 | struct azx *chip = card->private_data; | ||
312 | struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); | ||
313 | |||
314 | if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) | ||
315 | return 0; | ||
316 | |||
317 | azx_stop_chip(chip); | ||
318 | hda_tegra_disable_clocks(hda); | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int hda_tegra_runtime_resume(struct device *dev) | ||
324 | { | ||
325 | struct snd_card *card = dev_get_drvdata(dev); | ||
326 | struct azx *chip = card->private_data; | ||
327 | struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); | ||
328 | |||
329 | if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) | ||
330 | return 0; | ||
331 | |||
332 | hda_tegra_enable_clocks(hda); | ||
333 | |||
334 | hda_tegra_init(hda); | ||
335 | |||
336 | azx_init_chip(chip, 1); | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | #endif /* CONFIG_PM_RUNTIME */ | ||
341 | |||
287 | static const struct dev_pm_ops hda_tegra_pm = { | 342 | static const struct dev_pm_ops hda_tegra_pm = { |
288 | SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) | 343 | SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) |
344 | SET_RUNTIME_PM_OPS(hda_tegra_runtime_suspend, | ||
345 | hda_tegra_runtime_resume, NULL) | ||
289 | }; | 346 | }; |
290 | 347 | ||
291 | static int hda_tegra_dev_disconnect(struct snd_device *device) | 348 | static int hda_tegra_dev_disconnect(struct snd_device *device) |
@@ -313,6 +370,8 @@ static int hda_tegra_dev_free(struct snd_device *device) | |||
313 | azx_free_stream_pages(chip); | 370 | azx_free_stream_pages(chip); |
314 | azx_free_streams(chip); | 371 | azx_free_streams(chip); |
315 | snd_hdac_bus_exit(azx_bus(chip)); | 372 | snd_hdac_bus_exit(azx_bus(chip)); |
373 | hda_tegra_disable_clocks(hda); | ||
374 | pm_runtime_put(hda->dev); | ||
316 | 375 | ||
317 | return 0; | 376 | return 0; |
318 | } | 377 | } |
@@ -450,6 +509,14 @@ static int hda_tegra_create(struct snd_card *card, | |||
450 | struct azx *chip; | 509 | struct azx *chip; |
451 | int err; | 510 | int err; |
452 | 511 | ||
512 | if (hda->dev) { | ||
513 | err = pm_runtime_set_active(hda->dev); | ||
514 | if (err < 0) | ||
515 | return err; | ||
516 | pm_runtime_get_noresume(hda->dev); | ||
517 | pm_runtime_enable(hda->dev); | ||
518 | } | ||
519 | |||
453 | chip = &hda->chip; | 520 | chip = &hda->chip; |
454 | 521 | ||
455 | mutex_init(&chip->open_mutex); | 522 | mutex_init(&chip->open_mutex); |
@@ -488,11 +555,13 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match); | |||
488 | 555 | ||
489 | static int hda_tegra_probe(struct platform_device *pdev) | 556 | static int hda_tegra_probe(struct platform_device *pdev) |
490 | { | 557 | { |
491 | const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR; | 558 | const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR | |
559 | AZX_DCAPS_PM_RUNTIME; | ||
492 | struct snd_card *card; | 560 | struct snd_card *card; |
493 | struct azx *chip; | 561 | struct azx *chip; |
494 | struct hda_tegra *hda; | 562 | struct hda_tegra *hda; |
495 | int err; | 563 | int err; |
564 | const unsigned int driver_flags = 0; | ||
496 | 565 | ||
497 | hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); | 566 | hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL); |
498 | if (!hda) | 567 | if (!hda) |
@@ -500,6 +569,12 @@ static int hda_tegra_probe(struct platform_device *pdev) | |||
500 | hda->dev = &pdev->dev; | 569 | hda->dev = &pdev->dev; |
501 | chip = &hda->chip; | 570 | chip = &hda->chip; |
502 | 571 | ||
572 | hda->partition_id = tegra_pd_get_powergate_id(tegra_disb_pd); | ||
573 | if (hda->partition_id < 0) { | ||
574 | dev_err(&pdev->dev, "Failed to get hda power domain id\n"); | ||
575 | return -EINVAL; | ||
576 | } | ||
577 | |||
503 | err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, | 578 | err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, |
504 | THIS_MODULE, 0, &card); | 579 | THIS_MODULE, 0, &card); |
505 | if (err < 0) { | 580 | if (err < 0) { |
@@ -533,6 +608,8 @@ static void hda_tegra_probe_work(struct work_struct *work) | |||
533 | if (err < 0) | 608 | if (err < 0) |
534 | goto out_free; | 609 | goto out_free; |
535 | 610 | ||
611 | tegra_pd_add_device(hda->dev); | ||
612 | |||
536 | /* create codec instances */ | 613 | /* create codec instances */ |
537 | err = azx_probe_codecs(chip, 0); | 614 | err = azx_probe_codecs(chip, 0); |
538 | if (err < 0) | 615 | if (err < 0) |
@@ -549,12 +626,15 @@ static void hda_tegra_probe_work(struct work_struct *work) | |||
549 | chip->running = 1; | 626 | chip->running = 1; |
550 | snd_hda_set_power_save(&chip->bus, power_save * 1000); | 627 | snd_hda_set_power_save(&chip->bus, power_save * 1000); |
551 | 628 | ||
629 | pm_runtime_put(hda->dev); | ||
630 | |||
552 | out_free: | 631 | out_free: |
553 | return; /* no error return from async probe */ | 632 | return; /* no error return from async probe */ |
554 | } | 633 | } |
555 | 634 | ||
556 | static int hda_tegra_remove(struct platform_device *pdev) | 635 | static int hda_tegra_remove(struct platform_device *pdev) |
557 | { | 636 | { |
637 | pm_runtime_get_noresume(&pdev->dev); | ||
558 | return snd_card_free(dev_get_drvdata(&pdev->dev)); | 638 | return snd_card_free(dev_get_drvdata(&pdev->dev)); |
559 | } | 639 | } |
560 | 640 | ||