|
9 | 9 | #include <linux/of_address.h> |
10 | 10 | #include <linux/platform_data/simplefb.h> |
11 | 11 | #include <linux/platform_device.h> |
| 12 | +#include <linux/pm_domain.h> |
12 | 13 | #include <linux/regulator/consumer.h> |
13 | 14 |
|
14 | 15 | #include <drm/drm_aperture.h> |
@@ -230,6 +231,12 @@ struct simpledrm_device { |
230 | 231 | unsigned int regulator_count; |
231 | 232 | struct regulator **regulators; |
232 | 233 | #endif |
| 234 | + /* power-domains */ |
| 235 | +#if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS |
| 236 | + int pwr_dom_count; |
| 237 | + struct device **pwr_dom_devs; |
| 238 | + struct device_link **pwr_dom_links; |
| 239 | +#endif |
233 | 240 |
|
234 | 241 | /* simplefb settings */ |
235 | 242 | struct drm_display_mode mode; |
@@ -475,6 +482,101 @@ static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) |
475 | 482 | } |
476 | 483 | #endif |
477 | 484 |
|
| 485 | +#if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS |
| 486 | +/* |
| 487 | + * Generic power domain handling code. |
| 488 | + * |
| 489 | + * Here we handle the power-domains properties of our "simple-framebuffer" |
| 490 | + * dt node. This is only necessary if there is more than one power-domain. |
| 491 | + * A single power-domains is handled automatically by the driver core. Multiple |
| 492 | + * power-domains have to be handled by drivers since the driver core can't know |
| 493 | + * the correct power sequencing. Power sequencing is not an issue for simpledrm |
| 494 | + * since the bootloader has put the power domains already in the correct state. |
| 495 | + * simpledrm has only to ensure they remain active for its lifetime. |
| 496 | + * |
| 497 | + * When the driver unloads, we detach from the power-domains. |
| 498 | + * |
| 499 | + * We only complain about errors here, no action is taken as the most likely |
| 500 | + * error can only happen due to a mismatch between the bootloader which set |
| 501 | + * up the "simple-framebuffer" dt node, and the PM domain providers in the |
| 502 | + * device tree. Chances are that there are no adverse effects, and if there are, |
| 503 | + * a clean teardown of the fb probe will not help us much either. So just |
| 504 | + * complain and carry on, and hope that the user actually gets a working fb at |
| 505 | + * the end of things. |
| 506 | + */ |
| 507 | +static void simpledrm_device_detach_genpd(void *res) |
| 508 | +{ |
| 509 | + int i; |
| 510 | + struct simpledrm_device *sdev = res; |
| 511 | + |
| 512 | + if (sdev->pwr_dom_count <= 1) |
| 513 | + return; |
| 514 | + |
| 515 | + for (i = sdev->pwr_dom_count - 1; i >= 0; i--) { |
| 516 | + if (!sdev->pwr_dom_links[i]) |
| 517 | + device_link_del(sdev->pwr_dom_links[i]); |
| 518 | + if (!IS_ERR_OR_NULL(sdev->pwr_dom_devs[i])) |
| 519 | + dev_pm_domain_detach(sdev->pwr_dom_devs[i], true); |
| 520 | + } |
| 521 | +} |
| 522 | + |
| 523 | +static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) |
| 524 | +{ |
| 525 | + struct device *dev = sdev->dev.dev; |
| 526 | + int i; |
| 527 | + |
| 528 | + sdev->pwr_dom_count = of_count_phandle_with_args(dev->of_node, "power-domains", |
| 529 | + "#power-domain-cells"); |
| 530 | + /* |
| 531 | + * Single power-domain devices are handled by driver core nothing to do |
| 532 | + * here. The same for device nodes without "power-domains" property. |
| 533 | + */ |
| 534 | + if (sdev->pwr_dom_count <= 1) |
| 535 | + return 0; |
| 536 | + |
| 537 | + sdev->pwr_dom_devs = devm_kcalloc(dev, sdev->pwr_dom_count, |
| 538 | + sizeof(*sdev->pwr_dom_devs), |
| 539 | + GFP_KERNEL); |
| 540 | + if (!sdev->pwr_dom_devs) |
| 541 | + return -ENOMEM; |
| 542 | + |
| 543 | + sdev->pwr_dom_links = devm_kcalloc(dev, sdev->pwr_dom_count, |
| 544 | + sizeof(*sdev->pwr_dom_links), |
| 545 | + GFP_KERNEL); |
| 546 | + if (!sdev->pwr_dom_links) |
| 547 | + return -ENOMEM; |
| 548 | + |
| 549 | + for (i = 0; i < sdev->pwr_dom_count; i++) { |
| 550 | + sdev->pwr_dom_devs[i] = dev_pm_domain_attach_by_id(dev, i); |
| 551 | + if (IS_ERR(sdev->pwr_dom_devs[i])) { |
| 552 | + int ret = PTR_ERR(sdev->pwr_dom_devs[i]); |
| 553 | + if (ret == -EPROBE_DEFER) { |
| 554 | + simpledrm_device_detach_genpd(sdev); |
| 555 | + return ret; |
| 556 | + } |
| 557 | + drm_warn(&sdev->dev, |
| 558 | + "pm_domain_attach_by_id(%u) failed: %d\n", i, ret); |
| 559 | + continue; |
| 560 | + } |
| 561 | + |
| 562 | + sdev->pwr_dom_links[i] = device_link_add(dev, |
| 563 | + sdev->pwr_dom_devs[i], |
| 564 | + DL_FLAG_STATELESS | |
| 565 | + DL_FLAG_PM_RUNTIME | |
| 566 | + DL_FLAG_RPM_ACTIVE); |
| 567 | + if (!sdev->pwr_dom_links[i]) |
| 568 | + drm_warn(&sdev->dev, "failed to link power-domain %d\n", i); |
| 569 | + } |
| 570 | + |
| 571 | + return devm_add_action_or_reset(dev, simpledrm_device_detach_genpd, sdev); |
| 572 | +} |
| 573 | +#else |
| 574 | +static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) |
| 575 | +{ |
| 576 | + return 0; |
| 577 | +} |
| 578 | +#endif |
| 579 | + |
478 | 580 | /* |
479 | 581 | * Modesetting |
480 | 582 | */ |
@@ -689,6 +791,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, |
689 | 791 | if (ret) |
690 | 792 | return ERR_PTR(ret); |
691 | 793 | ret = simpledrm_device_init_regulators(sdev); |
| 794 | + if (ret) |
| 795 | + return ERR_PTR(ret); |
| 796 | + ret = simpledrm_device_attach_genpd(sdev); |
692 | 797 | if (ret) |
693 | 798 | return ERR_PTR(ret); |
694 | 799 |
|
|
0 commit comments