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