Skip to content

Commit f7be86a

Browse files
jannaumarcan
authored andcommitted
drm/simpledrm: Add support for multiple "power-domains"
Multiple power domains need to be handled explicitly in each driver. The driver core can not handle it automatically since it is not aware of power sequencing requirements the hardware might have. This is not a problem for simpledrm since everything is expected to be powered on by the bootloader. simpledrm has just ensure it remains powered on during its lifetime. This is required on Apple silicon M2 and M2 Pro/Max/Ultra desktop systems. The HDMI output initialized by the bootloader requires keeping the display controller and a DP phy power domain on. Reviewed-by: Sven Peter <[email protected]> Reviewed-by: Thomas Zimmermann <[email protected]> Reviewed-by: Javier Martinez Canillas <[email protected]> Reviewed-by: Neal Gompa <[email protected]> Reviewed-by: Eric Curtin <[email protected]> Signed-off-by: Janne Grunau <[email protected]>
1 parent d919825 commit f7be86a

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

drivers/gpu/drm/tiny/simpledrm.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <linux/of_address.h>
1010
#include <linux/platform_data/simplefb.h>
1111
#include <linux/platform_device.h>
12+
#include <linux/pm_domain.h>
1213
#include <linux/regulator/consumer.h>
1314

1415
#include <drm/drm_aperture.h>
@@ -230,6 +231,12 @@ struct simpledrm_device {
230231
unsigned int regulator_count;
231232
struct regulator **regulators;
232233
#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
233240

234241
/* simplefb settings */
235242
struct drm_display_mode mode;
@@ -475,6 +482,101 @@ static int simpledrm_device_init_regulators(struct simpledrm_device *sdev)
475482
}
476483
#endif
477484

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+
478580
/*
479581
* Modesetting
480582
*/
@@ -689,6 +791,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
689791
if (ret)
690792
return ERR_PTR(ret);
691793
ret = simpledrm_device_init_regulators(sdev);
794+
if (ret)
795+
return ERR_PTR(ret);
796+
ret = simpledrm_device_attach_genpd(sdev);
692797
if (ret)
693798
return ERR_PTR(ret);
694799

0 commit comments

Comments
 (0)